Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(") VALUES (?, ?, NULL, ?, ?)"); //$NON-NLS-1$
+ sqlInsertEntry = builder.toString();
+
+ // ----------------- remove current entry -----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_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(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteEntry = builder.toString();
+
+ // ----------------- update index -----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ 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_IDX);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlUpdateIndex = builder.toString();
+
+ // ----------------- get current value -----------------
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlGetValue = builder.toString();
+
+ // ----------- clear list items -------------------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_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(getTable());
+ 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"); //$NON-NLS-1$
+ sqlDeleteList = 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());
+ }
+
+ 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())
+ {
+ 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 {4} list values done for feature {0}.{1} of {2}v{3}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), revision.getID(), revision.getVersion(), list.size());
+ }
+ }
+
+ 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())
+ {
+ 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}", //$NON-NLS-1$
+ getContainingClass().getName(), 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);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing done"); //$NON-NLS-1$
+ }
+ }
+
+ protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int index, Object value)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER
+ .format(
+ "Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, revision.getID(), revision.getVersion(),
+ value);
+ }
+
+ addEntry(accessor, revision.getID(), revision.getVersion(), index, value);
+ }
+
+ /**
+ * 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);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Processing deltas..."); //$NON-NLS-1$
+ }
+
+ for (CDOFeatureDelta listDelta : delta.getListChanges())
+ {
+ listDelta.accept(visitor);
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
+ {
+ private IDBStoreAccessor accessor;
+
+ private CDOID id;
+
+ private int oldVersion;
+
+ private int newVersion;
+
+ private int lastIndex;
+
+ public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion,
+ int newVersion)
+ {
+ this.accessor = accessor;
+ id = originalRevision.getID();
+ this.oldVersion = oldVersion;
+ this.newVersion = newVersion;
+ lastIndex = originalRevision.getList(getFeature()).size() - 1;
+ }
+
+ 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);
+ }
+
+ 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());
+
+ ++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());
+ }
+
+ 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);
+ 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);
+ 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)
+ {
+ 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
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, version);
+ stmt.setInt(column++, index);
+ typeMapping.setValue(stmt, column++, value);
+
+ 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 Object getValue(IDBStoreAccessor accessor, CDOID id, int index)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ Object 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");
+ }
+
+ result = typeMapping.readValue(resultSet);
+ 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)
+ {
+
+ 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 sourceID = idHandler.getCDOID(resultSet, 1);
+ CDOID targetID = idHandler.getCDOID(resultSet, 2);
+ int idx = resultSet.getInt(3);
+
+ boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, 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);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java
new file mode 100644
index 0000000000..6e2e1b60e0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.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:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+/**
+ * @author Stefan Winkler
+ */
+public abstract class BasicAbstractListTableMapping implements IListMapping
+{
+ private IMappingStrategy mappingStrategy;
+
+ private EClass containingClass;
+
+ private EStructuralFeature feature;
+
+ public BasicAbstractListTableMapping(IMappingStrategy mappingStrategy, EClass containingClass,
+ EStructuralFeature feature)
+ {
+ this.mappingStrategy = mappingStrategy;
+ this.containingClass = containingClass;
+ this.feature = feature;
+ }
+
+ public final IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public final EClass getContainingClass()
+ {
+ return containingClass;
+ }
+
+ public final EStructuralFeature getFeature()
+ {
+ return feature;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java
new file mode 100644
index 0000000000..056ccc3e8a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java
@@ -0,0 +1,72 @@
+/**
+ * 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
+ * Stefan Winkler - derived branch mapping from audit mapping
+ */
+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
+ * @author Stefan Winkler
+ * @since 3.0
+ */
+public class BranchingFeatureMapTableMapping extends AbstractFeatureMapTableMapping
+{
+ private FieldInfo[] keyFields;
+
+ public BranchingFeatureMapTableMapping(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_BRANCH, DBType.INTEGER),
+ 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.getBranch().getID());
+ stmt.setInt(3, 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/BranchingFeatureMapTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java
new file mode 100644
index 0000000000..b1458ae679
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java
@@ -0,0 +1,1502 @@
+/**
+ * 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 taken from AuditFeatureMapTableMappingWithRanges
+ * 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.CDOBranchPoint;
+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.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader;
+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 BranchingFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements
+ IListMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG,
+ BranchingFeatureMapTableMappingWithRanges.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;
+
+ public BranchingFeatureMapTableMappingWithRanges(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());
+
+ IDBField branchField = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER);
+
+ // 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);
+ }
+
+ table.addIndex(Type.NON_UNIQUE, idField);
+ table.addIndex(Type.NON_UNIQUE, branchField);
+ table.addIndex(Type.NON_UNIQUE, versionAddedField);
+ table.addIndex(Type.NON_UNIQUE, versionRemovedField);
+ table.addIndex(Type.NON_UNIQUE, idxField);
+ table.addIndex(Type.NON_UNIQUE, tagField);
+ }
+
+ public Collection<IDBTable> getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ 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_IDX);
+ builder.append(", "); //$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_BRANCH);
+ 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_BRANCH);
+ 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_BRANCH);
+ 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_BRANCH);
+ 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_BRANCH);
+ 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_BRANCH);
+ 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_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlClearList = 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());
+ int valuesToRead = list.size();
+
+ if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead)
+ {
+ valuesToRead = listChunk;
+ }
+
+ if (valuesToRead == 0)
+ {
+ // nothing to read take shortcut
+ return;
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), getFeature() //$NON-NLS-1$
+ .getName(), revision);
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ IStoreChunkReader baseReader = null;
+
+ try
+ {
+ String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
+
+ CDOID id = revision.getID();
+ int branchID = revision.getBranch().getID();
+
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, id);
+ stmt.setInt(2, branchID);
+ stmt.setInt(3, revision.getVersion());
+ stmt.setInt(4, revision.getVersion());
+
+ stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows.
+
+ resultSet = stmt.executeQuery();
+
+ int currentIndex = 0;
+
+ while (valuesToRead > 0 && resultSet.next())
+ {
+ int index = resultSet.getInt(1);
+ if (index > currentIndex)
+ {
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(accessor, id, branchID);
+ }
+
+ baseReader.addRangedChunk(currentIndex, index);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$
+ }
+
+ valuesToRead -= index - currentIndex;
+ currentIndex = index;
+ }
+
+ CDOID tag = idHandler.getCDOID(resultSet, 2);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$
+ }
+
+ list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ valuesToRead--;
+ }
+
+ if (valuesToRead > 0)
+ {
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(accessor, id, branchID);
+ }
+
+ baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (baseReader != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading base revision chunks for featureMap {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision());
+ }
+
+ List<Chunk> baseChunks = baseReader.executeRead();
+ for (Chunk chunk : baseChunks)
+ {
+ int startIndex = chunk.getStartIndex();
+ for (int i = 0; i < chunk.size(); i++)
+ {
+ list.set(startIndex + i, chunk.get(i));
+ }
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), revision);
+ }
+ }
+
+ 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)
+ {
+ CDORevision revision = chunkReader.getRevision();
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), revision);
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ IStoreChunkReader baseReader = 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, revision.getID());
+ stmt.setInt(2, revision.getBranch().getID());
+ stmt.setInt(3, revision.getVersion());
+ stmt.setInt(4, revision.getVersion());
+
+ resultSet = stmt.executeQuery();
+
+ int nextDBIndex = Integer.MAX_VALUE; // next available DB index
+ if (resultSet.next())
+ {
+ nextDBIndex = resultSet.getInt(1);
+ }
+
+ for (Chunk chunk : chunks)
+ {
+ int startIndex = chunk.getStartIndex();
+ int missingValueStartIndex = -1;
+
+ for (int i = 0; i < chunk.size(); i++)
+ {
+ int nextListIndex = startIndex + i; // next expected list index
+
+ if (nextDBIndex == nextListIndex)
+ {
+ // DB value is available. check first if missing indexes were present before.
+ if (missingValueStartIndex != -1)
+ {
+ // read missing indexes from missingValueStartIndex to currentIndex
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
+ chunkReader.getRevision().getBranch().getID());
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$
+ }
+
+ baseReader.addRangedChunk(missingValueStartIndex, nextListIndex);
+
+ // reset missingValueStartIndex
+ missingValueStartIndex = -1;
+ }
+
+ // now read value and set to chunk
+ CDOID tag = idHandler.getCDOID(resultSet, 2);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$
+ }
+
+ chunk.add(i, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+
+ // advance DB cursor and read next available index
+ if (resultSet.next())
+ {
+ nextDBIndex = resultSet.getInt(1);
+ }
+ else
+ {
+ // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE
+ nextDBIndex = Integer.MAX_VALUE;
+ }
+ }
+ else
+ {
+ // gap between next DB index and next list index detected.
+ // skip until end of chunk or until DB value becomes available
+ if (missingValueStartIndex == -1)
+ {
+ missingValueStartIndex = nextListIndex;
+ }
+ }
+ }
+
+ // chunk complete. check for missing values at the end of the chunk.
+ if (missingValueStartIndex != -1)
+ {
+ // read missing indexes from missingValueStartIndex to last chunk index
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
+ chunkReader.getRevision().getBranch().getID());
+ }
+ baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size());
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ // now read missing values from base revision.
+ if (baseReader != null)
+ {
+ List<Chunk> baseChunks = baseReader.executeRead();
+
+ Iterator<Chunk> thisIterator = chunks.iterator();
+ Chunk thisChunk = thisIterator.next();
+
+ for (Chunk baseChunk : baseChunks)
+ {
+ int baseStartIndex = baseChunk.getStartIndex();
+
+ while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size())
+ {
+ // advance thisChunk, because it does not match baseChunk
+ thisChunk = thisIterator.next();
+ }
+
+ // baseChunk now corresponds to this chunk, but startIndex of baseChunk may be higher.
+ // therefore calculate offset
+ int offset = thisChunk.getStartIndex() - baseStartIndex;
+
+ // and copy values.
+ for (int i = 0; i < baseChunk.size(); i++)
+ {
+ thisChunk.add(i + offset, baseChunk.get(i));
+ }
+ } // finally, continue with the next baseChunk
+
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature(), revision);
+ }
+ }
+
+ 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.getBranch().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 created)
+ {
+ return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, created);
+ }
+
+ /**
+ * 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 branchId, int oldVersion, int newVersion,
+ int lastIndex, long timestamp)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmtDeleteTemp = null;
+ PreparedStatement stmtClear = null;
+
+ try
+ {
+ // check for each index if the value exists in the current branch
+ for (int i = 0; i <= lastIndex; i++)
+ {
+ if (getValue(accessor, id, branchId, i, false) == null)
+ {
+ // if not, add a historic entry for missing ones.
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i),
+ timestamp);
+ }
+ }
+
+ // clear rest of the list
+ stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
+ stmtClear.setInt(1, newVersion);
+ idHandler.setCDOID(stmtClear, 2, id);
+ stmtClear.setInt(3, branchId);
+
+ int 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)
+ {
+ InternalCDORevision revision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
+ int branchId = accessor.getTransaction().getBranch().getID();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id, branchId, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1,
+ revised);
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
+ final int newVersion, long created, CDOListFeatureDelta delta)
+ {
+ List<CDOFeatureDelta> listChanges = delta.getListChanges();
+ if (listChanges.size() == 0)
+ {
+ // nothing to do.
+ return;
+ }
+
+ InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
+ 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, branchId, oldVersion, newVersion,
+ created);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Processing deltas..."); //$NON-NLS-1$
+ }
+
+ // optimization: it's only necessary to process deltas
+ // starting with the last feature delta which clears the list
+ // (any operation before the clear is cascaded by it anyway)
+ int index = listChanges.size() - 1;
+ while (index > 0)
+ {
+ CDOFeatureDelta listDelta = listChanges.get(index);
+ if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta)
+ {
+ break;
+ }
+ index--;
+ }
+ while (index < listChanges.size())
+ {
+ listChanges.get(index++).accept(visitor);
+ }
+ }
+
+ private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
+ {
+ private IDBStoreAccessor accessor;
+
+ private InternalCDORevision originalRevision;
+
+ private CDOID id;
+
+ private int branchID;
+
+ private int oldVersion;
+
+ private int newVersion;
+
+ private int lastIndex;
+
+ private long timestamp;
+
+ public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID,
+ int oldVersion, int newVersion, long timestamp)
+ {
+ this.accessor = accessor;
+ this.originalRevision = originalRevision;
+ id = this.originalRevision.getID();
+ branchID = targetBranchID;
+ 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, branchID, fromIdx, true);
+
+ // remove the item
+ removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx, timestamp);
+
+ // adjust indexes and shift either up or down
+ if (fromIdx < toIdx)
+ {
+ moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx);
+ }
+ else
+ { // fromIdx > toIdx here
+ moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1);
+ }
+
+ // create the item
+ addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, startIndex, endIndex);
+ }
+
+ // create the item
+ addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, startIndex, timestamp);
+
+ // make room for the new item
+ moveOneUp(accessor, id, branchID, 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, branchID, oldVersion, newVersion, index, timestamp);
+
+ // create the item
+ addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, lastIndex, timestamp);
+ 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, branchID, oldVersion, newVersion, lastIndex, timestamp);
+ 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 branchId, 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, startIndex++, id);
+ stmt.setInt(column++, branchId);
+ stmt.setInt(column++, newVersion);
+ stmt.setInt(column++, index);
+
+ int result = DBUtil.update(stmt, false);
+ switch (result)
+ {
+ case 1:
+ // entry for current revision was already present.
+ // index update succeeded.
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
+ }
+
+ break;
+ case 0:
+ Object value = getValue(accessor, id, branchId, index, false);
+
+ if (value != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
+ }
+
+ removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp);
+ }
+ else
+ {
+ value = getValueFromBase(accessor, id, branchId, index);
+ {
+ TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$
+ }
+
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
+ }
+
+ addEntry(accessor, id, branchId, newVersion, index - 1, value, timestamp);
+ 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 branchId, 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++, branchId);
+ stmt.setInt(column++, newVersion);
+ stmt.setInt(column++, index);
+
+ int result = DBUtil.update(stmt, false);
+ switch (result)
+ {
+ case 1:
+ // entry for current revision was already present.
+ // index update succeeded.
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
+ }
+
+ break;
+ case 0:
+ Object value = getValue(accessor, id, branchId, index, false);
+ if (value != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
+ }
+
+ removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp);
+ }
+ else
+ {
+ value = getValueFromBase(accessor, id, branchId, index);
+ {
+ TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$
+ }
+
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp);
+ }
+
+ addEntry(accessor, id, branchId, newVersion, index + 1, value, timestamp);
+ 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 branchId, 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++, branchId);
+ 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 addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded,
+ int versionRemoved, int index, Object value, long timestamp)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved,
+ 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++, branchId);
+ stmt.setInt(column++, versionAdded);
+ stmt.setNull(column++, 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 branchId, int oldVersion, int newVersion,
+ int index, long timestamp)
+ {
+ 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++, branchId);
+ 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++, branchId);
+ stmt.setInt(column++, index);
+ result = DBUtil.update(stmt, false);
+
+ if (result == 0)
+ {
+ // no entry removed -> this means that we are in a branch and
+ // the entry has not been modified since the branch fork.
+ // therefore, we have to copy the base value and mark it as removed
+ Object value = getValueFromBase(accessor, id, branchId, index);
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp);
+ }
+ }
+ }
+ 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 branchId, int index, boolean getFromBase)
+ {
+ 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++, branchId);
+ stmt.setInt(column++, index);
+
+ ResultSet resultSet = stmt.executeQuery();
+ if (resultSet.next())
+ {
+ CDOID tag = idHandler.getCDOID(resultSet, 1);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+ result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value);
+ }
+ else
+ {
+ // value is not in this branch.
+ // -> read from base revision
+ if (getFromBase)
+ {
+ result = getValueFromBase(accessor, id, branchId, index);
+ } // else: result remains null
+ }
+ 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;
+ }
+
+ /**
+ * Read a single value (at a given index) from the base revision
+ *
+ * @param accessor
+ * the DBStoreAccessor
+ * @param id
+ * the ID of the revision
+ * @param branchID
+ * the ID of the current (child) branch
+ * @param index
+ * the index to read the value from
+ * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at
+ * the base of this branch (indicated by <code>branchID</code>).
+ */
+ private FeatureMap.Entry getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index)
+ {
+ IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID);
+ chunkReader.addSimpleChunk(index);
+ List<Chunk> chunks = chunkReader.executeRead();
+ return (FeatureMap.Entry)chunks.get(0).get(0);
+ }
+
+ private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID)
+ {
+ CDOBranchPoint base = accessor.getStore().getRepository().getBranchManager().getBranch(branchID).getBase();
+ InternalCDORevision baseRevision = (InternalCDORevision)accessor.getStore().getRepository().getRevisionManager()
+ .getRevision(id, base, /* referenceChunk = */0, /* prefetchDepth = */CDORevision.DEPTH_NONE, true);
+ IStoreChunkReader chunkReader = accessor.createChunkReader(baseRevision, getFeature());
+ return chunkReader;
+ }
+
+ 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/BranchingListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java
new file mode 100644
index 0000000000..858c34a0c3
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java
@@ -0,0 +1,71 @@
+/**
+ * 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 - derived branch mapping from audit mapping
+ */
+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
+ * @author Stefan Winkler
+ * @since 3.0
+ */
+public class BranchingListTableMapping extends AbstractListTableMapping
+{
+ private FieldInfo[] keyFields;
+
+ public BranchingListTableMapping(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_BRANCH, DBType.INTEGER),
+ 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.getBranch().getID());
+ stmt.setInt(3, 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/BranchingListTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java
new file mode 100644
index 0000000000..f7b056a00b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java
@@ -0,0 +1,1411 @@
+/**
+ * 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:
+ * Stefan Winkler - initial API and implementation taken from AuditListTableMappingWithRanges
+ * 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.CDOBranchPoint;
+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;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.ITransaction;
+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.Iterator;
+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 BranchingListTableMappingWithRanges extends BasicAbstractListTableMapping implements
+ IListMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, BranchingListTableMappingWithRanges.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;
+
+ public BranchingListTableMappingWithRanges(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[5];
+
+ dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType());
+ dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER);
+ dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER);
+ dbFields[3] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER);
+ dbFields[4] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER);
+
+ // add field for value
+ typeMapping = getMappingStrategy().createValueMapping(getFeature());
+ typeMapping.createDBField(table, CDODBSchema.LIST_VALUE);
+
+ // add table indexes
+ for (IDBField dbField : dbFields)
+ {
+ table.addIndex(Type.NON_UNIQUE, dbField);
+ }
+ }
+
+ 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_IDX);
+ builder.append(", "); //$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_BRANCH);
+ 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);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_BRANCH);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ 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$
+ sqlInsertEntry = builder.toString();
+
+ // ----------------- remove current entry -----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_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(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteEntry = builder.toString();
+
+ // ----------------- update index -----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlUpdateIndex = builder.toString();
+
+ // ----------------- get current value -----------------
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlGetValue = builder.toString();
+
+ // ----------- clear list items -------------------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlClearList = builder.toString();
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ protected final ITypeMapping getTypeMapping()
+ {
+ return typeMapping;
+ }
+
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, final int listChunk)
+ {
+ MoveableList<Object> list = revision.getList(getFeature());
+ int valuesToRead = list.size();
+ if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead)
+ {
+ valuesToRead = listChunk;
+ }
+
+ if (valuesToRead == 0)
+ {
+ // nothing to read take shortcut
+ return;
+ }
+
+ CDOID id = revision.getID();
+ int branchID = revision.getBranch().getID();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), revision);
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ IStoreChunkReader baseReader = null;
+ try
+ {
+ String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, id);
+ stmt.setInt(2, branchID);
+ stmt.setInt(3, revision.getVersion());
+ stmt.setInt(4, revision.getVersion());
+ stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows.
+
+ resultSet = stmt.executeQuery();
+
+ int currentIndex = 0;
+
+ while (valuesToRead > 0 && resultSet.next())
+ {
+ int index = resultSet.getInt(1);
+ if (index > currentIndex)
+ {
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(accessor, id, branchID);
+ }
+
+ baseReader.addRangedChunk(currentIndex, index);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$
+ }
+
+ valuesToRead -= index - currentIndex;
+ currentIndex = index;
+ }
+
+ Object value = typeMapping.readValue(resultSet);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$
+ }
+
+ list.set(currentIndex++, value);
+ valuesToRead--;
+ }
+
+ if (valuesToRead > 0)
+ {
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(accessor, id, branchID);
+ }
+
+ baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "Scheduling range {0}-{1} to be read from base revision", currentIndex, currentIndex + valuesToRead); //$NON-NLS-1$
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (baseReader != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading base revision chunks for feature {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision());
+ }
+
+ List<Chunk> baseChunks = baseReader.executeRead();
+ for (Chunk chunk : baseChunks)
+ {
+ int startIndex = chunk.getStartIndex();
+ for (int i = 0; i < chunk.size(); i++)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Copying value {0} at chunk index {1}+{2} to index {3}", //$NON-NLS-1$
+ chunk.get(i), startIndex, i, startIndex + i);
+ }
+
+ list.set(startIndex + i, chunk.get(i));
+ }
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading {3} list values done for feature {0}.{1} of {2}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), revision, list.size());
+ }
+ }
+
+ 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}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), chunkReader.getRevision());
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ IStoreChunkReader baseReader = 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().getBranch().getID());
+ stmt.setInt(3, chunkReader.getRevision().getVersion());
+ stmt.setInt(4, chunkReader.getRevision().getVersion());
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Readung Chunks: {0}", stmt); //$NON-NLS-1$
+ }
+
+ resultSet = stmt.executeQuery();
+
+ int nextDBIndex = Integer.MAX_VALUE; // next available DB index
+ if (resultSet.next())
+ {
+ nextDBIndex = resultSet.getInt(1);
+ }
+
+ for (Chunk chunk : chunks)
+ {
+ int startIndex = chunk.getStartIndex();
+ int missingValueStartIndex = -1;
+
+ for (int i = 0; i < chunk.size(); i++)
+ {
+ int nextListIndex = startIndex + i; // next expected list index
+
+ if (nextDBIndex == nextListIndex)
+ {
+ // DB value is available. check first if missing indexes were present before.
+ if (missingValueStartIndex != -1)
+ {
+ // read missing indexes from missingValueStartIndex to currentIndex
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
+ chunkReader.getRevision().getBranch().getID());
+ }
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$
+ }
+
+ baseReader.addRangedChunk(missingValueStartIndex, nextListIndex);
+
+ // reset missingValueStartIndex
+ missingValueStartIndex = -1;
+ }
+
+ // now read value and set to chunk
+ Object value = typeMapping.readValue(resultSet);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$
+ }
+ chunk.add(i, value);
+
+ // advance DB cursor and read next available index
+ if (resultSet.next())
+ {
+ nextDBIndex = resultSet.getInt(1);
+ }
+ else
+ {
+ // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE
+ nextDBIndex = Integer.MAX_VALUE;
+ }
+ }
+ else
+ {
+ // gap between next DB index and next list index detected.
+ // skip until end of chunk or until DB value becomes available
+ if (missingValueStartIndex == -1)
+ {
+ missingValueStartIndex = nextListIndex;
+ }
+ }
+ }
+
+ // chunk complete. check for missing values at the end of the chunk.
+ if (missingValueStartIndex != -1)
+ {
+ // read missing indexes from missingValueStartIndex to last chunk index
+ if (baseReader == null)
+ {
+ baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
+ chunkReader.getRevision().getBranch().getID());
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER
+ .format(
+ "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, chunk.getStartIndex() + chunk.size()); //$NON-NLS-1$
+ }
+ baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size());
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ // now read missing values from base revision.
+ if (baseReader != null)
+ {
+ List<Chunk> baseChunks = baseReader.executeRead();
+
+ Iterator<Chunk> thisIterator = chunks.iterator();
+ Chunk thisChunk = thisIterator.next();
+
+ for (Chunk baseChunk : baseChunks)
+ {
+ int baseStartIndex = baseChunk.getStartIndex();
+
+ while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size())
+ {
+ // advance thisChunk, because it does not match baseChunk
+ thisChunk = thisIterator.next();
+ }
+
+ // baseChunk now corresponds to thisChunk, but startIndex of baseChunk may be higher.
+ // therefore calculate offset
+ int offset = baseStartIndex - thisChunk.getStartIndex();
+
+ // and copy values.
+ for (int i = 0; i < baseChunk.size(); i++)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Copying base chunk reader value {0} at index {1} to current chunk reader at index {2}.",
+ baseChunk.get(i), baseChunk.getStartIndex() + i, thisChunk.getStartIndex() + i + offset);
+ }
+
+ thisChunk.add(i + offset, baseChunk.get(i));
+ } // finally, continue with the next baseChunk
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), chunkReader.getRevision());
+ }
+ }
+
+ 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 index, Object value)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing value for feature {0}.{1} index {2} of {3} : {4}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, revision, value);
+ }
+
+ addEntry(accessor, revision.getID(), revision.getBranch().getID(), revision.getVersion(), index, value);
+ }
+
+ /**
+ * 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
+ * @param lastIndex
+ */
+ public void clearList(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int lastIndex)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ // check for each index if the value exists in the current branch
+ for (int i = 0; i <= lastIndex; i++)
+ {
+ if (getValue(accessor, id, branchId, i, false) == null)
+ {
+ // if not, add a historic entry for missing ones.
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i));
+ }
+ }
+
+ // clear rest of the list
+ stmt = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
+ stmt.setInt(1, newVersion);
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 2, id);
+ stmt.setInt(3, branchId);
+
+ int result = DBUtil.update(stmt, false);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ ITransaction transaction = accessor.getTransaction();
+ InternalCDORevision revision = (InternalCDORevision)transaction.getRevision(id);
+ int branchID = transaction.getBranch().getID();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id, branchID, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1);
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, final int oldVersion,
+ final int newVersion, long created, CDOListFeatureDelta delta)
+ {
+ List<CDOFeatureDelta> listChanges = delta.getListChanges();
+ if (listChanges.size() == 0)
+ {
+ // nothing to do.
+ return;
+ }
+
+ InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
+ 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, branchId, oldVersion, newVersion);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Processing deltas..."); //$NON-NLS-1$
+ }
+
+ // optimization: it's only necessary to process deltas
+ // starting with the last feature delta which clears the list
+ // (any operation before the clear is cascaded by it anyway)
+ int index = listChanges.size() - 1;
+ while (index > 0)
+ {
+ CDOFeatureDelta listDelta = listChanges.get(index);
+ if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta)
+ {
+ break;
+ }
+ index--;
+ }
+ while (index < listChanges.size())
+ {
+ listChanges.get(index++).accept(visitor);
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
+ {
+ private IDBStoreAccessor accessor;
+
+ private CDOID id;
+
+ private int branchID;
+
+ private int oldVersion;
+
+ private int newVersion;
+
+ private int lastIndex;
+
+ public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID,
+ int oldVersion, int newVersion)
+ {
+ this.accessor = accessor;
+ id = originalRevision.getID();
+ branchID = targetBranchID;
+ this.oldVersion = oldVersion;
+ this.newVersion = newVersion;
+ lastIndex = originalRevision.getList(getFeature()).size() - 1;
+ }
+
+ 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, branchID, fromIdx, true);
+
+ // remove the item
+ removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx);
+
+ // adjust indexes and shift either up or down
+ if (fromIdx < toIdx)
+ {
+ moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx);
+ }
+ else
+ { // fromIdx > toIdx here
+ moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1);
+ }
+
+ // create the item
+ addEntry(accessor, id, branchID, newVersion, toIdx, value);
+ }
+
+ 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, branchID, oldVersion, newVersion, startIndex, endIndex);
+ }
+
+ // create the item
+ addEntry(accessor, id, branchID, newVersion, startIndex, delta.getValue());
+
+ ++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, branchID, oldVersion, newVersion, startIndex);
+
+ // make room for the new item
+ moveOneUp(accessor, id, branchID, 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, branchID, oldVersion, newVersion, index);
+
+ // create the item
+ addEntry(accessor, id, branchID, newVersion, index, delta.getValue());
+ }
+
+ 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, branchID, oldVersion, newVersion, lastIndex);
+ 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, branchID, oldVersion, newVersion, lastIndex);
+ 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 branchId, 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++, branchId);
+ stmt.setInt(column++, newVersion);
+ stmt.setInt(column++, index);
+
+ int result = DBUtil.update(stmt, false);
+ switch (result)
+ {
+ case 1:
+ // entry for current revision was already present.
+ // index update succeeded.
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
+ }
+
+ break;
+ // no entry for current revision there.
+ case 0:
+ Object value = getValue(accessor, id, branchId, index, false);
+
+ if (value != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
+ }
+
+ removeEntry(accessor, id, branchId, oldVersion, newVersion, index);
+ }
+ else
+ {
+ value = getValueFromBase(accessor, id, branchId, index);
+ {
+ TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$
+ }
+
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
+ }
+
+ addEntry(accessor, id, branchId, newVersion, index - 1, value);
+ 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 branchId, 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++, branchId);
+ stmt.setInt(column++, newVersion);
+ stmt.setInt(column++, index);
+
+ int result = DBUtil.update(stmt, false);
+ switch (result)
+ {
+ case 1:
+ // entry for current revision was already present.
+ // index update succeeded.
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
+ }
+
+ break;
+ case 0:
+ Object value = getValue(accessor, id, branchId, index, false);
+
+ if (value != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
+ }
+
+ removeEntry(accessor, id, branchId, oldVersion, newVersion, index);
+ }
+ else
+ {
+ value = getValueFromBase(accessor, id, branchId, index);
+ {
+ TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$
+ }
+
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$
+ }
+
+ addEntry(accessor, id, branchId, newVersion, index + 1, value);
+ 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 branchId, int version, int index, Object value)
+ {
+ 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}:{4}v{5} : {6}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, branchId, version, value);
+ }
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, branchId);
+ stmt.setInt(column++, version); // versionAdded
+ stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved
+ stmt.setInt(column++, index);
+ typeMapping.setValue(stmt, column++, value);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ catch (IllegalStateException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded,
+ int versionRemoved, int index, Object value)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved,
+ value);
+ }
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, branchId);
+ stmt.setInt(column++, versionAdded); // versionAdded
+ stmt.setInt(column++, versionRemoved); // versionRemoved
+ stmt.setInt(column++, index);
+ typeMapping.setValue(stmt, column++, value);
+
+ 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 branchId, 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}:{4}v{5}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, branchId, 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++, branchId);
+ 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++, branchId);
+ stmt.setInt(column++, index);
+
+ result = DBUtil.update(stmt, false);
+
+ if (result == 0)
+ {
+ // no entry removed -> this means that we are in a branch and
+ // the entry has not been modified since the branch fork.
+ // therefore, we have to copy the base value and mark it as removed
+ Object value = getValueFromBase(accessor, id, branchId, index);
+ addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value);
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Removing value for feature {0}.{1} index {2} of {3}:{4}v{5} FAILED {6}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, branchId, 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}:{4}v{5} FAILED {6}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, branchId, newVersion, e.getMessage());
+ }
+
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Read a single value from the current revision's list.
+ *
+ * @param accessor
+ * the store accessor
+ * @param id
+ * the revision's ID
+ * @param branchId
+ * the revision's branch ID
+ * @param index
+ * the index from which to get the value
+ * @param getFromBase
+ * if <code>true</code>, the value is recursively loaded from the base revision of a branch, if it is not
+ * present in the current branch (because it has not been changed since the branch fork). If
+ * <code>false</code>, <code>null</code> is returned in the former case.
+ */
+ private Object getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ Object result = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH);
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, branchId);
+ stmt.setInt(column++, index);
+
+ ResultSet resultSet = stmt.executeQuery();
+ if (resultSet.next())
+ {
+ result = typeMapping.readValue(resultSet);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ // value is not in this branch.
+ // -> read from base revision
+ if (getFromBase)
+ {
+ result = getValueFromBase(accessor, id, branchId, index);
+ } // else: result remains null
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ return result;
+ }
+
+ /**
+ * Read a single value (at a given index) from the base revision
+ *
+ * @param accessor
+ * the DBStoreAccessor
+ * @param id
+ * the ID of the revision
+ * @param branchID
+ * the ID of the current (child) branch
+ * @param index
+ * the index to read the value from
+ * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at
+ * the base of this branch (indicated by <code>branchID</code>).
+ */
+ private Object getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index)
+ {
+ IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID);
+ chunkReader.addSimpleChunk(index);
+ List<Chunk> chunks = chunkReader.executeRead();
+ return chunks.get(0).get(0);
+ }
+
+ private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID)
+ {
+ IRepository repository = accessor.getStore().getRepository();
+ CDOBranchPoint base = repository.getBranchManager().getBranch(branchID).getBase();
+ InternalCDORevision baseRevision = (InternalCDORevision)repository.getRevisionManager().getRevision(id, base, /*
+ * referenceChunk
+ * =
+ */0, /*
+ * prefetchDepth
+ * =
+ */
+ CDORevision.DEPTH_NONE, true);
+ IStoreChunkReader chunkReader = accessor.createChunkReader(baseRevision, getFeature());
+ return chunkReader;
+ }
+
+ public final 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 sourceID = idHandler.getCDOID(resultSet, 1);
+ CDOID targetID = idHandler.getCDOID(resultSet, 2);
+ int idx = resultSet.getInt(3);
+
+ boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, 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);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java
new file mode 100644
index 0000000000..3a1400d18c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java
@@ -0,0 +1,127 @@
+/**
+ * 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.horizontal;
+
+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.emf.cdo.server.internal.db.IObjectTypeMapper;
+
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+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 abstract class DelegatingObjectTypeMapper extends AbstractObjectTypeMapper
+{
+ private IObjectTypeMapper delegate;
+
+ public DelegatingObjectTypeMapper()
+ {
+ }
+
+ public IObjectTypeMapper getDelegate()
+ {
+ return delegate;
+ }
+
+ public void setDelegate(IObjectTypeMapper delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ public CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ CDOID type = doGetObjectType(accessor, id);
+ if (type != null)
+ {
+ EClass eClass = (EClass)getMetaDataManager().getMetaInstance(type);
+ return new CDOClassifierRef(eClass);
+ }
+
+ return delegate.getObjectType(accessor, id);
+ }
+
+ public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type)
+ {
+ CDOID classID = getMetaDataManager().getMetaID(type, timeStamp);
+ doPutObjectType(accessor, id, classID);
+
+ delegate.putObjectType(accessor, timeStamp, id, type);
+ }
+
+ public void removeObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ doRemoveObjectType(accessor, id);
+ delegate.removeObjectType(accessor, id);
+ }
+
+ public CDOID getMaxID(Connection connection, IIDHandler idHandler)
+ {
+ CDOID maxID = doGetMaxID(connection, idHandler);
+ if (maxID != null)
+ {
+ return maxID;
+ }
+
+ return delegate.getMaxID(connection, idHandler);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ delegate.rawExport(connection, out, fromCommitTime, toCommitTime);
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, OMMonitor monitor) throws IOException
+ {
+ delegate.rawImport(connection, in, monitor);
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ checkState(delegate, "delegate");
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ LifecycleUtil.activate(delegate);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(delegate);
+ super.doDeactivate();
+ }
+
+ protected abstract CDOID doGetObjectType(IDBStoreAccessor accessor, CDOID id);
+
+ protected abstract void doPutObjectType(IDBStoreAccessor accessor, CDOID id, CDOID type);
+
+ protected abstract void doRemoveObjectType(IDBStoreAccessor accessor, CDOID id);
+
+ protected abstract CDOID doGetMaxID(Connection connection, IIDHandler idHandler);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java
new file mode 100644
index 0000000000..d1aee6eb42
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java
@@ -0,0 +1,742 @@
+/**
+ * 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 249610: [DB] Support external references (Implementation)
+ * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode
+ * 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.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+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.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.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+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.IClassMappingAuditSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
+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.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.ImplementationError;
+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.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingAuditSupport,
+ IClassMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalAuditClassMapping.class);
+
+ private String sqlInsertAttributes;
+
+ private String sqlSelectCurrentAttributes;
+
+ private String sqlSelectAllObjectIDs;
+
+ private String sqlSelectAttributesByTime;
+
+ private String sqlSelectAttributesByVersion;
+
+ private String sqlReviseAttributes;
+
+ private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>()
+ {
+ @Override
+ protected FeatureDeltaWriter initialValue()
+ {
+ return new FeatureDeltaWriter();
+ }
+ };
+
+ public HorizontalAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
+ {
+ super(mappingStrategy, eClass);
+
+ initSQLStrings();
+ }
+
+ private void initSQLStrings()
+ {
+ Map<EStructuralFeature, String> unsettableFields = getUnsettableFields();
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+
+ // ----------- Select Revision ---------------------------
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+
+ for (ITypeMapping singleMapping : getValueMappings())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(singleMapping.getField());
+ }
+
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (String fieldName : listSizeFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append("=? AND ("); //$NON-NLS-1$
+
+ String sqlSelectAttributesPrefix = builder.toString();
+
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0)"); //$NON-NLS-1$
+
+ sqlSelectCurrentAttributes = builder.toString();
+
+ builder = new StringBuilder(sqlSelectAttributesPrefix);
+
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=?))"); //$NON-NLS-1$
+
+ sqlSelectAttributesByTime = builder.toString();
+
+ builder = new StringBuilder(sqlSelectAttributesPrefix);
+
+ builder.append("ABS(");
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(")=?)"); //$NON-NLS-1$
+
+ sqlSelectAttributesByVersion = builder.toString();
+
+ // ----------- Insert Attributes -------------------------
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(getTable());
+
+ builder.append("("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+
+ for (ITypeMapping singleMapping : getValueMappings())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(singleMapping.getField());
+ }
+
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (String fieldName : listSizeFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$
+
+ for (int i = 0; i < getValueMappings().size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+
+ if (unsettableFields != null)
+ {
+ for (int i = 0; i < unsettableFields.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (int i = 0; i < listSizeFields.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+ }
+
+ builder.append(")"); //$NON-NLS-1$
+ sqlInsertAttributes = builder.toString();
+
+ // ----------- Update to set revised ----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=? WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0"); //$NON-NLS-1$
+ sqlReviseAttributes = builder.toString();
+
+ // ----------- Select all unrevised Object IDs ------
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0"); //$NON-NLS-1$
+ sqlSelectAllObjectIDs = builder.toString();
+ }
+
+ public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ long timeStamp = revision.getTimeStamp();
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ stmt.setLong(2, timeStamp);
+ stmt.setLong(3, timeStamp);
+ }
+ else
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ }
+
+ // Read singleval-attribute table always (even without modeled attributes!)
+ boolean success = readValuesFromStatement(stmt, revision, accessor);
+
+ // Read multival tables only if revision exists
+ if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION)
+ {
+ readLists(accessor, revision, listChunk);
+ }
+
+ return success;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, revision.getVersion());
+
+ // Read singleval-attribute table always (even without modeled attributes!)
+ boolean success = readValuesFromStatement(stmt, revision, accessor);
+
+ // Read multival tables only if revision exists
+ if (success)
+ {
+ readLists(accessor, revision, listChunk);
+ }
+
+ return success;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
+ boolean exactMatch, CDOBranchPoint branchPoint)
+ {
+ EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name();
+ long timeStamp = branchPoint.getTimeStamp();
+
+ ITypeMapping nameValueMapping = getValueMapping(nameFeature);
+ if (nameValueMapping == null)
+ {
+ throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(">0 AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(nameValueMapping.getField());
+ if (name == null)
+ {
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ builder.append(" AND ("); //$NON-NLS-1$
+
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0)"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=?))"); //$NON-NLS-1$
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+
+ stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM);
+ idHandler.setCDOID(stmt, column++, folderId);
+
+ if (name != null)
+ {
+ String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$
+ nameValueMapping.setValue(stmt, column++, queryName);
+ }
+
+ if (timeStamp != CDORevision.UNSPECIFIED_DATE)
+ {
+ stmt.setLong(column++, timeStamp);
+ stmt.setLong(column++, timeStamp);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$
+ }
+
+ return stmt;
+ }
+ catch (SQLException ex)
+ {
+ statementCache.releasePreparedStatement(stmt); // only release on error
+ throw new DBException(ex);
+ }
+ }
+
+ public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH);
+ }
+
+ @Override
+ protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+ stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, column++, revision.getID());
+ stmt.setInt(column++, revision.getVersion());
+ stmt.setLong(column++, revision.getTimeStamp());
+ stmt.setLong(column++, revision.getRevised());
+ idHandler.setCDOID(stmt, column++, revision.getResourceID());
+ idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID());
+ stmt.setInt(column++, revision.getContainingFeatureID());
+
+ int isSetCol = column + getValueMappings().size();
+
+ for (ITypeMapping mapping : getValueMappings())
+ {
+ EStructuralFeature feature = mapping.getFeature();
+ if (feature.isUnsettable())
+ {
+ if (revision.getValue(feature) == null)
+ {
+ stmt.setBoolean(isSetCol++, false);
+
+ // also set value column to default value
+ mapping.setDefaultValue(stmt, column++);
+
+ continue;
+ }
+
+ stmt.setBoolean(isSetCol++, true);
+ }
+
+ mapping.setValueFromRevision(stmt, column++, revision);
+ }
+
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+ if (listSizeFields != null)
+ {
+ // isSetCol now points to the first listTableSize-column
+ column = isSetCol;
+
+ for (EStructuralFeature feature : listSizeFields.keySet())
+ {
+ CDOList list = revision.getList(feature);
+ stmt.setInt(column++, list.size());
+ }
+ }
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
+ OMMonitor mon)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
+
+ int column = 1;
+
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, -version); // cdo_version
+ stmt.setLong(column++, timeStamp); // cdo_created
+ stmt.setLong(column++, CDOBranchPoint.UNSPECIFIED_DATE); // cdo_revised
+ idHandler.setCDOID(stmt, column++, CDOID.NULL); // resource
+ idHandler.setCDOID(stmt, column++, CDOID.NULL); // container
+ stmt.setInt(column++, 0); // containing feature ID
+
+ int isSetCol = column + getValueMappings().size();
+
+ for (ITypeMapping mapping : getValueMappings())
+ {
+ EStructuralFeature feature = mapping.getFeature();
+ if (feature.isUnsettable())
+ {
+ stmt.setBoolean(isSetCol++, false);
+ }
+
+ mapping.setDefaultValue(stmt, column++);
+ }
+
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+ if (listSizeFields != null)
+ {
+ // list size columns begin after isSet-columns
+ column = isSetCol;
+
+ for (int i = 0; i < listSizeFields.size(); i++)
+ {
+ stmt.setInt(column++, 0);
+ }
+ }
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlReviseAttributes, ReuseProbability.HIGH);
+
+ stmt.setLong(1, revised);
+ idHandler.setCDOID(stmt, 2, id);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
+ OMMonitor monitor)
+ {
+ Async async = null;
+ monitor.begin();
+
+ try
+ {
+ try
+ {
+ async = monitor.forkAsync();
+ FeatureDeltaWriter writer = deltaWriter.get();
+ writer.process(accessor, delta, created);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ @Override
+ protected String getListXRefsWhere(QueryXRefsContext context)
+ {
+ if (CDOBranch.MAIN_BRANCH_ID != context.getBranch().getID())
+ {
+ throw new IllegalArgumentException("Non-audit mode does not support branch specification");
+ }
+
+ StringBuilder builder = new StringBuilder();
+ long timeStamp = context.getTimeStamp();
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=");
+ builder.append(timeStamp);
+ builder.append(" AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=");
+ builder.append(timeStamp);
+ builder.append(")"); //$NON-NLS-1$
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor
+ {
+ private IDBStoreAccessor accessor;
+
+ private long created;
+
+ private CDOID id;
+
+ private int oldVersion;
+
+ private InternalCDORevision newRevision;
+
+ private int branchId;
+
+ public void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created)
+ {
+ this.accessor = accessor;
+ this.created = created;
+ id = delta.getID();
+ branchId = delta.getBranch().getID();
+ oldVersion = delta.getVersion();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("FeatureDeltaWriter: old version: {0}, new version: {1}", oldVersion, oldVersion + 1); //$NON-NLS-1$
+ }
+
+ InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository()
+ .getRevisionManager().getRevisionByVersion(id, delta, 0, true);
+
+ newRevision = originalRevision.copy();
+
+ newRevision.setVersion(oldVersion + 1);
+ newRevision.setBranchPoint(delta.getBranch().getPoint(created));
+
+ // process revision delta tree
+ delta.accept(this);
+
+ long revised = newRevision.getTimeStamp() - 1;
+ reviseOldRevision(accessor, id, delta.getBranch(), revised);
+
+ writeValues(accessor, newRevision);
+ }
+
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature());
+ listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta);
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java
new file mode 100644
index 0000000000..c9eff1a73d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java
@@ -0,0 +1,72 @@
+/**
+ * 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.internal.db.mapping.horizontal;
+
+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.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalAuditMappingStrategy extends AbstractHorizontalMappingStrategy
+{
+ public HorizontalAuditMappingStrategy()
+ {
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return true;
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return false;
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return false;
+ }
+
+ @Override
+ public IClassMapping doCreateClassMapping(EClass eClass)
+ {
+ return new HorizontalAuditClassMapping(this, eClass);
+ }
+
+ @Override
+ public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new AuditListTableMapping(this, containingClass, feature);
+ }
+
+ @Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new AuditFeatureMapTableMapping(this, containingClass, feature);
+ }
+
+ @Override
+ public String getListJoin(String attrTable, String listTable)
+ {
+ String join = super.getListJoin(attrTable, listTable);
+ join += " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION;
+ join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION;
+ return join;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java
new file mode 100644
index 0000000000..93de7d84bb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java
@@ -0,0 +1,76 @@
+/**
+ * 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 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+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.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalAuditMappingStrategyWithRanges extends AbstractHorizontalMappingStrategy
+{
+ public HorizontalAuditMappingStrategyWithRanges()
+ {
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return true;
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return false;
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return true;
+ }
+
+ @Override
+ public IClassMapping doCreateClassMapping(EClass eClass)
+ {
+ return new HorizontalAuditClassMapping(this, eClass);
+ }
+
+ @Override
+ public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new AuditListTableMappingWithRanges(this, containingClass, feature);
+ }
+
+ @Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new AuditFeatureMapTableMappingWithRanges(this, containingClass, feature);
+ }
+
+ @Override
+ public String getListJoin(String attrTable, String listTable)
+ {
+ String join = super.getListJoin(attrTable, listTable);
+ join += " AND " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_ADDED;
+ join += "<=" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION;
+ join += " AND (" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED;
+ join += " IS NULL OR " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED;
+ join += ">" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION + ")";
+ return join;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java
new file mode 100644
index 0000000000..6f337b26e4
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java
@@ -0,0 +1,1124 @@
+/**
+ * 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 - 249610: [DB] Support external references (Implementation)
+ * Stefan Winkler - derived branch mapping from audit mapping
+ * 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.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.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.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+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.IClassMappingAuditSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
+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.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.server.InternalRepository;
+
+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.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+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.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 3.0
+ */
+public class HorizontalBranchingClassMapping extends AbstractHorizontalClassMapping implements
+ IClassMappingAuditSupport, IClassMappingDeltaSupport
+{
+ /**
+ * @author Stefan Winkler
+ */
+ private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor
+ {
+ private IDBStoreAccessor accessor;
+
+ private long created;
+
+ private CDOID id;
+
+ private CDOBranch targetBranch;
+
+ private int oldVersion;
+
+ private int newVersion;
+
+ private InternalCDORevision newRevision;
+
+ public void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created)
+ {
+ this.accessor = accessor;
+ this.created = created;
+ id = delta.getID();
+ oldVersion = delta.getVersion();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("FeatureDeltaWriter: old version: {0}, new version: {1}", oldVersion, oldVersion + 1); //$NON-NLS-1$
+ }
+
+ InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
+ newRevision = originalRevision.copy();
+ targetBranch = accessor.getTransaction().getBranch();
+ newRevision.adjustForCommit(targetBranch, created);
+
+ newVersion = newRevision.getVersion();
+
+ // process revision delta tree
+ delta.accept(this);
+
+ if (newVersion != CDORevision.FIRST_VERSION)
+ {
+ reviseOldRevision(accessor, id, delta.getBranch(), newRevision.getTimeStamp() - 1);
+ }
+
+ writeValues(accessor, newRevision);
+ }
+
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature());
+ listMapping.processDelta(accessor, id, targetBranch.getID(), oldVersion, newVersion, created, delta);
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ delta.apply(newRevision);
+ }
+ }
+
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalBranchingClassMapping.class);
+
+ private String sqlInsertAttributes;
+
+ private String sqlSelectCurrentAttributes;
+
+ private String sqlSelectAllObjectIDs;
+
+ private String sqlSelectAttributesByTime;
+
+ private String sqlSelectAttributesByVersion;
+
+ private String sqlReviseAttributes;
+
+ private String sqlSelectForHandle;
+
+ private String sqlSelectForChangeSet;
+
+ private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>()
+ {
+ @Override
+ protected FeatureDeltaWriter initialValue()
+ {
+ return new FeatureDeltaWriter();
+ }
+ };
+
+ public HorizontalBranchingClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
+ {
+ super(mappingStrategy, eClass);
+
+ initSQLStrings();
+ }
+
+ @Override
+ protected IDBField addBranchingField(IDBTable table)
+ {
+ return table.addField(CDODBSchema.ATTRIBUTES_BRANCH, DBType.INTEGER, true);
+ }
+
+ private void initSQLStrings()
+ {
+ Map<EStructuralFeature, String> unsettableFields = getUnsettableFields();
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+
+ // ----------- Select Revision ---------------------------
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+
+ for (ITypeMapping singleMapping : getValueMappings())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(singleMapping.getField());
+ }
+
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (String fieldName : listSizeFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append("=? AND ("); //$NON-NLS-1$
+ String sqlSelectAttributesPrefix = builder.toString();
+
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0)"); //$NON-NLS-1$
+
+ sqlSelectCurrentAttributes = builder.toString();
+
+ builder = new StringBuilder(sqlSelectAttributesPrefix);
+
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=?))"); //$NON-NLS-1$
+
+ sqlSelectAttributesByTime = builder.toString();
+
+ builder = new StringBuilder(sqlSelectAttributesPrefix);
+
+ builder.append("ABS("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(")=?)"); //$NON-NLS-1$
+
+ sqlSelectAttributesByVersion = builder.toString();
+
+ // ----------- Insert Attributes -------------------------
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(getTable());
+
+ builder.append("("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+
+ for (ITypeMapping singleMapping : getValueMappings())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(singleMapping.getField());
+ }
+
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (String fieldName : listSizeFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$
+
+ for (int i = 0; i < getValueMappings().size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+
+ if (unsettableFields != null)
+ {
+ for (int i = 0; i < unsettableFields.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (int i = 0; i < listSizeFields.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+ }
+
+ builder.append(")"); //$NON-NLS-1$
+ sqlInsertAttributes = builder.toString();
+
+ // ----------- Update to set revised ----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=? WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0"); //$NON-NLS-1$
+ sqlReviseAttributes = builder.toString();
+
+ // ----------- Select all unrevised Object IDs ------
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0"); //$NON-NLS-1$
+ sqlSelectAllObjectIDs = builder.toString();
+
+ // ----------- Select all revisions (for handleRevision) ---
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ sqlSelectForHandle = builder.toString();
+
+ // ----------- Select all revisions (for handleRevision) ---
+ 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();
+ }
+
+ public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ long timeStamp = revision.getTimeStamp();
+ int branchID = revision.getBranch().getID();
+
+ try
+ {
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, branchID);
+ stmt.setLong(3, timeStamp);
+ stmt.setLong(4, timeStamp);
+ }
+ else
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, branchID);
+ }
+
+ // Read singleval-attribute table always (even without modeled attributes!)
+ boolean success = readValuesFromStatement(stmt, revision, accessor);
+
+ // Read multival tables only if revision exists
+ if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION)
+ {
+ readLists(accessor, revision, listChunk);
+ }
+
+ return success;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, revision.getBranch().getID());
+ stmt.setInt(3, revision.getVersion());
+
+ // Read singleval-attribute table always (even without modeled attributes!)
+ boolean success = readValuesFromStatement(stmt, revision, accessor);
+
+ // Read multival tables only if revision exists
+ if (success)
+ {
+ readLists(accessor, revision, listChunk);
+ }
+
+ return success;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
+ boolean exactMatch, CDOBranchPoint branchPoint)
+ {
+ EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name();
+
+ ITypeMapping nameValueMapping = getValueMapping(nameFeature);
+ if (nameValueMapping == null)
+ {
+ throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$
+ }
+
+ int branchID = branchPoint.getBranch().getID();
+ long timeStamp = branchPoint.getTimeStamp();
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(">0 AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(nameValueMapping.getField());
+ if (name == null)
+ {
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(exactMatch ? " =?" : " LIKE ?"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ builder.append(" AND ("); //$NON-NLS-1$
+
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0)"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=?))"); //$NON-NLS-1$
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+
+ stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM);
+ stmt.setInt(column++, branchID);
+ idHandler.setCDOID(stmt, column++, folderId);
+
+ if (name != null)
+ {
+ String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$
+ nameValueMapping.setValue(stmt, column++, queryName);
+ }
+
+ if (timeStamp != CDORevision.UNSPECIFIED_DATE)
+ {
+ stmt.setLong(column++, timeStamp);
+ stmt.setLong(column++, timeStamp);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$
+ }
+
+ return stmt;
+ }
+ catch (SQLException ex)
+ {
+ statementCache.releasePreparedStatement(stmt); // only release on error
+ throw new DBException(ex);
+ }
+ }
+
+ public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH);
+ }
+
+ @Override
+ protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+ stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, column++, revision.getID());
+ stmt.setInt(column++, revision.getVersion());
+ stmt.setInt(column++, revision.getBranch().getID());
+ stmt.setLong(column++, revision.getTimeStamp());
+ stmt.setLong(column++, revision.getRevised());
+ idHandler.setCDOID(stmt, column++, revision.getResourceID());
+ idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID());
+ stmt.setInt(column++, revision.getContainingFeatureID());
+
+ int isSetCol = column + getValueMappings().size();
+
+ for (ITypeMapping mapping : getValueMappings())
+ {
+ EStructuralFeature feature = mapping.getFeature();
+ if (feature.isUnsettable())
+ {
+ if (revision.getValue(feature) == null)
+ {
+ stmt.setBoolean(isSetCol++, false);
+
+ // also set value column to default value
+ mapping.setDefaultValue(stmt, column++);
+ continue;
+ }
+
+ stmt.setBoolean(isSetCol++, true);
+ }
+
+ mapping.setValueFromRevision(stmt, column++, revision);
+ }
+
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+ if (listSizeFields != null)
+ {
+ // isSetCol now points to the first listTableSize-column
+ column = isSetCol;
+
+ for (EStructuralFeature feature : listSizeFields.keySet())
+ {
+ CDOList list = revision.getList(feature);
+ stmt.setInt(column++, list.size());
+ }
+ }
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
+ OMMonitor mon)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, -version); // cdo_version
+ stmt.setInt(column++, branch.getID());
+ stmt.setLong(column++, timeStamp); // cdo_created
+ stmt.setLong(column++, CDOBranchPoint.UNSPECIFIED_DATE); // cdo_revised
+ idHandler.setCDOID(stmt, column++, CDOID.NULL); // resource
+ idHandler.setCDOID(stmt, column++, CDOID.NULL); // container
+ stmt.setInt(column++, 0); // containing feature ID
+
+ int isSetCol = column + getValueMappings().size();
+
+ for (ITypeMapping mapping : getValueMappings())
+ {
+ EStructuralFeature feature = mapping.getFeature();
+ if (feature.isUnsettable())
+ {
+ stmt.setBoolean(isSetCol++, false);
+ }
+
+ mapping.setDefaultValue(stmt, column++);
+ }
+
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+ if (listSizeFields != null)
+ {
+ // list size columns begin after isSet-columns
+ column = isSetCol;
+
+ for (int i = 0; i < listSizeFields.size(); i++)
+ {
+ stmt.setInt(column++, 0);
+ }
+ }
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlReviseAttributes, ReuseProbability.HIGH);
+
+ stmt.setLong(1, revised);
+ idHandler.setCDOID(stmt, 2, id);
+ stmt.setInt(3, branch.getID());
+
+ DBUtil.update(stmt, false); // No row affected if old revision from other branch!
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ 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)
+ {
+ // put new objects into objectTypeMapper
+ long timeStamp = revision.getTimeStamp();
+ AbstractHorizontalMappingStrategy mappingStrategy = (AbstractHorizontalMappingStrategy)getMappingStrategy();
+ mappingStrategy.putObjectType(accessor, timeStamp, id, getEClass());
+ }
+ else if (revise && revision.getVersion() > CDOBranchVersion.FIRST_VERSION)
+ {
+ // if revision is not the first one, revise the old revision
+ 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
+ async = monitor.forkAsync(7);
+ if (getListMappings() != null)
+ {
+ writeLists(accessor, revision);
+ }
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ @Override
+ public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler)
+ {
+ StringBuilder builder = new StringBuilder(sqlSelectForHandle);
+ boolean whereAppend = false;
+
+ if (branch != null)
+ {
+ // TODO: Prepare this string literal
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append("=?"); //$NON-NLS-1$
+
+ whereAppend = true;
+ }
+
+ int timeParameters = 0;
+ if (timeStamp != CDOBranchPoint.INVALID_DATE)
+ {
+ if (exactTime)
+ {
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ builder.append(whereAppend ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("=?"); //$NON-NLS-1$
+ timeParameters = 1;
+ }
+ }
+ else
+ {
+ builder.append(whereAppend ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=?)"); //$NON-NLS-1$
+ timeParameters = 2;
+ }
+ else
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
+ }
+ }
+ }
+
+ 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;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW);
+
+ int column = 1;
+ if (branch != null)
+ {
+ stmt.setInt(column++, branch.getID());
+ }
+
+ for (int i = 0; i < timeParameters; i++)
+ {
+ stmt.setLong(column++, timeStamp);
+ }
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ int version = resultSet.getInt(2);
+
+ if (version >= CDOBranchVersion.FIRST_VERSION)
+ {
+ int branchID = resultSet.getInt(3);
+ CDOBranchVersion branchVersion = branchManager.getBranch(branchID).getVersion(Math.abs(version));
+ InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchVersion,
+ CDORevision.UNCHUNKED, true);
+
+ if (!handler.handleRevision(revision))
+ {
+ break;
+ }
+ }
+ else
+ {
+ // Tell handler about detached IDs
+ InternalCDORevision revision = new DetachedCDORevision(null, id, null, version, 0);
+ handler.handleRevision(revision);
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ 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_BRANCH);
+ builder.append("=? AND "); //$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.setInt(column++, segment.getBranch().getID());
+ 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);
+ }
+ }
+
+ @Override
+ protected String getListXRefsWhere(QueryXRefsContext context)
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(CDODBSchema.ATTRIBUTES_BRANCH);
+ builder.append("=");
+ builder.append(context.getBranch().getID());
+ builder.append(" AND (");
+
+ long timeStamp = context.getTimeStamp();
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0)"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("<=");
+ builder.append(timeStamp);
+ builder.append(" AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("=0 OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(">=");
+ builder.append(timeStamp);
+ builder.append("))"); //$NON-NLS-1$
+ }
+
+ return builder.toString();
+ }
+
+ public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
+ OMMonitor monitor)
+ {
+ monitor.begin();
+
+ try
+ {
+ if (accessor.getTransaction().getBranch() != delta.getBranch())
+ {
+ // new branch -> decide, if branch should be copied
+ if (((HorizontalBranchingMappingStrategyWithRanges)getMappingStrategy()).shallCopyOnBranch())
+ {
+ doCopyOnBranch(accessor, delta, created, monitor.fork());
+ return;
+ }
+ }
+
+ Async async = null;
+
+ try
+ {
+ async = monitor.forkAsync();
+ FeatureDeltaWriter writer = deltaWriter.get();
+ writer.process(accessor, delta, created);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void doCopyOnBranch(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, OMMonitor monitor)
+ {
+ monitor.begin(2);
+ try
+ {
+ InternalRepository repository = (InternalRepository)accessor.getStore().getRepository();
+
+ InternalCDORevision oldRevision = (InternalCDORevision)accessor.getTransaction().getRevision(delta.getID());
+ if (oldRevision == null)
+ {
+ throw new IllegalStateException("Origin revision not found for " + delta);
+ }
+
+ // Make sure all chunks are loaded
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(oldRevision.getEClass()))
+ {
+ if (feature.isMany())
+ {
+ repository.ensureChunk(oldRevision, feature, 0, oldRevision.getList(feature).size());
+ }
+ }
+
+ InternalCDORevision newRevision = oldRevision.copy();
+ newRevision.adjustForCommit(accessor.getTransaction().getBranch(), created);
+ delta.apply(newRevision);
+ monitor.worked();
+ writeRevision(accessor, newRevision, false, true, monitor.fork());
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java
new file mode 100644
index 0000000000..473883b53e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java
@@ -0,0 +1,196 @@
+/**
+ * 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.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+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.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+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.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalBranchingMappingStrategy extends AbstractHorizontalMappingStrategy
+{
+ public HorizontalBranchingMappingStrategy()
+ {
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return true;
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return true;
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return false;
+ }
+
+ @Override
+ public IClassMapping doCreateClassMapping(EClass eClass)
+ {
+ return new HorizontalBranchingClassMapping(this, eClass);
+ }
+
+ @Override
+ public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new BranchingListTableMapping(this, containingClass, feature);
+ }
+
+ @Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new BranchingFeatureMapTableMapping(this, containingClass, feature);
+ }
+
+ @Override
+ protected void rawImportReviseOldRevisions(Connection connection, IDBTable table, OMMonitor monitor)
+ {
+ String sqlUpdate = "UPDATE " + table + " SET " + CDODBSchema.ATTRIBUTES_REVISED + "=? WHERE "
+ + CDODBSchema.ATTRIBUTES_ID + "=? AND " + CDODBSchema.ATTRIBUTES_BRANCH + "=? AND "
+ + CDODBSchema.ATTRIBUTES_VERSION + "=?";
+
+ String sqlQuery = "SELECT cdo1." + CDODBSchema.ATTRIBUTES_ID + ", cdo1." + CDODBSchema.ATTRIBUTES_BRANCH
+ + ", cdo1." + CDODBSchema.ATTRIBUTES_VERSION + ", cdo2." + CDODBSchema.ATTRIBUTES_CREATED + " FROM " + table
+ + " cdo1, " + table + " cdo2 WHERE cdo1." + CDODBSchema.ATTRIBUTES_ID + "=cdo2." + CDODBSchema.ATTRIBUTES_ID
+ + " AND cdo1." + CDODBSchema.ATTRIBUTES_BRANCH + "=cdo2." + CDODBSchema.ATTRIBUTES_BRANCH + " AND (cdo1."
+ + CDODBSchema.ATTRIBUTES_VERSION + "=cdo2." + CDODBSchema.ATTRIBUTES_VERSION + "-1 OR (cdo1."
+ + CDODBSchema.ATTRIBUTES_VERSION + "+cdo2." + CDODBSchema.ATTRIBUTES_VERSION + "=-1 AND cdo1."
+ + CDODBSchema.ATTRIBUTES_VERSION + ">cdo2." + CDODBSchema.ATTRIBUTES_VERSION + ")) AND cdo1."
+ + CDODBSchema.ATTRIBUTES_REVISED + "=0";
+
+ IIDHandler idHandler = getStore().getIDHandler();
+ PreparedStatement stmtUpdate = null;
+ PreparedStatement stmtQuery = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmtUpdate = connection.prepareStatement(sqlUpdate);
+ stmtQuery = connection.prepareStatement(sqlQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+
+ resultSet = stmtQuery.executeQuery();
+ int size = DBUtil.getRowCount(resultSet);
+ if (size == 0)
+ {
+ return;
+ }
+
+ monitor.begin(2 * size);
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ int branch = resultSet.getInt(2);
+ int version = resultSet.getInt(3);
+ long revised = resultSet.getLong(4) - 1L;
+
+ stmtUpdate.setLong(1, revised);
+ idHandler.setCDOID(stmtUpdate, 2, id);
+ stmtUpdate.setInt(3, branch);
+ stmtUpdate.setInt(4, version);
+ stmtUpdate.addBatch();
+ monitor.worked();
+ }
+
+ Async async = monitor.forkAsync(size);
+ try
+ {
+ stmtUpdate.executeBatch();
+ }
+ finally
+ {
+ async.stop();
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(stmtQuery);
+ DBUtil.close(stmtUpdate);
+ monitor.done();
+ }
+ }
+
+ @Override
+ protected void rawImportUnreviseNewRevisions(Connection connection, IDBTable table, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor)
+ {
+ String sqlUpdate = "UPDATE " + table + " SET " + CDODBSchema.ATTRIBUTES_REVISED + "=0 WHERE "
+ + CDODBSchema.ATTRIBUTES_BRANCH + ">=0 AND " + CDODBSchema.ATTRIBUTES_CREATED + "<=" + toCommitTime + " AND "
+ + CDODBSchema.ATTRIBUTES_REVISED + ">" + toCommitTime + " AND " + CDODBSchema.ATTRIBUTES_VERSION + ">0";
+
+ PreparedStatement stmtUpdate = null;
+
+ try
+ {
+ stmtUpdate = connection.prepareStatement(sqlUpdate);
+
+ monitor.begin();
+ Async async = monitor.forkAsync();
+
+ try
+ {
+ stmtUpdate.executeUpdate();
+ }
+ finally
+ {
+ async.stop();
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(stmtUpdate);
+ monitor.done();
+ }
+ }
+
+ @Override
+ public String getListJoin(String attrTable, String listTable)
+ {
+ String join = super.getListJoin(attrTable, listTable);
+ join += " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION;
+ join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION;
+ join += " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_BRANCH;
+ join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_BRANCH;
+ return join;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java
new file mode 100644
index 0000000000..16508c6e0b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java
@@ -0,0 +1,95 @@
+/**
+ * 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 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.server.db.CDODBUtil;
+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.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalBranchingMappingStrategyWithRanges extends AbstractHorizontalMappingStrategy
+{
+ private boolean copyOnBranch;
+
+ public HorizontalBranchingMappingStrategyWithRanges()
+ {
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return true;
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return true;
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return true;
+ }
+
+ public boolean shallCopyOnBranch()
+ {
+ return copyOnBranch;
+ }
+
+ @Override
+ public IClassMapping doCreateClassMapping(EClass eClass)
+ {
+ return new HorizontalBranchingClassMapping(this, eClass);
+ }
+
+ @Override
+ public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new BranchingListTableMappingWithRanges(this, containingClass, feature);
+ }
+
+ @Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new BranchingFeatureMapTableMappingWithRanges(this, containingClass, feature);
+ }
+
+ @Override
+ public String getListJoin(String attrTable, String listTable)
+ {
+ String join = super.getListJoin(attrTable, listTable);
+ join += " AND " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_ADDED;
+ join += "<=" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION;
+ join += " AND (" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED;
+ join += " IS NULL OR " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED;
+ join += ">" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION;
+ join += ") AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_BRANCH;
+ join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_BRANCH;
+ return join;
+ }
+
+ @Override
+ protected void doAfterActivate() throws Exception
+ {
+ super.doAfterActivate();
+
+ String value = getProperties().get(CDODBUtil.PROP_COPY_ON_BRANCH);
+ copyOnBranch = value == null ? false : Boolean.valueOf(value);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java
new file mode 100644
index 0000000000..836014bb38
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java
@@ -0,0 +1,272 @@
+/**
+ * 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.horizontal;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+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.IRepository.Props;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+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.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.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.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+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.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class HorizontalMappingStrategy extends Lifecycle implements IMappingStrategy
+{
+ private Map<String, String> properties;
+
+ private IDBStore store;
+
+ private IMappingStrategy delegate;
+
+ public HorizontalMappingStrategy()
+ {
+ }
+
+ public IMappingStrategy getDelegate()
+ {
+ return delegate;
+ }
+
+ public Map<String, String> getProperties()
+ {
+ if (delegate != null)
+ {
+ return delegate.getProperties();
+ }
+
+ if (properties != null)
+ {
+ return properties;
+ }
+
+ return new HashMap<String, String>();
+ }
+
+ public void setProperties(Map<String, String> properties)
+ {
+ if (delegate != null)
+ {
+ delegate.setProperties(properties);
+ }
+ else
+ {
+ this.properties = properties;
+ }
+ }
+
+ public IDBStore getStore()
+ {
+ if (delegate != null)
+ {
+ return delegate.getStore();
+ }
+
+ return store;
+ }
+
+ public void setStore(IDBStore store)
+ {
+ if (delegate != null)
+ {
+ delegate.setStore(store);
+ }
+ else
+ {
+ this.store = store;
+ }
+ }
+
+ public ITypeMapping createValueMapping(EStructuralFeature feature)
+ {
+ return delegate.createValueMapping(feature);
+ }
+
+ public IListMapping createListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return delegate.createListMapping(containingClass, feature);
+ }
+
+ public String getTableName(ENamedElement element)
+ {
+ return delegate.getTableName(element);
+ }
+
+ public String getTableName(EClass containingClass, EStructuralFeature feature)
+ {
+ return delegate.getTableName(containingClass, feature);
+ }
+
+ public String getFieldName(EStructuralFeature feature)
+ {
+ return delegate.getFieldName(feature);
+ }
+
+ public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ delegate.createMapping(connection, packageUnits, monitor);
+ }
+
+ public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits)
+ {
+ delegate.removeMapping(connection, packageUnits);
+ }
+
+ public IClassMapping getClassMapping(EClass eClass)
+ {
+ return delegate.getClassMapping(eClass);
+ }
+
+ public Map<EClass, IClassMapping> getClassMappings()
+ {
+ return delegate.getClassMappings();
+ }
+
+ public Map<EClass, IClassMapping> getClassMappings(boolean createOnDemand)
+ {
+ return delegate.getClassMappings(createOnDemand);
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return delegate.hasDeltaSupport();
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return delegate.hasAuditSupport();
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return delegate.hasBranchingSupport();
+ }
+
+ public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context)
+ {
+ delegate.queryResources(accessor, context);
+ }
+
+ public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context)
+ {
+ delegate.queryXRefs(accessor, context);
+ }
+
+ public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ return delegate.readObjectType(accessor, id);
+ }
+
+ public CloseableIterator<CDOID> readObjectIDs(IDBStoreAccessor accessor)
+ {
+ return delegate.readObjectIDs(accessor);
+ }
+
+ public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection)
+ {
+ delegate.repairAfterCrash(dbAdapter, connection);
+ }
+
+ public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler)
+ {
+ delegate.handleRevisions(accessor, eClass, branch, timeStamp, exactTime, handler);
+ }
+
+ public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments)
+ {
+ return delegate.readChangeSet(accessor, monitor, segments);
+ }
+
+ public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int lastReplicatedBranchID, int lastBranchID,
+ long lastReplicatedCommitTime, long lastCommitTime) throws IOException
+ {
+ delegate.rawExport(accessor, out, lastReplicatedBranchID, lastBranchID, lastReplicatedCommitTime, lastCommitTime);
+ }
+
+ public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ delegate.rawImport(accessor, in, fromCommitTime, toCommitTime, monitor);
+ }
+
+ public String getListJoin(String attrTable, String listTable)
+ {
+ return delegate.getListJoin(attrTable, listTable);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ boolean auditing = getBooleanProperty(Props.SUPPORTING_AUDITS);
+ boolean branching = getBooleanProperty(Props.SUPPORTING_BRANCHES);
+
+ boolean withRanges = false;
+ if (auditing || branching)
+ {
+ withRanges = getBooleanProperty(CDODBUtil.PROP_WITH_RANGES);
+ }
+
+ delegate = CDODBUtil.createHorizontalMappingStrategy(auditing, branching, withRanges);
+ delegate.setStore(store);
+ delegate.setProperties(properties);
+ LifecycleUtil.activate(delegate);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(delegate);
+ super.doDeactivate();
+ }
+
+ private boolean getBooleanProperty(String prop)
+ {
+ String valueAudits = properties.get(prop);
+ if (valueAudits != null)
+ {
+ return Boolean.valueOf(valueAudits);
+ }
+
+ return false;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java
new file mode 100644
index 0000000000..d37b7144f0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java
@@ -0,0 +1,764 @@
+/**
+ * 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 - 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.CDOBranchPoint;
+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.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.CDORevisionDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
+import org.eclipse.emf.cdo.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+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.IClassMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
+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.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.collection.Pair;
+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.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalNonAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalNonAuditClassMapping.class);
+
+ private String sqlSelectAllObjectIDs;
+
+ private String sqlSelectCurrentAttributes;
+
+ private String sqlInsertAttributes;
+
+ private String sqlUpdateAffix;
+
+ private String sqlUpdatePrefix;
+
+ private String sqlUpdateContainerPart;
+
+ private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>()
+ {
+ @Override
+ protected FeatureDeltaWriter initialValue()
+ {
+ return new FeatureDeltaWriter();
+ }
+ };
+
+ public HorizontalNonAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
+ {
+ super(mappingStrategy, eClass);
+
+ initSQLStrings();
+ }
+
+ private void initSQLStrings()
+ {
+ Map<EStructuralFeature, String> unsettableFields = getUnsettableFields();
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+
+ // ----------- Select Revision ---------------------------
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+
+ for (ITypeMapping singleMapping : getValueMappings())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(singleMapping.getField());
+ }
+
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (String fieldName : listSizeFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append("=?"); //$NON-NLS-1$
+
+ sqlSelectCurrentAttributes = builder.toString();
+
+ // ----------- Insert Attributes -------------------------
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(getTable());
+
+ builder.append("("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+
+ for (ITypeMapping singleMapping : getValueMappings())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(singleMapping.getField());
+ }
+
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (String fieldName : listSizeFields.values())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(fieldName);
+ }
+ }
+
+ builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$
+ for (int i = 0; i < getValueMappings().size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+
+ if (unsettableFields != null)
+ {
+ for (int i = 0; i < unsettableFields.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+ }
+
+ if (listSizeFields != null)
+ {
+ for (int i = 0; i < listSizeFields.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+ }
+
+ builder.append(")"); //$NON-NLS-1$
+ sqlInsertAttributes = builder.toString();
+
+ // ----------- Select all unrevised Object IDs ------
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ sqlSelectAllObjectIDs = builder.toString();
+
+ // ----------- Update attributes --------------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append("=? ,"); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdatePrefix = builder.toString();
+
+ builder = new StringBuilder(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
+ builder.append("=? ,"); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append("=? ,"); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdateContainerPart = builder.toString();
+
+ builder = new StringBuilder(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdateAffix = builder.toString();
+ }
+
+ @Override
+ protected void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+ stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, column++, revision.getID());
+ stmt.setInt(column++, revision.getVersion());
+ stmt.setLong(column++, revision.getTimeStamp());
+ stmt.setLong(column++, revision.getRevised());
+ idHandler.setCDOID(stmt, column++, revision.getResourceID());
+ idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID());
+ stmt.setInt(column++, revision.getContainingFeatureID());
+
+ int isSetCol = column + getValueMappings().size();
+
+ for (ITypeMapping mapping : getValueMappings())
+ {
+ EStructuralFeature feature = mapping.getFeature();
+ if (feature.isUnsettable())
+ {
+ if (revision.getValue(feature) == null)
+ {
+ stmt.setBoolean(isSetCol++, false);
+
+ // also set value column to default value
+ mapping.setDefaultValue(stmt, column++);
+ continue;
+ }
+
+ stmt.setBoolean(isSetCol++, true);
+ }
+
+ mapping.setValueFromRevision(stmt, column++, revision);
+ }
+
+ Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
+ if (listSizeFields != null)
+ {
+ // isSetCol now points to the first listTableSize-column
+ column = isSetCol;
+
+ for (EStructuralFeature feature : listSizeFields.keySet())
+ {
+ CDOList list = revision.getList(feature);
+ stmt.setInt(column++, list.size());
+ }
+ }
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH);
+ }
+
+ public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
+ boolean exactMatch, CDOBranchPoint branchPoint)
+ {
+ long timeStamp = branchPoint.getTimeStamp();
+ if (timeStamp != CDORevision.UNSPECIFIED_DATE)
+ {
+ throw new IllegalArgumentException("Non-audit store does not support explicit timeStamp in resource query"); //$NON-NLS-1$
+ }
+
+ EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name();
+
+ ITypeMapping nameValueMapping = getValueMapping(nameFeature);
+ if (nameValueMapping == null)
+ {
+ throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(">0 AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(nameValueMapping.getField());
+ if (name == null)
+ {
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ }
+ else
+ {
+ builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+
+ stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM);
+ idHandler.setCDOID(stmt, column++, folderId);
+
+ if (name != null)
+ {
+ String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$
+ nameValueMapping.setValue(stmt, column++, queryName);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$
+ }
+
+ return stmt;
+ }
+ catch (SQLException ex)
+ {
+ statementCache.releasePreparedStatement(stmt); // only release on error
+ throw new DBException(ex);
+ }
+ }
+
+ public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ long timeStamp = revision.getTimeStamp();
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ throw new UnsupportedOperationException("Mapping strategy does not support audits"); //$NON-NLS-1$
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, revision.getID());
+
+ // Read singleval-attribute table always (even without modeled attributes!)
+ boolean success = readValuesFromStatement(stmt, revision, accessor);
+
+ // Read multival tables only if revision exists
+ if (success)
+ {
+ readLists(accessor, revision, listChunk);
+ }
+
+ return success;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
+ OMMonitor mon)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlUpdatePrefix + sqlUpdateAffix, ReuseProbability.HIGH);
+ stmt.setInt(1, -version);
+ stmt.setLong(2, timeStamp);
+ idHandler.setCDOID(stmt, 3, id);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
+ OMMonitor monitor)
+ {
+ Async async = null;
+ monitor.begin();
+
+ try
+ {
+ try
+ {
+ async = monitor.forkAsync();
+ FeatureDeltaWriter writer = deltaWriter.get();
+ writer.process(accessor, delta, created);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor
+ {
+ private CDOID id;
+
+ private int oldVersion;
+
+ private long created;
+
+ private IDBStoreAccessor accessor;
+
+ private boolean updateContainer;
+
+ private List<Pair<ITypeMapping, Object>> attributeChanges;
+
+ private List<Pair<EStructuralFeature, Integer>> listSizeChanges;
+
+ private int newContainingFeatureID;
+
+ private CDOID newContainerID;
+
+ private CDOID newResourceID;
+
+ private int branchId;
+
+ private int newVersion;
+
+ /*
+ * this is a temporary copy of the revision to track list size changes...
+ */
+ private InternalCDORevision tempRevision;
+
+ public FeatureDeltaWriter()
+ {
+ attributeChanges = new ArrayList<Pair<ITypeMapping, Object>>();
+ listSizeChanges = new ArrayList<Pair<EStructuralFeature, Integer>>();
+ }
+
+ protected void reset()
+ {
+ attributeChanges.clear();
+ listSizeChanges.clear();
+ updateContainer = false;
+ }
+
+ public void process(IDBStoreAccessor a, CDORevisionDelta d, long c)
+ {
+ // set context
+ id = d.getID();
+
+ branchId = d.getBranch().getID();
+ oldVersion = d.getVersion();
+ newVersion = oldVersion + 1;
+ created = c;
+ accessor = a;
+
+ tempRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id).copy();
+
+ // process revision delta tree
+ d.accept(this);
+
+ updateAttributes();
+ // clean up
+ reset();
+ }
+
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ if (delta.getFeature().isMany())
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ ITypeMapping am = getValueMapping(delta.getFeature());
+ if (am == null)
+ {
+ throw new IllegalArgumentException("AttributeMapping for " + delta.getFeature() + " is null!"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ attributeChanges.add(new Pair<ITypeMapping, Object>(am, delta.getValue()));
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ // TODO: correct this when DBStore implements unsettable features
+ // see Bugs 259868 and 263010
+ ITypeMapping tm = getValueMapping(delta.getFeature());
+ attributeChanges.add(new Pair<ITypeMapping, Object>(tm, null));
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ EStructuralFeature feature = delta.getFeature();
+
+ IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(feature);
+ listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta);
+
+ int oldSize = tempRevision.getList(feature).size();
+ delta.apply(tempRevision);
+ int newSize = tempRevision.getList(feature).size();
+
+ if (oldSize != newSize)
+ {
+ listSizeChanges.add(new Pair<EStructuralFeature, Integer>(feature, newSize));
+ }
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ newContainingFeatureID = delta.getContainerFeatureID();
+ newContainerID = (CDOID)delta.getContainerID();
+ newResourceID = delta.getResourceID();
+ updateContainer = true;
+ }
+
+ private void updateAttributes()
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ int column = 1;
+
+ stmt = statementCache.getPreparedStatement(buildUpdateStatement(), ReuseProbability.MEDIUM);
+ stmt.setInt(column++, newVersion);
+ stmt.setLong(column++, created);
+ if (updateContainer)
+ {
+ idHandler.setCDOID(stmt, column++, newResourceID, created);
+ idHandler.setCDOID(stmt, column++, newContainerID, created);
+ stmt.setInt(column++, newContainingFeatureID);
+ }
+
+ column = setUpdateAttributeValues(attributeChanges, stmt, column);
+ column = setUpdateListSizeChanges(listSizeChanges, stmt, column);
+
+ idHandler.setCDOID(stmt, column++, id);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private String buildUpdateStatement()
+ {
+ StringBuilder builder = new StringBuilder(sqlUpdatePrefix);
+ if (updateContainer)
+ {
+ builder.append(sqlUpdateContainerPart);
+ }
+
+ for (Pair<ITypeMapping, Object> change : attributeChanges)
+ {
+ builder.append(", "); //$NON-NLS-1$
+ ITypeMapping typeMapping = change.getElement1();
+ builder.append(typeMapping.getField());
+ builder.append("=?"); //$NON-NLS-1$
+
+ if (typeMapping.getFeature().isUnsettable())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(getUnsettableFields().get(typeMapping.getFeature()));
+ builder.append("=?"); //$NON-NLS-1$
+ }
+ }
+
+ for (Pair<EStructuralFeature, Integer> change : listSizeChanges)
+ {
+ builder.append(", "); //$NON-NLS-1$
+ EStructuralFeature feature = change.getElement1();
+ builder.append(getListSizeFields().get(feature));
+ builder.append("=?"); //$NON-NLS-1$
+ }
+
+ builder.append(sqlUpdateAffix);
+ return builder.toString();
+ }
+
+ private int setUpdateAttributeValues(List<Pair<ITypeMapping, Object>> attributeChanges, PreparedStatement stmt,
+ int col) throws SQLException
+ {
+ for (Pair<ITypeMapping, Object> change : attributeChanges)
+ {
+ ITypeMapping typeMapping = change.getElement1();
+ Object value = change.getElement2();
+ if (typeMapping.getFeature().isUnsettable())
+ {
+ // feature is unsettable
+ if (value == null)
+ {
+ // feature is unset
+ typeMapping.setDefaultValue(stmt, col++);
+ stmt.setBoolean(col++, false);
+ }
+ else
+ {
+ // feature is set
+ typeMapping.setValue(stmt, col++, value);
+ stmt.setBoolean(col++, true);
+ }
+ }
+ else
+ {
+ typeMapping.setValue(stmt, col++, change.getElement2());
+ }
+ }
+
+ return col;
+ }
+
+ private int setUpdateListSizeChanges(List<Pair<EStructuralFeature, Integer>> attributeChanges,
+ PreparedStatement stmt, int col) throws SQLException
+ {
+ for (Pair<EStructuralFeature, Integer> change : listSizeChanges)
+ {
+ stmt.setInt(col++, change.getElement2());
+ }
+
+ return col;
+ }
+ }
+
+ @Override
+ protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp)
+ {
+ // do nothing
+ }
+
+ @Override
+ protected String getListXRefsWhere(QueryXRefsContext context)
+ {
+ if (CDORevision.UNSPECIFIED_DATE != context.getTimeStamp())
+ {
+ throw new IllegalArgumentException("Non-audit mode does not support timestamp specification");
+ }
+
+ if (!context.getBranch().isMainBranch())
+ {
+ throw new IllegalArgumentException("Non-audit mode does not support branch specification");
+ }
+
+ return CDODBSchema.ATTRIBUTES_REVISED + "=0";
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java
new file mode 100644
index 0000000000..d8f7f6e09d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java
@@ -0,0 +1,62 @@
+/**
+ * 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.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class HorizontalNonAuditMappingStrategy extends AbstractHorizontalMappingStrategy
+{
+ public HorizontalNonAuditMappingStrategy()
+ {
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return false;
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return false;
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return true;
+ }
+
+ @Override
+ public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new NonAuditListTableMapping(this, containingClass, feature);
+ }
+
+ @Override
+ public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return new NonAuditFeatureMapTableMapping(this, containingClass, feature);
+ }
+
+ @Override
+ protected IClassMapping doCreateClassMapping(EClass eClass)
+ {
+ return new HorizontalNonAuditClassMapping(this, eClass);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java
new file mode 100644
index 0000000000..0f8c57ea80
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java
@@ -0,0 +1,590 @@
+/**
+ * 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
+ * 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.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.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.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.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.ImplementationError;
+
+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.SQLException;
+import java.util.Iterator;
+
+/**
+ * This is a featuremap-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta
+ * support.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public class NonAuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping implements IListMappingDeltaSupport
+{
+ private FieldInfo[] keyFields;
+
+ private static final int TEMP_INDEX = -1;
+
+ private static final int UNBOUNDED_MOVE = -1;
+
+ private String sqlClear;
+
+ private String sqlUpdateIndex;
+
+ private String sqlUpdateValue;
+
+ private String sqlDeleteItem;
+
+ private String sqlMoveDownWithLimit;
+
+ private String sqlMoveDown;
+
+ private String sqlMoveUpWithLimit;
+
+ private String sqlMoveUp;
+
+ public NonAuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initSQLStrings();
+ }
+
+ private void initSQLStrings()
+ {
+ // TODO: add key fields length support
+
+ StringBuilder builder = new StringBuilder();
+
+ // ----------- clear list -------------------------
+
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? "); //$NON-NLS-1$
+
+ sqlClear = builder.toString();
+
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+
+ sqlDeleteItem = builder.toString();
+
+ // ----------- update one item index --------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ 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("=? "); //$NON-NLS-1$
+ sqlUpdateIndex = builder.toString();
+
+ // ----------- update one item value --------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append("=?,"); //$NON-NLS-1$
+
+ Iterator<String> iter = getColumnNames().iterator();
+ while (iter.hasNext())
+ {
+ String column = iter.next();
+ builder.append(column);
+ builder.append("=?"); //$NON-NLS-1$
+
+ if (iter.hasNext())
+ {
+ 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("=? "); //$NON-NLS-1$
+ sqlUpdateValue = builder.toString();
+
+ // ----------- move down --------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("-1 WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(">? "); //$NON-NLS-1$
+ sqlMoveDown = builder.toString();
+
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("<=?"); //$NON-NLS-1$
+ sqlMoveDownWithLimit = builder.toString();
+
+ // ----------- move up --------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("+1 WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(">=? "); //$NON-NLS-1$
+ sqlMoveUp = builder.toString();
+
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("<?"); //$NON-NLS-1$
+ sqlMoveUpWithLimit = builder.toString();
+ }
+
+ @Override
+ protected FieldInfo[] getKeyFields()
+ {
+ if (keyFields == null)
+ {
+ DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType();
+ keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, dbType) };
+ }
+
+ return keyFields;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ clearList(accessor, id);
+ }
+
+ /**
+ * 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)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH);
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id);
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Insert a list item at a specified position.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the id of the revision to insert the value
+ * @param index
+ * the index where to insert the element
+ * @param value
+ * the value to insert.
+ */
+ public void insertListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp)
+ {
+ move1up(accessor, id, index, UNBOUNDED_MOVE);
+ insertValue(accessor, id, index, value, timestamp);
+ }
+
+ private void insertValue(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ CDOID tag = getTagByFeature(entryFeature, timestamp);
+ String columnName = getColumnName(tag);
+
+ String sql = sqlInsert;
+
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
+
+ idHandler.setCDOID(stmt, 1, id);
+ int column = getKeyFields().length + 1;
+
+ for (int i = 0; i < getColumnNames().size(); i++)
+ {
+ if (getColumnNames().get(i).equals(columnName))
+ {
+ getTypeMapping(tag).setValue(stmt, column++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(column++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ stmt.setInt(column++, index);
+ idHandler.setCDOID(stmt, column++, tag);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Move a list item from one position to another. Indices between both positions are updated so that the list remains
+ * consistent.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the id of the revision in which to move the item
+ * @param oldPosition
+ * the old position of the item.
+ * @param newPosition
+ * the new position of the item.
+ */
+ public void moveListItem(IDBStoreAccessor accessor, CDOID id, int oldPosition, int newPosition)
+ {
+ if (oldPosition == newPosition)
+ {
+ return;
+ }
+
+ // move element away temporarily
+ updateOneIndex(accessor, id, oldPosition, TEMP_INDEX);
+
+ // move elements in between
+ if (oldPosition < newPosition)
+ {
+ move1down(accessor, id, oldPosition, newPosition);
+ }
+ else
+ {
+ // oldPosition > newPosition -- equal case is handled above
+ move1up(accessor, id, newPosition, oldPosition);
+ }
+
+ // move temporary element to new position
+ updateOneIndex(accessor, id, TEMP_INDEX, newPosition);
+ }
+
+ private void updateOneIndex(IDBStoreAccessor accessor, CDOID id, int oldIndex, int newIndex)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ stmt.setInt(1, newIndex);
+ idHandler.setCDOID(stmt, 2, id);
+ stmt.setInt(3, oldIndex);
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Remove a list item from a specified a position.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the id of the revision from which to remove the item
+ * @param index
+ * the index of the item to remove
+ */
+ public void removeListItem(IDBStoreAccessor accessor, CDOID id, int index)
+ {
+ deleteItem(accessor, id, index);
+ move1down(accessor, id, index, UNBOUNDED_MOVE);
+ }
+
+ /**
+ * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with
+ * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down.
+ */
+ private void move1down(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit,
+ ReuseProbability.HIGH);
+
+ idHandler.setCDOID(stmt, 1, id);
+ stmt.setInt(2, index);
+ if (upperIndex != UNBOUNDED_MOVE)
+ {
+ stmt.setInt(3, upperIndex);
+ }
+
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with
+ * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down.
+ */
+ private void move1up(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit,
+ ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, id);
+ stmt.setInt(2, index);
+ if (upperIndex != UNBOUNDED_MOVE)
+ {
+ stmt.setInt(3, upperIndex);
+ }
+
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, id);
+ stmt.setInt(2, index);
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Set a value at a specified position to the given value.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the id of the revision to set the value
+ * @param index
+ * the index of the item to set
+ * @param value
+ * the value to be set.
+ */
+ public void setListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ CDOID tag = getTagByFeature(entryFeature, timestamp);
+ String columnName = getColumnName(tag);
+ ITypeMapping mapping = getTypeMapping(tag);
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmt, 1, tag);
+ int column = 2;
+
+ for (int i = 0; i < getColumnNames().size(); i++)
+ {
+ if (getColumnNames().get(i).equals(columnName))
+ {
+ mapping.setValue(stmt, column++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(column++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, index);
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
+ final int newVersion, final long created, CDOListFeatureDelta listDelta)
+ {
+ CDOFeatureDeltaVisitor visitor = new CDOFeatureDeltaVisitor()
+ {
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ moveListItem(accessor, id, delta.getOldPosition(), delta.getNewPosition());
+ }
+
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ insertListItem(accessor, id, delta.getIndex(), delta.getValue(), created);
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ removeListItem(accessor, id, delta.getIndex());
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ setListItem(accessor, id, delta.getIndex(), delta.getValue(), created);
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ if (delta.getFeature().isUnsettable())
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id);
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ clearList(accessor, id);
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+ };
+
+ for (CDOFeatureDelta delta : listDelta.getListChanges())
+ {
+ delta.accept(visitor);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java
new file mode 100644
index 0000000000..92d96aad20
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java
@@ -0,0 +1,825 @@
+/**
+ * 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.internal.db.mapping.horizontal;
+
+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.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.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+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.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.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+/**
+ * This is a list-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta support.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class NonAuditListTableMapping extends AbstractListTableMapping implements IListMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, NonAuditListTableMapping.class);
+
+ private FieldInfo[] keyFields;
+
+ private static final int UNBOUNDED_SHIFT = -1;
+
+ /**
+ * The central data structure which is used to calculate the outcomes of the list deltas.
+ */
+ private ArrayList<ManipulationElement> manipulations = new ArrayList<ManipulationElement>();
+
+ /**
+ * This is a flag to remember if a delta of type "clear" has been encountered. If so, the list in the DB has to be
+ * cleared before writing out the changes.
+ */
+ private boolean clearFirst;
+
+ private String sqlClear;
+
+ private String sqlUpdateValue;
+
+ private String sqlUpdateIndex;
+
+ private String sqlInsertValue;
+
+ private String sqlDeleteItem;
+
+ public NonAuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initSQLStrings();
+ }
+
+ private void initSQLStrings()
+ {
+ // ----------- clear list -------------------------
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? "); //$NON-NLS-1$
+
+ sqlClear = builder.toString();
+
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+
+ sqlDeleteItem = builder.toString();
+
+ // ----------- update one item --------------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdateValue = builder.toString();
+
+ // ----------- insert one item --------------------
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ 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$
+ sqlInsertValue = builder.toString();
+
+ // ----------- update one item index --------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdateIndex = builder.toString();
+ }
+
+ @Override
+ protected FieldInfo[] getKeyFields()
+ {
+ if (keyFields == null)
+ {
+ DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType();
+ keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.LIST_REVISION_ID, dbType) };
+ }
+
+ return keyFields;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ clearList(accessor, id);
+ }
+
+ /**
+ * 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)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(sqlClear, ReuseProbability.HIGH);
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id);
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, int branchId, int oldVersion,
+ final int newVersion, long created, CDOListFeatureDelta delta)
+ {
+ CDOBranchPoint main = accessor.getStore().getRepository().getBranchManager().getMainBranch().getHead();
+
+ InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository()
+ .getRevisionManager().getRevision(id, main, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
+ int oldListSize = originalRevision.getList(getFeature()).size();
+
+ // reset the clear-flag
+ clearFirst = false;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$
+ oldListSize);
+ }
+
+ if (manipulations == null)
+ {
+ manipulations = new ArrayList<ManipulationElement>();
+ }
+ else
+ {
+ manipulations.clear();
+ }
+
+ // create list and initialize with original indexes
+ for (int i = 0; i < oldListSize; i++)
+ {
+ manipulations.add(createOriginalElement(i));
+ }
+
+ // let the visitor collect the changes
+ ListDeltaVisitor visitor = new ListDeltaVisitor();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Procssing deltas..."); //$NON-NLS-1$
+ }
+
+ for (CDOFeatureDelta listDelta : delta.getListChanges())
+ {
+ listDelta.accept(visitor);
+ }
+
+ // TODO: here, we could implement further optimizations.
+ // e.g., if more than 50% of the list's items are removed,
+ // it's better to clear the list and reinsert all values
+ // from scratch.
+
+ // finally, write results to the database
+ writeResultToDatabase(accessor, id);
+ }
+
+ /**
+ * Write calculated changes to the database
+ *
+ * @param accessor
+ * ,
+ */
+ private void writeResultToDatabase(IDBStoreAccessor accessor, CDOID id)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ PreparedStatement deleteStmt = null;
+ PreparedStatement moveStmt = null;
+ PreparedStatement setValueStmt = null;
+ PreparedStatement insertStmt = null;
+
+ int deleteCounter = 0;
+ int moveCounter = 0;
+ int setValueCounter = 0;
+ int insertCounter = 0;
+
+ int tempIndex = -1;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing to database:"); //$NON-NLS-1$
+ }
+
+ if (clearFirst)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - clear list"); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id);
+ }
+
+ try
+ {
+ for (ManipulationElement element : manipulations)
+ {
+ if (element.is(ManipulationConstants.DELETE))
+ {
+ /*
+ * Step 1: DELETE all elements e which have e.is(REMOVE) by e.sourceIdx
+ */
+
+ if (deleteStmt == null)
+ {
+ deleteStmt = accessor.getStatementCache().getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH);
+ idHandler.setCDOID(deleteStmt, 1, id);
+ }
+
+ deleteStmt.setInt(2, element.sourceIndex);
+ deleteStmt.addBatch();
+ deleteCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - delete at {0} ", element.sourceIndex); //$NON-NLS-1$
+ }
+ }
+
+ if (element.is(ManipulationConstants.MOVE))
+ {
+ /*
+ * Step 2: MOVE all elements e (by e.sourceIdx) which have e.is(MOVE) to temporary idx (-1, -2, -3, -4, ...)
+ * and store temporary idx in e.tempIndex
+ */
+ if (moveStmt == null)
+ {
+ moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ idHandler.setCDOID(moveStmt, 2, id);
+ }
+
+ moveStmt.setInt(3, element.sourceIndex); // from index
+ moveStmt.setInt(1, --tempIndex); // to index
+ element.tempIndex = tempIndex;
+ moveStmt.addBatch();
+ moveCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.tempIndex); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /*
+ * Step 3: move all elements which have to be shifted up or down because of add, remove or move of other elements
+ * to their proper position. This has to be done in two phases to avoid collisions, as the index has to be unique
+ */
+ int size = manipulations.size();
+
+ /* Step 3a: shift down */
+ for (int i = 0; i < size; i++)
+ {
+ ManipulationElement element = manipulations.get(i);
+
+ if ((element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE)
+ && element.sourceIndex > element.destinationIndex)
+ {
+ if (moveStmt == null)
+ {
+ moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ idHandler.setCDOID(moveStmt, 2, id);
+ }
+
+ moveStmt.setInt(3, element.sourceIndex); // from index
+ moveStmt.setInt(1, element.destinationIndex); // to index
+ moveStmt.addBatch();
+ moveCounter++;
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.destinationIndex); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /* Step 3b: shift up */
+ for (int i = size - 1; i >= 0; i--)
+ {
+ ManipulationElement element = manipulations.get(i);
+
+ if ((element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE)
+ && element.sourceIndex < element.destinationIndex)
+ {
+ if (moveStmt == null)
+ {
+ moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ idHandler.setCDOID(moveStmt, 2, id);
+ }
+
+ moveStmt.setInt(3, element.sourceIndex); // from index
+ moveStmt.setInt(1, element.destinationIndex); // to index
+ moveStmt.addBatch();
+ moveCounter++;
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.destinationIndex); //$NON-NLS-1$
+ }
+ }
+ }
+
+ for (ManipulationElement element : manipulations)
+ {
+ if (element.is(ManipulationConstants.MOVE))
+ {
+ /*
+ * Step 4: MOVE all elements e have e.is(MOVE) from e.tempIdx to e.destinationIdx (because we have moved them
+ * before, moveStmt is always initialized
+ */
+ moveStmt.setInt(3, element.tempIndex); // from index
+ moveStmt.setInt(1, element.destinationIndex); // to index
+ element.tempIndex = tempIndex;
+ moveStmt.addBatch();
+ moveCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.tempIndex, element.destinationIndex); //$NON-NLS-1$
+ }
+ }
+
+ if (element.is(ManipulationConstants.SET_VALUE))
+ {
+ /*
+ * Step 5: SET all elements which have e.type == SET_VALUE by index == e.destinationIdx
+ */
+ if (setValueStmt == null)
+ {
+ setValueStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH);
+ idHandler.setCDOID(setValueStmt, 2, id);
+ }
+
+ setValueStmt.setInt(3, element.destinationIndex);
+ getTypeMapping().setValue(setValueStmt, 1, element.value);
+ setValueStmt.addBatch();
+ setValueCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - set value at {0} to {1} ", element.destinationIndex, element.value); //$NON-NLS-1$
+ }
+ }
+
+ if (element.is(ManipulationConstants.INSERT))
+ {
+ /*
+ * Step 6: INSERT all elements which have e.type == INSERT.
+ */
+ if (insertStmt == null)
+ {
+ insertStmt = accessor.getStatementCache().getPreparedStatement(sqlInsertValue, ReuseProbability.HIGH);
+ idHandler.setCDOID(insertStmt, 1, id);
+ }
+
+ insertStmt.setInt(2, element.destinationIndex);
+ getTypeMapping().setValue(insertStmt, 3, element.value);
+ insertStmt.addBatch();
+ insertCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - insert value at {0} : value {1} ", element.destinationIndex, element.value); //$NON-NLS-1$
+ }
+ }
+ }
+
+ // finally perform all operations
+ if (deleteCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} delete operations", deleteCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(deleteStmt, deleteCounter);
+ }
+
+ if (moveCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(moveStmt, moveCounter);
+ }
+
+ if (insertCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} insert operations", insertCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(insertStmt, insertCounter);
+ }
+
+ if (setValueCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} set operations", setValueCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(setValueStmt, setValueCounter);
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ releaseStatement(accessor, deleteStmt, moveStmt, insertStmt, setValueStmt);
+ }
+ }
+
+ private void releaseStatement(IDBStoreAccessor accessor, PreparedStatement... stmts)
+ {
+ Throwable t = null;
+
+ for (PreparedStatement stmt : stmts)
+ {
+ try
+ {
+ if (stmt != null)
+ {
+ try
+ {
+ stmt.clearBatch();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+ }
+ catch (Throwable th)
+ {
+ if (t == null)
+ {
+ // remember first exception
+ t = th;
+ }
+
+ // more exceptions go to the log
+ OM.LOG.error(t);
+ }
+ }
+
+ if (t != null)
+ {
+ throw new DBException(t);
+ }
+ }
+
+ /**
+ * Helper method: shift all (destination) indexes in the interval [from,to] (inclusive at both ends) by offset
+ * (positive or negative).
+ */
+ private void shiftIndexes(int from, int to, int offset)
+ {
+ for (ManipulationElement e : manipulations)
+ {
+ if (e.destinationIndex >= from && (to == UNBOUNDED_SHIFT || e.destinationIndex <= to))
+ {
+ e.destinationIndex += offset;
+ }
+ }
+ }
+
+ /**
+ * Find a manipulation item by destination index).
+ */
+ private ManipulationElement findElement(int index)
+ {
+ for (ManipulationElement e : manipulations)
+ {
+ if (e.destinationIndex == index)
+ {
+ return e;
+ }
+ }
+
+ // never reached
+ Assert.isTrue(false);
+ return null;
+ }
+
+ /**
+ * Delete an element (used in remove and clear)
+ */
+ private void deleteItem(ManipulationElement e)
+ {
+ if (e.is(ManipulationConstants.INSERT))
+ {
+ // newly inserted items are simply removed, as
+ // removing inserted items is equal to no change at all.
+ manipulations.remove(e);
+ }
+ else
+ {
+ // mark the existing item as to be deleted.
+ // (previous MOVE and SET conditions are overridden by setting
+ // the exclusive DELETE type).
+ e.type = ManipulationConstants.DELETE;
+ e.destinationIndex = ManipulationConstants.NO_INDEX;
+ }
+ }
+
+ /**
+ * Create a ManipulationElement which represents an element which already is in the list.
+ */
+ private ManipulationElement createOriginalElement(int index)
+ {
+ return new ManipulationElement(index, index, ManipulationConstants.NIL, ManipulationConstants.NONE);
+ }
+
+ /**
+ * Create a ManipulationElement which represents an element which is inserted in the list.
+ */
+ private ManipulationElement createInsertedElement(int index, Object value)
+ {
+ return new ManipulationElement(ManipulationConstants.NONE, index, value, ManipulationConstants.INSERT);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class ListDeltaVisitor implements CDOFeatureDeltaVisitor
+ {
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - insert at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$
+ }
+
+ // make room for the new item
+ shiftIndexes(delta.getIndex(), UNBOUNDED_SHIFT, +1);
+
+ // create the item
+ manipulations.add(createInsertedElement(delta.getIndex(), delta.getValue()));
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - remove at {0}", delta.getIndex()); //$NON-NLS-1$
+ }
+
+ ManipulationElement e = findElement(delta.getIndex());
+ deleteItem(e);
+
+ // fill the gap by shifting all subsequent items down
+ shiftIndexes(delta.getIndex() + 1, UNBOUNDED_SHIFT, -1);
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - set at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$
+ }
+
+ ManipulationElement e = findElement(delta.getIndex());
+ // set the new value
+ e.value = delta.getValue();
+
+ // if the item is freshly inserted we do not set the SET-mark.
+ // setting the value of a new item results in inserting with the
+ // new value at once.
+ if (!e.is(ManipulationConstants.INSERT))
+ {
+ // else mark the existing item to be set to a new value
+ e.addType(ManipulationConstants.SET_VALUE);
+ }
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - clear list"); //$NON-NLS-1$
+ }
+
+ // set the clear-flag
+ clearFirst = true;
+
+ // and also clear all manipulation items
+ manipulations.clear();
+ }
+
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ int fromIdx = delta.getOldPosition();
+ int toIdx = delta.getNewPosition();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1}", fromIdx, toIdx); //$NON-NLS-1$
+ }
+
+ // ignore the trivial case
+ if (fromIdx == toIdx)
+ {
+ return;
+ }
+
+ ManipulationElement e = findElement(fromIdx);
+
+ // adjust indexes and shift either up or down
+ if (fromIdx < toIdx)
+ {
+ shiftIndexes(fromIdx + 1, toIdx, -1);
+ }
+ else
+ { // fromIdx > toIdx here
+ shiftIndexes(toIdx, fromIdx - 1, +1);
+ }
+
+ // set the new index
+ e.destinationIndex = toIdx;
+
+ // if it is a new element, no MOVE mark needed, because we insert it
+ // at the new position
+ if (!e.is(ManipulationConstants.INSERT))
+ {
+ // else we need to handle the move of an existing item
+ e.addType(ManipulationConstants.MOVE);
+ }
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ if (delta.getFeature().isUnsettable())
+ {
+ Assert.isTrue(false);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - unset list"); //$NON-NLS-1$
+ }
+
+ // set the clear-flag
+ clearFirst = true;
+
+ // and also clear all manipulation items
+ manipulations.clear();
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ // never called
+ Assert.isTrue(false);
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ // never called
+ Assert.isTrue(false);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class ManipulationConstants
+ {
+ public static final int NO_INDEX = Integer.MIN_VALUE;
+
+ public static final int DELETE = 1 << 4;
+
+ public static final int INSERT = 1 << 3;
+
+ public static final int MOVE = 1 << 2;
+
+ public static final int SET_VALUE = 1 << 1;
+
+ public static final Object NIL = new Object();
+
+ public static final int NONE = 0;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class ManipulationElement
+ {
+ public int type;
+
+ public int sourceIndex;
+
+ public int tempIndex;
+
+ public int destinationIndex;
+
+ public Object value;
+
+ public ManipulationElement(int srcIdx, int dstIdx, Object val, int t)
+ {
+ sourceIndex = srcIdx;
+ tempIndex = ManipulationConstants.NONE;
+ destinationIndex = dstIdx;
+ value = val;
+ type = t;
+ }
+
+ public boolean is(int t)
+ {
+ return (type & t) > 0;
+ }
+
+ public void addType(int t)
+ {
+ type |= t;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java
new file mode 100644
index 0000000000..5f38c818ed
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java
@@ -0,0 +1,98 @@
+/**
+ * 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.horizontal;
+
+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 java.sql.Connection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class ObjectTypeCache extends DelegatingObjectTypeMapper
+{
+ public static final int DEFAULT_CACHE_CAPACITY = 100000;
+
+ private Map<CDOID, CDOID> memoryCache;
+
+ private int cacheSize;
+
+ public ObjectTypeCache(int cacheSize)
+ {
+ this.cacheSize = cacheSize;
+ }
+
+ @Override
+ protected CDOID doGetObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ return memoryCache.get(id);
+ }
+
+ @Override
+ protected void doPutObjectType(IDBStoreAccessor accessor, CDOID id, CDOID type)
+ {
+ memoryCache.put(id, type);
+ }
+
+ @Override
+ protected void doRemoveObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ memoryCache.remove(id);
+ }
+
+ @Override
+ protected CDOID doGetMaxID(Connection connection, IIDHandler idHandler)
+ {
+ return null;
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ memoryCache = Collections.synchronizedMap(new MemoryCache(cacheSize));
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ memoryCache = null;
+ super.doDeactivate();
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private static final class MemoryCache extends LinkedHashMap<CDOID, CDOID>
+ {
+ private static final long serialVersionUID = 1L;
+
+ private int capacity;
+
+ public MemoryCache(int capacity)
+ {
+ super(capacity, 0.75f, true);
+ this.capacity = capacity;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(java.util.Map.Entry<CDOID, CDOID> eldest)
+ {
+ return size() > capacity;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java
new file mode 100644
index 0000000000..c1b575490e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java
@@ -0,0 +1,256 @@
+/**
+ * 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.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.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.CDODBSchema;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBAdapter;
+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.om.monitor.OMMonitor;
+
+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;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class ObjectTypeTable extends AbstractObjectTypeMapper
+{
+ private IDBTable table;
+
+ private IDBField idField;
+
+ private IDBField typeField;
+
+ private IDBField timeField;
+
+ private String sqlDelete;
+
+ private String sqlInsert;
+
+ private String sqlSelect;
+
+ public ObjectTypeTable()
+ {
+ }
+
+ public final CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelect, ReuseProbability.MAX);
+ idHandler.setCDOID(stmt, 1, id);
+ DBUtil.trace(stmt.toString());
+ ResultSet resultSet = stmt.executeQuery();
+
+ if (!resultSet.next())
+ {
+ DBUtil.trace("ClassID for CDOID " + id + " not found"); //$NON-NLS-1$ //$NON-NLS-2$
+ return null;
+ }
+
+ CDOID classID = idHandler.getCDOID(resultSet, 1);
+ EClass eClass = (EClass)getMetaDataManager().getMetaInstance(classID);
+ return new CDOClassifierRef(eClass);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public final void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type)
+ {
+ IDBStore store = getMappingStrategy().getStore();
+ IIDHandler idHandler = store.getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.MAX);
+ idHandler.setCDOID(stmt, 1, id);
+ idHandler.setCDOID(stmt, 2, getMetaDataManager().getMetaID(type, timeStamp));
+ stmt.setLong(3, timeStamp);
+ DBUtil.trace(stmt.toString());
+ int result = stmt.executeUpdate();
+
+ if (result != 1)
+ {
+ throw new DBException("Object type could not be inserted: " + id); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException ex)
+ {
+ // Unique key violation can occur in rare cases (merging new objects from other branches)
+ if (!store.getDBAdapter().isDuplicateKeyException(ex))
+ {
+ throw new DBException(ex);
+ }
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public final void removeObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlDelete, ReuseProbability.MAX);
+ idHandler.setCDOID(stmt, 1, id);
+ DBUtil.trace(stmt.toString());
+ int result = stmt.executeUpdate();
+
+ if (result != 1)
+ {
+ throw new DBException("Object type could not be deleted: " + id); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public CDOID getMaxID(Connection connection, IIDHandler idHandler)
+ {
+ Statement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = connection.createStatement();
+ resultSet = stmt.executeQuery("SELECT MAX(" + idField + ") FROM " + table);
+
+ if (resultSet.next())
+ {
+ return idHandler.getCDOID(resultSet, 1);
+ }
+
+ return null;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(stmt);
+ }
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ String where = " WHERE " + timeField + " BETWEEN " + fromCommitTime + " AND " + toCommitTime;
+ DBUtil.serializeTable(out, connection, table, null, where);
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, OMMonitor monitor) throws IOException
+ {
+ DBUtil.deserializeTable(in, connection, table, monitor);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ IDBStore store = getMappingStrategy().getStore();
+ IDBSchema schema = store.getDBSchema();
+ DBType dbType = store.getIDHandler().getDBType();
+
+ table = schema.addTable(CDODBSchema.CDO_OBJECTS);
+ idField = table.addField(CDODBSchema.ATTRIBUTES_ID, dbType);
+ typeField = table.addField(CDODBSchema.ATTRIBUTES_CLASS, dbType);
+ timeField = table.addField(CDODBSchema.ATTRIBUTES_CREATED, DBType.BIGINT);
+ table.addIndex(IDBIndex.Type.UNIQUE, idField);
+
+ IDBAdapter dbAdapter = store.getDBAdapter();
+ IDBStoreAccessor writer = store.getWriter(null);
+ Connection connection = writer.getConnection();
+ Statement statement = null;
+
+ try
+ {
+ statement = connection.createStatement();
+ dbAdapter.createTable(table, statement);
+ connection.commit();
+ }
+ catch (SQLException ex)
+ {
+ connection.rollback();
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(statement);
+ writer.release();
+ }
+
+ sqlSelect = "SELECT " + typeField + " FROM " + table + " WHERE " + idField + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ sqlInsert = "INSERT INTO " + table + "(" + idField + "," + typeField + "," + timeField + ") VALUES (?, ?, ?)";
+ sqlDelete = "DELETE FROM " + table + " WHERE " + idField + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ table = null;
+ idField = null;
+ typeField = null;
+ super.doDeactivate();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java
new file mode 100644
index 0000000000..a60fb5db17
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java
@@ -0,0 +1,41 @@
+/**
+ * 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:
+ * Victor Roldan Betancort - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.server.internal.db.messages;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * @author Victor Roldan Betancort
+ */
+public class Messages
+{
+ private static final String BUNDLE_NAME = "org.eclipse.emf.cdo.server.internal.db.messages.messages"; //$NON-NLS-1$
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+ private Messages()
+ {
+ }
+
+ public static String getString(String key)
+ {
+ try
+ {
+ return RESOURCE_BUNDLE.getString(key);
+ }
+ catch (MissingResourceException e)
+ {
+ return '!' + key + '!';
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties
new file mode 100644
index 0000000000..e6baaa8ee1
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.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:
+# Victor Roldan Betancort - initial API and implementation
+# Eike Stepper - maintenance
+# Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+
+DBStore.0=dbConnectionProvider is null
+DBStore.1=dbAdapter is null
+DBStore.2=mappingStrategy is null
+DBStore.3=idHandler is null
+DBStore.9=Detected crash of repository {0}
+DBStore.10=Repaired crash of repository {0}: lastObjectID={1}, nextLocalObjectID={2}, lastBranchID={3}, lastCommitTime={4}, lastNonLocalCommitTime={5}
+DBStore.10b=Repaired crash of repository {0}: lastBranchID={1}, lastCommitTime={2}, lastNonLocalCommitTime={3}
+DBStore.11=Repairing crash of repository {0} failed.
+
+
+TypeMappingRegistry.1=No type mapping factory found for {0}: {1} --> DBType.{2}
+TypeMappingRegistry.2=TypeMapping {0} annotated at feature {1} could not be found in registry.
+TypeMappingRegistry.3=Runtime removal of ITypeMapping.Factory extensions is currently not supported.
+TypeMappingRegistry.4=Duplicate source:target typeMapping pairs are currently not supported!
+TypeMappingRegistry.5=Duplicate typeMapping ID: {0}
+
+FactoryTypeParserException.1=Invalid format for typeMapping factoryType {0}
+FactoryTypeParserException.2=EPackage {0} could not be resolved while registering typeMapping factoryType {1}
+FactoryTypeParserException.3=EClassifier {0} could not be resolved while registering typeMapping factoryType {1}
+FactoryTypeParserException.4=DBType {0} could not be resolved while registering typeMapping factoryType {1}
diff --git a/org.eclipse.emf.cdo.server/.classpath b/org.eclipse.emf.cdo.server/.classpath
new file mode 100644
index 0000000000..64c5e31b7a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.options b/org.eclipse.emf.cdo.server/.options
new file mode 100644
index 0000000000..83038cb0e0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.options
@@ -0,0 +1,11 @@
+# Debugging and tracing options
+
+org.eclipse.emf.cdo.server/debug = true
+org.eclipse.emf.cdo.server/debug.protocol = true
+org.eclipse.emf.cdo.server/debug.repository = true
+org.eclipse.emf.cdo.server/debug.session = true
+org.eclipse.emf.cdo.server/debug.transaction = true
+org.eclipse.emf.cdo.server/debug.revision = true
+org.eclipse.emf.cdo.server/debug.resource = true
+org.eclipse.emf.cdo.server/debug.store = true
+org.eclipse.emf.cdo.server/debug.types = true
diff --git a/org.eclipse.emf.cdo.server/.project b/org.eclipse.emf.cdo.server/.project
new file mode 100644
index 0000000000..08924a883b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.project
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.emf.cdo.server</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/.settings/.api_filters b/org.eclipse.emf.cdo.server/.settings/.api_filters
new file mode 100644
index 0000000000..833c77689b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.settings/.api_filters
@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.emf.cdo.server" version="2">
+ <resource path="src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java" type="org.eclipse.emf.cdo.internal.server.DelegatingRepository">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalRepository"/>
+ <message_argument value="CDOCommonRepository"/>
+ <message_argument value="DelegatingRepository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/LockManager.java" type="org.eclipse.emf.cdo.internal.server.LockManager$DurableView">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="DurableView"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="IView"/>
+ <message_argument value="CDOCommonView"/>
+ <message_argument value="DurableView"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/QueryManager.java" type="org.eclipse.emf.cdo.internal.server.QueryManager$QueryContext">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="IQueryContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/Repository.java" type="org.eclipse.emf.cdo.internal.server.Repository">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOReplicationInfo"/>
+ <message_argument value="Repository"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalRepository"/>
+ <message_argument value="CDOCommonRepository"/>
+ <message_argument value="Repository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java" type="org.eclipse.emf.cdo.internal.server.ResourcesQueryHandler$QueryContext">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="QueryResourcesContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java" type="org.eclipse.emf.cdo.internal.server.ServerCDOView">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="ServerCDOView"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java" type="org.eclipse.emf.cdo.internal.server.ServerCDOView$ServerCDOSession">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDORepositoryInfo"/>
+ <message_argument value="ServerCDOSession"/>
+ </message_arguments>
+ </filter>
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="InternalCDOSession"/>
+ <message_argument value="ServerCDOSession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/Session.java" type="org.eclipse.emf.cdo.internal.server.Session">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalSession"/>
+ <message_argument value="CDOCommonSession"/>
+ <message_argument value="Session"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java" type="org.eclipse.emf.cdo.internal.server.TransactionCommitContext$DeltaLockWrapper">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOIDAndBranch"/>
+ <message_argument value="DeltaLockWrapper"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java" type="org.eclipse.emf.cdo.internal.server.TransactionCommitContext$XRefContext">
+ <filter id="572522506">
+ <message_arguments>
+ <message_argument value="CDOIDReference"/>
+ <message_argument value="XRefContext"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="QueryXRefsContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="XRefContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/View.java" type="org.eclipse.emf.cdo.internal.server.View">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="View"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalView"/>
+ <message_argument value="CDOCommonView"/>
+ <message_argument value="View"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java" type="org.eclipse.emf.cdo.internal.server.XRefsQueryHandler$QueryContext">
+ <filter id="572522506">
+ <message_arguments>
+ <message_argument value="CDOIDReference"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="QueryXRefsContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java" type="org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration$RepositoryInfo">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDORepositoryInfo"/>
+ <message_argument value="RepositoryInfo"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java" type="org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionProtocol">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOSessionProtocol"/>
+ <message_argument value="EmbeddedClientSessionProtocol"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java" type="org.eclipse.emf.cdo.internal.server.embedded.EmbeddedServerSessionProtocol">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="ISessionProtocol"/>
+ <message_argument value="CDOProtocol"/>
+ <message_argument value="EmbeddedServerSessionProtocol"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java" type="org.eclipse.emf.cdo.internal.server.mem.MEMStore">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="DurableLocking"/>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="MEMStore"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java" type="org.eclipse.emf.cdo.internal.server.mem.MEMStoreAccessor">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="DurableLocking"/>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="MEMStoreAccessor"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java" type="org.eclipse.emf.cdo.internal.server.syncing.OfflineClone$CommitContextData">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOCommitData"/>
+ <message_argument value="CommitContextData"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IQueryContext.java" type="org.eclipse.emf.cdo.server.IQueryContext">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="IQueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IRepository.java" type="org.eclipse.emf.cdo.server.IRepository">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOCommonRepository"/>
+ <message_argument value="IRepository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/ISession.java" type="org.eclipse.emf.cdo.server.ISession">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOCommonSession"/>
+ <message_argument value="ISession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IStoreAccessor.java" type="org.eclipse.emf.cdo.server.IStoreAccessor$DurableLocking">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="DurableLocking"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IStoreAccessor.java" type="org.eclipse.emf.cdo.server.IStoreAccessor$QueryResourcesContext">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryResourcesContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IStoreAccessor.java" type="org.eclipse.emf.cdo.server.IStoreAccessor$QueryXRefsContext">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryXRefsContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/ITransaction.java" type="org.eclipse.emf.cdo.server.ITransaction">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOCommonTransaction"/>
+ <message_argument value="ITransaction"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IView.java" type="org.eclipse.emf.cdo.server.IView">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOCommonView"/>
+ <message_argument value="IView"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/embedded/CDOSession.java" type="org.eclipse.emf.cdo.server.embedded.CDOSession">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOSession"/>
+ <message_argument value="CDOSession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java" type="org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOSessionConfiguration"/>
+ <message_argument value="CDOSessionConfiguration"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java" type="org.eclipse.emf.cdo.spi.server.DurableLockArea">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="LockArea"/>
+ <message_argument value="DurableLockArea"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java" type="org.eclipse.emf.cdo.spi.server.ISessionProtocol">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOProtocol"/>
+ <message_argument value="ISessionProtocol"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java" type="org.eclipse.emf.cdo.spi.server.InternalLockManager">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="InternalLockManager"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/InternalSession.java" type="org.eclipse.emf.cdo.spi.server.InternalSession">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="InternalSession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java" type="org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOReplicationContext"/>
+ <message_argument value="InternalSynchronizableRepository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/Store.java" type="org.eclipse.emf.cdo.spi.server.Store">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="ExactMatch"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="Store"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..9d1e69b075
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Mon Jul 04 13:03:48 CEST 2011
+eclipse.preferences.version=1
+encoding//model/org.eclipse.emf.cdo.defs.ecorediag=UTF-8
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..611d1a92fb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.settings/org.eclipse.jdt.launching.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.launching.prefs
new file mode 100644
index 0000000000..4658ec1435
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..4277817dad
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.settings/org.eclipse.ltk.core.refactoring.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.ltk.core.refactoring.prefs
new file mode 100644
index 0000000000..864e30fe5d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..b050639a54
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..2f50f36c0c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000000..b0e52183ad
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,94 @@
+#Mon May 16 19:22:50 CEST 2011
+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/.settings/org.eclipse.pde.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000000..c6b96bb45e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/.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/CDOServer.launch b/org.eclipse.emf.cdo.server/CDOServer.launch
new file mode 100644
index 0000000000..bd3d8e91a1
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/CDOServer.launch
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.pde.ui.RuntimeWorkbench">
+<booleanAttribute key="append.args" value="true"/>
+<stringAttribute key="application" value="org.eclipse.emf.cdo.server.app"/>
+<booleanAttribute key="askclear" value="true"/>
+<booleanAttribute key="automaticAdd" value="false"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="org.eclipse.emf.cdo.server,org.eclipse.emf.cdo.server.db,org.eclipse.net4j,org.eclipse.net4j.db,org.eclipse.net4j.db.derby,org.eclipse.net4j.http.common,org.eclipse.net4j.http.server,org.eclipse.net4j.tcp,org.eclipse.net4j.util"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="false"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/CDOServer"/>
+<booleanAttribute key="default" value="false"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../cdo.server"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+<mapEntry key="[run]" value="org.eclipse.pde.ui.RuntimeWorkbench"/>
+</mapAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}&#13;&#10;-debug&#13;&#10;-console"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m&#13;&#10;-Xmx1024m&#13;&#10;-Ddebug=true&#13;&#10;-Dnet4j.config=&quot;${project_loc:/org.eclipse.emf.cdo.server.product}/config&quot;&#13;&#10;-Dorg.eclipse.emf.cdo.server.browser.port=7777&#13;&#10;-Dorg.osgi.service.http.port=8080"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.platform.ide"/>
+<stringAttribute key="selectedPlugin" value="org.eclipse.emf.cdo"/>
+<stringAttribute key="selected_target_plugins" value="com.mysql.jdbc@default:default,javax.servlet@default:default,org.apache.derby@default:default,org.eclipse.ant.core@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem.win32.x86_64@default:false,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime.compatibility.auth@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore.change@default:default,org.eclipse.emf.ecore.xmi@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.eclipse.team.core@default:default,org.h2@default:default,org.hsqldb@default:default,org.postgresql.jdbc3@default:default"/>
+<stringAttribute key="selected_workspace_plugins" value="com.mongodb@default:default,org.eclipse.emf.cdo.common@default:default,org.eclipse.emf.cdo.examples.company@default:default,org.eclipse.emf.cdo.server.db@default:default,org.eclipse.emf.cdo.server.mongodb@default:default,org.eclipse.emf.cdo.server.net4j@default:default,org.eclipse.emf.cdo.server@default:default,org.eclipse.emf.cdo@default:default,org.eclipse.net4j.db.derby@default:default,org.eclipse.net4j.db.h2@default:default,org.eclipse.net4j.db.hsqldb@default:default,org.eclipse.net4j.db.mysql@default:default,org.eclipse.net4j.db.postgresql@default:default,org.eclipse.net4j.db@default:default,org.eclipse.net4j.tcp@default:default,org.eclipse.net4j.util@default:default,org.eclipse.net4j@default:default"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<stringAttribute key="templateConfig" value="${target_home}\configuration\config.ini"/>
+<booleanAttribute key="tracing" value="true"/>
+<mapAttribute key="tracingOptions">
+<mapEntry key="org.eclipse.core.contenttype/debug" value="false"/>
+<mapEntry key="org.eclipse.core.expressions/tracePropertyResolving" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/beginend" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/errorondeadlock" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/locks" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/shutdown" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/timing" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/delta" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/failure" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/interrupt" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/invoking" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuild" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuildstack" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/stacktrace" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype/cache" value="false"/>
+<mapEntry key="org.eclipse.core.resources/debug" value="false"/>
+<mapEntry key="org.eclipse.core.resources/history" value="false"/>
+<mapEntry key="org.eclipse.core.resources/natures" value="false"/>
+<mapEntry key="org.eclipse.core.resources/perf/builders" value="10000"/>
+<mapEntry key="org.eclipse.core.resources/perf/listeners" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/save.participants" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/snapshot" value="1000"/>
+<mapEntry key="org.eclipse.core.resources/preferences" value="false"/>
+<mapEntry key="org.eclipse.core.resources/refresh" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/snapshots" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/strings" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/compatibility/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug/context" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf/success" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/preferences/plugin" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachecopy" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachelookup" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/connect" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/commands" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/events" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/contextlaunching" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/launchhistory" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/contentProvider" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/deltas" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/model" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/updateSequence" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/viewer" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.reading" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.writing" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server.db/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.types" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.adapter" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.object" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.transaction" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.util" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.view" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/perf" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision.loading" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/general" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/get" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/set" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug/events" value="false"/>
+<mapEntry key="org.eclipse.help.base/debug" value="true"/>
+<mapEntry key="org.eclipse.help.base/debug/search" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser/inprocess" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/infopop" value="false"/>
+<mapEntry key="org.eclipse.help.webapp/debug" value="true"/>
+<mapEntry key="org.eclipse.help.webapp/debug/workingsets" value="false"/>
+<mapEntry key="org.eclipse.help/debug" value="true"/>
+<mapEntry key="org.eclipse.help/debug/context" value="false"/>
+<mapEntry key="org.eclipse.help/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core.manipulation/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug/buffermanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/builder" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/compiler" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/completion" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution/advanced" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/hierarchy" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/indexmanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta/verbose" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel/cache" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/postaction" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/resolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/selection" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/sourcemapper" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/zipaccess" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/perf/completion" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/containerinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/javadeltalistener" value="500"/>
+<mapEntry key="org.eclipse.jdt.core/perf/reconcile" value="1000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/selection" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/variableinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ASTProvider" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ResultCollector" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/TypeConstraints" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist/extensions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist_sorters/extensions" value=""/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/RefactorActionGroup" value="150"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/createPartControl" value="1300"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/makeActions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/search/participants" value="300"/>
+<mapEntry key="org.eclipse.jface.text.source/debug/RevisionRulerColumn" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/AnnotationPainter" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/ContentAssistSubjectAdapters" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/FastPartitioner/PositionCache" value="false"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/checkConditions" value="300"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/createChanges" value="300"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.common/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.debug/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server.jdbc/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jvm/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.tcp/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.concurrency" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle.dump" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.om" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.registry" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.acceptor" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer.stream" value="false"/>
+<mapEntry key="org.eclipse.net4j/debug.channel" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.connector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.selector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.signal" value="true"/>
+<mapEntry key="org.eclipse.net4j/perf" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/bundleTime" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/events" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/filter" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/loader" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/manifest" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/messageBundles" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/packageadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/security" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/services" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/startlevel" value="false"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/buffersize" value="256"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logfilename" value=""/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logsynchronously" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/converter/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/location" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin/resolver" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/classes" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/resources" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/benchmark" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/impl" value="org.eclipse.osgi.internal.profile.DefaultProfileLogger"/>
+<mapEntry key="org.eclipse.osgi/profile/startup" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/cycles" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/generics" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/grouping" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/imports" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/requires" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/wiring" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/classLoading" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/filename" value="runtime.traces"/>
+<mapEntry key="org.eclipse.osgi/trace/filters" value="trace.properties"/>
+<mapEntry key="org.eclipse.pde.build/debug" value="false"/>
+<mapEntry key="org.eclipse.pde.core/cache" value="false"/>
+<mapEntry key="org.eclipse.pde.core/classpath" value="false"/>
+<mapEntry key="org.eclipse.pde.core/debug" value="true"/>
+<mapEntry key="org.eclipse.pde.core/validation" value="false"/>
+<mapEntry key="org.eclipse.team.core/backgroundevents" value="false"/>
+<mapEntry key="org.eclipse.team.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.core/refreshjob" value="false"/>
+<mapEntry key="org.eclipse.team.core/streams" value="false"/>
+<mapEntry key="org.eclipse.team.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/cvsprotocol" value="true"/>
+<mapEntry key="org.eclipse.team.cvs.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/dirtycaching" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/metafiles" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/syncchangeevents" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/ssh_protocol" value="false"/>
+<mapEntry key="org.eclipse.ui.browser/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/gc" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/internalerror/openDialog" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/undomonitor" value="false"/>
+<mapEntry key="org.eclipse.ui.intro.universal/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/flags/noBrowser" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/printHTML" value="false"/>
+<mapEntry key="org.eclipse.ui.workbench/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/contributions" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/declaredImages" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/job.stale" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/showAllJobs" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/swtdebug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/workingSets" value="false"/>
+<mapEntry key="org.eclipse.ui/experimental/menus" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPropertyChangeListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPartReference" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPageListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPerspectiveListener" value="false"/>
+<mapEntry key="org.eclipse.ui/perf/contentTypes" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/page.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.activate" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.control" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.init" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.input" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.switch" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/showHeapStatus" value="true"/>
+<mapEntry key="org.eclipse.ui/perf/uijob" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.restore" value="30000"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.start" value="45000"/>
+<mapEntry key="org.eclipse.ui/trace/commands" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/graphics" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose.commandId" value=""/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/multipageeditor" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/sources" value="false"/>
+<mapEntry key="org.eclipse.update.configurator/debug" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug" value="true"/>
+<mapEntry key="org.eclipse.update.core/debug/configuration" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/install" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/installhandler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/parsing" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/reconciler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/type" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/warning" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/web" value="false"/>
+</mapAttribute>
+<booleanAttribute key="useCustomFeatures" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<booleanAttribute key="useNamedJRE" value="true"/>
+<booleanAttribute key="useProduct" value="false"/>
+<booleanAttribute key="usefeatures" value="false"/>
+</launchConfiguration>
diff --git a/org.eclipse.emf.cdo.server/CDOServer_SSL.launch b/org.eclipse.emf.cdo.server/CDOServer_SSL.launch
new file mode 100644
index 0000000000..a668fd5ffd
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/CDOServer_SSL.launch
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.pde.ui.RuntimeWorkbench">
+<booleanAttribute key="append.args" value="true"/>
+<stringAttribute key="application" value="org.eclipse.emf.cdo.server.app"/>
+<booleanAttribute key="askclear" value="true"/>
+<booleanAttribute key="automaticAdd" value="false"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="org.eclipse.emf.cdo.server,org.eclipse.emf.cdo.server.db,org.eclipse.net4j,org.eclipse.net4j.db,org.eclipse.net4j.db.derby,org.eclipse.net4j.http.common,org.eclipse.net4j.http.server,org.eclipse.net4j.tcp,org.eclipse.net4j.util"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="false"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/CDOServer_SSL"/>
+<booleanAttribute key="default" value="false"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../cdo.server"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+<mapEntry key="[run]" value="org.eclipse.pde.ui.RuntimeWorkbench"/>
+</mapAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}&#13;&#10;-debug&#13;&#10;-console"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m&#13;&#10;-Xmx1024m&#13;&#10;-Ddebug=true&#13;&#10;-Dnet4j.config=&quot;${resource_loc:/org.eclipse.emf.cdo.server.product-feature/rootfiles/configuration-ssl}&quot;&#13;&#10;-Dorg.eclipse.net4j.tcp.ssl.passphrase=ab987c&#10;-Dorg.eclipse.net4j.tcp.ssl.trust=file:///${workspace_loc:org.eclipse.emf.cdo.server}/sslKey/testTrust&#10;-Dorg.eclipse.net4j.tcp.ssl.key=file:///${workspace_loc:org.eclipse.emf.cdo.server}/sslKey/testKeys&#10;-Dorg.eclipse.emf.cdo.server.browser.port=7777&#13;&#10;-Dorg.osgi.service.http.port=8080"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.platform.ide"/>
+<stringAttribute key="selectedPlugin" value="org.eclipse.emf.cdo"/>
+<stringAttribute key="selected_target_plugins" value="com.mysql.jdbc@default:default,javax.servlet@default:default,org.apache.derby@default:default,org.eclipse.ant.core@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem.win32.x86_64@default:false,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime.compatibility.auth@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore.change@default:default,org.eclipse.emf.ecore.xmi@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.h2@default:default,org.hsqldb@default:default,org.postgresql.jdbc3@default:default"/>
+<stringAttribute key="selected_workspace_plugins" value="org.eclipse.emf.cdo.common@default:default,org.eclipse.emf.cdo.server.db@default:default,org.eclipse.emf.cdo.server.net4j@default:default,org.eclipse.emf.cdo.server@default:default,org.eclipse.emf.cdo@default:default,org.eclipse.net4j.db.derby@default:default,org.eclipse.net4j.db.h2@default:default,org.eclipse.net4j.db.hsqldb@default:default,org.eclipse.net4j.db.mysql@default:default,org.eclipse.net4j.db.postgresql@default:default,org.eclipse.net4j.db@default:default,org.eclipse.net4j.tcp@default:default,org.eclipse.net4j.util@default:default,org.eclipse.net4j@default:default"/>
+<booleanAttribute key="show_selected_only" value="true"/>
+<stringAttribute key="templateConfig" value="${target_home}\configuration\config.ini"/>
+<booleanAttribute key="tracing" value="true"/>
+<mapAttribute key="tracingOptions">
+<mapEntry key="org.eclipse.core.contenttype/debug" value="false"/>
+<mapEntry key="org.eclipse.core.expressions/tracePropertyResolving" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/beginend" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/errorondeadlock" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/locks" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/shutdown" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/timing" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/delta" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/failure" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/interrupt" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/invoking" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuild" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuildstack" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/stacktrace" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype/cache" value="false"/>
+<mapEntry key="org.eclipse.core.resources/debug" value="false"/>
+<mapEntry key="org.eclipse.core.resources/history" value="false"/>
+<mapEntry key="org.eclipse.core.resources/natures" value="false"/>
+<mapEntry key="org.eclipse.core.resources/perf/builders" value="10000"/>
+<mapEntry key="org.eclipse.core.resources/perf/listeners" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/save.participants" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/snapshot" value="1000"/>
+<mapEntry key="org.eclipse.core.resources/preferences" value="false"/>
+<mapEntry key="org.eclipse.core.resources/refresh" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/snapshots" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/strings" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/compatibility/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug/context" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf/success" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/preferences/plugin" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachecopy" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachelookup" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/connect" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/commands" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/events" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/contextlaunching" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/launchhistory" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/contentProvider" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/deltas" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/model" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/updateSequence" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/viewer" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.reading" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.writing" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server.db/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.types" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.adapter" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.object" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.transaction" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.util" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.view" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/perf" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision.loading" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/general" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/get" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/set" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug/events" value="false"/>
+<mapEntry key="org.eclipse.help.base/debug" value="true"/>
+<mapEntry key="org.eclipse.help.base/debug/search" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser/inprocess" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/infopop" value="false"/>
+<mapEntry key="org.eclipse.help.webapp/debug" value="true"/>
+<mapEntry key="org.eclipse.help.webapp/debug/workingsets" value="false"/>
+<mapEntry key="org.eclipse.help/debug" value="true"/>
+<mapEntry key="org.eclipse.help/debug/context" value="false"/>
+<mapEntry key="org.eclipse.help/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core.manipulation/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug/buffermanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/builder" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/compiler" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/completion" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution/advanced" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/hierarchy" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/indexmanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta/verbose" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel/cache" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/postaction" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/resolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/selection" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/sourcemapper" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/zipaccess" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/perf/completion" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/containerinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/javadeltalistener" value="500"/>
+<mapEntry key="org.eclipse.jdt.core/perf/reconcile" value="1000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/selection" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/variableinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ASTProvider" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ResultCollector" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/TypeConstraints" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist/extensions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist_sorters/extensions" value=""/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/RefactorActionGroup" value="150"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/createPartControl" value="1300"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/makeActions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/search/participants" value="300"/>
+<mapEntry key="org.eclipse.jface.text.source/debug/RevisionRulerColumn" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/AnnotationPainter" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/ContentAssistSubjectAdapters" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/FastPartitioner/PositionCache" value="false"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/checkConditions" value="300"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/createChanges" value="300"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.common/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.debug/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server.jdbc/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jvm/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.tcp/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.concurrency" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle.dump" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.om" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.registry" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.acceptor" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer.stream" value="false"/>
+<mapEntry key="org.eclipse.net4j/debug.channel" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.connector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.selector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.signal" value="true"/>
+<mapEntry key="org.eclipse.net4j/perf" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/bundleTime" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/events" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/filter" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/loader" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/manifest" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/messageBundles" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/packageadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/security" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/services" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/startlevel" value="false"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/buffersize" value="256"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logfilename" value=""/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logsynchronously" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/converter/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/location" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin/resolver" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/classes" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/resources" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/benchmark" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/impl" value="org.eclipse.osgi.internal.profile.DefaultProfileLogger"/>
+<mapEntry key="org.eclipse.osgi/profile/startup" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/cycles" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/generics" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/grouping" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/imports" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/requires" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/wiring" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/classLoading" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/filename" value="runtime.traces"/>
+<mapEntry key="org.eclipse.osgi/trace/filters" value="trace.properties"/>
+<mapEntry key="org.eclipse.pde.build/debug" value="false"/>
+<mapEntry key="org.eclipse.pde.core/cache" value="false"/>
+<mapEntry key="org.eclipse.pde.core/classpath" value="false"/>
+<mapEntry key="org.eclipse.pde.core/debug" value="true"/>
+<mapEntry key="org.eclipse.pde.core/validation" value="false"/>
+<mapEntry key="org.eclipse.team.core/backgroundevents" value="false"/>
+<mapEntry key="org.eclipse.team.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.core/refreshjob" value="false"/>
+<mapEntry key="org.eclipse.team.core/streams" value="false"/>
+<mapEntry key="org.eclipse.team.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/cvsprotocol" value="true"/>
+<mapEntry key="org.eclipse.team.cvs.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/dirtycaching" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/metafiles" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/syncchangeevents" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/ssh_protocol" value="false"/>
+<mapEntry key="org.eclipse.ui.browser/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/gc" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/internalerror/openDialog" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/undomonitor" value="false"/>
+<mapEntry key="org.eclipse.ui.intro.universal/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/flags/noBrowser" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/printHTML" value="false"/>
+<mapEntry key="org.eclipse.ui.workbench/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/contributions" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/declaredImages" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/job.stale" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/showAllJobs" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/swtdebug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/workingSets" value="false"/>
+<mapEntry key="org.eclipse.ui/experimental/menus" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPropertyChangeListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPartReference" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPageListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPerspectiveListener" value="false"/>
+<mapEntry key="org.eclipse.ui/perf/contentTypes" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/page.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.activate" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.control" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.init" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.input" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.switch" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/showHeapStatus" value="true"/>
+<mapEntry key="org.eclipse.ui/perf/uijob" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.restore" value="30000"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.start" value="45000"/>
+<mapEntry key="org.eclipse.ui/trace/commands" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/graphics" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose.commandId" value=""/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/multipageeditor" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/sources" value="false"/>
+<mapEntry key="org.eclipse.update.configurator/debug" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug" value="true"/>
+<mapEntry key="org.eclipse.update.core/debug/configuration" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/install" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/installhandler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/parsing" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/reconciler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/type" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/warning" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/web" value="false"/>
+</mapAttribute>
+<booleanAttribute key="useCustomFeatures" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<booleanAttribute key="useNamedJRE" value="true"/>
+<booleanAttribute key="useProduct" value="false"/>
+<booleanAttribute key="usefeatures" value="false"/>
+</launchConfiguration>
diff --git a/org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF b/org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..b35cecd3ec
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF
@@ -0,0 +1,29 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.emf.cdo.server;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.internal.server.bundle.OM$Activator
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ClassPath: .
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)";resolution:=optional,
+ org.eclipse.emf.cdo;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
+ org.eclipse.net4j.util;bundle-version="[3.1.0,4.0.0)";visibility:=reexport
+Export-Package: org.eclipse.emf.cdo.internal.server;version="4.1.0";
+ x-friends:="org.eclipse.emf.cdo.server.db,
+ org.eclipse.emf.cdo.server.net4j,
+ org.eclipse.emf.cdo.tests,
+ org.eclipse.emf.cdo.workspace,
+ org.eclipse.emf.cdo.server.hibernate",
+ org.eclipse.emf.cdo.internal.server.bundle;version="4.1.0";x-internal:=true,
+ org.eclipse.emf.cdo.internal.server.embedded;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests",
+ org.eclipse.emf.cdo.internal.server.mem;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests",
+ org.eclipse.emf.cdo.internal.server.messages;version="4.1.0";x-internal:=true,
+ org.eclipse.emf.cdo.internal.server.syncing;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests",
+ org.eclipse.emf.cdo.server;version="4.1.0",
+ org.eclipse.emf.cdo.server.embedded;version="4.1.0",
+ org.eclipse.emf.cdo.server.mem;version="4.1.0",
+ org.eclipse.emf.cdo.spi.server;version="4.1.0"
diff --git a/org.eclipse.emf.cdo.server/about.html b/org.eclipse.emf.cdo.server/about.html
new file mode 100644
index 0000000000..d35d5aed64
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/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/about.ini b/org.eclipse.emf.cdo.server/about.ini
new file mode 100644
index 0000000000..b7e87ca285
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/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/about.mappings b/org.eclipse.emf.cdo.server/about.mappings
new file mode 100644
index 0000000000..bddaab4310
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/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/about.properties b/org.eclipse.emf.cdo.server/about.properties
new file mode 100644
index 0000000000..24af1ffb0c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/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
+featureText = CDO Model Repository Server\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/build.properties b/org.eclipse.emf.cdo.server/build.properties
new file mode 100644
index 0000000000..2c3acd3537
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/build.properties
@@ -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
+
+# NLS_MESSAGEFORMAT_VAR
+
+bin.includes = .,\
+ META-INF/,\
+ plugin.properties,\
+ .options,\
+ about.html,\
+ copyright.txt,\
+ plugin.xml,\
+ schema/,\
+ about.ini,\
+ about.mappings,\
+ about.properties,\
+ modeling32.png
+jars.compile.order = .
+source.. = src/
+output.. = bin/
+src.includes = about.html,\
+ copyright.txt,\
+ CDOServer.launch,\
+ CDOServer_SSL.launch
+
+org.eclipse.emf.cdo.releng.javadoc.project = org.eclipse.emf.cdo.doc
diff --git a/org.eclipse.emf.cdo.server/copyright.txt b/org.eclipse.emf.cdo.server/copyright.txt
new file mode 100644
index 0000000000..e921242cf0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/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/modeling32.png b/org.eclipse.emf.cdo.server/modeling32.png
new file mode 100644
index 0000000000..6b08de2ada
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/modeling32.png
Binary files differ
diff --git a/org.eclipse.emf.cdo.server/plugin.properties b/org.eclipse.emf.cdo.server/plugin.properties
new file mode 100644
index 0000000000..9cc5cf8608
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/plugin.properties
@@ -0,0 +1,17 @@
+# 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
+providerName = Eclipse Modeling Project
+
+app.name = CDOServer
+
+extension-point.name = CDO Store Factories
+extension-point.name.0 = CDO Repository Factories
+extension-point.name.1 = CDO Application Extensions
diff --git a/org.eclipse.emf.cdo.server/plugin.xml b/org.eclipse.emf.cdo.server/plugin.xml
new file mode 100644
index 0000000000..124577ba01
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/plugin.xml
@@ -0,0 +1,54 @@
+<?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="storeFactories" name="%extension-point.name" schema="schema/storeFactories.exsd"/>
+ <extension-point id="repositoryFactories" name="%extension-point.name.0" schema="schema/repositoryFactories.exsd"/>
+ <extension-point id="appExtensions" name="%extension-point.name.1" schema="schema/appExtensions.exsd"/>
+
+ <extension point="org.eclipse.net4j.util.factories">
+ <factory
+ productGroup="org.eclipse.net4j.Negotiators"
+ type="challenge"
+ class="org.eclipse.net4j.util.security.ChallengeNegotiatorFactory"/>
+ <factory
+ productGroup="org.eclipse.emf.cdo.server.browsers"
+ type="default"
+ class="org.eclipse.emf.cdo.server.CDOServerBrowser$ContainerBased$Factory"/>
+ </extension>
+
+ <extension point="org.eclipse.net4j.util.elementProcessors">
+ <elementProcessor class="org.eclipse.net4j.util.security.ChallengeNegotiatorConfigurer"/>
+ <elementProcessor class="org.eclipse.emf.cdo.spi.server.RepositoryUserManager$RepositoryInjector"/>
+ </extension>
+
+ <extension point="org.eclipse.emf.cdo.server.repositoryFactories">
+ <repositoryFactory
+ class="org.eclipse.emf.cdo.spi.server.RepositoryFactory"
+ repositoryType="default"/>
+ </extension>
+
+ <extension point="org.eclipse.emf.cdo.server.storeFactories">
+ <storeFactory
+ class="org.eclipse.emf.cdo.internal.server.mem.MEMStoreFactory"
+ storeType="mem"/>
+ </extension>
+
+ <extension id="app" point="org.eclipse.core.runtime.applications" name="%app.name">
+ <application cardinality="1" thread="any">
+ <run class="org.eclipse.emf.cdo.internal.server.bundle.CDOServerApplication"/>
+ </application>
+ </extension>
+
+</plugin>
diff --git a/org.eclipse.emf.cdo.server/schema/appExtensions.exsd b/org.eclipse.emf.cdo.server/schema/appExtensions.exsd
new file mode 100644
index 0000000000..3b3f70e5a5
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/schema/appExtensions.exsd
@@ -0,0 +1,103 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.cdo.server" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.emf.cdo.server" id="appExtensions" name="Application Extensions"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="appExtension" 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="appExtension">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.emf.cdo.spi.server.IAppExtension"/>
+ </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="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/schema/repositoryFactories.exsd b/org.eclipse.emf.cdo.server/schema/repositoryFactories.exsd
new file mode 100644
index 0000000000..1cdabf2f3b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/schema/repositoryFactories.exsd
@@ -0,0 +1,113 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.cdo.server">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.emf.cdo.server" id="repositoryFactories" name="CDO Repository Factories"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="repositoryFactory" 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="repositoryFactory">
+ <complexType>
+ <attribute name="repositoryType" 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.IRepositoryFactory"/>
+ </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/schema/storeFactories.exsd b/org.eclipse.emf.cdo.server/schema/storeFactories.exsd
new file mode 100644
index 0000000000..b6cd6bd4ec
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/schema/storeFactories.exsd
@@ -0,0 +1,113 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.cdo.server">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.emf.cdo.server" id="storeFactories" name="CDO Store Factories"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="storeFactory" 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="storeFactory">
+ <complexType>
+ <attribute name="storeType" 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.IStoreFactory"/>
+ </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/src/org/eclipse/emf/cdo/internal/server/CommitManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java
new file mode 100644
index 0000000000..9a0d7598b0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java
@@ -0,0 +1,191 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalCommitManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+public class CommitManager extends Lifecycle implements InternalCommitManager
+{
+ private InternalRepository repository;
+
+ @ExcludeFromDump
+ private transient ExecutorService executors;
+
+ private boolean shutdownExecutorService;
+
+ @ExcludeFromDump
+ private transient Map<InternalTransaction, TransactionCommitContextEntry> contextEntries = new ConcurrentHashMap<InternalTransaction, TransactionCommitContextEntry>();
+
+ public CommitManager()
+ {
+ }
+
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ public void setRepository(InternalRepository repository)
+ {
+ this.repository = repository;
+ }
+
+ public synchronized ExecutorService getExecutors()
+ {
+ if (executors == null)
+ {
+ shutdownExecutorService = true;
+ executors = Executors.newFixedThreadPool(10);
+ }
+
+ return executors;
+ }
+
+ public synchronized void setExecutors(ExecutorService executors)
+ {
+ if (shutdownExecutorService)
+ {
+ this.executors.shutdown();
+ shutdownExecutorService = false;
+ }
+
+ this.executors = executors;
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ super.doDeactivate();
+ setExecutors(null);
+ }
+
+ /**
+ * Create a future to execute commitContext in a different thread.
+ */
+ public void preCommit(InternalCommitContext commitContext, OMMonitor monitor)
+ {
+ TransactionCommitContextEntry contextEntry = new TransactionCommitContextEntry(monitor);
+ contextEntry.setContext(commitContext);
+
+ Future<Object> future = getExecutors().submit(contextEntry.createCallable());
+ contextEntry.setFuture(future);
+
+ contextEntries.put(commitContext.getTransaction(), contextEntry);
+ }
+
+ /**
+ * Called after a commitContext is done successfully or not.
+ */
+ public void remove(InternalCommitContext commitContext)
+ {
+ contextEntries.remove(commitContext.getTransaction());
+ }
+
+ public void rollback(InternalCommitContext commitContext)
+ {
+ TransactionCommitContextEntry contextEntry = contextEntries.get(commitContext.getTransaction());
+ if (contextEntry != null)
+ {
+ contextEntry.getFuture().cancel(true);
+ commitContext.rollback("Remote rollback"); //$NON-NLS-1$
+ commitContext.postCommit(false);
+ }
+ }
+
+ /**
+ * Waiting for a commit to be done.
+ */
+ public void waitForTermination(InternalTransaction transaction) throws InterruptedException, ExecutionException
+ {
+ TransactionCommitContextEntry contextEntry = contextEntries.get(transaction);
+ contextEntry.getFuture().get();
+ }
+
+ public InternalCommitContext get(InternalTransaction transaction)
+ {
+ TransactionCommitContextEntry contextEntry = contextEntries.get(transaction);
+ if (contextEntry != null)
+ {
+ return contextEntry.getContext();
+ }
+
+ return null;
+ }
+
+ /**
+ * @author Simon McDuff
+ */
+ private static final class TransactionCommitContextEntry
+ {
+ private InternalCommitContext context;
+
+ private Future<Object> future;
+
+ private OMMonitor monitor;
+
+ public TransactionCommitContextEntry(OMMonitor monitor)
+ {
+ this.monitor = monitor;
+ }
+
+ public Callable<Object> createCallable()
+ {
+ return new Callable<Object>()
+ {
+ public Object call() throws Exception
+ {
+ context.write(monitor);
+ return null;
+ }
+ };
+ }
+
+ public InternalCommitContext getContext()
+ {
+ return context;
+ }
+
+ public void setContext(InternalCommitContext context)
+ {
+ this.context = context;
+ }
+
+ public Future<Object> getFuture()
+ {
+ return future;
+ }
+
+ public void setFuture(Future<Object> future)
+ {
+ this.future = future;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java
new file mode 100644
index 0000000000..f724419603
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java
@@ -0,0 +1,109 @@
+/**
+ * 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.internal.server;
+
+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.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.ITransaction;
+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.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.emf.ecore.EClass;
+
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ */
+public abstract class DelegatingCommitContext implements IStoreAccessor.CommitContext
+{
+ protected abstract CommitContext getDelegate();
+
+ public ITransaction getTransaction()
+ {
+ return getDelegate().getTransaction();
+ }
+
+ public CDOBranchPoint getBranchPoint()
+ {
+ return getDelegate().getBranchPoint();
+ }
+
+ public String getUserID()
+ {
+ return getDelegate().getUserID();
+ }
+
+ public String getCommitComment()
+ {
+ return getDelegate().getCommitComment();
+ }
+
+ public boolean isAutoReleaseLocksEnabled()
+ {
+ return getDelegate().isAutoReleaseLocksEnabled();
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry()
+ {
+ return getDelegate().getPackageRegistry();
+ }
+
+ public InternalCDOPackageUnit[] getNewPackageUnits()
+ {
+ return getDelegate().getNewPackageUnits();
+ }
+
+ public InternalCDORevision[] getNewObjects()
+ {
+ return getDelegate().getNewObjects();
+ }
+
+ public InternalCDORevision[] getDirtyObjects()
+ {
+ return getDelegate().getDirtyObjects();
+ }
+
+ public InternalCDORevisionDelta[] getDirtyObjectDeltas()
+ {
+ return getDelegate().getDirtyObjectDeltas();
+ }
+
+ public CDOID[] getDetachedObjects()
+ {
+ return getDelegate().getDetachedObjects();
+ }
+
+ public Map<CDOID, EClass> getDetachedObjectTypes()
+ {
+ return getDelegate().getDetachedObjectTypes();
+ }
+
+ public CDORevision getRevision(CDOID id)
+ {
+ return getDelegate().getRevision(id);
+ }
+
+ public Map<CDOID, CDOID> getIDMappings()
+ {
+ return getDelegate().getIDMappings();
+ }
+
+ public String getRollbackMessage()
+ {
+ return getDelegate().getRollbackMessage();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java
new file mode 100644
index 0000000000..8c4fd91a3b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java
@@ -0,0 +1,286 @@
+/**
+ * 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.internal.server;
+
+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.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.IQueryHandlerProvider;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo;
+import org.eclipse.emf.cdo.spi.server.InternalCommitManager;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.InternalQueryManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
+import org.eclipse.emf.cdo.spi.server.InternalStore;
+
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ */
+public abstract class DelegatingRepository implements InternalRepository
+{
+ public DelegatingRepository()
+ {
+ }
+
+ protected abstract InternalRepository getDelegate();
+
+ public void addHandler(Handler handler)
+ {
+ getDelegate().addHandler(handler);
+ }
+
+ public void addListener(IListener listener)
+ {
+ getDelegate().addListener(listener);
+ }
+
+ public long[] createCommitTimeStamp(OMMonitor monitor)
+ {
+ return getDelegate().createCommitTimeStamp(monitor);
+ }
+
+ public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart,
+ int chunkEnd)
+ {
+ return getDelegate().ensureChunk(revision, feature, chunkStart, chunkEnd);
+ }
+
+ public InternalCommitManager getCommitManager()
+ {
+ return getDelegate().getCommitManager();
+ }
+
+ public long getCreationTime()
+ {
+ return getDelegate().getCreationTime();
+ }
+
+ public Object[] getElements()
+ {
+ return getDelegate().getElements();
+ }
+
+ public long getLastCommitTimeStamp()
+ {
+ return getDelegate().getLastCommitTimeStamp();
+ }
+
+ public IListener[] getListeners()
+ {
+ return getDelegate().getListeners();
+ }
+
+ public InternalLockManager getLockManager()
+ {
+ return getDelegate().getLockManager();
+ }
+
+ public String getName()
+ {
+ return getDelegate().getName();
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry()
+ {
+ return getDelegate().getPackageRegistry();
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext)
+ {
+ return getDelegate().getPackageRegistry(considerCommitContext);
+ }
+
+ public Map<String, String> getProperties()
+ {
+ return getDelegate().getProperties();
+ }
+
+ public IQueryHandler getQueryHandler(CDOQueryInfo info)
+ {
+ return getDelegate().getQueryHandler(info);
+ }
+
+ public IQueryHandlerProvider getQueryHandlerProvider()
+ {
+ return getDelegate().getQueryHandlerProvider();
+ }
+
+ public InternalQueryManager getQueryManager()
+ {
+ return getDelegate().getQueryManager();
+ }
+
+ public InternalCDORevisionManager getRevisionManager()
+ {
+ return getDelegate().getRevisionManager();
+ }
+
+ public InternalSessionManager getSessionManager()
+ {
+ return getDelegate().getSessionManager();
+ }
+
+ public InternalStore getStore()
+ {
+ return getDelegate().getStore();
+ }
+
+ public String getUUID()
+ {
+ return getDelegate().getUUID();
+ }
+
+ public boolean hasListeners()
+ {
+ return getDelegate().hasListeners();
+ }
+
+ public boolean isEmpty()
+ {
+ return getDelegate().isEmpty();
+ }
+
+ public boolean isSupportingAudits()
+ {
+ return getDelegate().isSupportingAudits();
+ }
+
+ public boolean isSupportingBranches()
+ {
+ return getDelegate().isSupportingBranches();
+ }
+
+ public EPackage[] loadPackages(CDOPackageUnit packageUnit)
+ {
+ return getDelegate().loadPackages(packageUnit);
+ }
+
+ public InternalCDOBranchManager getBranchManager()
+ {
+ return getDelegate().getBranchManager();
+ }
+
+ public void setBranchManager(InternalCDOBranchManager branchManager)
+ {
+ getDelegate().setBranchManager(branchManager);
+ }
+
+ public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
+ {
+ return getDelegate().createBranch(branchID, branchInfo);
+ }
+
+ public BranchInfo loadBranch(int branchID)
+ {
+ return getDelegate().loadBranch(branchID);
+ }
+
+ public SubBranchInfo[] loadSubBranches(int branchID)
+ {
+ return getDelegate().loadSubBranches(branchID);
+ }
+
+ public List<InternalCDORevision> loadRevisions(List<RevisionInfo> infos, CDOBranchPoint branchPoint,
+ int referenceChunk, int prefetchDepth)
+ {
+ return getDelegate().loadRevisions(infos, branchPoint, referenceChunk, prefetchDepth);
+ }
+
+ public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk)
+ {
+ return getDelegate().loadRevisionByVersion(id, branchVersion, referenceChunk);
+ }
+
+ public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions,
+ List<CDORevision> additionalRevisions)
+ {
+ getDelegate().notifyReadAccessHandlers(session, revisions, additionalRevisions);
+ }
+
+ public void notifyWriteAccessHandlers(ITransaction transaction, CommitContext commitContext, boolean beforeCommit,
+ OMMonitor monitor)
+ {
+ getDelegate().notifyWriteAccessHandlers(transaction, commitContext, beforeCommit, monitor);
+ }
+
+ public void removeHandler(Handler handler)
+ {
+ getDelegate().removeHandler(handler);
+ }
+
+ public void removeListener(IListener listener)
+ {
+ getDelegate().removeListener(listener);
+ }
+
+ public void setName(String name)
+ {
+ getDelegate().setName(name);
+ }
+
+ public void setProperties(Map<String, String> properties)
+ {
+ getDelegate().setProperties(properties);
+ }
+
+ public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider)
+ {
+ getDelegate().setQueryHandlerProvider(queryHandlerProvider);
+ }
+
+ public void setRevisionManager(InternalCDORevisionManager revisionManager)
+ {
+ getDelegate().setRevisionManager(revisionManager);
+ }
+
+ public void setSessionManager(InternalSessionManager sessionManager)
+ {
+ getDelegate().setSessionManager(sessionManager);
+ }
+
+ public void setStore(InternalStore store)
+ {
+ getDelegate().setStore(store);
+ }
+
+ public long getTimeStamp()
+ {
+ return getDelegate().getTimeStamp();
+ }
+
+ public void validateTimeStamp(long timeStamp) throws IllegalArgumentException
+ {
+ getDelegate().validateTimeStamp(timeStamp);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java
new file mode 100644
index 0000000000..5db93a490f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java
@@ -0,0 +1,694 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ * Caspar De Groot - write options
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonView;
+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.revision.CDOIDAndBranch;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.ISessionManager;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking;
+import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalStore;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+
+import org.eclipse.net4j.util.CheckUtil;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.concurrent.RWOLockManager;
+import org.eclipse.net4j.util.container.ContainerEventAdapter;
+import org.eclipse.net4j.util.container.IContainer;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.ILifecycle;
+import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
+import org.eclipse.net4j.util.options.IOptionsContainer;
+
+import java.text.MessageFormat;
+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 Simon McDuff
+ * @since 3.0
+ */
+public class LockManager extends RWOLockManager<Object, IView> implements InternalLockManager
+{
+ private InternalRepository repository;
+
+ private Map<String, InternalView> openViews = new HashMap<String, InternalView>();
+
+ private Map<String, DurableView> durableViews = new HashMap<String, DurableView>();
+
+ @ExcludeFromDump
+ private transient IListener sessionListener = new ContainerEventAdapter<IView>()
+ {
+ @Override
+ protected void onRemoved(IContainer<IView> container, IView view)
+ {
+ String durableLockingID = view.getDurableLockingID();
+ if (durableLockingID == null)
+ {
+ unlock(view);
+ }
+ else
+ {
+ changeContext(view, new DurableView(durableLockingID));
+ unregisterOpenView(durableLockingID);
+ }
+ }
+ };
+
+ @ExcludeFromDump
+ private transient IListener sessionManagerListener = new ContainerEventAdapter<ISession>()
+ {
+ @Override
+ protected void onAdded(IContainer<ISession> container, ISession session)
+ {
+ session.addListener(sessionListener);
+ }
+
+ @Override
+ protected void onRemoved(IContainer<ISession> container, ISession session)
+ {
+ session.removeListener(sessionListener);
+ }
+ };
+
+ public LockManager()
+ {
+ }
+
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ public void setRepository(InternalRepository repository)
+ {
+ this.repository = repository;
+ }
+
+ public synchronized Object getLockEntryObject(Object key)
+ {
+ LockState<Object, IView> lockState = getObjectToLocksMap().get(key);
+ return lockState == null ? null : lockState.getLockedObject();
+ }
+
+ public Object getLockKey(CDOID id, CDOBranch branch)
+ {
+ if (repository.isSupportingBranches())
+ {
+ return CDOIDUtil.createIDAndBranch(id, branch);
+ }
+
+ return id;
+ }
+
+ public synchronized Map<CDOID, LockGrade> getLocks(final IView view)
+ {
+ final Map<CDOID, LockGrade> result = new HashMap<CDOID, LockGrade>();
+
+ for (LockState<Object, IView> lockState : getObjectToLocksMap().values())
+ {
+ LockGrade grade = LockGrade.NONE;
+ if (lockState.hasLock(LockType.READ, view, false))
+ {
+ grade = grade.getUpdated(LockType.READ, true);
+ }
+
+ if (lockState.hasLock(LockType.WRITE, view, false))
+ {
+ grade = grade.getUpdated(LockType.WRITE, true);
+ }
+
+ if (lockState.hasLock(LockType.OPTION, view, false))
+ {
+ grade = grade.getUpdated(LockType.OPTION, true);
+ }
+
+ if (grade != LockGrade.NONE)
+ {
+ CDOID id = getLockKeyID(lockState.getLockedObject());
+ result.put(id, grade);
+ }
+ }
+
+ return result;
+ }
+
+ @Deprecated
+ public void lock(boolean explicit, LockType type, IView view, Collection<? extends Object> objectsToLock, long timeout)
+ throws InterruptedException
+ {
+ lock2(explicit, type, view, objectsToLock, timeout);
+ }
+
+ public List<LockState<Object, IView>> lock2(boolean explicit, LockType type, IView view,
+ Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException
+ {
+ String durableLockingID = null;
+ DurableLocking accessor = null;
+
+ if (explicit)
+ {
+ durableLockingID = view.getDurableLockingID();
+ if (durableLockingID != null)
+ {
+ accessor = getDurableLocking();
+ }
+ }
+
+ List<LockState<Object, IView>> newLockStates = super.lock2(type, view, objectsToLock, timeout);
+
+ if (accessor != null)
+ {
+ accessor.lock(durableLockingID, type, objectsToLock);
+ }
+
+ return newLockStates;
+ }
+
+ @Deprecated
+ public synchronized void unlock(boolean explicit, LockType type, IView view,
+ Collection<? extends Object> objectsToUnlock)
+ {
+ unlock2(explicit, type, view, objectsToUnlock);
+ }
+
+ public synchronized List<LockState<Object, IView>> unlock2(boolean explicit, LockType type, IView view,
+ Collection<? extends Object> objectsToUnlock)
+ {
+ List<LockState<Object, IView>> newLockStates = super.unlock2(type, view, objectsToUnlock);
+
+ if (explicit)
+ {
+ String durableLockingID = view.getDurableLockingID();
+ if (durableLockingID != null)
+ {
+ DurableLocking accessor = getDurableLocking();
+ accessor.unlock(durableLockingID, type, objectsToUnlock);
+ }
+ }
+
+ return newLockStates;
+ }
+
+ @Deprecated
+ public synchronized void unlock(boolean explicit, IView view)
+ {
+ unlock(explicit, view);
+ }
+
+ public synchronized List<LockState<Object, IView>> unlock2(boolean explicit, IView view)
+ {
+ if (explicit)
+ {
+ String durableLockingID = view.getDurableLockingID();
+ if (durableLockingID != null)
+ {
+ DurableLocking accessor = getDurableLocking();
+ accessor.unlock(durableLockingID);
+ }
+ }
+
+ return super.unlock2(view);
+ }
+
+ public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ return createLockArea(userID, branchPoint, readOnly, locks, null);
+ }
+
+ private LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks, String lockAreaID)
+ {
+ if (lockAreaID == null)
+ {
+ DurableLocking accessor = getDurableLocking();
+ return accessor.createLockArea(userID, branchPoint, readOnly, locks);
+ }
+
+ DurableLocking2 accessor = getDurableLocking2();
+ return accessor.createLockArea(lockAreaID, userID, branchPoint, readOnly, locks);
+ }
+
+ public LockArea createLockArea(InternalView view)
+ {
+ return createLockArea(view, null);
+ }
+
+ public LockArea createLockArea(InternalView view, String lockAreaID)
+ {
+ String userID = view.getSession().getUserID();
+ CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view);
+ boolean readOnly = view.isReadOnly();
+ Map<CDOID, LockGrade> locks = getLocks(view);
+
+ LockArea area = createLockArea(userID, branchPoint, readOnly, locks, lockAreaID);
+ synchronized (openViews)
+ {
+ openViews.put(area.getDurableLockingID(), view);
+ }
+
+ return area;
+ }
+
+ public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
+ {
+ DurableLocking accessor = getDurableLocking();
+ return accessor.getLockArea(durableLockingID);
+ }
+
+ public void getLockAreas(String userIDPrefix, LockArea.Handler handler)
+ {
+ if (userIDPrefix == null)
+ {
+ userIDPrefix = "";
+ }
+
+ DurableLocking accessor = getDurableLocking();
+ accessor.getLockAreas(userIDPrefix, handler);
+ }
+
+ public void deleteLockArea(String durableLockingID)
+ {
+ DurableLocking accessor = getDurableLocking();
+ accessor.deleteLockArea(durableLockingID);
+ unregisterOpenView(durableLockingID);
+ }
+
+ public IView openView(ISession session, int viewID, boolean readOnly, final String durableLockingID)
+ {
+ synchronized (openViews)
+ {
+ InternalView view = openViews.get(durableLockingID);
+ if (view != null)
+ {
+ throw new IllegalStateException("Durable view is already open: " + view);
+ }
+
+ LockArea area = getLockArea(durableLockingID);
+ if (area.isReadOnly() != readOnly)
+ {
+ throw new IllegalStateException("Durable read-only state does not match the request");
+ }
+
+ if (readOnly)
+ {
+ view = (InternalView)session.openView(viewID, area);
+ }
+ else
+ {
+ view = (InternalView)session.openTransaction(viewID, area);
+ }
+
+ changeContext(new DurableView(durableLockingID), view);
+ view.setDurableLockingID(durableLockingID);
+ view.addListener(new LifecycleEventAdapter()
+ {
+ @Override
+ protected void onDeactivated(ILifecycle lifecycle)
+ {
+ synchronized (openViews)
+ {
+ openViews.remove(durableLockingID);
+ }
+ }
+ });
+
+ openViews.put(durableLockingID, view);
+ return view;
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ loadDurableLocks();
+ getRepository().getSessionManager().addListener(sessionManagerListener);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ ISessionManager sessionManager = getRepository().getSessionManager();
+ sessionManager.removeListener(sessionManagerListener);
+ for (ISession session : sessionManager.getSessions())
+ {
+ session.removeListener(sessionListener);
+ }
+
+ super.doDeactivate();
+ }
+
+ private DurableLocking getDurableLocking()
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ if (accessor instanceof DurableLocking)
+ {
+ return (DurableLocking)accessor;
+ }
+
+ throw new IllegalStateException("Store does not implement " + DurableLocking.class.getSimpleName());
+ }
+
+ private DurableLocking2 getDurableLocking2()
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ if (accessor instanceof DurableLocking2)
+ {
+ return (DurableLocking2)accessor;
+ }
+
+ throw new IllegalStateException("Store does not implement " + DurableLocking2.class.getSimpleName());
+ }
+
+ private void loadDurableLocks()
+ {
+ InternalStore store = repository.getStore();
+ IStoreAccessor reader = null;
+
+ try
+ {
+ reader = store.getReader(null);
+ if (reader instanceof DurableLocking)
+ {
+ StoreThreadLocal.setAccessor(reader);
+
+ DurableLockLoader handler = new DurableLockLoader();
+ getLockAreas(null, handler);
+ }
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ private void unregisterOpenView(String durableLockingID)
+ {
+ synchronized (openViews)
+ {
+ InternalView view = openViews.remove(durableLockingID);
+ if (view != null)
+ {
+ view.setDurableLockingID(null);
+ }
+ }
+ }
+
+ public CDOID getLockKeyID(Object key)
+ {
+ if (key instanceof CDOID)
+ {
+ return (CDOID)key;
+ }
+
+ if (key instanceof CDOIDAndBranch)
+ {
+ return ((CDOIDAndBranch)key).getID();
+ }
+
+ throw new ImplementationError("Unexpected lock object: " + key);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class DurableView implements IView, CDOCommonView.Options
+ {
+ private String durableLockingID;
+
+ public DurableView(String durableLockingID)
+ {
+ this.durableLockingID = durableLockingID;
+ }
+
+ public String getDurableLockingID()
+ {
+ return durableLockingID;
+ }
+
+ public int getViewID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isReadOnly()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOBranch getBranch()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public long getTimeStamp()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDORevision getRevision(CDOID id)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isClosed()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public IRepository getRepository()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public ISession getSession()
+ {
+ return null;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return durableLockingID.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (obj instanceof DurableView)
+ {
+ DurableView that = (DurableView)obj;
+ return durableLockingID.equals(that.getDurableLockingID());
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("DurableView[{0}]", durableLockingID);
+ }
+
+ public IOptionsContainer getContainer()
+ {
+ return null;
+ }
+
+ public void addListener(IListener listener)
+ {
+ }
+
+ public void removeListener(IListener listener)
+ {
+ }
+
+ public boolean hasListeners()
+ {
+ return false;
+ }
+
+ public IListener[] getListeners()
+ {
+ return null;
+ }
+
+ public Options options()
+ {
+ return this;
+ }
+
+ public boolean isLockNotificationEnabled()
+ {
+ return false;
+ }
+
+ public void setLockNotificationEnabled(boolean enabled)
+ {
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class DurableLockLoader implements LockArea.Handler
+ {
+ public DurableLockLoader()
+ {
+ }
+
+ public boolean handleLockArea(LockArea area)
+ {
+ String durableLockingID = area.getDurableLockingID();
+ IView view = durableViews.get(durableLockingID);
+ if (view == null)
+ {
+ view = new DurableView(durableLockingID);
+ durableViews.put(durableLockingID, (DurableView)view);
+ }
+
+ Collection<Object> readLocks = new ArrayList<Object>();
+ Collection<Object> writeLocks = new ArrayList<Object>();
+ Collection<Object> writeOptions = new ArrayList<Object>();
+ for (Entry<CDOID, LockGrade> entry : area.getLocks().entrySet())
+ {
+ Object key = getLockKey(entry.getKey(), area.getBranch());
+ LockGrade grade = entry.getValue();
+ if (grade.isRead())
+ {
+ readLocks.add(key);
+ }
+
+ if (grade.isWrite())
+ {
+ writeLocks.add(key);
+ }
+
+ if (grade.isOption())
+ {
+ writeOptions.add(key);
+ }
+ }
+
+ try
+ {
+ lock(LockType.READ, view, readLocks, 1000L);
+ lock(LockType.WRITE, view, writeLocks, 1000L);
+ lock(LockType.OPTION, view, writeOptions, 1000L);
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+
+ return true;
+ }
+ }
+
+ public LockGrade getLockGrade(Object key)
+ {
+ LockState<Object, IView> lockState = getObjectToLocksMap().get(key);
+ LockGrade grade = LockGrade.NONE;
+ if (lockState != null)
+ {
+ for (LockType type : LockType.values())
+ {
+ if (lockState.hasLock(type))
+ {
+ grade = grade.getUpdated(type, true);
+ }
+ }
+ }
+ return grade;
+ }
+
+ private IView getView(String lockAreaID)
+ {
+ IView view = openViews.get(lockAreaID);
+ if (view == null)
+ {
+ view = durableViews.get(lockAreaID);
+ }
+ return view;
+ }
+
+ private LockArea getLockAreaNoEx(String durableLockingID)
+ {
+ try
+ {
+ return getLockArea(durableLockingID);
+ }
+ catch (LockAreaNotFoundException e)
+ {
+ return null;
+ }
+ }
+
+ public void updateLockArea(LockArea lockArea)
+ {
+ String durableLockingID = lockArea.getDurableLockingID();
+ DurableLocking2 accessor = getDurableLocking2();
+
+ if (lockArea.isMissing())
+ {
+ LockArea localLockArea = getLockAreaNoEx(durableLockingID);
+ if (localLockArea != null && localLockArea.getLocks().size() > 0)
+ {
+ accessor.deleteLockArea(durableLockingID);
+ DurableView deletedView = durableViews.remove(durableLockingID);
+ CheckUtil.checkNull(deletedView, "deletedView");
+ }
+ }
+ else
+ {
+ accessor.updateLockArea(lockArea);
+ IView view = getView(durableLockingID);
+ if (view != null)
+ {
+ unlock2(view);
+ }
+ new DurableLockLoader().handleLockArea(lockArea);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java
new file mode 100644
index 0000000000..963092d439
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java
@@ -0,0 +1,319 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.IQueryContext;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
+import org.eclipse.emf.cdo.spi.server.InternalQueryManager;
+import org.eclipse.emf.cdo.spi.server.InternalQueryResult;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+
+import org.eclipse.net4j.util.container.IContainerDelta.Kind;
+import org.eclipse.net4j.util.container.SingleDeltaContainerEvent;
+import org.eclipse.net4j.util.event.IEvent;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+public class QueryManager extends Lifecycle implements InternalQueryManager
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SESSION, QueryManager.class);
+
+ private InternalRepository repository;
+
+ private Map<Integer, QueryContext> queryContexts = new ConcurrentHashMap<Integer, QueryContext>();
+
+ private ExecutorService executors;
+
+ private boolean shutdownExecutorService;
+
+ private int nextQuery;
+
+ private boolean allowInterruptRunningQueries = true;
+
+ public QueryManager()
+ {
+ }
+
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ public void setRepository(InternalRepository repository)
+ {
+ this.repository = repository;
+
+ String value = repository.getProperties().get(IRepository.Props.ALLOW_INTERRUPT_RUNNING_QUERIES);
+ if (value != null)
+ {
+ allowInterruptRunningQueries = Boolean.parseBoolean(value);
+ }
+ }
+
+ public synchronized ExecutorService getExecutors()
+ {
+ if (executors == null)
+ {
+ shutdownExecutorService = true;
+ executors = Executors.newFixedThreadPool(10);
+ }
+
+ return executors;
+ }
+
+ public synchronized void setExecutors(ExecutorService executors)
+ {
+ if (shutdownExecutorService)
+ {
+ this.executors.shutdown();
+ shutdownExecutorService = false;
+ }
+
+ this.executors = executors;
+ }
+
+ public InternalQueryResult execute(InternalView view, CDOQueryInfo queryInfo)
+ {
+ InternalQueryResult queryResult = new QueryResult(view, queryInfo, nextQuery());
+ QueryContext queryContext = new QueryContext(queryResult);
+ execute(queryContext);
+ return queryResult;
+ }
+
+ public boolean isRunning(int queryID)
+ {
+ QueryContext queryContext = queryContexts.get(queryID);
+ return queryContext != null;
+ }
+
+ public void cancel(int queryID)
+ {
+ QueryContext queryContext = queryContexts.get(queryID);
+ if (queryContext == null || queryContext.getFuture().isDone())
+ {
+ throw new RuntimeException("Query " + queryID + " is not running anymore"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Cancelling query for context: " + queryContext); //$NON-NLS-1$
+ }
+
+ queryContext.cancel();
+ }
+
+ public synchronized void register(final QueryContext queryContext)
+ {
+ queryContexts.put(queryContext.getQueryResult().getQueryID(), queryContext);
+ queryContext.addListener();
+ }
+
+ public synchronized void unregister(final QueryContext queryContext)
+ {
+ if (queryContexts.remove(queryContext.getQueryResult().getQueryID()) != null)
+ {
+ queryContext.removeListener();
+ }
+ }
+
+ public synchronized int nextQuery()
+ {
+ return nextQuery++;
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ super.doDeactivate();
+ setExecutors(null);
+ }
+
+ private Future<?> execute(QueryContext queryContext)
+ {
+ Future<?> future = getExecutors().submit(queryContext);
+ queryContext.setFuture(future);
+ register(queryContext);
+ return future;
+ }
+
+ /**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+ private class QueryContext implements IQueryContext, Runnable
+ {
+ private CDOBranchPoint branchPoint;
+
+ private InternalQueryResult queryResult;
+
+ private boolean started;
+
+ private boolean cancelled;
+
+ private int resultCount;
+
+ private Future<?> future;
+
+ private IListener sessionListener = new IListener()
+ {
+ public void notifyEvent(IEvent event)
+ {
+ if (event instanceof SingleDeltaContainerEvent<?>)
+ {
+ IView view = getQueryResult().getView();
+ SingleDeltaContainerEvent<?> deltaEvent = (SingleDeltaContainerEvent<?>)event;
+ if (deltaEvent.getDeltaKind() == Kind.REMOVED && deltaEvent.getDeltaElement() == view)
+ {
+ // Cancel the query when view is closing
+ cancel();
+ }
+ }
+ }
+ };
+
+ public QueryContext(InternalQueryResult queryResult)
+ {
+ this.queryResult = queryResult;
+
+ // Remember the branchPoint because it can change
+ InternalView view = getView();
+
+ // long timeStamp = view.getTimeStamp();
+ // if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE && repository.isSupportingAudits())
+ // {
+ // timeStamp = repository.getTimeStamp();
+ // }
+ //
+ // branchPoint = view.getBranch().getPoint(timeStamp);
+
+ branchPoint = CDOBranchUtil.copyBranchPoint(view);
+ }
+
+ public InternalQueryResult getQueryResult()
+ {
+ return queryResult;
+ }
+
+ public InternalView getView()
+ {
+ return queryResult.getView();
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public Future<?> getFuture()
+ {
+ return future;
+ }
+
+ public void setFuture(Future<?> future)
+ {
+ this.future = future;
+ }
+
+ public void cancel()
+ {
+ cancelled = true;
+ if (future != null)
+ {
+ future.cancel(allowInterruptRunningQueries);
+ }
+
+ if (!started)
+ {
+ unregister(this);
+ }
+ }
+
+ public int getResultCount()
+ {
+ return resultCount;
+ }
+
+ public boolean addResult(Object object)
+ {
+ if (resultCount == 0)
+ {
+ throw new IllegalStateException("Maximum number of results exceeded"); //$NON-NLS-1$
+ }
+
+ queryResult.getQueue().add(object);
+ return !cancelled && --resultCount > 0;
+ }
+
+ public void run()
+ {
+ InternalSession session = queryResult.getView().getSession();
+ StoreThreadLocal.setSession(session);
+
+ try
+ {
+ started = true;
+ CDOQueryInfo info = queryResult.getQueryInfo();
+ resultCount = info.getMaxResults() < 0 ? Integer.MAX_VALUE : info.getMaxResults();
+ IQueryHandler handler = repository.getQueryHandler(info);
+ handler.executeQuery(info, this);
+ }
+ catch (Throwable exception)
+ {
+ queryResult.getQueue().setException(exception);
+ }
+ finally
+ {
+ queryResult.getQueue().close();
+ unregister(this);
+ StoreThreadLocal.release();
+ }
+ }
+
+ public void addListener()
+ {
+ InternalView view = getQueryResult().getView();
+ view.getSession().addListener(sessionListener);
+ }
+
+ public void removeListener()
+ {
+ InternalView view = getQueryResult().getView();
+ view.getSession().removeListener(sessionListener);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java
new file mode 100644
index 0000000000..c5c17b405e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java
@@ -0,0 +1,35 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.spi.common.AbstractQueryResult;
+import org.eclipse.emf.cdo.spi.server.InternalQueryResult;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+
+/**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+public class QueryResult extends AbstractQueryResult<Object> implements InternalQueryResult
+{
+ public QueryResult(InternalView view, CDOQueryInfo queryInfo, int queryID)
+ {
+ super(view, queryInfo, queryID);
+ }
+
+ @Override
+ public InternalView getView()
+ {
+ return (InternalView)super.getView();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
new file mode 100644
index 0000000000..70dacf074b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
@@ -0,0 +1,1923 @@
+/**
+ * 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
+ * Simon McDuff - bug 201266
+ * Simon McDuff - bug 233273
+ * Simon McDuff - bug 233490
+ * Stefan Winkler - changed order of determining audit and revision delta support.
+ * Andre Dietisheim - bug 256649
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonView;
+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.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitData;
+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.id.CDOIDGenerator;
+import org.eclipse.emf.cdo.common.id.CDOIDTemp;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation;
+import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
+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.CDODataOutput;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.common.util.RepositoryStateChangedEvent;
+import org.eclipse.emf.cdo.common.util.RepositoryTypeChangedEvent;
+import org.eclipse.emf.cdo.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.etypes.EtypesPackage;
+import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.IQueryHandlerProvider;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreChunkReader;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+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.spi.common.CDOReplicationContext;
+import org.eclipse.emf.cdo.spi.common.CDOReplicationInfo;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
+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.CDOCommitInfoUtil;
+import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
+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.common.revision.DetachedCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo;
+import org.eclipse.emf.cdo.spi.server.ContainerQueryHandlerProvider;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalCommitManager;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.InternalQueryManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
+import org.eclipse.emf.cdo.spi.server.InternalStore;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+
+import org.eclipse.emf.internal.cdo.object.CDOFactoryImpl;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState;
+import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException;
+import org.eclipse.net4j.util.container.Container;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.Monitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.transaction.TransactionException;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.EcorePackage;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Semaphore;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class Repository extends Container<Object> implements InternalRepository
+{
+ private String name;
+
+ private String uuid;
+
+ private InternalStore store;
+
+ private Type type = Type.MASTER;
+
+ private State state = State.ONLINE;
+
+ private Map<String, String> properties;
+
+ private boolean supportingAudits;
+
+ private boolean supportingBranches;
+
+ private boolean supportingEcore;
+
+ private boolean ensuringReferentialIntegrity;
+
+ private IDGenerationLocation idGenerationLocation;
+
+ /**
+ * Must not be thread-bound to support XA commits.
+ */
+ private Semaphore packageRegistryCommitLock = new Semaphore(1);
+
+ private InternalCDOPackageRegistry packageRegistry;
+
+ private InternalCDOBranchManager branchManager;
+
+ private InternalCDORevisionManager revisionManager;
+
+ private InternalCDOCommitInfoManager commitInfoManager;
+
+ private InternalSessionManager sessionManager;
+
+ private InternalQueryManager queryManager;
+
+ private InternalCommitManager commitManager;
+
+ private InternalLockManager lockManager;
+
+ private IQueryHandlerProvider queryHandlerProvider;
+
+ private List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>();
+
+ private List<WriteAccessHandler> writeAccessHandlers = new ArrayList<WriteAccessHandler>();
+
+ private List<CDOCommitInfoHandler> commitInfoHandlers = new ArrayList<CDOCommitInfoHandler>();
+
+ private EPackage[] initialPackages;
+
+ // Bugzilla 297940
+ private TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this);
+
+ @ExcludeFromDump
+ private transient Object createBranchLock = new Object();
+
+ private boolean skipInitialization;
+
+ private CDOID rootResourceID;
+
+ public Repository()
+ {
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getUUID()
+ {
+ if (uuid == null)
+ {
+ uuid = getProperties().get(Props.OVERRIDE_UUID);
+ if (uuid == null)
+ {
+ uuid = UUID.randomUUID().toString();
+ }
+ else if (uuid.length() == 0)
+ {
+ uuid = getName();
+ }
+ }
+
+ return uuid;
+ }
+
+ public InternalStore getStore()
+ {
+ return store;
+ }
+
+ public void setStore(InternalStore store)
+ {
+ this.store = store;
+ }
+
+ public Type getType()
+ {
+ return type;
+ }
+
+ public void setType(Type type)
+ {
+ checkArg(type, "type"); //$NON-NLS-1$
+ if (this.type != type)
+ {
+ changingType(this.type, type);
+ }
+ }
+
+ protected void changingType(Type oldType, Type newType)
+ {
+ type = newType;
+ fireEvent(new RepositoryTypeChangedEvent(this, oldType, newType));
+
+ if (sessionManager != null)
+ {
+ sessionManager.sendRepositoryTypeNotification(oldType, newType);
+ }
+ }
+
+ public State getState()
+ {
+ return state;
+ }
+
+ public void setState(State state)
+ {
+ checkArg(state, "state"); //$NON-NLS-1$
+ if (this.state != state)
+ {
+ changingState(this.state, state);
+ }
+ }
+
+ protected void changingState(State oldState, State newState)
+ {
+ state = newState;
+ fireEvent(new RepositoryStateChangedEvent(this, oldState, newState));
+
+ if (sessionManager != null)
+ {
+ sessionManager.sendRepositoryStateNotification(oldState, newState, getRootResourceID());
+ }
+ }
+
+ 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;
+ }
+
+ public boolean isSupportingAudits()
+ {
+ return supportingAudits;
+ }
+
+ public boolean isSupportingBranches()
+ {
+ return supportingBranches;
+ }
+
+ public boolean isSupportingEcore()
+ {
+ return supportingEcore;
+ }
+
+ public boolean isEnsuringReferentialIntegrity()
+ {
+ return ensuringReferentialIntegrity;
+ }
+
+ public IDGenerationLocation getIDGenerationLocation()
+ {
+ return idGenerationLocation;
+ }
+
+ public String getStoreType()
+ {
+ return store.getType();
+ }
+
+ public Set<CDOID.ObjectType> getObjectIDTypes()
+ {
+ return store.getObjectIDTypes();
+ }
+
+ public CDOID getRootResourceID()
+ {
+ return rootResourceID;
+ }
+
+ public void setRootResourceID(CDOID rootResourceID)
+ {
+ this.rootResourceID = rootResourceID;
+ }
+
+ public Object processPackage(Object value)
+ {
+ CDOFactoryImpl.prepareDynamicEPackage(value);
+ return value;
+ }
+
+ public EPackage[] loadPackages(CDOPackageUnit packageUnit)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.loadPackageUnit((InternalCDOPackageUnit)packageUnit);
+ }
+
+ public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
+ {
+ if (!isSupportingBranches())
+ {
+ throw new IllegalStateException("Branching is not supported by " + this);
+ }
+
+ long baseTimeStamp = branchInfo.getBaseTimeStamp();
+ if (baseTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ baseTimeStamp = getTimeStamp();
+ branchInfo = new BranchInfo(branchInfo.getName(), branchInfo.getBaseBranchID(), baseTimeStamp);
+ }
+
+ synchronized (createBranchLock)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.createBranch(branchID, branchInfo);
+ }
+ }
+
+ public BranchInfo loadBranch(int branchID)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.loadBranch(branchID);
+ }
+
+ public SubBranchInfo[] loadSubBranches(int branchID)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.loadSubBranches(branchID);
+ }
+
+ public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.loadBranches(startID, endID, branchHandler);
+ }
+
+ public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ accessor.loadCommitInfos(branch, startTime, endTime, handler);
+ }
+
+ public CDOCommitData loadCommitData(long timeStamp)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.loadCommitData(timeStamp);
+ }
+
+ public List<InternalCDORevision> loadRevisions(List<RevisionInfo> infos, CDOBranchPoint branchPoint,
+ int referenceChunk, int prefetchDepth)
+ {
+ for (RevisionInfo info : infos)
+ {
+ CDOID id = info.getID();
+ RevisionInfo.Type type = info.getType();
+ switch (type)
+ {
+ case AVAILABLE_NORMAL: // direct == false
+ {
+ RevisionInfo.Available.Normal availableInfo = (RevisionInfo.Available.Normal)info;
+ checkArg(availableInfo.isDirect() == false, "Load is not needed");
+ break;
+ }
+
+ case AVAILABLE_POINTER: // direct == false || target == null
+ {
+ RevisionInfo.Available.Pointer pointerInfo = (RevisionInfo.Available.Pointer)info;
+ boolean needsTarget = !pointerInfo.hasTarget();
+ checkArg(pointerInfo.isDirect() == false || needsTarget, "Load is not needed");
+
+ if (needsTarget)
+ {
+ CDOBranchVersion targetBranchVersion = pointerInfo.getTargetBranchVersion();
+ InternalCDORevision target = loadRevisionByVersion(id, targetBranchVersion, referenceChunk);
+ PointerCDORevision pointer = new PointerCDORevision(target.getEClass(), id, pointerInfo
+ .getAvailableBranchVersion().getBranch(), CDORevision.UNSPECIFIED_DATE, target);
+
+ info.setResult(target);
+ info.setSynthetic(pointer);
+ continue;
+ }
+
+ break;
+ }
+
+ case AVAILABLE_DETACHED: // direct == false
+ {
+ RevisionInfo.Available.Detached detachedInfo = (RevisionInfo.Available.Detached)info;
+ checkArg(detachedInfo.isDirect() == false, "Load is not needed");
+ break;
+ }
+
+ case MISSING:
+ {
+ break;
+ }
+
+ default:
+ throw new IllegalStateException("Invalid revision info type: " + type);
+ }
+
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ InternalCDORevision revision = accessor.readRevision(id, branchPoint, referenceChunk, revisionManager);
+ if (revision == null)
+ {
+ if (isSupportingAudits())
+ {
+ // Case "Pointer"?
+ InternalCDORevision target = loadRevisionTarget(id, branchPoint, referenceChunk, accessor);
+ if (target != null)
+ {
+ CDOBranch branch = branchPoint.getBranch();
+ long revised = loadRevisionRevised(id, branch);
+ PointerCDORevision pointer = new PointerCDORevision(target.getEClass(), id, branch, revised, target);
+ info.setSynthetic(pointer);
+ }
+
+ info.setResult(target);
+ }
+ else
+ {
+ DetachedCDORevision detachedRevision = new DetachedCDORevision(EcorePackage.Literals.ECLASS, id,
+ branchPoint.getBranch(), 0, CDORevision.UNSPECIFIED_DATE);
+ info.setSynthetic(detachedRevision);
+ }
+ }
+ else if (revision instanceof DetachedCDORevision)
+ {
+ DetachedCDORevision detached = (DetachedCDORevision)revision;
+ info.setSynthetic(detached);
+ }
+ else
+ {
+ revision.freeze();
+ info.setResult(revision);
+ }
+ }
+
+ return null;
+ }
+
+ private InternalCDORevision loadRevisionTarget(CDOID id, CDOBranchPoint branchPoint, int referenceChunk,
+ IStoreAccessor accessor)
+ {
+ CDOBranch branch = branchPoint.getBranch();
+ while (!branch.isMainBranch())
+ {
+ branchPoint = branch.getBase();
+ branch = branchPoint.getBranch();
+
+ InternalCDORevision revision = accessor.readRevision(id, branchPoint, referenceChunk, revisionManager);
+ if (revision != null)
+ {
+ revision.freeze();
+ return revision;
+ }
+ }
+
+ return null;
+ }
+
+ private long loadRevisionRevised(CDOID id, CDOBranch branch)
+ {
+ InternalCDORevision revision = loadRevisionByVersion(id, branch.getVersion(CDORevision.FIRST_VERSION),
+ CDORevision.UNCHUNKED);
+ if (revision != null)
+ {
+ return revision.getTimeStamp() - 1;
+ }
+
+ return CDORevision.UNSPECIFIED_DATE;
+ }
+
+ public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ return accessor.readRevisionByVersion(id, branchVersion, referenceChunk, revisionManager);
+ }
+
+ protected void ensureChunks(InternalCDORevision revision, int referenceChunk, IStoreAccessor accessor)
+ {
+ EClass eClass = revision.getEClass();
+ EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass);
+ for (int i = 0; i < features.length; i++)
+ {
+ EStructuralFeature feature = features[i];
+ if (feature.isMany())
+ {
+ MoveableList<Object> list = revision.getList(feature);
+ int chunkEnd = Math.min(referenceChunk, list.size());
+ accessor = ensureChunk(revision, feature, accessor, list, 0, chunkEnd);
+ }
+ }
+ }
+
+ public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart,
+ int chunkEnd)
+ {
+ MoveableList<Object> list = revision.getList(feature);
+ chunkEnd = Math.min(chunkEnd, list.size());
+ return ensureChunk(revision, feature, StoreThreadLocal.getAccessor(), list, chunkStart, chunkEnd);
+ }
+
+ protected IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature,
+ IStoreAccessor accessor, MoveableList<Object> list, int chunkStart, int chunkEnd)
+ {
+ IStoreChunkReader chunkReader = null;
+ int fromIndex = -1;
+ for (int j = chunkStart; j < chunkEnd; j++)
+ {
+ if (list.get(j) == InternalCDOList.UNINITIALIZED)
+ {
+ if (fromIndex == -1)
+ {
+ fromIndex = j;
+ }
+ }
+ else
+ {
+ if (fromIndex != -1)
+ {
+ if (chunkReader == null)
+ {
+ if (accessor == null)
+ {
+ accessor = StoreThreadLocal.getAccessor();
+ }
+
+ chunkReader = accessor.createChunkReader(revision, feature);
+ }
+
+ int toIndex = j;
+ if (fromIndex == toIndex - 1)
+ {
+ chunkReader.addSimpleChunk(fromIndex);
+ }
+ else
+ {
+ chunkReader.addRangedChunk(fromIndex, toIndex);
+ }
+
+ fromIndex = -1;
+ }
+ }
+ }
+
+ // Add last chunk
+ if (fromIndex != -1)
+ {
+ if (chunkReader == null)
+ {
+ if (accessor == null)
+ {
+ accessor = StoreThreadLocal.getAccessor();
+ }
+
+ chunkReader = accessor.createChunkReader(revision, feature);
+ }
+
+ int toIndex = chunkEnd;
+ if (fromIndex == toIndex - 1)
+ {
+ chunkReader.addSimpleChunk(fromIndex);
+ }
+ else
+ {
+ chunkReader.addRangedChunk(fromIndex, toIndex);
+ }
+ }
+
+ if (chunkReader != null)
+ {
+ InternalCDOList cdoList = list instanceof InternalCDOList ? (InternalCDOList)list : null;
+
+ List<Chunk> chunks = chunkReader.executeRead();
+ for (Chunk chunk : chunks)
+ {
+ int startIndex = chunk.getStartIndex();
+ for (int indexInChunk = 0; indexInChunk < chunk.size(); indexInChunk++)
+ {
+ Object id = chunk.get(indexInChunk);
+ if (cdoList != null)
+ {
+ cdoList.setWithoutFrozenCheck(startIndex + indexInChunk, id);
+ }
+ else
+ {
+ list.set(startIndex + indexInChunk, id);
+ }
+ }
+ }
+ }
+
+ return accessor;
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext)
+ {
+ if (considerCommitContext)
+ {
+ IStoreAccessor.CommitContext commitContext = StoreThreadLocal.getCommitContext();
+ if (commitContext != null)
+ {
+ InternalCDOPackageRegistry contextualPackageRegistry = commitContext.getPackageRegistry();
+ if (contextualPackageRegistry != null)
+ {
+ return contextualPackageRegistry;
+ }
+ }
+ }
+
+ return packageRegistry;
+ }
+
+ public Semaphore getPackageRegistryCommitLock()
+ {
+ return packageRegistryCommitLock;
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry()
+ {
+ return getPackageRegistry(true);
+ }
+
+ public void setPackageRegistry(InternalCDOPackageRegistry packageRegistry)
+ {
+ checkInactive();
+ this.packageRegistry = packageRegistry;
+ }
+
+ public InternalSessionManager getSessionManager()
+ {
+ return sessionManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setSessionManager(InternalSessionManager sessionManager)
+ {
+ checkInactive();
+ this.sessionManager = sessionManager;
+ }
+
+ public InternalCDOBranchManager getBranchManager()
+ {
+ return branchManager;
+ }
+
+ public void setBranchManager(InternalCDOBranchManager branchManager)
+ {
+ checkInactive();
+ this.branchManager = branchManager;
+ }
+
+ public InternalCDOCommitInfoManager getCommitInfoManager()
+ {
+ return commitInfoManager;
+ }
+
+ public void setCommitInfoManager(InternalCDOCommitInfoManager commitInfoManager)
+ {
+ checkInactive();
+ this.commitInfoManager = commitInfoManager;
+ }
+
+ public InternalCDORevisionManager getRevisionManager()
+ {
+ return revisionManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setRevisionManager(InternalCDORevisionManager revisionManager)
+ {
+ checkInactive();
+ this.revisionManager = revisionManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalQueryManager getQueryManager()
+ {
+ return queryManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setQueryManager(InternalQueryManager queryManager)
+ {
+ checkInactive();
+ this.queryManager = queryManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalCommitManager getCommitManager()
+ {
+ return commitManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setCommitManager(InternalCommitManager commitManager)
+ {
+ checkInactive();
+ this.commitManager = commitManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalLockManager getLockManager()
+ {
+ return lockManager;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setLockManager(InternalLockManager lockManager)
+ {
+ checkInactive();
+ this.lockManager = lockManager;
+ }
+
+ public InternalCommitContext createCommitContext(InternalTransaction transaction)
+ {
+ return new TransactionCommitContext(transaction);
+ }
+
+ public long getLastCommitTimeStamp()
+ {
+ return timeStampAuthority.getLastFinishedTimeStamp();
+ }
+
+ public void setLastCommitTimeStamp(long lastCommitTimeStamp)
+ {
+ timeStampAuthority.setLastFinishedTimeStamp(lastCommitTimeStamp);
+ }
+
+ public long waitForCommit(long timeout)
+ {
+ return timeStampAuthority.waitForCommit(timeout);
+ }
+
+ public long[] createCommitTimeStamp(OMMonitor monitor)
+ {
+ return timeStampAuthority.startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor);
+ }
+
+ public long[] forceCommitTimeStamp(long override, OMMonitor monitor)
+ {
+ return timeStampAuthority.startCommit(override, monitor);
+ }
+
+ public void endCommit(long timestamp)
+ {
+ timeStampAuthority.endCommit(timestamp);
+ }
+
+ public void failCommit(long timestamp)
+ {
+ timeStampAuthority.failCommit(timestamp);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public void addCommitInfoHandler(CDOCommitInfoHandler handler)
+ {
+ synchronized (commitInfoHandlers)
+ {
+ if (!commitInfoHandlers.contains(handler))
+ {
+ commitInfoHandlers.add(handler);
+ }
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ public void removeCommitInfoHandler(CDOCommitInfoHandler handler)
+ {
+ synchronized (commitInfoHandlers)
+ {
+ commitInfoHandlers.remove(handler);
+ }
+ }
+
+ public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo)
+ {
+ sessionManager.sendCommitNotification(sender, commitInfo);
+
+ CDOCommitInfoHandler[] handlers;
+ synchronized (commitInfoHandlers)
+ {
+ handlers = commitInfoHandlers.toArray(new CDOCommitInfoHandler[commitInfoHandlers.size()]);
+ }
+
+ for (CDOCommitInfoHandler handler : handlers)
+ {
+ try
+ {
+ handler.handleCommitInfo(commitInfo);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IQueryHandlerProvider getQueryHandlerProvider()
+ {
+ return queryHandlerProvider;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider)
+ {
+ this.queryHandlerProvider = queryHandlerProvider;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized IQueryHandler getQueryHandler(CDOQueryInfo info)
+ {
+ String language = info.getQueryLanguage();
+ if (CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES.equals(language))
+ {
+ return new ResourcesQueryHandler();
+ }
+
+ if (CDOProtocolConstants.QUERY_LANGUAGE_XREFS.equals(language))
+ {
+ return new XRefsQueryHandler();
+ }
+
+ IStoreAccessor storeAccessor = StoreThreadLocal.getAccessor();
+ if (storeAccessor != null)
+ {
+ IQueryHandler handler = storeAccessor.getQueryHandler(info);
+ if (handler != null)
+ {
+ return handler;
+ }
+ }
+
+ if (queryHandlerProvider == null)
+ {
+ queryHandlerProvider = new ContainerQueryHandlerProvider(IPluginContainer.INSTANCE);
+ }
+
+ IQueryHandler handler = queryHandlerProvider.getQueryHandler(info);
+ if (handler != null)
+ {
+ return handler;
+ }
+
+ return null;
+ }
+
+ public Object[] getElements()
+ {
+ final Object[] elements = { packageRegistry, branchManager, revisionManager, sessionManager, queryManager,
+ commitManager, commitInfoManager, lockManager, store };
+ return elements;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return false;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public long getCreationTime()
+ {
+ return store.getCreationTime();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void validateTimeStamp(long timeStamp) throws IllegalArgumentException
+ {
+ long creationTimeStamp = getCreationTime();
+ if (timeStamp < creationTimeStamp)
+ {
+ throw new IllegalArgumentException(
+ MessageFormat
+ .format(
+ "timeStamp ({0}) < repository creation time ({1})", CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(creationTimeStamp))); //$NON-NLS-1$
+ }
+
+ long currentTimeStamp = getTimeStamp();
+ if (timeStamp > currentTimeStamp)
+ {
+ throw new IllegalArgumentException(
+ MessageFormat
+ .format(
+ "timeStamp ({0}) > current time ({1})", CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(currentTimeStamp))); //$NON-NLS-1$
+ }
+ }
+
+ public long getTimeStamp()
+ {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void addHandler(Handler handler)
+ {
+ if (handler instanceof ReadAccessHandler)
+ {
+ synchronized (readAccessHandlers)
+ {
+ if (!readAccessHandlers.contains(handler))
+ {
+ readAccessHandlers.add((ReadAccessHandler)handler);
+ }
+ }
+ }
+
+ if (handler instanceof WriteAccessHandler)
+ {
+ synchronized (writeAccessHandlers)
+ {
+ if (!writeAccessHandlers.contains(handler))
+ {
+ writeAccessHandlers.add((WriteAccessHandler)handler);
+ }
+ }
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void removeHandler(Handler handler)
+ {
+ if (handler instanceof ReadAccessHandler)
+ {
+ synchronized (readAccessHandlers)
+ {
+ readAccessHandlers.remove(handler);
+ }
+ }
+
+ if (handler instanceof WriteAccessHandler)
+ {
+ synchronized (writeAccessHandlers)
+ {
+ writeAccessHandlers.remove(handler);
+ }
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions,
+ List<CDORevision> additionalRevisions)
+ {
+ ReadAccessHandler[] handlers;
+ synchronized (readAccessHandlers)
+ {
+ int size = readAccessHandlers.size();
+ if (size == 0)
+ {
+ return;
+ }
+
+ handlers = readAccessHandlers.toArray(new ReadAccessHandler[size]);
+ }
+
+ for (ReadAccessHandler handler : handlers)
+ {
+ // Do *not* protect against unchecked exceptions from handlers!
+ handler.handleRevisionsBeforeSending(session, revisions, additionalRevisions);
+ }
+ }
+
+ public void notifyWriteAccessHandlers(ITransaction transaction, IStoreAccessor.CommitContext commitContext,
+ boolean beforeCommit, OMMonitor monitor)
+ {
+ WriteAccessHandler[] handlers;
+ synchronized (writeAccessHandlers)
+ {
+ int size = writeAccessHandlers.size();
+ if (size == 0)
+ {
+ return;
+ }
+
+ handlers = writeAccessHandlers.toArray(new WriteAccessHandler[size]);
+ }
+
+ try
+ {
+ monitor.begin(handlers.length);
+ for (WriteAccessHandler handler : handlers)
+ {
+ try
+ {
+ if (beforeCommit)
+ {
+ handler.handleTransactionBeforeCommitting(transaction, commitContext, monitor.fork());
+ }
+ else
+ {
+ handler.handleTransactionAfterCommitted(transaction, commitContext, monitor.fork());
+ }
+ }
+ catch (RuntimeException ex)
+ {
+ if (!beforeCommit)
+ {
+ OM.LOG.error(ex);
+ }
+ else
+ {
+ // Do *not* protect against unchecked exceptions from handlers on before case!
+ throw ex;
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void setInitialPackages(EPackage... initialPackages)
+ {
+ checkInactive();
+ this.initialPackages = initialPackages;
+ }
+
+ public CDOReplicationInfo replicateRaw(CDODataOutput out, int lastReplicatedBranchID, long lastReplicatedCommitTime)
+ throws IOException
+ {
+ final int fromBranchID = lastReplicatedBranchID + 1;
+ final int toBranchID = getStore().getLastBranchID();
+
+ final long fromCommitTime = lastReplicatedCommitTime + 1L;
+ final long toCommitTime = getStore().getLastCommitTime();
+
+ out.writeInt(toBranchID);
+ out.writeLong(toCommitTime);
+
+ IStoreAccessor.Raw accessor = (IStoreAccessor.Raw)StoreThreadLocal.getAccessor();
+ accessor.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime);
+
+ return new CDOReplicationInfo()
+ {
+ public int getLastReplicatedBranchID()
+ {
+ return toBranchID;
+ }
+
+ public long getLastReplicatedCommitTime()
+ {
+ return toCommitTime;
+ }
+
+ public String[] getLockAreaIDs()
+ {
+ return null; // TODO (CD) Raw replication of lockAreas
+ }
+ };
+ }
+
+ public void replicate(CDOReplicationContext context)
+ {
+ int startID = context.getLastReplicatedBranchID() + 1;
+ branchManager.getBranches(startID, 0, context);
+
+ long startTime = context.getLastReplicatedCommitTime();
+ commitInfoManager.getCommitInfos(null, startTime + 1L, CDOBranchPoint.UNSPECIFIED_DATE, context);
+
+ lockManager.getLockAreas(null, context);
+ }
+
+ public CDOChangeSetData getChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint)
+ {
+ CDOChangeSetSegment[] segments = CDOChangeSetSegment.createFrom(startPoint, endPoint);
+
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ Set<CDOID> ids = accessor.readChangeSet(new Monitor(), segments);
+
+ return CDORevisionUtil.createChangeSetData(ids, startPoint, endPoint, revisionManager);
+ }
+
+ public Set<CDOID> getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+ CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor)
+ {
+ CDOBranchPoint target = targetInfo.getBranchPoint();
+ CDOBranchPoint source = sourceInfo.getBranchPoint();
+
+ monitor.begin(5);
+
+ try
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ Set<CDOID> ids = new HashSet<CDOID>();
+
+ if (targetBaseInfo == null && sourceBaseInfo == null)
+ {
+ if (CDOBranchUtil.isContainedBy(source, target))
+ {
+ ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(source, target)));
+ }
+ else if (CDOBranchUtil.isContainedBy(target, source))
+ {
+ ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(target, source)));
+ }
+ else
+ {
+ CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source);
+ ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, target)));
+ ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, source)));
+ }
+ }
+ else
+ {
+ CDORevisionAvailabilityInfo sourceBaseInfoToUse = sourceBaseInfo == null ? targetBaseInfo : sourceBaseInfo;
+
+ ids.addAll(accessor.readChangeSet(monitor.fork(),
+ CDOChangeSetSegment.createFrom(targetBaseInfo.getBranchPoint(), target)));
+
+ ids.addAll(accessor.readChangeSet(monitor.fork(),
+ CDOChangeSetSegment.createFrom(sourceBaseInfoToUse.getBranchPoint(), source)));
+ }
+
+ loadMergeData(ids, targetInfo, monitor.fork());
+ loadMergeData(ids, sourceInfo, monitor.fork());
+
+ if (targetBaseInfo != null)
+ {
+ loadMergeData(ids, targetBaseInfo, monitor.fork());
+ }
+
+ if (sourceBaseInfo != null)
+ {
+ loadMergeData(ids, sourceBaseInfo, monitor.fork());
+ }
+
+ return ids;
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void loadMergeData(Set<CDOID> ids, CDORevisionAvailabilityInfo info, OMMonitor monitor)
+ {
+ int size = ids.size();
+ monitor.begin(size);
+
+ try
+ {
+ CDOBranchPoint branchPoint = info.getBranchPoint();
+ for (CDOID id : ids)
+ {
+ if (info.containsRevision(id))
+ {
+ info.removeRevision(id);
+ }
+ else
+ {
+ InternalCDORevision revision = getRevisionFromBranch(id, branchPoint);
+ if (revision != null)
+ {
+ info.addRevision(revision);
+ }
+ else
+ {
+ info.removeRevision(id);
+ }
+ }
+
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private InternalCDORevision getRevisionFromBranch(CDOID id, CDOBranchPoint branchPoint)
+ {
+ InternalCDORevision revision = revisionManager.getRevision(id, branchPoint, CDORevision.UNCHUNKED,
+ CDORevision.DEPTH_NONE, true);
+ // if (revision == null || !ObjectUtil.equals(revision.getBranch(), branchPoint.getBranch()))
+ // {
+ // return null;
+ // }
+
+ return revision;
+ }
+
+ public void queryLobs(List<byte[]> ids)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ accessor.queryLobs(ids);
+ }
+
+ public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ accessor.handleLobs(fromTime, toTime, handler);
+ }
+
+ public void loadLob(byte[] id, OutputStream out) throws IOException
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ accessor.loadLob(id, out);
+ }
+
+ public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime,
+ final CDORevisionHandler handler)
+ {
+ CDORevisionHandler wrapper = handler;
+ if (!exactBranch && !branch.isMainBranch())
+ {
+ if (exactTime && timeStamp == CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ throw new IllegalArgumentException("Time stamp must be specified if exactBranch==false and exactTime==true");
+ }
+
+ wrapper = new CDORevisionHandler()
+ {
+ private Set<CDOID> handled = new HashSet<CDOID>();
+
+ public boolean handleRevision(CDORevision revision)
+ {
+ CDOID id = revision.getID();
+ if (handled.add(id))
+ {
+ return handler.handleRevision(revision);
+ }
+
+ return true;
+ }
+ };
+ }
+
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ while (branch != null)
+ {
+ accessor.handleRevisions(eClass, branch, timeStamp, exactTime, wrapper);
+ if (exactBranch)
+ {
+ break;
+ }
+
+ CDOBranchPoint base = branch.getBase();
+ branch = base.getBranch();
+ timeStamp = base.getTimeStamp();
+ }
+ }
+
+ public static List<Object> revisionKeysToObjects(List<CDORevisionKey> revisionKeys, CDOBranch viewedBranch,
+ boolean isSupportingBranches)
+ {
+ List<Object> lockables = new ArrayList<Object>();
+ for (CDORevisionKey revKey : revisionKeys)
+ {
+ CDOID id = revKey.getID();
+ if (isSupportingBranches)
+ {
+ lockables.add(CDOIDUtil.createIDAndBranch(id, viewedBranch));
+ }
+ else
+ {
+ lockables.add(id);
+ }
+ }
+ return lockables;
+ }
+
+ public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revKeys, long timeout)
+ {
+ List<Object> lockables = revisionKeysToObjects(revKeys, view.getBranch(), isSupportingBranches());
+ return lock(view, lockType, lockables, revKeys, timeout);
+ }
+
+ protected LockObjectsResult lock(InternalView view, LockType type, List<Object> lockables,
+ List<CDORevisionKey> loadedRevs, long timeout)
+ {
+ List<LockState<Object, IView>> newLockStates = null;
+ try
+ {
+ newLockStates = lockManager.lock2(true, type, view, lockables, timeout);
+ }
+ catch (TimeoutRuntimeException ex)
+ {
+ return new LockObjectsResult(false, true, false, 0, new CDORevisionKey[0], new CDOLockState[0], getTimeStamp());
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+
+ long[] requiredTimestamp = { 0L };
+ CDORevisionKey[] staleRevisionsArray = checkStaleRevisions(view, loadedRevs, lockables, type, requiredTimestamp);
+
+ // If some of the clients' revisions are stale and it has passiveUpdates disabled,
+ // then the locks are useless so we release them and report the stale revisions
+ //
+ InternalSession session = view.getSession();
+ boolean staleNoUpdate = staleRevisionsArray.length > 0 && !session.isPassiveUpdateEnabled();
+ if (staleNoUpdate)
+ {
+ lockManager.unlock2(true, type, view, lockables);
+ return new LockObjectsResult(false, false, false, requiredTimestamp[0], staleRevisionsArray, new CDOLockState[0],
+ getTimeStamp());
+ }
+
+ CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates);
+ sendLockNotifications(view, Operation.LOCK, type, cdoLockStates);
+
+ boolean waitForUpdate = staleRevisionsArray.length > 0;
+ return new LockObjectsResult(true, false, waitForUpdate, requiredTimestamp[0], staleRevisionsArray, cdoLockStates,
+ getTimeStamp());
+ }
+
+ private CDORevisionKey[] checkStaleRevisions(InternalView view, List<CDORevisionKey> revisionKeys,
+ List<Object> objectsToLock, LockType lockType, long[] requiredTimestamp)
+ {
+ List<CDORevisionKey> staleRevisions = new LinkedList<CDORevisionKey>();
+ if (revisionKeys != null)
+ {
+ InternalCDORevisionManager revManager = getRevisionManager();
+ CDOBranch viewedBranch = view.getBranch();
+ for (CDORevisionKey revKey : revisionKeys)
+ {
+ CDOID id = revKey.getID();
+ InternalCDORevision rev = revManager.getRevision(id, viewedBranch.getHead(), CDORevision.UNCHUNKED,
+ CDORevision.DEPTH_NONE, true);
+
+ if (rev == null)
+ {
+ lockManager.unlock2(true, lockType, view, objectsToLock);
+ throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id,
+ viewedBranch));
+ }
+
+ if (!revKey.equals(rev))
+ {
+ staleRevisions.add(revKey);
+ requiredTimestamp[0] = Math.max(requiredTimestamp[0], rev.getTimeStamp());
+ }
+ }
+ }
+
+ // Convert the list to an array, to satisfy the API later
+ //
+ CDORevisionKey[] staleRevisionsArray = new CDORevisionKey[staleRevisions.size()];
+ staleRevisions.toArray(staleRevisionsArray);
+
+ return staleRevisionsArray;
+ }
+
+ private void sendLockNotifications(IView view, Operation operation, LockType lockType, CDOLockState[] cdoLockStates)
+ {
+ long timestamp = getTimeStamp();
+ CDOLockChangeInfo lockChangeInfo = CDOLockUtil.createLockChangeInfo(timestamp, view, view.getBranch(), operation,
+ lockType, cdoLockStates);
+ getSessionManager().sendLockNotification((InternalSession)view.getSession(), lockChangeInfo);
+ }
+
+ // TODO (CD) This doesn't really belong here.. but getting it into CDOLockUtil isn't possible
+ public static CDOLockState[] toCDOLockStates(List<LockState<Object, IView>> lockStates)
+ {
+ CDOLockState[] cdoLockStates = new CDOLockState[lockStates.size()];
+ int i = 0;
+
+ for (LockState<Object, ? extends CDOCommonView> lockState : lockStates)
+ {
+ CDOLockState cdoLockState = CDOLockUtil.createLockState(lockState);
+ cdoLockStates[i++] = cdoLockState;
+ }
+
+ return cdoLockStates;
+ }
+
+ public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs)
+ {
+ List<Object> unlockables = null;
+ if (objectIDs != null)
+ {
+ unlockables = new ArrayList<Object>(objectIDs.size());
+ CDOBranch branch = view.getBranch();
+ for (CDOID id : objectIDs)
+ {
+ Object key = supportingBranches ? CDOIDUtil.createIDAndBranch(id, branch) : id;
+ unlockables.add(key);
+ }
+ }
+
+ return doUnlock(view, lockType, unlockables);
+ }
+
+ protected UnlockObjectsResult doUnlock(InternalView view, LockType lockType, List<Object> unlockables)
+ {
+ List<LockState<Object, IView>> newLockStates = null;
+ if (lockType == null) // Signals an unlock-all operation
+ {
+ newLockStates = lockManager.unlock2(true, view);
+ }
+ else
+ {
+ newLockStates = lockManager.unlock2(true, lockType, view, unlockables);
+ }
+
+ long timestamp = getTimeStamp();
+ CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates);
+ sendLockNotifications(view, Operation.UNLOCK, lockType, cdoLockStates);
+
+ return new UnlockObjectsResult(cdoLockStates, timestamp);
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("Repository[{0}]", name); //$NON-NLS-1$
+ }
+
+ public boolean isSkipInitialization()
+ {
+ return skipInitialization;
+ }
+
+ public void setSkipInitialization(boolean skipInitialization)
+ {
+ this.skipInitialization = skipInitialization;
+ }
+
+ protected void initProperties()
+ {
+ String valueAudits = properties.get(Props.SUPPORTING_AUDITS);
+ if (valueAudits != null)
+ {
+ supportingAudits = Boolean.valueOf(valueAudits);
+ }
+ else
+ {
+ supportingAudits = store.getRevisionTemporality() == IStore.RevisionTemporality.AUDITING;
+ }
+
+ String valueBranches = properties.get(Props.SUPPORTING_BRANCHES);
+ if (valueBranches != null)
+ {
+ supportingBranches = Boolean.valueOf(valueBranches);
+ }
+ else
+ {
+ supportingBranches = store.getRevisionParallelism() == IStore.RevisionParallelism.BRANCHING;
+ }
+
+ String valueEcore = properties.get(Props.SUPPORTING_ECORE);
+ if (valueEcore != null)
+ {
+ supportingEcore = Boolean.valueOf(valueEcore);
+ }
+
+ String valueIntegrity = properties.get(Props.ENSURE_REFERENTIAL_INTEGRITY);
+ if (valueIntegrity != null)
+ {
+ ensuringReferentialIntegrity = Boolean.valueOf(valueIntegrity);
+ }
+
+ String valueIDLocation = properties.get(Props.ID_GENERATION_LOCATION);
+ if (valueIDLocation != null)
+ {
+ idGenerationLocation = IDGenerationLocation.valueOf(valueIDLocation);
+ }
+
+ if (idGenerationLocation == null)
+ {
+ idGenerationLocation = IDGenerationLocation.STORE;
+ }
+ }
+
+ public void initSystemPackages()
+ {
+ IStoreAccessor writer = store.getWriter(null);
+ StoreThreadLocal.setAccessor(writer);
+
+ try
+ {
+ List<InternalCDOPackageUnit> units = new ArrayList<InternalCDOPackageUnit>();
+ units.add(initSystemPackage(EcorePackage.eINSTANCE));
+ units.add(initSystemPackage(EresourcePackage.eINSTANCE));
+ units.add(initSystemPackage(EtypesPackage.eINSTANCE));
+
+ if (initialPackages != null)
+ {
+ for (EPackage initialPackage : initialPackages)
+ {
+ if (!packageRegistry.containsKey(initialPackage.getNsURI()))
+ {
+ units.add(initSystemPackage(initialPackage));
+ }
+ }
+ }
+
+ InternalCDOPackageUnit[] systemUnits = units.toArray(new InternalCDOPackageUnit[units.size()]);
+ writer.writePackageUnits(systemUnits, new Monitor());
+ writer.commit(new Monitor());
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ protected InternalCDOPackageUnit initSystemPackage(EPackage ePackage)
+ {
+ EMFUtil.registerPackage(ePackage, packageRegistry);
+ InternalCDOPackageInfo packageInfo = packageRegistry.getPackageInfo(ePackage);
+
+ InternalCDOPackageUnit packageUnit = packageInfo.getPackageUnit();
+ packageUnit.setTimeStamp(store.getCreationTime());
+ packageUnit.setState(CDOPackageUnit.State.LOADED);
+ return packageUnit;
+ }
+
+ public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp)
+ {
+ branchManager.initMainBranch(false, timeStamp);
+ }
+
+ protected void initRootResource()
+ {
+ CDOBranchPoint head = branchManager.getMainBranch().getHead();
+
+ CDORevisionFactory factory = getRevisionManager().getFactory();
+ InternalCDORevision rootResource = (InternalCDORevision)factory
+ .createRevision(EresourcePackage.Literals.CDO_RESOURCE);
+
+ rootResource.setBranchPoint(head);
+ rootResource.setContainerID(CDOID.NULL);
+ rootResource.setContainingFeatureID(0);
+
+ CDOID id = createRootResourceID();
+ rootResource.setID(id);
+ rootResource.setResourceID(id);
+
+ InternalSession session = getSessionManager().openSession(null);
+ InternalTransaction transaction = session.openTransaction(1, head);
+ InternalCommitContext commitContext = new TransactionCommitContext(transaction)
+ {
+ @Override
+ protected long[] createTimeStamp(OMMonitor monitor)
+ {
+ InternalRepository repository = getTransaction().getSession().getManager().getRepository();
+ return repository.forceCommitTimeStamp(store.getCreationTime(), monitor);
+ }
+
+ @Override
+ public String getUserID()
+ {
+ return SYSTEM_USER_ID;
+ }
+
+ @Override
+ public String getCommitComment()
+ {
+ return "<initialize>"; //$NON-NLS-1$
+ }
+ };
+
+ commitContext.setNewObjects(new InternalCDORevision[] { rootResource });
+ commitContext.preWrite();
+
+ commitContext.write(new Monitor());
+ commitContext.commit(new Monitor());
+
+ String rollbackMessage = commitContext.getRollbackMessage();
+ if (rollbackMessage != null)
+ {
+ throw new TransactionException(rollbackMessage);
+ }
+
+ rootResourceID = id instanceof CDOIDTemp ? commitContext.getIDMappings().get(id) : id;
+
+ commitContext.postCommit(true);
+ session.close();
+ }
+
+ protected CDOID createRootResourceID()
+ {
+ if (getIDGenerationLocation() == IDGenerationLocation.STORE)
+ {
+ return CDOIDUtil.createTempObject(1);
+ }
+
+ return CDOIDGenerator.UUID.generateCDOID(null);
+ }
+
+ protected void readRootResource()
+ {
+ IStoreAccessor reader = store.getReader(null);
+ StoreThreadLocal.setAccessor(reader);
+
+ try
+ {
+ CDOBranchPoint head = branchManager.getMainBranch().getHead();
+ rootResourceID = reader.readResourceID(CDOID.NULL, null, head);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ protected void readPackageUnits()
+ {
+ IStoreAccessor reader = store.getReader(null);
+ StoreThreadLocal.setAccessor(reader);
+
+ try
+ {
+ Collection<InternalCDOPackageUnit> packageUnits = reader.readPackageUnits();
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ packageRegistry.putPackageUnit(packageUnit);
+ }
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ checkState(store, "store"); //$NON-NLS-1$
+ checkState(!StringUtil.isEmpty(name), "name is empty"); //$NON-NLS-1$
+ checkState(packageRegistry, "packageRegistry"); //$NON-NLS-1$
+ checkState(sessionManager, "sessionManager"); //$NON-NLS-1$
+ checkState(branchManager, "branchManager"); //$NON-NLS-1$
+ checkState(revisionManager, "revisionManager"); //$NON-NLS-1$
+ checkState(queryManager, "queryManager"); //$NON-NLS-1$
+ checkState(commitInfoManager, "commitInfoManager"); //$NON-NLS-1$
+ checkState(commitManager, "commitManager"); //$NON-NLS-1$
+ checkState(lockManager, "lockingManager"); //$NON-NLS-1$
+
+ packageRegistry.setReplacingDescriptors(true);
+ packageRegistry.setPackageProcessor(this);
+ packageRegistry.setPackageLoader(this);
+
+ branchManager.setBranchLoader(this);
+ branchManager.setTimeProvider(this);
+
+ revisionManager.setRevisionLoader(this);
+ sessionManager.setRepository(this);
+ queryManager.setRepository(this);
+ commitInfoManager.setCommitInfoLoader(this);
+ commitManager.setRepository(this);
+ lockManager.setRepository(this);
+ store.setRepository(this);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ initProperties();
+ if (idGenerationLocation == IDGenerationLocation.CLIENT && !(store instanceof CanHandleClientAssignedIDs))
+ {
+ throw new IllegalStateException("Store can not handle client assigned IDs: " + store);
+ }
+
+ store.setRevisionTemporality(supportingAudits ? IStore.RevisionTemporality.AUDITING
+ : IStore.RevisionTemporality.NONE);
+ store.setRevisionParallelism(supportingBranches ? IStore.RevisionParallelism.BRANCHING
+ : IStore.RevisionParallelism.NONE);
+ revisionManager.setSupportingAudits(supportingAudits);
+ revisionManager.setSupportingBranches(supportingBranches);
+
+ LifecycleUtil.activate(store);
+ LifecycleUtil.activate(packageRegistry);
+ LifecycleUtil.activate(sessionManager);
+ LifecycleUtil.activate(revisionManager);
+ LifecycleUtil.activate(branchManager);
+ LifecycleUtil.activate(queryManager);
+ LifecycleUtil.activate(commitInfoManager);
+ LifecycleUtil.activate(commitManager);
+ LifecycleUtil.activate(queryHandlerProvider);
+
+ if (!skipInitialization)
+ {
+ long lastCommitTimeStamp = Math.max(store.getCreationTime(), store.getLastCommitTime());
+ timeStampAuthority.setLastFinishedTimeStamp(lastCommitTimeStamp);
+ initMainBranch(branchManager, lastCommitTimeStamp);
+
+ if (store.isFirstStart())
+ {
+ initSystemPackages();
+ initRootResource();
+ }
+ else
+ {
+ readPackageUnits();
+ readRootResource();
+ }
+
+ // This check does not work for CDOWorkspace:
+ // if (CDOIDUtil.isNull(rootResourceID))
+ // {
+ // throw new IllegalStateException("Root resource ID is null");
+ // }
+ }
+
+ LifecycleUtil.activate(lockManager); // Needs an initialized main branch / branch manager
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(lockManager);
+ LifecycleUtil.deactivate(queryHandlerProvider);
+ LifecycleUtil.deactivate(commitManager);
+ LifecycleUtil.deactivate(commitInfoManager);
+ LifecycleUtil.deactivate(queryManager);
+ LifecycleUtil.deactivate(revisionManager);
+ LifecycleUtil.deactivate(sessionManager);
+ LifecycleUtil.deactivate(store);
+ LifecycleUtil.deactivate(branchManager);
+ LifecycleUtil.deactivate(packageRegistry);
+ super.doDeactivate();
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public static class Default extends Repository
+ {
+ public Default()
+ {
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ if (getPackageRegistry(false) == null)
+ {
+ setPackageRegistry(createPackageRegistry());
+ }
+
+ if (getSessionManager() == null)
+ {
+ setSessionManager(createSessionManager());
+ }
+
+ if (getBranchManager() == null)
+ {
+ setBranchManager(createBranchManager());
+ }
+
+ if (getRevisionManager() == null)
+ {
+ setRevisionManager(createRevisionManager());
+ }
+
+ if (getQueryManager() == null)
+ {
+ setQueryManager(createQueryManager());
+ }
+
+ if (getCommitInfoManager() == null)
+ {
+ setCommitInfoManager(createCommitInfoManager());
+ }
+
+ if (getCommitManager() == null)
+ {
+ setCommitManager(createCommitManager());
+ }
+
+ if (getLockManager() == null)
+ {
+ setLockManager(createLockManager());
+ }
+
+ super.doBeforeActivate();
+ }
+
+ protected InternalCDOPackageRegistry createPackageRegistry()
+ {
+ return new CDOPackageRegistryImpl();
+ }
+
+ protected InternalSessionManager createSessionManager()
+ {
+ return new SessionManager();
+ }
+
+ protected InternalCDOBranchManager createBranchManager()
+ {
+ return CDOBranchUtil.createBranchManager();
+ }
+
+ protected InternalCDORevisionManager createRevisionManager()
+ {
+ return (InternalCDORevisionManager)CDORevisionUtil.createRevisionManager();
+ }
+
+ protected InternalQueryManager createQueryManager()
+ {
+ return new QueryManager();
+ }
+
+ protected InternalCDOCommitInfoManager createCommitInfoManager()
+ {
+ return CDOCommitInfoUtil.createCommitInfoManager();
+ }
+
+ protected InternalCommitManager createCommitManager()
+ {
+ return new CommitManager();
+ }
+
+ protected InternalLockManager createLockManager()
+ {
+ return new LockManager();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java
new file mode 100644
index 0000000000..81c653db65
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java
@@ -0,0 +1,146 @@
+/**
+ * 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.internal.server;
+
+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.protocol.CDOProtocolConstants;
+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.IStoreAccessor;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.server.QueryHandlerFactory;
+
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class ResourcesQueryHandler implements IQueryHandler
+{
+ public ResourcesQueryHandler()
+ {
+ }
+
+ public void executeQuery(CDOQueryInfo info, IQueryContext context)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ QueryContext resourcesContext = new QueryContext(info, context);
+ accessor.queryResources(resourcesContext);
+
+ CDOBranchPoint branchPoint = context;
+ CDOBranch branch = branchPoint.getBranch();
+ while (!branch.isMainBranch() && resourcesContext.getResourceIDs().size() < info.getMaxResults())
+ {
+ branchPoint = branch.getBase();
+ branch = branchPoint.getBranch();
+
+ resourcesContext.setBranchPoint(branchPoint);
+ accessor.queryResources(resourcesContext);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 3.0
+ */
+ private static final class QueryContext implements IStoreAccessor.QueryResourcesContext
+ {
+ private CDOQueryInfo info;
+
+ private IQueryContext context;
+
+ private CDOBranchPoint branchPoint;
+
+ private Set<CDOID> resourceIDs = new HashSet<CDOID>();
+
+ public QueryContext(CDOQueryInfo info, IQueryContext context)
+ {
+ this.info = info;
+ this.context = context;
+ branchPoint = context;
+ }
+
+ public void setBranchPoint(CDOBranchPoint branchPoint)
+ {
+ this.branchPoint = branchPoint;
+ }
+
+ public Set<CDOID> getResourceIDs()
+ {
+ return resourceIDs;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public CDOID getFolderID()
+ {
+ return (CDOID)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES_FOLDER_ID);
+ }
+
+ public String getName()
+ {
+ return info.getQueryString();
+ }
+
+ public boolean exactMatch()
+ {
+ return (Boolean)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES_EXACT_MATCH);
+ }
+
+ public int getMaxResults()
+ {
+ return info.getMaxResults();
+ }
+
+ public boolean addResource(CDOID resourceID)
+ {
+ if (resourceIDs.add(resourceID))
+ {
+ return context.addResult(resourceID);
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public static class Factory extends QueryHandlerFactory
+ {
+ public Factory()
+ {
+ super(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES);
+ }
+
+ @Override
+ public ResourcesQueryHandler create(String description) throws ProductCreationException
+ {
+ return new ResourcesQueryHandler();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java
new file mode 100644
index 0000000000..f551a61840
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java
@@ -0,0 +1,901 @@
+/**
+ * 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.internal.server;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDGenerator;
+import org.eclipse.emf.cdo.common.lob.CDOLobStore;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.session.CDORepositoryInfo;
+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.CDORevisionAvailabilityInfo;
+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.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.transaction.CDOTransaction;
+import org.eclipse.emf.cdo.util.CDOUtil;
+import org.eclipse.emf.cdo.view.CDOAdapterPolicy;
+import org.eclipse.emf.cdo.view.CDOFeatureAnalyzer;
+import org.eclipse.emf.cdo.view.CDOFetchRuleManager;
+import org.eclipse.emf.cdo.view.CDOInvalidationPolicy;
+import org.eclipse.emf.cdo.view.CDORevisionPrefetchingPolicy;
+import org.eclipse.emf.cdo.view.CDOStaleReferencePolicy;
+import org.eclipse.emf.cdo.view.CDOView;
+
+import org.eclipse.emf.internal.cdo.session.SessionUtil;
+import org.eclipse.emf.internal.cdo.view.AbstractCDOView;
+
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.LifecycleException;
+import org.eclipse.net4j.util.lifecycle.LifecycleState;
+import org.eclipse.net4j.util.ref.ReferenceType;
+import org.eclipse.net4j.util.ref.ReferenceValueMap;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager;
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
+import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
+import org.eclipse.emf.spi.cdo.InternalCDOView;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cdo.view.CDOView.Options
+{
+ private static final CDOAdapterPolicy[] ADAPTER_POLICIES = new CDOAdapterPolicy[0];
+
+ private static final CDORevisionPrefetchingPolicy REVISION_PREFETCHING = CDOUtil
+ .createRevisionPrefetchingPolicy(NO_REVISION_PREFETCHING);
+
+ private InternalCDOSession session;
+
+ private CDORevisionProvider revisionProvider;
+
+ public ServerCDOView(InternalSession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled,
+ CDORevisionProvider revisionProvider)
+ {
+ super(branchPoint, legacyModeEnabled);
+ this.session = new ServerCDOSession(session);
+ this.revisionProvider = revisionProvider;
+
+ setViewSet(SessionUtil.prepareResourceSet(new ResourceSetImpl()));
+ setObjects(new ReferenceValueMap.Weak<CDOID, InternalCDOObject>());
+ activate();
+ }
+
+ public int getViewID()
+ {
+ return 1;
+ }
+
+ public InternalCDOSession getSession()
+ {
+ return session;
+ }
+
+ public long getLastUpdateTime()
+ {
+ return getTimeStamp();
+ }
+
+ public void setLastUpdateTime(long lastUpdateTime)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Options options()
+ {
+ return this;
+ }
+
+ public synchronized InternalCDORevision getRevision(CDOID id, boolean loadOnDemand)
+ {
+ return (InternalCDORevision)revisionProvider.getRevision(id);
+ }
+
+ @Override
+ protected synchronized void excludeNewObject(CDOID id)
+ {
+ // Do nothing
+ }
+
+ public boolean isInvalidationRunnerActive()
+ {
+ return false;
+ }
+
+ public boolean setBranchPoint(CDOBranchPoint branchPoint)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void lockObjects(Collection<? extends CDOObject> objects, LockType lockType, long timeout)
+ throws InterruptedException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unlockObjects(Collection<? extends CDOObject> objects, LockType lockType)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unlockObjects()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean waitForUpdate(long updateTime, long timeoutMillis)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setViewID(int viewId)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setSession(InternalCDOSession session)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getDurableLockingID()
+ {
+ return null;
+ }
+
+ public String enableDurableLocking(boolean enable)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOFeatureAnalyzer getFeatureAnalyzer()
+ {
+ return CDOFeatureAnalyzer.NOOP;
+ }
+
+ public void setFeatureAnalyzer(CDOFeatureAnalyzer featureAnalyzer)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public InternalCDOTransaction toTransaction()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void invalidate(CDOBranch branch, long lastUpdateTime, List<CDORevisionKey> allChangedObjects,
+ List<CDOIDAndVersion> allDetachedObjects, Map<CDOID, InternalCDORevision> oldRevisions, boolean async)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void handleLockNotification(InternalCDOView sender, CDOLockChangeInfo lockChangeInfo)
+ {
+ // Do nothing
+ }
+
+ public void prefetchRevisions(CDOID id, int depth)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isObjectLocked(CDOObject object, LockType lockType, boolean byOthers)
+ {
+ return false;
+ }
+
+ public void handleAddAdapter(InternalCDOObject eObject, Adapter adapter)
+ {
+ // Do nothing
+ }
+
+ public void handleRemoveAdapter(InternalCDOObject eObject, Adapter adapter)
+ {
+ // Do nothing
+ }
+
+ public void subscribe(EObject eObject, Adapter adapter)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unsubscribe(EObject eObject, Adapter adapter)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean hasSubscription(CDOID id)
+ {
+ return false;
+ }
+
+ public CDOView getContainer()
+ {
+ return this;
+ }
+
+ public ReferenceType getCacheReferenceType()
+ {
+ return ReferenceType.WEAK;
+ }
+
+ public boolean setCacheReferenceType(ReferenceType referenceType)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOInvalidationPolicy getInvalidationPolicy()
+ {
+ return CDOInvalidationPolicy.DEFAULT;
+ }
+
+ public void setInvalidationPolicy(CDOInvalidationPolicy policy)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isInvalidationNotificationEnabled()
+ {
+ return false;
+ }
+
+ public void setInvalidationNotificationEnabled(boolean enabled)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isLockNotificationEnabled()
+ {
+ return false;
+ }
+
+ public void setLockNotificationEnabled(boolean enabled)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOAdapterPolicy[] getChangeSubscriptionPolicies()
+ {
+ return ADAPTER_POLICIES;
+ }
+
+ public void addChangeSubscriptionPolicy(CDOAdapterPolicy policy)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeChangeSubscriptionPolicy(CDOAdapterPolicy policy)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOAdapterPolicy getStrongReferencePolicy()
+ {
+ return CDOAdapterPolicy.ALL;
+ }
+
+ public void setStrongReferencePolicy(CDOAdapterPolicy policy)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Deprecated
+ public CDOStaleReferencePolicy getStaleReferenceBehaviour()
+ {
+ return getStaleReferencePolicy();
+ }
+
+ @Deprecated
+ public void setStaleReferenceBehaviour(CDOStaleReferencePolicy policy)
+ {
+ setStaleReferencePolicy(policy);
+ }
+
+ public CDOStaleReferencePolicy getStaleReferencePolicy()
+ {
+ return CDOStaleReferencePolicy.EXCEPTION;
+ }
+
+ public void setStaleReferencePolicy(CDOStaleReferencePolicy policy)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDORevisionPrefetchingPolicy getRevisionPrefetchingPolicy()
+ {
+ return REVISION_PREFETCHING;
+ }
+
+ public void setRevisionPrefetchingPolicy(CDORevisionPrefetchingPolicy prefetchingPolicy)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOLockState[] getLockStates(Collection<CDOID> ids)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class ServerCDOSession implements InternalCDOSession, CDORepositoryInfo
+ {
+ private InternalSession internalSession;
+
+ private InternalRepository repository;
+
+ public ServerCDOSession(InternalSession internalSession)
+ {
+ this.internalSession = internalSession;
+ repository = internalSession.getManager().getRepository();
+ }
+
+ public CDOView[] getElements()
+ {
+ return new ServerCDOView[] { ServerCDOView.this };
+ }
+
+ public InternalCDOTransaction getTransaction(int viewID)
+ {
+ return null;
+ }
+
+ public InternalCDOTransaction[] getTransactions()
+ {
+ return new InternalCDOTransaction[0];
+ }
+
+ public CDOView[] getViews()
+ {
+ return getElements();
+ }
+
+ public CDOView getView(int viewID)
+ {
+ return viewID == getViewID() ? ServerCDOView.this : null;
+ }
+
+ public CDOSessionProtocol getSessionProtocol()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOLobStore getLobStore()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public InternalCDORevisionManager getRevisionManager()
+ {
+ return repository.getRevisionManager();
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry()
+ {
+ if (revisionProvider instanceof IStoreAccessor.CommitContext)
+ {
+ IStoreAccessor.CommitContext context = (IStoreAccessor.CommitContext)revisionProvider;
+ return context.getPackageRegistry();
+ }
+
+ return repository.getPackageRegistry(false);
+ }
+
+ public InternalCDOCommitInfoManager getCommitInfoManager()
+ {
+ return repository.getCommitInfoManager();
+ }
+
+ public InternalCDOBranchManager getBranchManager()
+ {
+ return repository.getBranchManager();
+ }
+
+ public void setMainBranchLocal(boolean mainBranchLocal)
+ {
+ // Do nothing
+ }
+
+ public boolean hasListeners()
+ {
+ return false;
+ }
+
+ public IListener[] getListeners()
+ {
+ return null;
+ }
+
+ public void addListener(IListener listener)
+ {
+ // Do nothing
+ }
+
+ public void removeListener(IListener listener)
+ {
+ // Do nothing
+ }
+
+ public void activate() throws LifecycleException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Exception deactivate()
+ {
+ return ServerCDOView.this.deactivate();
+ }
+
+ public LifecycleState getLifecycleState()
+ {
+ return LifecycleState.ACTIVE;
+ }
+
+ public boolean isActive()
+ {
+ return ServerCDOView.this.isActive();
+ }
+
+ public boolean isClosed()
+ {
+ return !isActive();
+ }
+
+ public void close()
+ {
+ deactivate();
+ }
+
+ public CDORepositoryInfo getRepositoryInfo()
+ {
+ return this;
+ }
+
+ public String getName()
+ {
+ return repository.getName();
+ }
+
+ public String getUUID()
+ {
+ return repository.getUUID();
+ }
+
+ public Type getType()
+ {
+ return repository.getType();
+ }
+
+ public State getState()
+ {
+ return repository.getState();
+ }
+
+ public long getCreationTime()
+ {
+ return repository.getCreationTime();
+ }
+
+ public long getTimeStamp()
+ {
+ return repository.getTimeStamp();
+ }
+
+ public long getTimeStamp(boolean forceRefresh)
+ {
+ return getTimeStamp();
+ }
+
+ public String getStoreType()
+ {
+ return repository.getStoreType();
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return repository.getObjectIDTypes();
+ }
+
+ public CDOID getRootResourceID()
+ {
+ return repository.getRootResourceID();
+ }
+
+ public boolean isSupportingAudits()
+ {
+ return repository.isSupportingAudits();
+ }
+
+ public boolean isSupportingBranches()
+ {
+ return repository.isSupportingBranches();
+ }
+
+ public boolean isSupportingEcore()
+ {
+ return repository.isSupportingEcore();
+ }
+
+ public boolean isEnsuringReferentialIntegrity()
+ {
+ return repository.isEnsuringReferentialIntegrity();
+ }
+
+ public IDGenerationLocation getIDGenerationLocation()
+ {
+ return repository.getIDGenerationLocation();
+ }
+
+ public void handleRepositoryTypeChanged(Type oldType, Type newType)
+ {
+ }
+
+ public void handleRepositoryStateChanged(State oldState, State newState)
+ {
+ }
+
+ public EPackage[] loadPackages(CDOPackageUnit packageUnit)
+ {
+ return null;
+ }
+
+ public void releaseAtomicRequestLock(Object key)
+ {
+ // Do nothing
+ }
+
+ public void acquireAtomicRequestLock(Object key)
+ {
+ // Do nothing
+ }
+
+ public Object processPackage(Object value)
+ {
+ return value;
+ }
+
+ public boolean isEmpty()
+ {
+ return false;
+ }
+
+ public boolean waitForUpdate(long updateTime, long timeoutMillis)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void waitForUpdate(long updateTime)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public long getLastUpdateTime()
+ {
+ return getBranchPoint().getTimeStamp();
+ }
+
+ public String getUserID()
+ {
+ return null;
+ }
+
+ public int getSessionID()
+ {
+ return internalSession.getSessionID();
+ }
+
+ public long refresh()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Options options()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(String durableLockingID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(String durableLockingID, ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(long timeStamp)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(CDOBranch branch)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(CDOBranch branch, long timeStamp)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(CDOBranch branch, long timeStamp, ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(CDOBranchPoint target, ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(CDOBranchPoint target)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(CDOBranchPoint target, ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOView openView(CDOBranchPoint target)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(String durableLockingID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(String durableLockingID, ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(CDOBranch branch)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOTransaction openTransaction(CDOBranch branch, ResourceSet resourceSet)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOFetchRuleManager getFetchRuleManager()
+ {
+ return null;
+ }
+
+ public ExceptionHandler getExceptionHandler()
+ {
+ return null;
+ }
+
+ public CDOIDGenerator getIDGenerator()
+ {
+ return null;
+ }
+
+ public void viewDetached(InternalCDOView view)
+ {
+ // Do nothing
+ }
+
+ public void setUserID(String userID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setSessionProtocol(CDOSessionProtocol sessionProtocol)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setSessionID(int sessionID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRepositoryInfo(CDORepositoryInfo repositoryInfo)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRemoteSessionManager(InternalCDORemoteSessionManager remoteSessionManager)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setLastUpdateTime(long lastUpdateTime)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setFetchRuleManager(CDOFetchRuleManager fetchRuleManager)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setExceptionHandler(ExceptionHandler exceptionHandler)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setIDGenerator(CDOIDGenerator idGenerator)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object resolveElementProxy(CDORevision revision, EStructuralFeature feature, int accessIndex, int serverIndex)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void resolveAllElementProxies(CDORevision revision)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void processRefreshSessionResult(RefreshSessionResult result, CDOBranch branch,
+ List<InternalCDOView> branchViews, Map<CDOBranch, Map<CDOID, InternalCDORevision>> viewedRevisions)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void handleCommitNotification(CDOCommitInfo commitInfo)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void handleLockNotification(CDOLockChangeInfo lockChangeInfo, InternalCDOView sender)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void handleBranchNotification(InternalCDOBranch branch)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public InternalCDORemoteSessionManager getRemoteSessionManager()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOAuthenticator getAuthenticator()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setAuthenticator(CDOAuthenticator authenticator)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRevisionManager(InternalCDORevisionManager revisionManager)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setBranchManager(InternalCDOBranchManager branchManager)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setCommitInfoManager(InternalCDOCommitInfoManager commitInfoManager)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setPackageRegistry(InternalCDOPackageRegistry packageRegistry)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isSticky()
+ {
+ return false;
+ }
+
+ public CDOBranchPoint getCommittedSinceLastRefresh(CDOID id)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setCommittedSinceLastRefresh(CDOID id, CDOBranchPoint branchPoint)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void clearCommittedSinceLastRefresh()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOChangeSetData compareRevisions(CDOBranchPoint source, CDOBranchPoint target)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDORevisionAvailabilityInfo createRevisionAvailabilityInfo(CDOBranchPoint branchPoint)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void cacheRevisions(CDORevisionAvailabilityInfo info)
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java
new file mode 100644
index 0000000000..bb0205d2ba
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java
@@ -0,0 +1,574 @@
+/**
+ * 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
+ * Simon McDuff - bug 201266
+ * Simon McDuff - bug 230832
+ * Simon McDuff - bug 233490
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.CDOCommonSession;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.internal.common.commit.DelegatingCommitInfo;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.server.ISessionProtocol;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.collection.IndexedList;
+import org.eclipse.net4j.util.container.Container;
+import org.eclipse.net4j.util.event.EventUtil;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.ILifecycle;
+import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.log.OMLogger;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Eike Stepper
+ */
+public class Session extends Container<IView> implements InternalSession
+{
+ private InternalSessionManager manager;
+
+ private ISessionProtocol protocol;
+
+ private int sessionID;
+
+ private String userID;
+
+ private boolean passiveUpdateEnabled = true;
+
+ private PassiveUpdateMode passiveUpdateMode = PassiveUpdateMode.INVALIDATIONS;
+
+ private LockNotificationMode lockNotificationMode = LockNotificationMode.IF_REQUIRED_BY_VIEWS;
+
+ private long lastUpdateTime;
+
+ @ExcludeFromDump
+ private Object lastUpdateTimeLock = new Object();
+
+ private ConcurrentMap<Integer, InternalView> views = new ConcurrentHashMap<Integer, InternalView>();
+
+ private AtomicInteger lastTempViewID = new AtomicInteger();
+
+ @ExcludeFromDump
+ private IListener protocolListener = new LifecycleEventAdapter()
+ {
+ @Override
+ protected void onDeactivated(ILifecycle lifecycle)
+ {
+ deactivate();
+ }
+ };
+
+ private boolean subscribed;
+
+ /**
+ * @since 2.0
+ */
+ public Session(InternalSessionManager manager, ISessionProtocol protocol, int sessionID, String userID)
+ {
+ this.manager = manager;
+ this.protocol = protocol;
+ this.sessionID = sessionID;
+ this.userID = userID;
+
+ EventUtil.addListener(protocol, protocolListener);
+ activate();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public Options options()
+ {
+ return this;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public CDOCommonSession getContainer()
+ {
+ return this;
+ }
+
+ public InternalSessionManager getManager()
+ {
+ return manager;
+ }
+
+ public ISessionProtocol getProtocol()
+ {
+ return protocol;
+ }
+
+ public int getSessionID()
+ {
+ return sessionID;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public String getUserID()
+ {
+ return userID;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public boolean isSubscribed()
+ {
+ return subscribed;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setSubscribed(boolean subscribed)
+ {
+ checkActive();
+ if (this.subscribed != subscribed)
+ {
+ this.subscribed = subscribed;
+ byte opcode = subscribed ? CDOProtocolConstants.REMOTE_SESSION_SUBSCRIBED
+ : CDOProtocolConstants.REMOTE_SESSION_UNSUBSCRIBED;
+ manager.sendRemoteSessionNotification(this, opcode);
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public boolean isPassiveUpdateEnabled()
+ {
+ return passiveUpdateEnabled;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setPassiveUpdateEnabled(boolean passiveUpdateEnabled)
+ {
+ checkActive();
+ this.passiveUpdateEnabled = passiveUpdateEnabled;
+ }
+
+ public PassiveUpdateMode getPassiveUpdateMode()
+ {
+ return passiveUpdateMode;
+ }
+
+ public void setPassiveUpdateMode(PassiveUpdateMode passiveUpdateMode)
+ {
+ checkActive();
+ checkArg(passiveUpdateMode, "passiveUpdateMode");
+ this.passiveUpdateMode = passiveUpdateMode;
+ }
+
+ public LockNotificationMode getLockNotificationMode()
+ {
+ return lockNotificationMode;
+ }
+
+ public void setLockNotificationMode(LockNotificationMode lockNotificationMode)
+ {
+ checkActive();
+ checkArg(lockNotificationMode, "lockNotificationMode");
+ this.lockNotificationMode = lockNotificationMode;
+ }
+
+ public long getLastUpdateTime()
+ {
+ synchronized (lastUpdateTimeLock)
+ {
+ return lastUpdateTime;
+ }
+ }
+
+ public InternalView[] getElements()
+ {
+ checkActive();
+ return getViews();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ checkActive();
+ return views.isEmpty();
+ }
+
+ public InternalView[] getViews()
+ {
+ checkActive();
+ return getViewsArray();
+ }
+
+ private InternalView[] getViewsArray()
+ {
+ return views.values().toArray(new InternalView[views.size()]);
+ }
+
+ public InternalView getView(int viewID)
+ {
+ checkActive();
+ return views.get(viewID);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalView openView(int viewID, CDOBranchPoint branchPoint)
+ {
+ checkActive();
+ if (viewID == TEMP_VIEW_ID)
+ {
+ viewID = -lastTempViewID.incrementAndGet();
+ }
+
+ InternalView view = new View(this, viewID, branchPoint);
+ view.activate();
+ addView(view);
+ return view;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalTransaction openTransaction(int viewID, CDOBranchPoint branchPoint)
+ {
+ checkActive();
+ if (viewID == TEMP_VIEW_ID)
+ {
+ viewID = -lastTempViewID.incrementAndGet();
+ }
+
+ InternalTransaction transaction = new Transaction(this, viewID, branchPoint);
+ transaction.activate();
+ addView(transaction);
+ return transaction;
+ }
+
+ private void addView(InternalView view)
+ {
+ checkActive();
+ int viewID = view.getViewID();
+ views.put(viewID, view);
+ fireElementAddedEvent(view);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void viewClosed(InternalView view)
+ {
+ int viewID = view.getViewID();
+ if (views.remove(viewID) == view)
+ {
+ view.doClose();
+ fireElementRemovedEvent(view);
+ }
+ }
+
+ /**
+ * TODO I can't see how recursion is controlled/limited
+ *
+ * @since 2.0
+ */
+ public void collectContainedRevisions(InternalCDORevision revision, CDOBranchPoint branchPoint, int referenceChunk,
+ Set<CDOID> revisions, List<CDORevision> additionalRevisions)
+ {
+ InternalCDORevisionManager revisionManager = getManager().getRepository().getRevisionManager();
+ EClass eClass = revision.getEClass();
+ EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass);
+ for (int i = 0; i < features.length; i++)
+ {
+ EStructuralFeature feature = features[i];
+ // TODO Clarify feature maps
+ if (feature instanceof EReference && !feature.isMany() && ((EReference)feature).isContainment())
+ {
+ Object value = revision.getValue(feature);
+ if (value instanceof CDOID)
+ {
+ CDOID id = (CDOID)value;
+ if (!CDOIDUtil.isNull(id) && !revisions.contains(id))
+ {
+ InternalCDORevision containedRevision = revisionManager.getRevision(id, branchPoint, referenceChunk,
+ CDORevision.DEPTH_NONE, true);
+ revisions.add(id);
+ additionalRevisions.add(containedRevision);
+
+ // Recurse
+ collectContainedRevisions(containedRevision, branchPoint, referenceChunk, revisions, additionalRevisions);
+ }
+ }
+ }
+ }
+ }
+
+ public CDOID provideCDOID(Object idObject)
+ {
+ return (CDOID)idObject;
+ }
+
+ public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType)
+ throws Exception
+ {
+ if (protocol != null)
+ {
+ protocol.sendRepositoryTypeNotification(oldType, newType);
+ }
+ }
+
+ @Deprecated
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState)
+ throws Exception
+ {
+ sendRepositoryStateNotification(oldState, newState, null);
+ }
+
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState,
+ CDOID rootResourceID) throws Exception
+ {
+ if (protocol != null)
+ {
+ protocol.sendRepositoryStateNotification(oldState, newState, rootResourceID);
+ }
+ }
+
+ public void sendBranchNotification(InternalCDOBranch branch) throws Exception
+ {
+ if (protocol != null)
+ {
+ protocol.sendBranchNotification(branch);
+ }
+ }
+
+ public void sendCommitNotification(final CDOCommitInfo commitInfo) throws Exception
+ {
+ if (protocol == null)
+ {
+ return;
+ }
+
+ if (!isPassiveUpdateEnabled())
+ {
+ return;
+ }
+
+ final InternalView[] views = getViews();
+ protocol.sendCommitNotification(new DelegatingCommitInfo()
+ {
+ private final PassiveUpdateMode passiveUpdateMode = getPassiveUpdateMode();
+
+ private final boolean additions = passiveUpdateMode == PassiveUpdateMode.ADDITIONS;
+
+ private final boolean changes = passiveUpdateMode == PassiveUpdateMode.CHANGES;
+
+ @Override
+ protected CDOCommitInfo getDelegate()
+ {
+ return commitInfo;
+ }
+
+ @Override
+ public List<CDOIDAndVersion> getNewObjects()
+ {
+ final List<CDOIDAndVersion> newObjects = super.getNewObjects();
+ return new IndexedList<CDOIDAndVersion>()
+ {
+ @Override
+ public CDOIDAndVersion get(int index)
+ {
+ // The following will always be a CDORevision!
+ CDOIDAndVersion newObject = newObjects.get(index);
+ if (additions)
+ {
+ // Return full revisions if not in INVALIDATION mode
+ return newObject;
+ }
+
+ // Prevent sending whole revisions by copying the id and version
+ return CDOIDUtil.createIDAndVersion(newObject);
+ }
+
+ @Override
+ public int size()
+ {
+ return newObjects.size();
+ }
+ };
+ }
+
+ @Override
+ public List<CDORevisionKey> getChangedObjects()
+ {
+ final List<CDORevisionKey> changedObjects = super.getChangedObjects();
+ return new IndexedList<CDORevisionKey>()
+ {
+ @Override
+ public CDORevisionKey get(int index)
+ {
+ // The following will always be a CDORevisionDelta!
+ CDORevisionKey changedObject = changedObjects.get(index);
+ if (changes || additions || hasSubscription(changedObject.getID(), views))
+ {
+ return changedObject;
+ }
+
+ // Prevent sending whole revisions by copying the id and version
+ return CDORevisionUtil.copyRevisionKey(changedObject);
+ }
+
+ @Override
+ public int size()
+ {
+ return changedObjects.size();
+ }
+ };
+ }
+ });
+
+ synchronized (lastUpdateTimeLock)
+ {
+ lastUpdateTime = commitInfo.getTimeStamp();
+ }
+ }
+
+ public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception
+ {
+ if (protocol != null)
+ {
+ if (options().getLockNotificationMode() == LockNotificationMode.ALWAYS)
+ {
+ protocol.sendLockNotification(lockChangeInfo);
+ return;
+ }
+
+ if (options().getLockNotificationMode() == LockNotificationMode.IF_REQUIRED_BY_VIEWS)
+ {
+ // If this session has one (or more) views configured for this branch,
+ // only then do we send the lockChangeInfo.
+ for (InternalView view : getViews())
+ {
+ if (view.options().isLockNotificationEnabled() && view.getBranch().equals(lockChangeInfo.getBranch()))
+ {
+ protocol.sendLockNotification(lockChangeInfo);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private boolean hasSubscription(CDOID id, InternalView[] views)
+ {
+ for (InternalView view : views)
+ {
+ if (view.hasSubscription(id))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception
+ {
+ if (protocol != null)
+ {
+ protocol.sendRemoteSessionNotification(sender, opcode);
+ }
+ }
+
+ public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception
+ {
+ if (protocol != null)
+ {
+ protocol.sendRemoteMessageNotification(sender, message);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("Session[{0}]", sessionID); //$NON-NLS-1$
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void close()
+ {
+ LifecycleUtil.deactivate(this, OMLogger.Level.DEBUG);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public boolean isClosed()
+ {
+ return !isActive();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ EventUtil.removeListener(protocol, protocolListener);
+ protocolListener = null;
+
+ LifecycleUtil.deactivate(protocol, OMLogger.Level.DEBUG);
+ protocol = null;
+
+ for (IView view : getViewsArray())
+ {
+ view.close();
+ }
+
+ views = null;
+ manager.sessionClosed(this);
+ manager = null;
+ super.doDeactivate();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java
new file mode 100644
index 0000000000..22f71b80d7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java
@@ -0,0 +1,482 @@
+/**
+ * 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
+ * Simon McDuff - bug 201266
+ * Simon McDuff - bug 202725
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+import org.eclipse.emf.cdo.spi.server.ISessionProtocol;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.container.Container;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+import org.eclipse.net4j.util.security.IRandomizer;
+import org.eclipse.net4j.util.security.IUserManager;
+import org.eclipse.net4j.util.security.NegotiationException;
+import org.eclipse.net4j.util.security.Randomizer;
+import org.eclipse.net4j.util.security.SecurityUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Eike Stepper
+ */
+public class SessionManager extends Container<ISession> implements InternalSessionManager
+{
+ public static final int DEFAULT_TOKEN_LENGTH = 1024;
+
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SESSION, SessionManager.class);
+
+ private InternalRepository repository;
+
+ @ExcludeFromDump
+ private String encryptionAlgorithmName = SecurityUtil.PBE_WITH_MD5_AND_DES;
+
+ @ExcludeFromDump
+ private byte[] encryptionSaltBytes = SecurityUtil.DEFAULT_SALT;
+
+ @ExcludeFromDump
+ private int encryptionIterationCount = SecurityUtil.DEFAULT_ITERATION_COUNT;
+
+ private int tokenLength = DEFAULT_TOKEN_LENGTH;
+
+ private IRandomizer randomizer;
+
+ private IUserManager userManager;
+
+ private final Map<Integer, InternalSession> sessions = new HashMap<Integer, InternalSession>();
+
+ private final AtomicInteger lastSessionID = new AtomicInteger();
+
+ /**
+ * @since 2.0
+ */
+ public SessionManager()
+ {
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setRepository(InternalRepository repository)
+ {
+ checkInactive();
+ this.repository = repository;
+ }
+
+ public String getEncryptionAlgorithmName()
+ {
+ return encryptionAlgorithmName;
+ }
+
+ public void setEncryptionAlgorithmName(String encryptionAlgorithmName)
+ {
+ checkInactive();
+ this.encryptionAlgorithmName = encryptionAlgorithmName;
+ }
+
+ public byte[] getEncryptionSaltBytes()
+ {
+ return encryptionSaltBytes;
+ }
+
+ public void setEncryptionSaltBytes(byte[] encryptionSaltBytes)
+ {
+ checkInactive();
+ this.encryptionSaltBytes = encryptionSaltBytes;
+ }
+
+ public int getEncryptionIterationCount()
+ {
+ return encryptionIterationCount;
+ }
+
+ public void setEncryptionIterationCount(int encryptionIterationCount)
+ {
+ checkInactive();
+ this.encryptionIterationCount = encryptionIterationCount;
+ }
+
+ public int getTokenLength()
+ {
+ return tokenLength;
+ }
+
+ public void setTokenLength(int tokenLength)
+ {
+ checkInactive();
+ this.tokenLength = tokenLength;
+ }
+
+ public IRandomizer getRandomizer()
+ {
+ return randomizer;
+ }
+
+ public void setRandomizer(IRandomizer randomizer)
+ {
+ checkInactive();
+ this.randomizer = randomizer;
+ }
+
+ public IUserManager getUserManager()
+ {
+ return userManager;
+ }
+
+ public void setUserManager(IUserManager userManager)
+ {
+ this.userManager = userManager;
+ }
+
+ public InternalSession[] getSessions()
+ {
+ synchronized (sessions)
+ {
+ return sessions.values().toArray(new InternalSession[sessions.size()]);
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalSession getSession(int sessionID)
+ {
+ checkActive();
+ synchronized (sessions)
+ {
+ return sessions.get(sessionID);
+ }
+ }
+
+ public InternalSession[] getElements()
+ {
+ return getSessions();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ synchronized (sessions)
+ {
+ return sessions.isEmpty();
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalSession openSession(ISessionProtocol sessionProtocol)
+ {
+ int id = lastSessionID.incrementAndGet();
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Opening session " + id); //$NON-NLS-1$
+ }
+
+ String userID = authenticateUser(sessionProtocol);
+ InternalSession session = createSession(id, userID, sessionProtocol);
+ LifecycleUtil.activate(session);
+
+ synchronized (sessions)
+ {
+ sessions.put(id, session);
+ }
+
+ fireElementAddedEvent(session);
+ sendRemoteSessionNotification(session, CDOProtocolConstants.REMOTE_SESSION_OPENED);
+ return session;
+ }
+
+ protected InternalSession createSession(int id, String userID, ISessionProtocol protocol)
+ {
+ return new Session(this, protocol, id, userID);
+ }
+
+ public void sessionClosed(InternalSession session)
+ {
+ int sessionID = session.getSessionID();
+ InternalSession removeSession = null;
+ synchronized (sessions)
+ {
+ removeSession = sessions.remove(sessionID);
+ }
+
+ if (removeSession != null)
+ {
+ fireElementRemovedEvent(session);
+ sendRemoteSessionNotification(session, CDOProtocolConstants.REMOTE_SESSION_CLOSED);
+ }
+ }
+
+ public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType)
+ {
+ for (InternalSession session : getSessions())
+ {
+ try
+ {
+ session.sendRepositoryTypeNotification(oldType, newType);
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(session, ex);
+ }
+ }
+ }
+
+ @Deprecated
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState)
+ {
+ sendRepositoryStateNotification(oldState, newState, null);
+ }
+
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState,
+ CDOID rootResourceID)
+ {
+ for (InternalSession session : getSessions())
+ {
+ try
+ {
+ session.sendRepositoryStateNotification(oldState, newState, rootResourceID);
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(session, ex);
+ }
+ }
+ }
+
+ public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch)
+ {
+ for (InternalSession session : getSessions())
+ {
+ if (session != sender)
+ {
+ try
+ {
+ session.sendBranchNotification(branch);
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(session, ex);
+ }
+ }
+ }
+ }
+
+ public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo)
+ {
+ for (InternalSession session : getSessions())
+ {
+ if (session != sender)
+ {
+ try
+ {
+ session.sendCommitNotification(commitInfo);
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(session, ex);
+ }
+ }
+ }
+ }
+
+ public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo)
+ {
+ for (InternalSession session : getSessions())
+ {
+ if (session == sender || session.options().getLockNotificationMode() == LockNotificationMode.OFF)
+ {
+ continue;
+ }
+
+ try
+ {
+ session.sendLockNotification(lockChangeInfo);
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(session, ex);
+ }
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void sendRemoteSessionNotification(InternalSession sender, byte opcode)
+ {
+ try
+ {
+ for (InternalSession session : getSessions())
+ {
+ if (session != sender && session.isSubscribed())
+ {
+ try
+ {
+ session.sendRemoteSessionNotification(sender, opcode);
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(session, ex);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.warn("A problem occured while notifying other sessions", ex);
+ }
+ }
+
+ public List<Integer> sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message,
+ int[] recipients)
+ {
+ List<Integer> result = new ArrayList<Integer>();
+ for (int i = 0; i < recipients.length; i++)
+ {
+ InternalSession recipient = getSession(recipients[i]);
+
+ try
+ {
+ if (recipient != null && recipient.isSubscribed())
+ {
+ recipient.sendRemoteMessageNotification(sender, message);
+ result.add(recipient.getSessionID());
+ }
+ }
+ catch (Exception ex)
+ {
+ handleNotificationProblem(recipient, ex);
+ }
+ }
+
+ return result;
+ }
+
+ protected void handleNotificationProblem(InternalSession session, Throwable t)
+ {
+ OM.LOG.warn("A problem occured while notifying session " + session, t);
+ }
+
+ protected String authenticateUser(ISessionProtocol protocol) throws SecurityException
+ {
+ if (protocol == null)
+ {
+ return null;
+ }
+
+ if (userManager == null)
+ {
+ return null;
+ }
+
+ try
+ {
+ byte[] randomToken = createRandomToken();
+ CDOAuthenticationResult result = protocol.sendAuthenticationChallenge(randomToken);
+ String userID = result.getUserID();
+
+ byte[] cryptedToken = encryptToken(userID, randomToken);
+ boolean success = Arrays.equals(result.getCryptedToken(), cryptedToken);
+ if (success)
+ {
+ return userID;
+ }
+
+ throw new SecurityException("User not authenticated"); //$NON-NLS-1$
+ }
+ catch (SecurityException ex)
+ {
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ throw new SecurityException(ex);
+ }
+ }
+
+ protected byte[] createRandomToken()
+ {
+ byte[] token = new byte[tokenLength];
+ randomizer.nextBytes(token);
+ return token;
+ }
+
+ protected byte[] encryptToken(String userID, byte[] token) throws NegotiationException
+ {
+ try
+ {
+ return userManager.encrypt(userID, token, getEncryptionAlgorithmName(), getEncryptionSaltBytes(),
+ getEncryptionIterationCount());
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error("Token encryption failed", ex); //$NON-NLS-1$
+ return null;
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ if (userManager != null)
+ {
+ if (randomizer == null)
+ {
+ randomizer = new Randomizer();
+ }
+
+ LifecycleUtil.activate(randomizer);
+ }
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ InternalSession[] activeSessions = getSessions();
+ for (int i = 0; i < activeSessions.length; i++)
+ {
+ LifecycleUtil.deactivate(activeSessions[i]);
+ }
+
+ super.doDeactivate();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
new file mode 100644
index 0000000000..eebbbfbc73
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
@@ -0,0 +1,230 @@
+/**
+ * Copyright (c) 2004 - 2009 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:
+ * Caspar De Groot - initial API and implementation
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Bugzilla 297940, 290032
+ *
+ * @author Caspar De Groot
+ */
+class TimeStampAuthority
+{
+ private InternalRepository repository;
+
+ /**
+ * Holds the timestamp that was issued in response to the last call to {@link #createTimestamp()}
+ */
+ @ExcludeFromDump
+ private transient long lastIssuedTimeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
+
+ /**
+ * Holds the timestamp that was last reported finished by a call to {@link #endCommit(long)}
+ */
+ private long lastFinishedTimeStamp;
+
+ private LastCommitTimeStampLock lastCommitTimeStampLock = new LastCommitTimeStampLock();
+
+ private boolean strictOrdering; // TODO (CD) Should be a repo property
+
+ /**
+ * A lock to block on if strict commit ordering is enabled
+ */
+ private StrictOrderingLock strictOrderingLock = new StrictOrderingLock();
+
+ /**
+ * An ordered list of timestamps that have been issued but have not (yet) been reported finished. (It is ordered
+ * because the timestamps are added sequentially.)
+ */
+ private List<Long> runningTransactions = new LinkedList<Long>();
+
+ /**
+ * A set of timestamps that have been reported finished but have not yet been
+ */
+ private SortedSet<Long> finishedTransactions = new TreeSet<Long>();
+
+ TimeStampAuthority(InternalRepository repository)
+ {
+ this.repository = repository;
+ }
+
+ synchronized long[] startCommit(OMMonitor monitor)
+ {
+ return startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor);
+ }
+
+ synchronized long[] startCommit(long timeStampOverride, OMMonitor monitor)
+ {
+ monitor.begin();
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.lock();
+ }
+
+ try
+ {
+ long now = repository.getTimeStamp();
+ if (lastIssuedTimeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ while (lastIssuedTimeStamp == now)
+ {
+ ConcurrencyUtil.sleep(1);
+ now = repository.getTimeStamp();
+ monitor.checkCanceled();
+ }
+ }
+
+ if (timeStampOverride != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ now = timeStampOverride;
+ }
+
+ lastIssuedTimeStamp = now;
+
+ runningTransactions.add(lastIssuedTimeStamp);
+ return new long[] { lastIssuedTimeStamp, getLastFinishedTimeStamp() };
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ synchronized void endCommit(long timeStamp)
+ {
+ if (!runningTransactions.remove(timeStamp))
+ {
+ throw new IllegalArgumentException("Cannot end transaction with unknown timestamp " + timeStamp);
+ }
+
+ finishedTransactions.add(timeStamp);
+
+ // We can remove a timestamp from finishedTransactions if it is smaller (i.e. older) than any
+ // of the runningTransactions. Since both sets are sorted, we only need to compare the heads.
+ long oldestRunning = runningTransactions.isEmpty() ? Long.MAX_VALUE : runningTransactions.get(0);
+ long oldestFinished;
+ synchronized (lastCommitTimeStampLock)
+ {
+ long oldValue = lastFinishedTimeStamp;
+ while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning)
+ {
+ finishedTransactions.remove(oldestFinished);
+ lastFinishedTimeStamp = oldestFinished;
+ }
+
+ // If we actually changed the lastFinishedTimeStamp, we need to notify waiting threads
+ if (lastFinishedTimeStamp != oldValue)
+ {
+ lastCommitTimeStampLock.notifyAll();
+ }
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized void failCommit(long timeStamp)
+ {
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) // Exclude problems before TransactionCommitContext.setTimeStamp()
+ {
+ if (!runningTransactions.remove(timeStamp))
+ {
+ throw new IllegalArgumentException("Cannot fail transaction with unknown timestamp " + timeStamp);
+ }
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized long getLastFinishedTimeStamp()
+ {
+ if (lastFinishedTimeStamp != 0)
+ {
+ return lastFinishedTimeStamp;
+ }
+
+ // If we get here, no commit has finished since the server was started
+ if (lastIssuedTimeStamp == 0) // No commit has started either
+ {
+ // We can safely return the current system time minus one milli.
+ return repository.getTimeStamp() - 1;
+ }
+
+ // If we get here, one or more commits are running
+ // We can safely return the start time of the longest-running, minus one milli.
+ return runningTransactions.get(0) - 1;
+ }
+
+ long waitForCommit(long timeout)
+ {
+ synchronized (lastCommitTimeStampLock)
+ {
+ try
+ {
+ lastCommitTimeStampLock.wait(timeout);
+ }
+ catch (Exception ignore)
+ {
+ }
+
+ return lastFinishedTimeStamp;
+ }
+ }
+
+ void setLastFinishedTimeStamp(long lastCommitTimeStamp)
+ {
+ synchronized (lastCommitTimeStampLock)
+ {
+ if (lastFinishedTimeStamp < lastCommitTimeStamp)
+ {
+ lastFinishedTimeStamp = lastCommitTimeStamp;
+ lastCommitTimeStampLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class LastCommitTimeStampLock
+ {
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class StrictOrderingLock extends ReentrantLock
+ {
+ private static final long serialVersionUID = 1L;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java
new file mode 100644
index 0000000000..31f7452c78
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java
@@ -0,0 +1,88 @@
+/**
+ * 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
+ * Simon McDuff - bug 233490
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class Transaction extends View implements InternalTransaction
+{
+ public Transaction(InternalSession session, int viewID, CDOBranchPoint branchPoint)
+ {
+ super(session, viewID, branchPoint);
+ }
+
+ @Override
+ public boolean isReadOnly()
+ {
+ return false;
+ }
+
+ @Override
+ protected String getClassName()
+ {
+ return "Transaction"; //$NON-NLS-1$
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalCommitContext createCommitContext()
+ {
+ checkOpen();
+ return getRepository().createCommitContext(this);
+ }
+
+ /**
+ * For tests only.
+ *
+ * @since 2.0
+ */
+ public InternalCommitContext testCreateCommitContext(final long timeStamp)
+ {
+ checkOpen();
+ return new TransactionCommitContext(this)
+ {
+ @Override
+ protected long[] createTimeStamp(OMMonitor monitor)
+ {
+ return new long[] { timeStamp, CDOBranchPoint.UNSPECIFIED_DATE };
+ }
+ };
+ }
+
+ @Override
+ protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException
+ {
+ if (timeStamp != UNSPECIFIED_DATE)
+ {
+ throw new IllegalArgumentException("Changing the target time is not supported by transactions");
+ }
+ }
+
+ private void checkOpen()
+ {
+ if (isClosed())
+ {
+ throw new IllegalStateException("View closed"); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
new file mode 100644
index 0000000000..e978f5c7e0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
@@ -0,0 +1,1404 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ * Martin Fluegge - maintenance, bug 318518
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOCommitData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDObject;
+import org.eclipse.emf.cdo.common.id.CDOIDReference;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation;
+import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+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.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.CDORevisionDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl;
+import org.eclipse.emf.cdo.internal.common.commit.FailureCommitInfo;
+import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.ContainmentCycleDetectedException;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
+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.common.revision.CDOFeatureDeltaVisitorImpl;
+import org.eclipse.emf.cdo.spi.common.revision.CDOIDMapper;
+import org.eclipse.emf.cdo.spi.common.revision.CDOReferenceAdjuster;
+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.common.revision.StubCDORevision;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+import org.eclipse.net4j.util.CheckUtil;
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.collection.IndexedList;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState;
+import org.eclipse.net4j.util.io.ExtendedDataInputStream;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.Monitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+public class TransactionCommitContext implements InternalCommitContext
+{
+ private static final InternalCDORevision DETACHED = new StubCDORevision(null);
+
+ private final InternalTransaction transaction;
+
+ private InternalRepository repository;
+
+ private InternalCDORevisionManager revisionManager;
+
+ private InternalLockManager lockManager;
+
+ private InternalCDOPackageRegistry repositoryPackageRegistry;
+
+ private boolean packageRegistryLocked;
+
+ private TransactionPackageRegistry packageRegistry;
+
+ private IStoreAccessor accessor;
+
+ private long timeStamp = CDORevision.UNSPECIFIED_DATE;
+
+ private long previousTimeStamp = CDORevision.UNSPECIFIED_DATE;
+
+ private String commitComment;
+
+ private InternalCDOPackageUnit[] newPackageUnits = new InternalCDOPackageUnit[0];
+
+ private InternalCDORevision[] newObjects = new InternalCDORevision[0];
+
+ private InternalCDORevisionDelta[] dirtyObjectDeltas = new InternalCDORevisionDelta[0];
+
+ private CDOID[] detachedObjects = new CDOID[0];
+
+ private Map<CDOID, EClass> detachedObjectTypes;
+
+ private InternalCDORevision[] dirtyObjects = new InternalCDORevision[0];
+
+ private InternalCDORevision[] cachedDetachedRevisions = new InternalCDORevision[0];
+
+ private Map<CDOID, InternalCDORevision> cachedRevisions;
+
+ private Set<Object> lockedObjects = new HashSet<Object>();
+
+ private List<CDOID> lockedTargets;
+
+ private ConcurrentMap<CDOID, CDOID> idMappings = new ConcurrentHashMap<CDOID, CDOID>();
+
+ private CDOReferenceAdjuster idMapper = new CDOIDMapper(idMappings);
+
+ private String rollbackMessage;
+
+ private List<CDOIDReference> xRefs;
+
+ private List<LockState<Object, IView>> postCommitLockStates;
+
+ private boolean ensuringReferentialIntegrity;
+
+ private boolean autoReleaseLocksEnabled;
+
+ private ExtendedDataInputStream lobs;
+
+ public TransactionCommitContext(InternalTransaction transaction)
+ {
+ this.transaction = transaction;
+
+ repository = transaction.getRepository();
+ revisionManager = repository.getRevisionManager();
+ lockManager = repository.getLockManager();
+ ensuringReferentialIntegrity = repository.isEnsuringReferentialIntegrity();
+
+ repositoryPackageRegistry = repository.getPackageRegistry(false);
+ packageRegistry = new TransactionPackageRegistry(repositoryPackageRegistry);
+ packageRegistry.activate();
+ }
+
+ public InternalTransaction getTransaction()
+ {
+ return transaction;
+ }
+
+ public CDOBranchPoint getBranchPoint()
+ {
+ return transaction.getBranch().getPoint(timeStamp);
+ }
+
+ public String getUserID()
+ {
+ return transaction.getSession().getUserID();
+ }
+
+ public String getCommitComment()
+ {
+ return commitComment;
+ }
+
+ public boolean isAutoReleaseLocksEnabled()
+ {
+ return autoReleaseLocksEnabled;
+ }
+
+ public String getRollbackMessage()
+ {
+ return rollbackMessage;
+ }
+
+ public List<CDOIDReference> getXRefs()
+ {
+ return xRefs;
+ }
+
+ public InternalCDOPackageRegistry getPackageRegistry()
+ {
+ return packageRegistry;
+ }
+
+ public InternalCDOPackageUnit[] getNewPackageUnits()
+ {
+ return newPackageUnits;
+ }
+
+ public InternalCDORevision[] getNewObjects()
+ {
+ return newObjects;
+ }
+
+ public InternalCDORevision[] getDirtyObjects()
+ {
+ return dirtyObjects;
+ }
+
+ public CDOID[] getDetachedObjects()
+ {
+ return detachedObjects;
+ }
+
+ public Map<CDOID, EClass> getDetachedObjectTypes()
+ {
+ return detachedObjectTypes;
+ }
+
+ public InternalCDORevision[] getDetachedRevisions()
+ {
+ // TODO This array can contain null values as they only come from the cache
+ for (InternalCDORevision cachedDetachedRevision : cachedDetachedRevisions)
+ {
+ if (cachedDetachedRevision == null)
+ {
+ throw new AssertionError("Detached revisions are incomplete");
+ }
+ }
+
+ return cachedDetachedRevisions;
+ }
+
+ public InternalCDORevisionDelta[] getDirtyObjectDeltas()
+ {
+ return dirtyObjectDeltas;
+ }
+
+ public CDORevision getRevision(CDOID id)
+ {
+ if (cachedRevisions == null)
+ {
+ cachedRevisions = cacheRevisions();
+ }
+
+ // Try "after state"
+ InternalCDORevision revision = cachedRevisions.get(id);
+ if (revision == DETACHED)
+ {
+ return null;
+ }
+
+ if (revision != null)
+ {
+ return revision;
+ }
+
+ // Fall back to "before state"
+ return transaction.getRevision(id);
+ }
+
+ private Map<CDOID, InternalCDORevision> cacheRevisions()
+ {
+ Map<CDOID, InternalCDORevision> cache = new HashMap<CDOID, InternalCDORevision>();
+ if (newObjects != null)
+ {
+ for (int i = 0; i < newObjects.length; i++)
+ {
+ InternalCDORevision revision = newObjects[i];
+ cache.put(revision.getID(), revision);
+ }
+ }
+
+ if (dirtyObjects != null)
+ {
+ for (int i = 0; i < dirtyObjects.length; i++)
+ {
+ InternalCDORevision revision = dirtyObjects[i];
+ cache.put(revision.getID(), revision);
+ }
+ }
+
+ if (detachedObjects != null)
+ {
+ for (int i = 0; i < detachedObjects.length; i++)
+ {
+ cache.put(detachedObjects[i], DETACHED);
+ }
+ }
+
+ return cache;
+ }
+
+ public Map<CDOID, CDOID> getIDMappings()
+ {
+ return Collections.unmodifiableMap(idMappings);
+ }
+
+ public void addIDMapping(CDOID oldID, CDOID newID)
+ {
+ if (CDOIDUtil.isNull(newID) || newID.isTemporary())
+ {
+ throw new IllegalStateException("newID=" + newID); //$NON-NLS-1$
+ }
+
+ CDOID previousMapping = idMappings.putIfAbsent(oldID, newID);
+ if (previousMapping != null)
+ {
+ throw new IllegalStateException("previousMapping != null"); //$NON-NLS-1$
+ }
+ }
+
+ public void applyIDMappings(OMMonitor monitor)
+ {
+ if (idMappings.isEmpty())
+ {
+ return;
+ }
+
+ try
+ {
+ monitor.begin(newObjects.length + dirtyObjects.length + dirtyObjectDeltas.length);
+ applyIDMappings(newObjects, monitor.fork(newObjects.length));
+ applyIDMappings(dirtyObjects, monitor.fork(dirtyObjects.length));
+ for (CDORevisionDelta dirtyObjectDelta : dirtyObjectDeltas)
+ {
+ ((InternalCDORevisionDelta)dirtyObjectDelta).adjustReferences(idMapper);
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void preWrite()
+ {
+ // Allocate a store writer
+ accessor = repository.getStore().getWriter(transaction);
+
+ // Make the store writer available in a ThreadLocal variable
+ StoreThreadLocal.setAccessor(accessor);
+ StoreThreadLocal.setCommitContext(this);
+ }
+
+ public void setNewPackageUnits(InternalCDOPackageUnit[] newPackageUnits)
+ {
+ this.newPackageUnits = newPackageUnits;
+ }
+
+ public void setNewObjects(InternalCDORevision[] newObjects)
+ {
+ this.newObjects = newObjects;
+ }
+
+ public void setDirtyObjectDeltas(InternalCDORevisionDelta[] dirtyObjectDeltas)
+ {
+ this.dirtyObjectDeltas = dirtyObjectDeltas;
+ }
+
+ public void setDetachedObjects(CDOID[] detachedObjects)
+ {
+ this.detachedObjects = detachedObjects;
+ }
+
+ public void setDetachedObjectTypes(Map<CDOID, EClass> detachedObjectTypes)
+ {
+ this.detachedObjectTypes = detachedObjectTypes;
+ }
+
+ public void setAutoReleaseLocksEnabled(boolean on)
+ {
+ autoReleaseLocksEnabled = on;
+ }
+
+ public void setCommitComment(String commitComment)
+ {
+ this.commitComment = commitComment;
+ }
+
+ public ExtendedDataInputStream getLobs()
+ {
+ return lobs;
+ }
+
+ public void setLobs(ExtendedDataInputStream in)
+ {
+ lobs = in;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void write(OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(107);
+ dirtyObjects = new InternalCDORevision[dirtyObjectDeltas.length];
+
+ if (newPackageUnits.length != 0)
+ {
+ repository.getPackageRegistryCommitLock().acquire();
+ packageRegistryLocked = true;
+
+ List<InternalCDOPackageUnit> noDuplicates = new ArrayList<InternalCDOPackageUnit>();
+ for (InternalCDOPackageUnit newPackageUnit : newPackageUnits)
+ {
+ String id = newPackageUnit.getID();
+ if (!repositoryPackageRegistry.containsKey(id))
+ {
+ noDuplicates.add(newPackageUnit);
+ }
+ }
+
+ int newSize = noDuplicates.size();
+ if (newPackageUnits.length != newSize)
+ {
+ newPackageUnits = noDuplicates.toArray(new InternalCDOPackageUnit[newSize]);
+ }
+ }
+
+ lockObjects(); // Can take long and must come before setTimeStamp()
+ monitor.worked();
+
+ setTimeStamp(monitor.fork());
+
+ adjustForCommit();
+ monitor.worked();
+
+ computeDirtyObjects(monitor.fork());
+
+ checkXRefs();
+ monitor.worked();
+
+ if (rollbackMessage == null)
+ {
+ detachObjects(monitor.fork());
+ repository.notifyWriteAccessHandlers(transaction, this, true, monitor.fork());
+ accessor.write(this, monitor.fork(100));
+ }
+ }
+ catch (Throwable t)
+ {
+ handleException(t);
+ }
+ finally
+ {
+ finishMonitor(monitor);
+ }
+ }
+
+ public void commit(OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(101);
+ accessor.commit(monitor.fork(100));
+ updateInfraStructure(monitor.fork());
+
+ // Bugzilla 297940
+ repository.endCommit(timeStamp);
+ }
+ catch (Throwable ex)
+ {
+ handleException(ex);
+ }
+ finally
+ {
+ finishMonitor(monitor);
+ }
+ }
+
+ public List<LockState<Object, IView>> getPostCommmitLockStates()
+ {
+ return postCommitLockStates;
+ }
+
+ private void handleException(Throwable ex)
+ {
+ try
+ {
+ OM.LOG.error(ex);
+ String storeClass = repository.getStore().getClass().getSimpleName();
+ rollback("Rollback in " + storeClass + ": " + StringUtil.formatException(ex)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ catch (Exception ex1)
+ {
+ if (rollbackMessage == null)
+ {
+ rollbackMessage = ex1.getMessage();
+ }
+
+ try
+ {
+ OM.LOG.error(ex1);
+ }
+ catch (Exception ignore)
+ {
+ }
+ }
+ }
+
+ private void finishMonitor(OMMonitor monitor)
+ {
+ try
+ {
+ monitor.done();
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+ OM.LOG.warn(ex);
+ }
+ catch (Exception ignore)
+ {
+ }
+ }
+ }
+
+ private void setTimeStamp(OMMonitor mmonitor)
+ {
+ long[] times = createTimeStamp(mmonitor); // Could throw an exception
+ timeStamp = times[0];
+ previousTimeStamp = times[1];
+ CheckUtil.checkState(timeStamp != CDOBranchPoint.UNSPECIFIED_DATE, "Commit timestamp must not be 0");
+ }
+
+ protected long[] createTimeStamp(OMMonitor monitor)
+ {
+ return repository.createCommitTimeStamp(monitor);
+ }
+
+ protected long getTimeStamp()
+ {
+ return timeStamp;
+ }
+
+ protected void setTimeStamp(long timeStamp)
+ {
+ repository.forceCommitTimeStamp(timeStamp, new Monitor());
+ this.timeStamp = timeStamp;
+ }
+
+ public long getPreviousTimeStamp()
+ {
+ return previousTimeStamp;
+ }
+
+ public void postCommit(boolean success)
+ {
+ if (packageRegistryLocked)
+ {
+ repository.getPackageRegistryCommitLock().release();
+ }
+
+ try
+ {
+ InternalSession sender = transaction.getSession();
+ CDOCommitInfo commitInfo = success ? createCommitInfo() : createFailureCommitInfo();
+
+ repository.sendCommitNotification(sender, commitInfo);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.warn("A problem occured while notifying other sessions", ex);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ accessor = null;
+ lockedTargets = null;
+
+ if (packageRegistry != null)
+ {
+ packageRegistry.deactivate();
+ packageRegistry = null;
+ }
+ }
+ }
+
+ public CDOCommitInfo createCommitInfo()
+ {
+ CDOBranch branch = transaction.getBranch();
+ String userID = transaction.getSession().getUserID();
+ CDOCommitData commitData = createCommitData();
+
+ InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager();
+ return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, commitData);
+ }
+
+ public CDOCommitInfo createFailureCommitInfo()
+ {
+ return new FailureCommitInfo(timeStamp, previousTimeStamp);
+ }
+
+ private CDOCommitData createCommitData()
+ {
+ List<CDOPackageUnit> newPackageUnitsCollection = new IndexedList.ArrayBacked<CDOPackageUnit>()
+ {
+ @Override
+ protected CDOPackageUnit[] getArray()
+ {
+ return newPackageUnits;
+ }
+ };
+
+ List<CDOIDAndVersion> newObjectsCollection = new IndexedList.ArrayBacked<CDOIDAndVersion>()
+ {
+ @Override
+ protected CDOIDAndVersion[] getArray()
+ {
+ return newObjects;
+ }
+ };
+
+ List<CDORevisionKey> changedObjectsCollection = new IndexedList.ArrayBacked<CDORevisionKey>()
+ {
+ @Override
+ protected CDORevisionKey[] getArray()
+ {
+ return dirtyObjectDeltas;
+ }
+ };
+
+ List<CDOIDAndVersion> detachedObjectsCollection = new IndexedList<CDOIDAndVersion>()
+ {
+ @Override
+ public CDOIDAndVersion get(int i)
+ {
+ if (cachedDetachedRevisions[i] != null)
+ {
+ return cachedDetachedRevisions[i];
+ }
+
+ return CDOIDUtil.createIDAndVersion(detachedObjects[i], CDORevision.UNSPECIFIED_VERSION);
+ }
+
+ @Override
+ public int size()
+ {
+ return detachedObjects.length;
+ }
+ };
+
+ return new CDOCommitDataImpl(newPackageUnitsCollection, newObjectsCollection, changedObjectsCollection,
+ detachedObjectsCollection);
+ }
+
+ protected void adjustForCommit()
+ {
+ for (InternalCDOPackageUnit newPackageUnit : newPackageUnits)
+ {
+ newPackageUnit.setTimeStamp(timeStamp);
+ }
+
+ CDOBranch branch = transaction.getBranch();
+ for (InternalCDORevision newObject : newObjects)
+ {
+ newObject.adjustForCommit(branch, timeStamp);
+ }
+ }
+
+ protected void lockObjects() throws InterruptedException
+ {
+ lockedObjects.clear();
+ lockedTargets = null;
+
+ try
+ {
+ final boolean supportingBranches = repository.isSupportingBranches();
+
+ CDOFeatureDeltaVisitor deltaTargetLocker = null;
+ if (ensuringReferentialIntegrity)
+ {
+ final Set<CDOID> newIDs = new HashSet<CDOID>();
+ for (int i = 0; i < newObjects.length; i++)
+ {
+ InternalCDORevision newRevision = newObjects[i];
+ CDOID newID = newRevision.getID();
+ if (newID instanceof CDOIDObject)
+ {
+ // After merges newObjects may contain non-TEMP ids
+ newIDs.add(newID);
+ }
+ }
+
+ deltaTargetLocker = new CDOFeatureDeltaVisitorImpl()
+ {
+ @Override
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ lockTarget(delta.getValue(), newIDs, supportingBranches);
+ }
+
+ @Override
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ lockTarget(delta.getValue(), newIDs, supportingBranches);
+ }
+ };
+
+ CDOReferenceAdjuster revisionTargetLocker = new CDOReferenceAdjuster()
+ {
+ public Object adjustReference(Object value, EStructuralFeature feature, int index)
+ {
+ lockTarget(value, newIDs, supportingBranches);
+ return value;
+ }
+ };
+
+ for (int i = 0; i < newObjects.length; i++)
+ {
+ InternalCDORevision newRevision = newObjects[i];
+ newRevision.adjustReferences(revisionTargetLocker);
+ }
+ }
+
+ for (int i = 0; i < dirtyObjectDeltas.length; i++)
+ {
+ InternalCDORevisionDelta delta = dirtyObjectDeltas[i];
+ CDOID id = delta.getID();
+ Object key = lockManager.getLockKey(id, transaction.getBranch());
+ lockedObjects.add(new DeltaLockWrapper(key, delta));
+
+ if (hasContainmentChanges(delta))
+ {
+ if (isContainerLocked(delta))
+ {
+ throw new ContainmentCycleDetectedException("Parent (" + key
+ + ") is already locked for containment changes");
+ }
+ }
+ }
+
+ for (int i = 0; i < dirtyObjectDeltas.length; i++)
+ {
+ InternalCDORevisionDelta delta = dirtyObjectDeltas[i];
+ if (deltaTargetLocker != null)
+ {
+ delta.accept(deltaTargetLocker);
+ }
+ }
+
+ for (int i = 0; i < detachedObjects.length; i++)
+ {
+ CDOID id = detachedObjects[i];
+ Object key = lockManager.getLockKey(id, transaction.getBranch());
+ lockedObjects.add(key);
+ }
+
+ if (!lockedObjects.isEmpty())
+ {
+ // First lock all objects (incl. possible ref targets).
+ // This is a transient operation, it does not check for existance!
+ lockManager.lock(LockType.WRITE, transaction, lockedObjects, 1000);
+
+ // If all locks could be acquired, check if locked targets do still exist
+ if (lockedTargets != null)
+ {
+ for (CDOID id : lockedTargets)
+ {
+ InternalCDORevision revision = //
+ revisionManager.getRevision(id, transaction, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
+
+ if (revision == null || revision instanceof DetachedCDORevision)
+ {
+ throw new IllegalStateException("Object " + id
+ + " can not be referenced anymore because it has been detached");
+ }
+ }
+ }
+ }
+ }
+ catch (RuntimeException ex)
+ {
+ lockedObjects.clear();
+ lockedTargets = null;
+ throw ex;
+ }
+ }
+
+ /**
+ * Iterates up the eContainers of an object and returns <code>true</code> on the first parent locked by another view.
+ *
+ * @return <code>true</code> if any parent is locked, <code>false</code> otherwise.
+ */
+ private boolean isContainerLocked(InternalCDORevisionDelta delta)
+ {
+ CDOID id = delta.getID();
+ InternalCDORevision revision = revisionManager.getRevisionByVersion(id, delta, CDORevision.UNCHUNKED, true);
+ if (revision == null)
+ {
+ // Can happen with non-auditing cache
+ throw new ConcurrentModificationException("Attempt by " + transaction + " to modify historical revision: "
+ + CDORevisionUtil.copyRevisionKey(delta));
+ }
+
+ return isContainerLocked(revision);
+ }
+
+ private boolean isContainerLocked(InternalCDORevision revision)
+ {
+ CDOID id = (CDOID)revision.getContainerID();
+ if (CDOIDUtil.isNull(id))
+ {
+ return false;
+ }
+
+ Object key = lockManager.getLockKey(id, transaction.getBranch());
+ DeltaLockWrapper lockWrapper = new DeltaLockWrapper(key, null);
+
+ if (lockManager.hasLockByOthers(LockType.WRITE, transaction, lockWrapper))
+ {
+ Object object = lockManager.getLockEntryObject(lockWrapper);
+ if (object instanceof DeltaLockWrapper)
+ {
+ InternalCDORevisionDelta delta = ((DeltaLockWrapper)object).getDelta();
+ if (delta != null && hasContainmentChanges(delta))
+ {
+ return true;
+ }
+ }
+ }
+
+ InternalCDORevision parent = revisionManager.getRevision(id, transaction, CDORevision.UNCHUNKED,
+ CDORevision.DEPTH_NONE, true);
+
+ if (parent != null)
+ {
+ return isContainerLocked(parent);
+ }
+
+ return false;
+ }
+
+ private boolean hasContainmentChanges(InternalCDORevisionDelta delta)
+ {
+ for (CDOFeatureDelta featureDelta : delta.getFeatureDeltas())
+ {
+ EStructuralFeature feature = featureDelta.getFeature();
+ if (feature instanceof EReference)
+ {
+ if (((EReference)feature).isContainment())
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void lockTarget(Object value, Set<CDOID> newIDs, boolean supportingBranches)
+ {
+ if (value instanceof CDOIDObject)
+ {
+ CDOIDObject id = (CDOIDObject)value;
+ if (id.isNull())
+ {
+ return;
+ }
+
+ if (newIDs.contains(id))
+ {
+ // After merges newObjects may contain non-TEMP ids
+ return;
+ }
+
+ if (detachedObjectTypes != null && detachedObjectTypes.containsKey(id))
+ {
+ throw new IllegalStateException("This commit deletes object " + id + " and adds a reference at the same time");
+ }
+
+ // Let this object be locked
+ Object key = lockManager.getLockKey(id, transaction.getBranch());
+ lockedObjects.add(key);
+
+ // Let this object be checked for existance after it has been locked
+ if (lockedTargets == null)
+ {
+ lockedTargets = new ArrayList<CDOID>();
+ }
+
+ lockedTargets.add(id);
+ }
+ }
+
+ protected void checkXRefs()
+ {
+ if (ensuringReferentialIntegrity && detachedObjectTypes != null)
+ {
+ XRefContext context = new XRefContext();
+ xRefs = context.getXRefs(accessor);
+ if (!xRefs.isEmpty())
+ {
+ rollbackMessage = "Referential integrity violated";
+ }
+ }
+ }
+
+ private synchronized void unlockObjects()
+ {
+ if (!lockedObjects.isEmpty())
+ {
+ lockManager.unlock(LockType.WRITE, transaction, lockedObjects);
+ lockedObjects.clear();
+ }
+ }
+
+ private void computeDirtyObjects(OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(dirtyObjectDeltas.length);
+ for (int i = 0; i < dirtyObjectDeltas.length; i++)
+ {
+ dirtyObjects[i] = computeDirtyObject(dirtyObjectDeltas[i]);
+ if (dirtyObjects[i] == null)
+ {
+ throw new IllegalStateException("Can not retrieve origin revision for " + dirtyObjectDeltas[i]); //$NON-NLS-1$
+ }
+
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private InternalCDORevision computeDirtyObject(InternalCDORevisionDelta delta)
+ {
+ CDOID id = delta.getID();
+
+ InternalCDORevision oldRevision = revisionManager.getRevisionByVersion(id, delta, CDORevision.UNCHUNKED, true);
+ if (oldRevision == null)
+ {
+ throw new IllegalStateException("Origin revision not found for " + delta);
+ }
+
+ CDOBranch branch = transaction.getBranch();
+ if (ObjectUtil.equals(oldRevision.getBranch(), branch) && oldRevision.isHistorical())
+ {
+ throw new ConcurrentModificationException("Attempt by " + transaction + " to modify historical revision: "
+ + oldRevision);
+ }
+
+ // Make sure all chunks are loaded
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(oldRevision.getEClass()))
+ {
+ if (feature.isMany())
+ {
+ repository.ensureChunk(oldRevision, feature, 0, oldRevision.getList(feature).size());
+ }
+ }
+
+ InternalCDORevision newRevision = oldRevision.copy();
+ newRevision.adjustForCommit(branch, timeStamp);
+
+ delta.apply(newRevision);
+ return newRevision;
+ }
+
+ private void applyIDMappings(InternalCDORevision[] revisions, OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(revisions.length);
+ for (InternalCDORevision revision : revisions)
+ {
+ if (revision != null)
+ {
+ CDOID newID = idMappings.get(revision.getID());
+ if (newID != null)
+ {
+ revision.setID(newID);
+ }
+
+ revision.adjustReferences(idMapper);
+ monitor.worked();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public synchronized void rollback(String message)
+ {
+ // Check if we already rolled back
+ if (rollbackMessage == null)
+ {
+ rollbackMessage = message;
+ unlockObjects();
+ if (accessor != null)
+ {
+ try
+ {
+ accessor.rollback();
+ }
+ catch (RuntimeException ex)
+ {
+ OM.LOG.warn("Problem while rolling back the transaction", ex); //$NON-NLS-1$
+ }
+ finally
+ {
+ repository.failCommit(timeStamp);
+ }
+ }
+ }
+ }
+
+ protected IStoreAccessor getAccessor()
+ {
+ return accessor;
+ }
+
+ private void updateInfraStructure(OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(7);
+ addNewPackageUnits(monitor.fork());
+ addRevisions(newObjects, monitor.fork());
+ addRevisions(dirtyObjects, monitor.fork());
+ reviseDetachedObjects(monitor.fork());
+
+ unlockObjects();
+ monitor.worked();
+
+ if (isAutoReleaseLocksEnabled())
+ {
+ postCommitLockStates = repository.getLockManager().unlock2(true, transaction);
+ if (!postCommitLockStates.isEmpty())
+ {
+ sendLockNotifications(postCommitLockStates);
+ }
+ }
+
+ monitor.worked();
+ repository.notifyWriteAccessHandlers(transaction, this, false, monitor.fork());
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void sendLockNotifications(List<LockState<Object, IView>> newLockStates)
+ {
+ CDOLockState[] newStates = Repository.toCDOLockStates(newLockStates);
+
+ long timeStamp = getTimeStamp();
+ InternalTransaction tx = getTransaction();
+ CDOBranch branch = tx.getBranch();
+ Operation unlock = Operation.UNLOCK;
+
+ CDOLockChangeInfo info = CDOLockUtil.createLockChangeInfo(timeStamp, tx, branch, unlock, null, newStates);
+ repository.getSessionManager().sendLockNotification(tx.getSession(), info);
+ }
+
+ private void addNewPackageUnits(OMMonitor monitor)
+ {
+ InternalCDOPackageRegistry repositoryPackageRegistry = repository.getPackageRegistry(false);
+ synchronized (repositoryPackageRegistry)
+ {
+ try
+ {
+ monitor.begin(newPackageUnits.length);
+ for (int i = 0; i < newPackageUnits.length; i++)
+ {
+ newPackageUnits[i].setState(CDOPackageUnit.State.LOADED);
+ repositoryPackageRegistry.putPackageUnit(newPackageUnits[i]);
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+ }
+
+ private void addRevisions(CDORevision[] revisions, OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(revisions.length);
+ for (CDORevision revision : revisions)
+ {
+ if (revision != null)
+ {
+ revisionManager.addRevision(revision);
+ }
+
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void reviseDetachedObjects(OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(cachedDetachedRevisions.length);
+ long revised = getBranchPoint().getTimeStamp() - 1;
+ for (InternalCDORevision revision : cachedDetachedRevisions)
+ {
+ if (revision != null)
+ {
+ revision.setRevised(revised);
+ }
+
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void detachObjects(OMMonitor monitor)
+ {
+ int size = detachedObjects.length;
+ cachedDetachedRevisions = new InternalCDORevision[size];
+
+ CDOID[] detachedObjects = getDetachedObjects();
+
+ try
+ {
+ monitor.begin(size);
+ for (int i = 0; i < size; i++)
+ {
+ CDOID id = detachedObjects[i];
+
+ // Remember the cached revision that must be revised after successful commit through updateInfraStructure
+ cachedDetachedRevisions[i] = (InternalCDORevision)revisionManager.getCache().getRevision(id, transaction);
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("TransactionCommitContext[{0}, {1}, {2}]", transaction.getSession(), transaction, //$NON-NLS-1$
+ CDOCommonUtil.formatTimeStamp(timeStamp));
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class TransactionPackageRegistry extends CDOPackageRegistryImpl
+ {
+ private static final long serialVersionUID = 1L;
+
+ public TransactionPackageRegistry(InternalCDOPackageRegistry repositoryPackageRegistry)
+ {
+ delegateRegistry = repositoryPackageRegistry;
+ setPackageLoader(repositoryPackageRegistry.getPackageLoader());
+ }
+
+ @Override
+ public synchronized void putPackageUnit(InternalCDOPackageUnit packageUnit)
+ {
+ LifecycleUtil.checkActive(this);
+ packageUnit.setPackageRegistry(this);
+ for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos())
+ {
+ EPackage ePackage = packageInfo.getEPackage();
+ basicPut(ePackage.getNsURI(), ePackage);
+ }
+
+ resetInternalCaches();
+ }
+
+ @Override
+ protected void disposePackageUnits()
+ {
+ // Do nothing
+ }
+
+ @Override
+ public Collection<Object> values()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * @author Martin Fluegge
+ */
+ private static final class DeltaLockWrapper implements CDOIDAndBranch
+ {
+ private Object key;
+
+ private InternalCDORevisionDelta delta;
+
+ public DeltaLockWrapper(Object key, InternalCDORevisionDelta delta)
+ {
+ this.key = key;
+ this.delta = delta;
+ }
+
+ public Object getKey()
+ {
+ return key;
+ }
+
+ public InternalCDORevisionDelta getDelta()
+ {
+ return delta;
+ }
+
+ public CDOID getID()
+ {
+ return key instanceof CDOIDAndBranch ? ((CDOIDAndBranch)key).getID() : (CDOID)key;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return key instanceof CDOIDAndBranch ? ((CDOIDAndBranch)key).getBranch() : null;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof DeltaLockWrapper)
+ {
+ DeltaLockWrapper wrapper = (DeltaLockWrapper)obj;
+ return key.equals(wrapper.getKey());
+ }
+
+ return key.equals(obj);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return key.hashCode();
+ }
+
+ @Override
+ public String toString()
+ {
+ return key.toString();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class XRefContext implements QueryXRefsContext
+ {
+ private Map<EClass, List<EReference>> sourceCandidates = new HashMap<EClass, List<EReference>>();
+
+ private Set<CDOID> detachedIDs = new HashSet<CDOID>();
+
+ private Set<CDOID> dirtyIDs = new HashSet<CDOID>();
+
+ private List<CDOIDReference> result = new ArrayList<CDOIDReference>();
+
+ public XRefContext()
+ {
+ XRefsQueryHandler.collectSourceCandidates(transaction, detachedObjectTypes.values(), sourceCandidates);
+
+ for (CDOID id : detachedObjects)
+ {
+ detachedIDs.add(id);
+ }
+
+ for (InternalCDORevision revision : dirtyObjects)
+ {
+ dirtyIDs.add(revision.getID());
+ }
+ }
+
+ public List<CDOIDReference> getXRefs(IStoreAccessor accessor)
+ {
+ accessor.queryXRefs(this);
+ checkDirtyObjects();
+ return result;
+ }
+
+ private void checkDirtyObjects()
+ {
+ final CDOID[] dirtyID = { null };
+ CDOReferenceAdjuster dirtyObjectChecker = new CDOReferenceAdjuster()
+ {
+ public Object adjustReference(Object targetID, EStructuralFeature feature, int index)
+ {
+ if (feature != CDOContainerFeatureDelta.CONTAINER_FEATURE)
+ {
+ if (detachedIDs.contains(targetID))
+ {
+ result.add(new CDOIDReference((CDOID)targetID, dirtyID[0], feature, index));
+ }
+
+ }
+
+ return targetID;
+ }
+ };
+
+ for (InternalCDORevision dirtyObject : dirtyObjects)
+ {
+ dirtyID[0] = dirtyObject.getID();
+ dirtyObject.adjustReferences(dirtyObjectChecker);
+ }
+ }
+
+ public long getTimeStamp()
+ {
+ return CDOBranchPoint.UNSPECIFIED_DATE;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return transaction.getBranch();
+ }
+
+ public Map<CDOID, EClass> getTargetObjects()
+ {
+ return detachedObjectTypes;
+ }
+
+ public EReference[] getSourceReferences()
+ {
+ return new EReference[0];
+ }
+
+ public Map<EClass, List<EReference>> getSourceCandidates()
+ {
+ return sourceCandidates;
+ }
+
+ public int getMaxResults()
+ {
+ return CDOQueryInfo.UNLIMITED_RESULTS;
+ }
+
+ public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex)
+ {
+ if (CDOIDUtil.isNull(targetID))
+ {
+ // Compensate potential issues with the XRef implementation in the store accessor.
+ return true;
+ }
+
+ if (detachedIDs.contains(sourceID))
+ {
+ // Ignore XRefs from objects that are about to be detached themselves by this commit.
+ return true;
+ }
+
+ if (dirtyIDs.contains(sourceID))
+ {
+ // Ignore XRefs from objects that are about to be modified by this commit. They're handled later in getXRefs().
+ return true;
+ }
+
+ result.add(new CDOIDReference(targetID, sourceID, sourceReference, sourceIndex));
+ return true;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java
new file mode 100644
index 0000000000..8e72f32596
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.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
+ * Simon McDuff - bug 233490
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonView;
+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.CDORevisionManager;
+import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.options.IOptionsContainer;
+
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class View extends Lifecycle implements InternalView, CDOCommonView.Options
+{
+ private InternalSession session;
+
+ private final int viewID;
+
+ private final int sessionID; // Needed here so we can compute the hashCode even after session becomes null due to
+ // deactivation!
+
+ private CDOBranchPoint branchPoint;
+
+ private String durableLockingID;
+
+ private InternalRepository repository;
+
+ private Set<CDOID> changeSubscriptionIDs = new HashSet<CDOID>();
+
+ private boolean lockNotificationsEnabled;
+
+ /**
+ * @since 2.0
+ */
+ public View(InternalSession session, int viewID, CDOBranchPoint branchPoint)
+ {
+ this.session = session;
+ this.viewID = viewID;
+ sessionID = session.getSessionID();
+
+ repository = session.getManager().getRepository();
+ setBranchPoint(branchPoint);
+ }
+
+ public InternalSession getSession()
+ {
+ return session;
+ }
+
+ public int getViewID()
+ {
+ return viewID;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public boolean isReadOnly()
+ {
+ return true;
+ }
+
+ public String getDurableLockingID()
+ {
+ return durableLockingID;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public InternalRepository getRepository()
+ {
+ checkOpen();
+ return repository;
+ }
+
+ public InternalCDORevision getRevision(CDOID id)
+ {
+ CDORevisionManager revisionManager = repository.getRevisionManager();
+ return (InternalCDORevision)revisionManager.getRevision(id, this, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE,
+ true);
+ }
+
+ public void changeTarget(CDOBranchPoint branchPoint, List<CDOID> invalidObjects,
+ List<CDORevisionDelta> allChangedObjects, List<CDOID> allDetachedObjects)
+ {
+ List<CDORevision> oldRevisions = getRevisions(invalidObjects);
+ setBranchPoint(branchPoint);
+ List<CDORevision> newRevisions = getRevisions(invalidObjects);
+
+ Iterator<CDORevision> it = newRevisions.iterator();
+ for (CDORevision oldRevision : oldRevisions)
+ {
+ CDORevision newRevision = it.next();
+ if (newRevision == null)
+ {
+ allDetachedObjects.add(oldRevision.getID());
+ }
+ else if (newRevision != oldRevision)
+ {
+ CDORevisionDelta delta = newRevision.compare(oldRevision);
+ allChangedObjects.add(delta);
+ }
+ }
+ }
+
+ private List<CDORevision> getRevisions(List<CDOID> ids)
+ {
+ return repository.getRevisionManager().getRevisions(ids, branchPoint, CDORevision.UNCHUNKED,
+ CDORevision.DEPTH_NONE, true);
+ }
+
+ public void setBranchPoint(CDOBranchPoint branchPoint)
+ {
+ checkOpen();
+ long timeStamp = branchPoint.getTimeStamp();
+ branchPoint = branchPoint.getBranch().getPoint(timeStamp);
+ validateTimeStamp(timeStamp);
+ this.branchPoint = branchPoint;
+ }
+
+ protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException
+ {
+ if (timeStamp != UNSPECIFIED_DATE)
+ {
+ repository.validateTimeStamp(timeStamp);
+ }
+ }
+
+ public void setDurableLockingID(String durableLockingID)
+ {
+ this.durableLockingID = durableLockingID;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized void subscribe(CDOID id)
+ {
+ checkOpen();
+ changeSubscriptionIDs.add(id);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized void unsubscribe(CDOID id)
+ {
+ checkOpen();
+ changeSubscriptionIDs.remove(id);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized boolean hasSubscription(CDOID id)
+ {
+ checkOpen();
+ return changeSubscriptionIDs.contains(id);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized void clearChangeSubscription()
+ {
+ checkOpen();
+ changeSubscriptionIDs.clear();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return ObjectUtil.hashCode(sessionID, viewID);
+ }
+
+ @Override
+ public String toString()
+ {
+ int sessionID = session == null ? 0 : session.getSessionID();
+ return MessageFormat.format("{0}[{1}:{2}]", getClassName(), sessionID, viewID); //$NON-NLS-1$
+ }
+
+ protected String getClassName()
+ {
+ return "View"; //$NON-NLS-1$
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void close()
+ {
+ deactivate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ if (!isClosed())
+ {
+ session.viewClosed(this);
+ }
+
+ super.doDeactivate();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void doClose()
+ {
+ clearChangeSubscription();
+ changeSubscriptionIDs = null;
+ session = null;
+ repository = null;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public boolean isClosed()
+ {
+ return repository == null;
+ }
+
+ private void checkOpen()
+ {
+ if (isClosed())
+ {
+ throw new IllegalStateException("View closed"); //$NON-NLS-1$
+ }
+ }
+
+ public IOptionsContainer getContainer()
+ {
+ return this;
+ }
+
+ public Options options()
+ {
+ return this;
+ }
+
+ public boolean isLockNotificationEnabled()
+ {
+ return lockNotificationsEnabled;
+ }
+
+ public void setLockNotificationEnabled(boolean enable)
+ {
+ lockNotificationsEnabled = enable;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java
new file mode 100644
index 0000000000..a3c23c0f8b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java
@@ -0,0 +1,185 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.concurrent.ConcurrentValue;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+/**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+public class XATransactionCommitContext extends TransactionCommitContext
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, XATransactionCommitContext.class);
+
+ private ConcurrentValue<CommitState> state = new ConcurrentValue<CommitState>(CommitState.STARTING);
+
+ public XATransactionCommitContext(InternalTransaction transaction)
+ {
+ super(transaction);
+ }
+
+ public ConcurrentValue<CommitState> getState()
+ {
+ return state;
+ }
+
+ @Override
+ public void preWrite()
+ {
+ super.preWrite();
+ StoreThreadLocal.setAccessor(null);
+ }
+
+ @Override
+ public void commit(OMMonitor monitor)
+ {
+ StoreThreadLocal.setAccessor(getAccessor());
+ try
+ {
+ super.commit(monitor);
+ }
+ finally
+ {
+ StoreThreadLocal.setAccessor(null);
+ }
+ }
+
+ @Override
+ public void write(OMMonitor monitor)
+ {
+ StoreThreadLocal.setAccessor(getAccessor());
+ try
+ {
+ super.write(monitor);
+ }
+ finally
+ {
+ StoreThreadLocal.setAccessor(null);
+ }
+ }
+
+ @Override
+ public void postCommit(boolean success)
+ {
+ StoreThreadLocal.setAccessor(getAccessor());
+ InternalRepository repository = getTransaction().getRepository();
+ repository.getCommitManager().remove(this);
+ super.postCommit(success);
+ }
+
+ @Override
+ public synchronized void rollback(String message)
+ {
+ super.rollback(message);
+
+ // Change the state to unblock call.
+ state.set(CommitState.ROLLED_BACK);
+ }
+
+ /**
+ * Wait until another thread fills ID mapping for external objects.
+ */
+ @Override
+ public void applyIDMappings(OMMonitor monitor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Notify phase2 to fill ID mapping."); //$NON-NLS-1$
+ }
+
+ state.set(CommitState.APPLY_ID_MAPPING);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Waiting for phase2 to be completed before continueing."); //$NON-NLS-1$
+ }
+
+ try
+ {
+ state.acquire(PHASEAPPLYMAPPING_DONE);
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Received signal to continue."); //$NON-NLS-1$
+ }
+
+ super.applyIDMappings(monitor);
+ }
+
+ /**
+ * Object to test if the process is at ApplyIDMapping
+ */
+ final public static Object PHASEAPPLYMAPPING = new Object()
+ {
+ @Override
+ public int hashCode()
+ {
+ return CommitState.APPLY_ID_MAPPING.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object object)
+ {
+ if (object == CommitState.ROLLED_BACK)
+ {
+ throw new RuntimeException("RolledBack"); //$NON-NLS-1$
+ }
+
+ return CommitState.APPLY_ID_MAPPING == object;
+ }
+ };
+
+ /**
+ * Object to test if the process did applyIDMapping
+ */
+ final public static Object PHASEAPPLYMAPPING_DONE = new Object()
+ {
+ @Override
+ public int hashCode()
+ {
+ return CommitState.APPLY_ID_MAPPING_DONE.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object object)
+ {
+ if (object == CommitState.ROLLED_BACK)
+ {
+ throw new RuntimeException("RolledBack"); //$NON-NLS-1$
+ }
+
+ return CommitState.APPLY_ID_MAPPING_DONE == object;
+ }
+ };
+
+ /**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+ public enum CommitState
+ {
+ STARTING, APPLY_ID_MAPPING, APPLY_ID_MAPPING_DONE, ROLLED_BACK
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java
new file mode 100644
index 0000000000..248d30cafc
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java
@@ -0,0 +1,357 @@
+/**
+ * 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 331619 - Support cross-referencing (XRef) for abstract classes and class hierarchies
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+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.CDOIDReference;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.model.CDOPackageInfo;
+import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit.State;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+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.IRepository;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.QueryHandlerFactory;
+
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * @author Eike Stepper
+ */
+public class XRefsQueryHandler implements IQueryHandler
+{
+ public XRefsQueryHandler()
+ {
+ }
+
+ public void executeQuery(CDOQueryInfo info, IQueryContext context)
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ QueryContext xrefsContext = new QueryContext(info, context);
+ accessor.queryXRefs(xrefsContext);
+
+ CDOBranchPoint branchPoint = context;
+ CDOBranch branch = branchPoint.getBranch();
+ while (!branch.isMainBranch() && context.getResultCount() < info.getMaxResults())
+ {
+ branchPoint = branch.getBase();
+ branch = branchPoint.getBranch();
+
+ xrefsContext.setBranchPoint(branchPoint);
+ accessor.queryXRefs(xrefsContext);
+ }
+ }
+
+ public static void collectSourceCandidates(IView view, Collection<EClass> concreteTypes,
+ Map<EClass, List<EReference>> sourceCandidates)
+ {
+ InternalRepository repository = (InternalRepository)view.getRepository();
+ CDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+
+ for (CDOPackageInfo packageInfo : packageRegistry.getPackageInfos())
+ {
+ // System.out.println();
+ // System.out.println();
+ // System.out.println(packageInfo);
+ collectSourceCandidates(packageInfo, concreteTypes, sourceCandidates);
+ // for (Entry<EClass, List<EReference>> entry : sourceCandidates.entrySet())
+ // {
+ // System.out.println(" ---> " + entry.getKey().getName());
+ // for (EReference eReference : entry.getValue())
+ // {
+ // System.out.println(" ---> " + eReference.getName());
+ // }
+ // }
+ //
+ // System.out.println();
+ // System.out.println();
+ }
+ }
+
+ public static void collectSourceCandidates(CDOPackageInfo packageInfo, Collection<EClass> concreteTypes,
+ Map<EClass, List<EReference>> sourceCandidates)
+ {
+ State state = packageInfo.getPackageUnit().getState();
+ if (state == CDOPackageUnit.State.LOADED || state == CDOPackageUnit.State.PROXY)
+ {
+ EPackage ePackage = packageInfo.getEPackage();
+ for (EClassifier eClassifier : ePackage.getEClassifiers())
+ {
+ if (eClassifier instanceof EClass)
+ {
+ collectSourceCandidates((EClass)eClassifier, concreteTypes, sourceCandidates);
+ }
+ }
+ }
+ }
+
+ public static void collectSourceCandidates(EClass eClass, Collection<EClass> concreteTypes,
+ Map<EClass, List<EReference>> sourceCandidates)
+ {
+ if (!eClass.isAbstract() && !eClass.isInterface())
+ {
+ for (EStructuralFeature eStructuralFeature : eClass.getEAllStructuralFeatures())
+ {
+ if (eStructuralFeature instanceof EReference && EMFUtil.isPersistent(eStructuralFeature))
+ {
+ collectSourceCandidates(eClass, (EReference)eStructuralFeature, concreteTypes, sourceCandidates);
+ }
+ }
+ }
+ }
+
+ public static void collectSourceCandidates(EReference eReference, Collection<EClass> concreteTypes,
+ Map<EClass, List<EReference>> sourceCandidates, CDOPackageRegistry packageRegistry)
+ {
+ EClass rootClass = eReference.getEContainingClass();
+ collectSourceCandidates(rootClass, eReference, concreteTypes, sourceCandidates);
+
+ Collection<EClass> descendentClasses = packageRegistry.getSubTypes().get(rootClass);
+ if (descendentClasses != null)
+ {
+ for (EClass candidateClass : descendentClasses)
+ {
+ collectSourceCandidates(candidateClass, eReference, concreteTypes, sourceCandidates);
+ }
+ }
+ }
+
+ public static void collectSourceCandidates(EClass eClass, EReference eReference, Collection<EClass> concreteTypes,
+ Map<EClass, List<EReference>> sourceCandidates)
+ {
+ if (!eClass.isAbstract() && !eClass.isInterface())
+ {
+ if (!eReference.isContainer() && !eReference.isContainment())
+ {
+ if (canReference(eReference.getEReferenceType(), concreteTypes))
+ {
+ List<EReference> list = sourceCandidates.get(eClass);
+ if (list == null)
+ {
+ list = new ArrayList<EReference>();
+ sourceCandidates.put(eClass, list);
+ }
+
+ list.add(eReference);
+ }
+ }
+ }
+ }
+
+ private static boolean canReference(EClass declaredType, Collection<EClass> concreteTypes)
+ {
+ for (EClass concreteType : concreteTypes)
+ {
+ if (declaredType.isSuperTypeOf(concreteType))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 3.0
+ */
+ private static final class QueryContext implements IStoreAccessor.QueryXRefsContext
+ {
+ private CDOQueryInfo info;
+
+ private IQueryContext context;
+
+ private CDOBranchPoint branchPoint;
+
+ private Map<CDOID, EClass> targetObjects;
+
+ private Map<EClass, List<EReference>> sourceCandidates;
+
+ private EReference[] sourceReferences;
+
+ public QueryContext(CDOQueryInfo info, IQueryContext context)
+ {
+ this.info = info;
+ this.context = context;
+ branchPoint = context;
+ }
+
+ public void setBranchPoint(CDOBranchPoint branchPoint)
+ {
+ this.branchPoint = branchPoint;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public Map<CDOID, EClass> getTargetObjects()
+ {
+ if (targetObjects == null)
+ {
+ IRepository repository = context.getView().getRepository();
+ IStore store = repository.getStore();
+ CDOPackageRegistry packageRegistry = repository.getPackageRegistry();
+
+ targetObjects = new HashMap<CDOID, EClass>();
+ StringTokenizer tokenizer = new StringTokenizer(info.getQueryString(), "|");
+ while (tokenizer.hasMoreTokens())
+ {
+ String val = tokenizer.nextToken();
+ CDOID id = store.createObjectID(val);
+
+ CDOClassifierRef classifierRef;
+ if (id instanceof CDOClassifierRef.Provider)
+ {
+ classifierRef = ((CDOClassifierRef.Provider)id).getClassifierRef();
+ }
+ else
+ {
+ val = tokenizer.nextToken();
+ classifierRef = new CDOClassifierRef(val);
+ }
+
+ EClass eClass = (EClass)classifierRef.resolve(packageRegistry);
+ targetObjects.put(id, eClass);
+ }
+ }
+
+ return targetObjects;
+ }
+
+ public EReference[] getSourceReferences()
+ {
+ if (sourceReferences == null)
+ {
+ sourceReferences = parseSourceReferences();
+ }
+
+ return sourceReferences;
+ }
+
+ private EReference[] parseSourceReferences()
+ {
+ List<EReference> result = new ArrayList<EReference>();
+ CDOPackageRegistry packageRegistry = context.getView().getRepository().getPackageRegistry();
+
+ String params = (String)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_XREFS_SOURCE_REFERENCES);
+ if (params == null)
+ {
+ return new EReference[0];
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(params, "|");
+ while (tokenizer.hasMoreTokens())
+ {
+ String className = tokenizer.nextToken();
+ CDOClassifierRef classifierRef = new CDOClassifierRef(className);
+ EClass eClass = (EClass)classifierRef.resolve(packageRegistry);
+
+ String featureName = tokenizer.nextToken();
+ EReference sourceReference = (EReference)eClass.getEStructuralFeature(featureName);
+ result.add(sourceReference);
+ }
+
+ return result.toArray(new EReference[result.size()]);
+ }
+
+ public Map<EClass, List<EReference>> getSourceCandidates()
+ {
+ if (sourceCandidates == null)
+ {
+ sourceCandidates = new HashMap<EClass, List<EReference>>();
+ Collection<EClass> concreteTypes = getTargetObjects().values();
+ EReference[] sourceReferences = getSourceReferences();
+
+ if (sourceReferences.length != 0)
+ {
+ InternalRepository repository = (InternalRepository)context.getView().getRepository();
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ for (EReference eReference : sourceReferences)
+ {
+ collectSourceCandidates(eReference, concreteTypes, sourceCandidates, packageRegistry);
+ }
+ }
+ else
+ {
+ collectSourceCandidates(context.getView(), concreteTypes, sourceCandidates);
+ }
+ }
+
+ return sourceCandidates;
+ }
+
+ public int getMaxResults()
+ {
+ return info.getMaxResults();
+ }
+
+ public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex)
+ {
+ if (CDOIDUtil.isNull(targetID))
+ {
+ return true;
+ }
+
+ return context.addResult(new CDOIDReference(targetID, sourceID, sourceReference, sourceIndex));
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends QueryHandlerFactory
+ {
+ public Factory()
+ {
+ super(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES);
+ }
+
+ @Override
+ public XRefsQueryHandler create(String description) throws ProductCreationException
+ {
+ return new XRefsQueryHandler();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java
new file mode 100644
index 0000000000..6f7df7da50
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java
@@ -0,0 +1,405 @@
+/**
+ * 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.internal.server.bundle;
+
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.server.CDOServerExporter;
+import org.eclipse.emf.cdo.server.CDOServerImporter;
+import org.eclipse.emf.cdo.server.CDOServerUtil;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+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.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+import org.eclipse.emf.cdo.spi.server.RepositoryConfigurator;
+import org.eclipse.emf.cdo.spi.server.RepositoryFactory;
+
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+
+import org.osgi.framework.BundleContext;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @author Eike Stepper
+ */
+public class CDOCommandProvider implements CommandProvider
+{
+ private static final String INDENT = " ";
+
+ public CDOCommandProvider(BundleContext bundleContext)
+ {
+ bundleContext.registerService(CommandProvider.class.getName(), this, null);
+ }
+
+ public String getHelp()
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("---CDO commands---\n");
+ buffer.append("\tcdo list - list all active repositories\n");
+ buffer.append("\tcdo start - start repositories from a config file\n");
+ buffer.append("\tcdo stop - stop a repository\n");
+ buffer.append("\tcdo export - export the contents of a repository to an XML file\n");
+ buffer.append("\tcdo import - import the contents of a repository from an XML file\n");
+ buffer.append("\tcdo sessions - dump the sessions of a repository\n");
+ buffer.append("\tcdo packages - dump the packages of a repository\n");
+ buffer.append("\tcdo branches - dump the branches of a repository\n");
+ buffer.append("\tcdo locks - dump the durable locking areas of a repository\n");
+ buffer.append("\tcdo deletelocks - delete a durable locking area of a repository\n");
+ return buffer.toString();
+ }
+
+ public Object _cdo(CommandInterpreter interpreter)
+ {
+ try
+ {
+ String cmd = interpreter.nextArgument();
+ if ("list".equals(cmd))
+ {
+ list(interpreter);
+ return null;
+ }
+
+ if ("start".equals(cmd))
+ {
+ start(interpreter);
+ return null;
+ }
+
+ if ("stop".equals(cmd))
+ {
+ stop(interpreter);
+ return null;
+ }
+
+ if ("export".equals(cmd))
+ {
+ exportXML(interpreter);
+ return null;
+ }
+
+ if ("import".equals(cmd))
+ {
+ importXML(interpreter);
+ return null;
+ }
+
+ if ("sessions".equals(cmd))
+ {
+ sessions(interpreter);
+ return null;
+ }
+
+ if ("packages".equals(cmd))
+ {
+ packages(interpreter);
+ return null;
+ }
+
+ if ("branches".equals(cmd))
+ {
+ branches(interpreter);
+ return null;
+ }
+
+ if ("locks".equals(cmd))
+ {
+ locks(interpreter);
+ return null;
+ }
+
+ if ("deletelocks".equals(cmd))
+ {
+ deleteLocks(interpreter);
+ return null;
+ }
+
+ interpreter.println(getHelp());
+ }
+ catch (CommandException ex)
+ {
+ interpreter.println(ex.getMessage());
+ }
+ catch (Exception ex)
+ {
+ interpreter.printStackTrace(ex);
+ }
+
+ return null;
+ }
+
+ protected void list(CommandInterpreter interpreter) throws Exception
+ {
+ IManagedContainer container = CDOServerApplication.getContainer();
+ for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP))
+ {
+ if (element instanceof InternalRepository)
+ {
+ InternalRepository repository = (InternalRepository)element;
+ interpreter.println(repository.getName());
+ }
+ }
+ }
+
+ protected void start(CommandInterpreter interpreter) throws Exception
+ {
+ String configFile = nextArgument(interpreter, "Syntax: cdo start <config-file>");
+
+ IManagedContainer container = CDOServerApplication.getContainer();
+ RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container);
+ IRepository[] repositories = repositoryConfigurator.configure(new File(configFile));
+
+ interpreter.println("Repositories started:");
+ if (repositories != null)
+ {
+ for (IRepository repository : repositories)
+ {
+ interpreter.println(repository.getName());
+ }
+ }
+ }
+
+ protected void stop(CommandInterpreter interpreter) throws Exception
+ {
+ InternalRepository repository = getRepository(interpreter, "Syntax: cdo stop <repository-name>");
+ LifecycleUtil.deactivate(repository);
+ interpreter.println("Repository stopped");
+ }
+
+ protected void exportXML(CommandInterpreter interpreter) throws Exception
+ {
+ String syntax = "Syntax: cdo export <repository-name> <export-file>";
+ InternalRepository repository = getRepository(interpreter, syntax);
+ String exportFile = nextArgument(interpreter, syntax);
+ OutputStream out = null;
+
+ try
+ {
+ out = new FileOutputStream(exportFile);
+
+ CDOServerExporter.XML exporter = new CDOServerExporter.XML(repository);
+ exporter.exportRepository(out);
+ interpreter.println("Repository exported");
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+
+ protected void importXML(CommandInterpreter interpreter) throws Exception
+ {
+ String syntax = "Syntax: cdo import <repository-name> <import-file>";
+ InternalRepository repository = getRepository(interpreter, syntax);
+ String importFile = nextArgument(interpreter, syntax);
+ InputStream in = null;
+
+ try
+ {
+ in = new FileInputStream(importFile);
+ LifecycleUtil.deactivate(repository);
+
+ CDOServerImporter.XML importer = new CDOServerImporter.XML(repository);
+ importer.importRepository(in);
+
+ IManagedContainer container = CDOServerApplication.getContainer();
+ CDOServerUtil.addRepository(container, repository);
+
+ interpreter.println("Repository imported");
+ }
+ finally
+ {
+ IOUtil.close(in);
+ }
+ }
+
+ protected void sessions(CommandInterpreter interpreter)
+ {
+ InternalRepository repository = getRepository(interpreter, "Syntax: cdo sessions <repository-name>");
+ InternalSessionManager sessionManager = repository.getSessionManager();
+ for (InternalSession session : sessionManager.getSessions())
+ {
+ interpreter.println(session);
+ for (InternalView view : session.getViews())
+ {
+ interpreter.println(INDENT + view);
+ }
+ }
+ }
+
+ protected void packages(CommandInterpreter interpreter)
+ {
+ InternalRepository repository = getRepository(interpreter, "Syntax: cdo packages <repository-name>");
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ for (InternalCDOPackageUnit packageUnit : packageRegistry.getPackageUnits())
+ {
+ interpreter.println(packageUnit);
+ for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos())
+ {
+ interpreter.println(INDENT + packageInfo);
+ }
+ }
+ }
+
+ protected void branches(CommandInterpreter interpreter)
+ {
+ InternalRepository repository = getRepository(interpreter, "Syntax: cdo branches <repository-name>");
+ branches(interpreter, repository.getBranchManager().getMainBranch(), "");
+ }
+
+ protected void locks(final CommandInterpreter interpreter)
+ {
+ final InternalRepository repository = getRepository(interpreter,
+ "Syntax: cdo locks <repository-name> [<username-prefix>]");
+ final String userIDPrefix = nextArgument(interpreter, null);
+
+ new WithAccessor()
+ {
+ @Override
+ protected void doExecute(IStoreAccessor accessor)
+ {
+ repository.getLockManager().getLockAreas(userIDPrefix, new IDurableLockingManager.LockArea.Handler()
+ {
+ public boolean handleLockArea(LockArea area)
+ {
+ interpreter.println(area.getDurableLockingID());
+ interpreter.println(INDENT + "userID = " + area.getUserID());
+ interpreter.println(INDENT + "branch = " + area.getBranch());
+ interpreter.println(INDENT + "timeStamp = " + CDOCommonUtil.formatTimeStamp(area.getTimeStamp()));
+ interpreter.println(INDENT + "readOnly = " + area.isReadOnly());
+ interpreter.println(INDENT + "locks = " + area.getLocks());
+ return true;
+ }
+ });
+ }
+ }.execute(repository);
+ }
+
+ protected void deleteLocks(CommandInterpreter interpreter)
+ {
+ String syntax = "Syntax: cdo deletelocks <repository-name> <area-id>";
+ final InternalRepository repository = getRepository(interpreter, syntax);
+ final String durableLockingID = nextArgument(interpreter, syntax);
+
+ new WithAccessor()
+ {
+ @Override
+ protected void doExecute(IStoreAccessor accessor)
+ {
+ repository.getLockManager().deleteLockArea(durableLockingID);
+ }
+ }.execute(repository);
+ }
+
+ private void branches(CommandInterpreter interpreter, InternalCDOBranch branch, String prefix)
+ {
+ interpreter.println(prefix + branch);
+ prefix += INDENT;
+ for (InternalCDOBranch child : branch.getBranches())
+ {
+ branches(interpreter, child, prefix);
+ }
+ }
+
+ private String nextArgument(CommandInterpreter interpreter, String syntax)
+ {
+ String argument = interpreter.nextArgument();
+ if (argument == null && syntax != null)
+ {
+ throw new CommandException(syntax);
+ }
+
+ return argument;
+ }
+
+ private InternalRepository getRepository(CommandInterpreter interpreter, String syntax)
+ {
+ String repositoryName = nextArgument(interpreter, syntax);
+ InternalRepository repository = getRepository(repositoryName);
+ if (repository == null)
+ {
+ throw new CommandException("Repository not found: " + repositoryName);
+ }
+
+ return repository;
+ }
+
+ private InternalRepository getRepository(String name)
+ {
+ IManagedContainer container = CDOServerApplication.getContainer();
+ for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP))
+ {
+ if (element instanceof InternalRepository)
+ {
+ InternalRepository repository = (InternalRepository)element;
+ if (repository.getName().equals(name))
+ {
+ return repository;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ protected static abstract class WithAccessor
+ {
+ public void execute(InternalRepository repository)
+ {
+ IStoreAccessor accessor = repository.getStore().getReader(null);
+ StoreThreadLocal.setAccessor(accessor);
+
+ try
+ {
+ doExecute(accessor);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ protected abstract void doExecute(IStoreAccessor accessor);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class CommandException extends RuntimeException
+ {
+ private static final long serialVersionUID = 1L;
+
+ public CommandException(String message)
+ {
+ super(message);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java
new file mode 100644
index 0000000000..deebb3659c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java
@@ -0,0 +1,137 @@
+/**
+ * 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.internal.server.bundle;
+
+import org.eclipse.emf.cdo.internal.server.messages.Messages;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.spi.server.IAppExtension;
+import org.eclipse.emf.cdo.spi.server.RepositoryConfigurator;
+
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.OMPlatform;
+import org.eclipse.net4j.util.om.OSGiApplication;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ */
+public class CDOServerApplication extends OSGiApplication
+{
+ public static final String ID = OM.BUNDLE_ID + ".app"; //$NON-NLS-1$
+
+ public static final String PROP_BROWSER_PORT = "org.eclipse.emf.cdo.server.browser.port"; //$NON-NLS-1$
+
+ private IRepository[] repositories;
+
+ private List<IAppExtension> extensions = new ArrayList<IAppExtension>();
+
+ public CDOServerApplication()
+ {
+ super(ID);
+ }
+
+ @Override
+ protected void doStart() throws Exception
+ {
+ super.doStart();
+ IManagedContainer container = getContainer();
+
+ OM.LOG.info(Messages.getString("CDOServerApplication.1")); //$NON-NLS-1$
+ File configFile = OMPlatform.INSTANCE.getConfigFile("cdo-server.xml"); //$NON-NLS-1$
+ if (configFile != null && configFile.exists())
+ {
+ RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container);
+ repositories = repositoryConfigurator.configure(configFile);
+ if (repositories == null || repositories.length == 0)
+ {
+ OM.LOG.warn(Messages.getString("CDOServerApplication.3") + " " + configFile.getAbsolutePath()); //$NON-NLS-1$
+ }
+
+ String port = OMPlatform.INSTANCE.getProperty(PROP_BROWSER_PORT);
+ if (port != null)
+ {
+ container.getElement("org.eclipse.emf.cdo.server.browsers", "default", port); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ startExtensions(configFile);
+ }
+ else
+ {
+ OM.LOG.warn(Messages.getString("CDOServerApplication.5") + " " + configFile.getAbsolutePath()); //$NON-NLS-1$
+ }
+
+ OM.LOG.info(Messages.getString("CDOServerApplication.6")); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ OM.LOG.info(Messages.getString("CDOServerApplication.7")); //$NON-NLS-1$
+ for (IAppExtension extension : extensions)
+ {
+ try
+ {
+ extension.stop();
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+
+ if (repositories != null)
+ {
+ for (IRepository repository : repositories)
+ {
+ LifecycleUtil.deactivate(repository);
+ }
+ }
+
+ OM.LOG.info(Messages.getString("CDOServerApplication.8")); //$NON-NLS-1$
+ super.doStop();
+ }
+
+ private void startExtensions(File configFile)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, IAppExtension.EXT_POINT);
+ for (final IConfigurationElement element : elements)
+ {
+ if ("appExtension".equals(element.getName())) //$NON-NLS-1$
+ {
+ try
+ {
+ IAppExtension extension = (IAppExtension)element.createExecutableExtension("class"); //$NON-NLS-1$
+ extension.start(configFile);
+ extensions.add(extension);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+ }
+ }
+
+ public static IManagedContainer getContainer()
+ {
+ return IPluginContainer.INSTANCE;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java
new file mode 100644
index 0000000000..245e61b703
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java
@@ -0,0 +1,66 @@
+/**
+ * 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.internal.server.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"; //$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 OMTracer DEBUG_PROTOCOL = DEBUG.tracer("protocol"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_REPOSITORY = DEBUG.tracer("repository"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_SESSION = DEBUG.tracer("session"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_TRANSACTION = DEBUG.tracer("transaction"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_REVISION = DEBUG.tracer("revision"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_RESOURCE = DEBUG.tracer("resource"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_STORE = DEBUG.tracer("store"); //$NON-NLS-1$
+
+ public static final OMTracer DEBUG_TYPES = DEBUG.tracer("types"); //$NON-NLS-1$
+
+ public static final OMLogger LOG = BUNDLE.logger();
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Activator extends OSGiActivator
+ {
+ public Activator()
+ {
+ super(BUNDLE);
+ }
+
+ @Override
+ protected void doStart() throws Exception
+ {
+ new CDOCommandProvider(bundleContext);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java
new file mode 100644
index 0000000000..0552041bc8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java
@@ -0,0 +1,98 @@
+/**
+ * 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
+ * Andre Dietisheim - bug 256649
+ */
+package org.eclipse.emf.cdo.internal.server.embedded;
+
+import org.eclipse.emf.cdo.common.lob.CDOLobStore;
+import org.eclipse.emf.cdo.common.revision.CDORevisionCache;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration.RepositoryInfo;
+import org.eclipse.emf.cdo.server.embedded.CDOSession;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+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.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.emf.internal.cdo.session.CDOSessionImpl;
+
+/**
+ * @author Eike Stepper
+ */
+public class EmbeddedClientSession extends CDOSessionImpl implements CDOSession
+{
+ private InternalRepository repository;
+
+ public EmbeddedClientSession()
+ {
+ }
+
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ @Override
+ public InternalCDOPackageRegistry getPackageRegistry()
+ {
+ return getRepository().getPackageRegistry();
+ }
+
+ @Override
+ public InternalCDOBranchManager getBranchManager()
+ {
+ return getRepository().getBranchManager();
+ }
+
+ @Override
+ public InternalCDOCommitInfoManager getCommitInfoManager()
+ {
+ return getRepository().getCommitInfoManager();
+ }
+
+ @Override
+ public CDOLobStore getLobStore()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ EmbeddedClientSessionProtocol protocol = new EmbeddedClientSessionProtocol(this);
+ setSessionProtocol(protocol);
+ protocol.activate();
+ protocol.openSession(options().isPassiveUpdateEnabled());
+
+ setLastUpdateTime(repository.getLastCommitTimeStamp());
+ setRepositoryInfo(new RepositoryInfo(this));
+
+ InternalCDORevisionManager revisionManager = (InternalCDORevisionManager)CDORevisionUtil.createRevisionManager();
+ setRevisionManager(revisionManager);
+ revisionManager.setSupportingAudits(getRepositoryInfo().isSupportingAudits());
+ revisionManager.setSupportingBranches(getRepositoryInfo().isSupportingBranches());
+ revisionManager.setCache(CDORevisionCache.NOOP);
+ revisionManager.setRevisionLoader(getSessionProtocol());
+ revisionManager.setRevisionLocker(this);
+ revisionManager.activate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ super.doDeactivate();
+
+ getRevisionManager().deactivate();
+ setRevisionManager(null);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java
new file mode 100644
index 0000000000..38706105b2
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java
@@ -0,0 +1,153 @@
+/**
+ * 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.internal.server.embedded;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.emf.internal.cdo.session.CDOSessionConfigurationImpl;
+
+import org.eclipse.net4j.util.CheckUtil;
+
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
+
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class EmbeddedClientSessionConfiguration extends CDOSessionConfigurationImpl implements CDOSessionConfiguration
+{
+ private InternalRepository repository;
+
+ public EmbeddedClientSessionConfiguration()
+ {
+ throw new UnsupportedOperationException("Embedded sessions are not yet supported");
+ }
+
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ public void setRepository(IRepository repository)
+ {
+ checkNotOpen();
+ this.repository = (InternalRepository)repository;
+ }
+
+ @Override
+ public org.eclipse.emf.cdo.server.embedded.CDOSession openSession()
+ {
+ return (org.eclipse.emf.cdo.server.embedded.CDOSession)super.openSession();
+ }
+
+ public InternalCDOSession createSession()
+ {
+ if (isActivateOnOpen())
+ {
+ CheckUtil.checkState(repository, "Specify a repository"); //$NON-NLS-1$
+ }
+
+ return new EmbeddedClientSession();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ protected static class RepositoryInfo implements org.eclipse.emf.cdo.session.CDORepositoryInfo
+ {
+ private EmbeddedClientSession session;
+
+ public RepositoryInfo(EmbeddedClientSession session)
+ {
+ this.session = session;
+ }
+
+ public String getName()
+ {
+ return session.getRepository().getName();
+ }
+
+ public String getUUID()
+ {
+ return session.getRepository().getUUID();
+ }
+
+ public Type getType()
+ {
+ return session.getRepository().getType();
+ }
+
+ public State getState()
+ {
+ return session.getRepository().getState();
+ }
+
+ public long getCreationTime()
+ {
+ return session.getRepository().getCreationTime();
+ }
+
+ public long getTimeStamp()
+ {
+ return getTimeStamp(false);
+ }
+
+ public long getTimeStamp(boolean forceRefresh)
+ {
+ return System.currentTimeMillis();
+ }
+
+ public CDOID getRootResourceID()
+ {
+ return session.getRepository().getRootResourceID();
+ }
+
+ public boolean isSupportingAudits()
+ {
+ return session.getRepository().isSupportingAudits();
+ }
+
+ public boolean isSupportingBranches()
+ {
+ return session.getRepository().isSupportingBranches();
+ }
+
+ public boolean isSupportingEcore()
+ {
+ return session.getRepository().isSupportingEcore();
+ }
+
+ public boolean isEnsuringReferentialIntegrity()
+ {
+ return session.getRepository().isEnsuringReferentialIntegrity();
+ }
+
+ public IDGenerationLocation getIDGenerationLocation()
+ {
+ return session.getRepository().getIDGenerationLocation();
+ }
+
+ public String getStoreType()
+ {
+ return session.getRepository().getStoreType();
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return session.getRepository().getObjectIDTypes();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
new file mode 100644
index 0000000000..531f5a9494
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
@@ -0,0 +1,584 @@
+/**
+ * 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.internal.server.embedded;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode;
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode;
+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.CDOBranchPointRange;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDProvider;
+import org.eclipse.emf.cdo.common.lob.CDOLob;
+import org.eclipse.emf.cdo.common.lob.CDOLobInfo;
+import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.util.CDOQueryQueue;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult;
+import org.eclipse.emf.cdo.spi.common.CDORawReplicationContext;
+import org.eclipse.emf.cdo.spi.common.CDOReplicationContext;
+import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+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.RevisionInfo;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalQueryManager;
+import org.eclipse.emf.cdo.spi.server.InternalQueryResult;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+import org.eclipse.emf.cdo.view.CDOView;
+
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.spi.cdo.AbstractQueryIterator;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager;
+import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class EmbeddedClientSessionProtocol extends Lifecycle implements CDOSessionProtocol
+{
+ private EmbeddedClientSession session;
+
+ // A separate session protocol instance is required because the getSession() methods are ambiguous!
+ private EmbeddedServerSessionProtocol serverSessionProtocol;
+
+ private InternalRepository repository;
+
+ public EmbeddedClientSessionProtocol(EmbeddedClientSession session)
+ {
+ this.session = session;
+ }
+
+ public EmbeddedClientSession getSession()
+ {
+ return session;
+ }
+
+ public EmbeddedServerSessionProtocol getServerSessionProtocol()
+ {
+ return serverSessionProtocol;
+ }
+
+ public InternalSession openSession(boolean passiveUpdateEnabled)
+ {
+ repository = session.getRepository();
+ activate();
+ return serverSessionProtocol.openSession(repository, passiveUpdateEnabled);
+ }
+
+ public EPackage[] loadPackages(CDOPackageUnit packageUnit)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public BranchInfo loadBranch(int branchID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public SubBranchInfo[] loadSubBranches(int branchID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public RepositoryTimeResult getRepositoryTime()
+ {
+ RepositoryTimeResult result = new RepositoryTimeResult();
+ long timeStamp = System.currentTimeMillis();
+ result.setRequested(timeStamp);
+ result.setIndicated(timeStamp);
+ result.setResponded(timeStamp);
+ result.setConfirmed(timeStamp);
+ return result;
+ }
+
+ public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void enableLockNotifications(int viewID, boolean enable)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void disablePassiveUpdate()
+ {
+ // serverSessionProtocol.getSession().setPassiveUpdateEnabled(passiveUpdateEnabled);
+ // TODO: implement EmbeddedClientSessionProtocol.setPassiveUpdate(idAndVersions, initialChunkSize,
+ // passiveUpdateEnabled)
+ throw new UnsupportedOperationException();
+ }
+
+ public void setPassiveUpdateMode(PassiveUpdateMode mode)
+ {
+ // TODO: implement EmbeddedClientSessionProtocol.setPassiveUpdateMode(mode)
+ throw new UnsupportedOperationException();
+ }
+
+ public void setLockNotificationMode(LockNotificationMode mode)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object loadChunk(InternalCDORevision revision, EStructuralFeature feature, int accessIndex, int fetchIndex,
+ int fromIndex, int toIndex)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOCommitData loadCommitData(long timeStamp)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk)
+ {
+ try
+ {
+ InternalSession session = serverSessionProtocol.getSession();
+ StoreThreadLocal.setSession(session);
+ return repository.getRevisionManager().getRevisionByVersion(id, branchVersion, referenceChunk, true);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ public List<InternalCDORevision> loadRevisions(List<RevisionInfo> infos, CDOBranchPoint branchPoint,
+ int referenceChunk, int prefetchDepth)
+ {
+ try
+ {
+ InternalSession session = serverSessionProtocol.getSession();
+ StoreThreadLocal.setSession(session);
+
+ List<CDOID> ids = new ArrayList<CDOID>(infos.size());
+ for (RevisionInfo info : infos)
+ {
+ ids.add(info.getID());
+ }
+
+ // @SuppressWarnings("unchecked")
+ // List<InternalCDORevision> revisions = (List<InternalCDORevision>)(List<?>)repository.getRevisionManager()
+ // .getRevisions(ids, branchPoint, referenceChunk, prefetchDepth, true);
+
+ // TODO: implement EmbeddedClientSessionProtocol.loadRevisions(infos, branchPoint, referenceChunk, prefetchDepth)
+ throw new UnsupportedOperationException();
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ public RefreshSessionResult refresh(long lastUpdateTime,
+ Map<CDOBranch, Map<CDOID, InternalCDORevision>> viewedRevisions, int initialChunkSize,
+ boolean enablePassiveUpdates)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void openView(int viewID, boolean readOnly, CDOBranchPoint branchPoint)
+ {
+ InternalSession session = serverSessionProtocol.getSession();
+ if (readOnly)
+ {
+ session.openView(viewID, branchPoint);
+ }
+ else
+ {
+ session.openTransaction(viewID, branchPoint);
+ }
+ }
+
+ public CDOBranchPoint openView(int viewID, boolean readOnly, String durableLockingID)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void switchTarget(int viewID, CDOBranchPoint branchPoint, List<InternalCDOObject> invalidObjects,
+ List<CDORevisionKey> allChangedObjects, List<CDOIDAndVersion> allDetachedObjects, OMMonitor monitor)
+ {
+ // TODO: implement EmbeddedClientSessionProtocol.changeView(viewID, branchPoint, invalidObjects, allChangedObjects,
+ // allDetachedObjects, monitor)
+ throw new UnsupportedOperationException();
+
+ // try
+ // {
+ // monitor.begin();
+ // Async async = monitor.forkAsync();
+ //
+ // try
+ // {
+ // InternalView view = serverSessionProtocol.getSession().getView(viewID);
+ // if (view != null)
+ // {
+ // List<CDOID> ids = new ArrayList<CDOID>(invalidObjects.size());
+ // for (InternalCDOObject object : invalidObjects)
+ // {
+ // ids.add(object.cdoID());
+ // }
+ //
+ // view.changeTarget(branchPoint, ids, allChangedObjects, allDetachedObjects);
+ // }
+ // }
+ // finally
+ // {
+ // async.stop();
+ // }
+ // }
+ // finally
+ // {
+ // monitor.done();
+ // }
+ }
+
+ public void closeView(int viewID)
+ {
+ InternalView view = serverSessionProtocol.getSession().getView(viewID);
+ if (view != null)
+ {
+ view.close();
+ }
+ }
+
+ public void changeSubscription(int viewID, List<CDOID> ids, boolean subscribeMode, boolean clear)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void query(CDOView view, AbstractQueryIterator<?> query)
+ {
+ InternalView serverView = serverSessionProtocol.getSession().getView(view.getViewID());
+ InternalQueryManager queryManager = repository.getQueryManager();
+ InternalQueryResult result = queryManager.execute(serverView, query.getQueryInfo());
+
+ query.setQueryID(result.getQueryID());
+ CDOQueryQueue<Object> resultQueue = query.getQueue();
+
+ try
+ {
+ while (result.hasNext())
+ {
+ Object object = result.next();
+ resultQueue.add(object);
+ }
+ }
+ catch (RuntimeException ex)
+ {
+ resultQueue.setException(ex);
+ }
+ catch (Throwable throwable)
+ {
+ resultQueue.setException(new RuntimeException(throwable.getMessage(), throwable));
+ }
+ finally
+ {
+ resultQueue.close();
+ }
+ }
+
+ public boolean cancelQuery(int queryID)
+ {
+ repository.getQueryManager().cancel(queryID);
+ return true;
+ }
+
+ public boolean isObjectLocked(CDOView view, CDOObject object, LockType lockType, boolean byOthers)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Deprecated
+ public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
+ LockType lockType, long timeout) throws InterruptedException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @since 4.1
+ */
+ public LockObjectsResult lockObjects2(List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch,
+ LockType lockType, long timeout) throws InterruptedException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unlockObjects(CDOView view, Collection<CDOID> objectIDs, LockType lockType)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public UnlockObjectsResult unlockObjects2(CDOView view, Collection<CDOID> objectIDs, LockType lockType)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public LockObjectsResult delegateLockObjects(String lockAreaID, List<CDORevisionKey> revisionKeys,
+ CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection<CDOID> objectIDs, LockType lockType)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String changeLockArea(CDOView view, boolean create)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public List<byte[]> queryLobs(Set<byte[]> ids)
+ {
+ // TODO: implement EmbeddedClientSessionProtocol.queryLobs(ids)
+ throw new UnsupportedOperationException();
+ }
+
+ public void loadLob(CDOLobInfo info, Object outputStreamOrWriter)
+ {
+ // TODO: implement EmbeddedClientSessionProtocol.loadLob(info, out)
+ throw new UnsupportedOperationException();
+ }
+
+ public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler)
+ {
+ // TODO: implement EmbeddedClientSessionProtocol.handleRevisions(eClass, branch, exactBranch, timeStamp, exactTime,
+ // handler)
+ throw new UnsupportedOperationException();
+ }
+
+ public CommitTransactionResult commitTransaction(int transactionID, String comment, boolean releaseLocks,
+ CDOIDProvider idProvider, CDOCommitData commitData, Collection<CDOLob<?>> lobs, OMMonitor monitor)
+ {
+ monitor.begin(2);
+ boolean success = false;
+ InternalCommitContext serverCommitContext = null;
+ CommitTransactionResult result = null;
+
+ try
+ {
+ InternalTransaction serverTransaction = (InternalTransaction)serverSessionProtocol.getSession().getView(
+ transactionID);
+ serverCommitContext = serverTransaction.createCommitContext();
+ serverCommitContext.preWrite();
+ serverCommitContext.setAutoReleaseLocksEnabled(releaseLocks);
+
+ List<CDOPackageUnit> npu = commitData.getNewPackageUnits();
+ serverCommitContext.setNewPackageUnits(npu.toArray(new InternalCDOPackageUnit[npu.size()]));
+
+ List<CDOIDAndVersion> no = commitData.getNewObjects();
+ InternalCDORevision[] array = new InternalCDORevision[no.size()];
+ int index = 0;
+ for (CDOIDAndVersion object : no)
+ {
+ InternalCDORevision revision = (InternalCDORevision)object;
+ // revision.convertEObjects(clientTransaction);
+ array[index++] = revision;
+ }
+
+ serverCommitContext.setNewObjects(array);
+
+ List<CDORevisionKey> rd = commitData.getChangedObjects();
+ serverCommitContext.setDirtyObjectDeltas(rd.toArray(new InternalCDORevisionDelta[rd.size()]));
+
+ List<CDOIDAndVersion> detachedObjects = commitData.getDetachedObjects();
+ serverCommitContext.setDetachedObjects(detachedObjects.toArray(new CDOID[detachedObjects.size()]));
+
+ serverCommitContext.write(monitor.fork());
+ success = serverCommitContext.getRollbackMessage() == null;
+ if (success)
+ {
+ serverCommitContext.commit(monitor.fork());
+ }
+ else
+ {
+ monitor.worked();
+ }
+
+ // result = new CommitTransactionResult(commitData, serverCommitContext.getBranchPoint().getTimeStamp());
+ // for (Entry<CDOID, CDOID> entry : serverCommitContext.getIDMappings().entrySet())
+ // {
+ // result.addIDMapping(entry.getKey(), entry.getValue());
+ // }
+ }
+ finally
+ {
+ if (serverCommitContext != null)
+ {
+ serverCommitContext.postCommit(success);
+ }
+
+ monitor.done();
+ }
+
+ return result;
+ }
+
+ public CommitTransactionResult commitDelegation(CDOBranch branch, String userID, String comment,
+ CDOCommitData commitData, Map<CDOID, EClass> detachedObjectTypes, Collection<CDOLob<?>> lobs, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CommitTransactionResult commitXATransactionCancel(InternalCDOXACommitContext xaContext, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CommitTransactionResult commitXATransactionPhase1(InternalCDOXACommitContext xaContext, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CommitTransactionResult commitXATransactionPhase2(InternalCDOXACommitContext xaContext, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CommitTransactionResult commitXATransactionPhase3(InternalCDOXACommitContext xaContext, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public List<CDORemoteSession> getRemoteSessions(InternalCDORemoteSessionManager manager, boolean subscribe)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Set<Integer> sendRemoteMessage(CDORemoteSessionMessage message, List<CDORemoteSession> recipients)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean unsubscribeRemoteSessions()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void replicateRepository(CDOReplicationContext context, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void replicateRepositoryRaw(CDORawReplicationContext context, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOChangeSetData[] loadChangeSets(CDOBranchPointRange... ranges)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Set<CDOID> loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+ CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOAuthenticationResult handleAuthenticationChallenge(byte[] randomToken) throws Exception
+ {
+ CDOAuthenticator authenticator = getSession().getAuthenticator();
+ if (authenticator == null)
+ {
+ throw new IllegalStateException("No authenticator configured"); //$NON-NLS-1$
+ }
+
+ CDOAuthenticationResult result = authenticator.authenticate(randomToken);
+ if (result == null)
+ {
+ throw new SecurityException("Not authenticated"); //$NON-NLS-1$
+ }
+
+ String userID = result.getUserID();
+ if (userID == null)
+ {
+ throw new SecurityException("No user ID"); //$NON-NLS-1$
+ }
+
+ byte[] cryptedToken = result.getCryptedToken();
+ if (cryptedToken == null)
+ {
+ throw new SecurityException("No crypted token"); //$NON-NLS-1$
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ serverSessionProtocol = new EmbeddedServerSessionProtocol(this);
+ serverSessionProtocol.activate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ serverSessionProtocol.deactivate();
+ serverSessionProtocol = null;
+ super.doDeactivate();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java
new file mode 100644
index 0000000000..3dd8efaa73
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java
@@ -0,0 +1,109 @@
+/**
+ * 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.internal.server.embedded;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+import org.eclipse.emf.cdo.spi.server.ISessionProtocol;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+
+/**
+ * @author Eike Stepper
+ */
+public class EmbeddedServerSessionProtocol extends Lifecycle implements ISessionProtocol
+{
+ // A separate session protocol instance is required because the getSession() methods are ambiguous!
+ private EmbeddedClientSessionProtocol clientSessionProtocol;
+
+ private InternalSession session;
+
+ public EmbeddedServerSessionProtocol(EmbeddedClientSessionProtocol clientSessionProtocol)
+ {
+ this.clientSessionProtocol = clientSessionProtocol;
+ }
+
+ public EmbeddedClientSessionProtocol getClientSessionProtocol()
+ {
+ return clientSessionProtocol;
+ }
+
+ public InternalSession openSession(InternalRepository repository, boolean passiveUpdateEnabled)
+ {
+ session = repository.getSessionManager().openSession(this);
+ session.setPassiveUpdateEnabled(passiveUpdateEnabled);
+ return session;
+ }
+
+ public InternalSession getSession()
+ {
+ return session;
+ }
+
+ public CDOAuthenticationResult sendAuthenticationChallenge(byte[] randomToken) throws Exception
+ {
+ return clientSessionProtocol.handleAuthenticationChallenge(randomToken);
+ }
+
+ public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType)
+ {
+ EmbeddedClientSession clientSession = clientSessionProtocol.getSession();
+ clientSession.handleRepositoryTypeChanged(oldType, newType);
+ }
+
+ @Deprecated
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState)
+ {
+ sendRepositoryStateNotification(oldState, newState, null);
+ }
+
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState,
+ CDOID rootResourceID)
+ {
+ EmbeddedClientSession clientSession = clientSessionProtocol.getSession();
+ clientSession.handleRepositoryStateChanged(oldState, newState);
+ }
+
+ public void sendBranchNotification(InternalCDOBranch branch)
+ {
+ EmbeddedClientSession clientSession = clientSessionProtocol.getSession();
+ clientSession.handleBranchNotification(branch);
+ }
+
+ public void sendCommitNotification(CDOCommitInfo commitInfo)
+ {
+ EmbeddedClientSession clientSession = clientSessionProtocol.getSession();
+ clientSession.handleCommitNotification(commitInfo);
+ }
+
+ public void sendLockNotification(CDOLockChangeInfo lockChangeInfo)
+ {
+ EmbeddedClientSession clientSession = clientSessionProtocol.getSession();
+ clientSession.handleLockNotification(lockChangeInfo, null);
+ }
+
+ public void sendRemoteSessionNotification(InternalSession sender, byte opcode)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message)
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java
new file mode 100644
index 0000000000..96f26ac283
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java
@@ -0,0 +1,1310 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Simon McDuff - bug 233273
+ * Eike Stepper - maintenance
+ * Andre Dietisheim - bug 256649
+ */
+package org.eclipse.emf.cdo.internal.server.mem;
+
+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.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
+import org.eclipse.emf.cdo.common.model.CDOModelConstants;
+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.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+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.IStoreAccessor.QueryXRefsContext;
+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.mem.IMEMStore;
+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.branch.InternalCDOBranchManager.BranchLoader;
+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.revision.DetachedCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.LongIDStore;
+import org.eclipse.emf.cdo.spi.server.StoreAccessorPool;
+
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+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.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+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.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * @author Simon McDuff
+ */
+public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader, DurableLocking2
+{
+ public static final String TYPE = "mem"; //$NON-NLS-1$
+
+ private long creationTime;
+
+ private Map<String, String> properties = new HashMap<String, String>();
+
+ private Map<Integer, BranchInfo> branchInfos = new HashMap<Integer, BranchInfo>();
+
+ private int lastBranchID;
+
+ private int lastLocalBranchID;
+
+ private Map<Object, List<InternalCDORevision>> revisions = new HashMap<Object, List<InternalCDORevision>>();
+
+ private List<CommitInfo> commitInfos = new ArrayList<CommitInfo>();
+
+ private Map<CDOID, EClass> objectTypes = new HashMap<CDOID, EClass>();
+
+ private Map<String, LockArea> lockAreas = new HashMap<String, LockArea>();
+
+ private Map<String, Object> lobs = new HashMap<String, Object>();
+
+ private int listLimit;
+
+ @ExcludeFromDump
+ private transient EStructuralFeature resourceNameFeature;
+
+ /**
+ * @param listLimit
+ * See {@link #setListLimit(int)}.
+ * @since 2.0
+ */
+ public MEMStore(int listLimit)
+ {
+ super(TYPE, set(ChangeFormat.REVISION, ChangeFormat.DELTA), set(RevisionTemporality.NONE,
+ RevisionTemporality.AUDITING), set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING));
+ setRevisionTemporality(RevisionTemporality.AUDITING);
+ setRevisionParallelism(RevisionParallelism.BRANCHING);
+ this.listLimit = listLimit;
+ }
+
+ public MEMStore()
+ {
+ this(UNLIMITED);
+ }
+
+ @Override
+ public CDOID createObjectID(String val)
+ {
+ if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT)
+ {
+ byte[] decoded = CDOIDUtil.decodeUUID(val);
+ return CDOIDUtil.createUUID(decoded);
+ }
+
+ return super.createObjectID(val);
+ }
+
+ @Override
+ public boolean isLocal(CDOID id)
+ {
+ if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT)
+ {
+ return false;
+ }
+
+ return super.isLocal(id);
+ }
+
+ @Override
+ public void ensureLastObjectID(CDOID id)
+ {
+ if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT)
+ {
+ return;
+ }
+
+ super.ensureLastObjectID(id);
+ }
+
+ public synchronized Map<String, String> getPersistentProperties(Set<String> names)
+ {
+ if (names == null || names.isEmpty())
+ {
+ return new HashMap<String, String>(properties);
+ }
+
+ Map<String, String> result = new HashMap<String, String>();
+ for (String name : names)
+ {
+ String value = properties.get(name);
+ if (value != null)
+ {
+ result.put(name, value);
+ }
+ }
+
+ return result;
+ }
+
+ public synchronized void setPersistentProperties(Map<String, String> properties)
+ {
+ this.properties.putAll(properties);
+ }
+
+ public synchronized void removePersistentProperties(Set<String> names)
+ {
+ for (String name : names)
+ {
+ properties.remove(name);
+ }
+ }
+
+ public synchronized Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
+ {
+ if (branchID == NEW_BRANCH)
+ {
+ branchID = ++lastBranchID;
+ }
+ else if (branchID == NEW_LOCAL_BRANCH)
+ {
+ branchID = --lastLocalBranchID;
+ }
+
+ branchInfos.put(branchID, branchInfo);
+ return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp());
+ }
+
+ public synchronized BranchInfo loadBranch(int branchID)
+ {
+ return branchInfos.get(branchID);
+ }
+
+ public synchronized SubBranchInfo[] loadSubBranches(int branchID)
+ {
+ List<SubBranchInfo> result = new ArrayList<SubBranchInfo>();
+ for (Entry<Integer, BranchInfo> entry : branchInfos.entrySet())
+ {
+ BranchInfo branchInfo = entry.getValue();
+ if (branchInfo.getBaseBranchID() == branchID)
+ {
+ int id = entry.getKey();
+ result.add(new SubBranchInfo(id, branchInfo.getName(), branchInfo.getBaseTimeStamp()));
+ }
+ }
+
+ return result.toArray(new SubBranchInfo[result.size()]);
+ }
+
+ public synchronized int loadBranches(int startID, int endID, CDOBranchHandler handler)
+ {
+ int count = 0;
+ InternalCDOBranchManager branchManager = getRepository().getBranchManager();
+ for (Entry<Integer, BranchInfo> entry : branchInfos.entrySet())
+ {
+ int id = entry.getKey();
+ if (startID <= id && (id <= endID || endID == 0))
+ {
+ BranchInfo branchInfo = entry.getValue();
+ InternalCDOBranch branch = branchManager.getBranch(id, branchInfo);
+ handler.handleBranch(branch);
+ ++count;
+ }
+ }
+
+ return count;
+ }
+
+ public synchronized void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
+ {
+ InternalCDOCommitInfoManager manager = getRepository().getCommitInfoManager();
+ for (int i = 0; i < commitInfos.size(); i++)
+ {
+ CommitInfo info = commitInfos.get(i);
+ if (startTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() < startTime)
+ {
+ continue;
+ }
+
+ if (endTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() > endTime)
+ {
+ continue;
+ }
+
+ if (branch != null && !ObjectUtil.equals(info.getBranch(), branch))
+ {
+ continue;
+ }
+
+ info.handle(manager, handler);
+ }
+ }
+
+ public synchronized Set<CDOID> readChangeSet(CDOChangeSetSegment[] segments)
+ {
+ Set<CDOID> ids = new HashSet<CDOID>();
+ for (CDOChangeSetSegment segment : segments)
+ {
+ for (List<InternalCDORevision> list : revisions.values())
+ {
+ readChangeSet(segment, list, ids);
+ }
+ }
+
+ return ids;
+ }
+
+ private void readChangeSet(CDOChangeSetSegment segment, List<InternalCDORevision> list, Set<CDOID> ids)
+ {
+ long startTime = segment.getTimeStamp();
+ long endTime = segment.getEndTime();
+ boolean listCheckDone = false;
+ for (InternalCDORevision revision : list)
+ {
+ CDOID id = revision.getID();
+ if (!listCheckDone)
+ {
+ if (ids.contains(id))
+ {
+ return;
+ }
+
+ if (!ObjectUtil.equals(revision.getBranch(), segment.getBranch()))
+ {
+ return;
+ }
+
+ listCheckDone = true;
+ }
+
+ if (CDOCommonUtil.isValidTimeStamp(revision.getTimeStamp(), startTime, endTime))
+ {
+ ids.add(id);
+ }
+ }
+ }
+
+ public synchronized void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler)
+ {
+ for (List<InternalCDORevision> list : revisions.values())
+ {
+ for (InternalCDORevision revision : list)
+ {
+ if (!handleRevision(revision, eClass, branch, timeStamp, exactTime, handler))
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ private boolean handleRevision(InternalCDORevision revision, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler)
+ {
+ if (eClass != null && revision.getEClass() != eClass)
+ {
+ return true;
+ }
+
+ if (branch != null && !ObjectUtil.equals(revision.getBranch(), branch))
+ {
+ return true;
+ }
+
+ if (timeStamp != CDOBranchPoint.INVALID_DATE)
+ {
+ if (exactTime)
+ {
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE && revision.getTimeStamp() != timeStamp)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!revision.isValid(timeStamp))
+ {
+ return true;
+ }
+ }
+ }
+
+ return handler.handleRevision(revision);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public int getListLimit()
+ {
+ return listLimit;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized void setListLimit(int listLimit)
+ {
+ if (listLimit != UNLIMITED && this.listLimit != listLimit)
+ {
+ for (List<InternalCDORevision> list : revisions.values())
+ {
+ enforceListLimit(list);
+ }
+ }
+
+ this.listLimit = listLimit;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized List<InternalCDORevision> getCurrentRevisions()
+ {
+ ArrayList<InternalCDORevision> simpleRevisions = new ArrayList<InternalCDORevision>();
+ Iterator<List<InternalCDORevision>> itr = revisions.values().iterator();
+ while (itr.hasNext())
+ {
+ List<InternalCDORevision> list = itr.next();
+ InternalCDORevision revision = list.get(list.size() - 1);
+ simpleRevisions.add(revision);
+ }
+
+ return simpleRevisions;
+ }
+
+ public synchronized InternalCDORevision getRevisionByVersion(CDOID id, CDOBranchVersion branchVersion)
+ {
+ Object listKey = getListKey(id, branchVersion.getBranch());
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list == null)
+ {
+ return null;
+ }
+
+ return getRevisionByVersion(list, branchVersion.getVersion());
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized InternalCDORevision getRevision(CDOID id, CDOBranchPoint branchPoint)
+ {
+ Object listKey = getListKey(id, branchPoint.getBranch());
+ if (branchPoint.getTimeStamp() == CDORevision.UNSPECIFIED_DATE)
+ {
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list == null)
+ {
+ return null;
+ }
+
+ return list.get(list.size() - 1);
+ }
+
+ if (!getRepository().isSupportingAudits())
+ {
+ throw new UnsupportedOperationException("Auditing not supported");
+ }
+
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list == null)
+ {
+ return null;
+ }
+
+ return getRevision(list, branchPoint);
+ }
+
+ public synchronized void addRevision(InternalCDORevision revision, boolean raw)
+ {
+ Object listKey = getListKey(revision.getID(), revision.getBranch());
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list == null)
+ {
+ list = new ArrayList<InternalCDORevision>();
+ revisions.put(listKey, list);
+ }
+
+ addRevision(list, revision, raw);
+
+ if (raw)
+ {
+ ensureLastObjectID(revision.getID());
+ }
+ }
+
+ public synchronized void addCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
+ String comment)
+ {
+ int index = commitInfos.size() - 1;
+ while (index >= 0)
+ {
+ CommitInfo info = commitInfos.get(index);
+ if (timeStamp > info.getTimeStamp())
+ {
+ break;
+ }
+
+ --index;
+ }
+
+ CommitInfo commitInfo = new CommitInfo(branch, timeStamp, previousTimeStamp, userID, comment);
+ commitInfos.add(index + 1, commitInfo);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized boolean rollbackRevision(InternalCDORevision revision)
+ {
+ CDOID id = revision.getID();
+ CDOBranch branch = revision.getBranch();
+ int version = revision.getVersion();
+
+ Object listKey = getListKey(id, branch);
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list == null)
+ {
+ return false;
+ }
+
+ for (Iterator<InternalCDORevision> it = list.iterator(); it.hasNext();)
+ {
+ InternalCDORevision rev = it.next();
+ if (rev.getVersion() == version)
+ {
+ it.remove();
+ return true;
+ }
+ else if (rev.getVersion() == version - 1)
+ {
+ rev.setRevised(CDORevision.UNSPECIFIED_DATE);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public synchronized DetachedCDORevision detachObject(CDOID id, CDOBranch branch, long timeStamp)
+ {
+ Object listKey = getListKey(id, branch);
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list != null)
+ {
+ InternalCDORevision revision = getRevision(list, branch.getHead());
+ if (revision != null)
+ {
+ revision.setRevised(timeStamp - 1);
+ }
+ }
+
+ int version;
+ if (list == null)
+ {
+ list = new ArrayList<InternalCDORevision>();
+ revisions.put(listKey, list);
+ version = CDOBranchVersion.FIRST_VERSION;
+ }
+ else
+ {
+ version = getHighestVersion(list) + 1;
+ }
+
+ EClass eClass = getObjectType(id);
+ DetachedCDORevision detached = new DetachedCDORevision(eClass, id, branch, version, timeStamp);
+ addRevision(list, detached, false);
+ return detached;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public synchronized void queryResources(IStoreAccessor.QueryResourcesContext context)
+ {
+ CDOID folderID = context.getFolderID();
+ String name = context.getName();
+ boolean exactMatch = context.exactMatch();
+ for (Entry<Object, List<InternalCDORevision>> entry : revisions.entrySet())
+ {
+ CDOBranch branch = getBranch(entry.getKey());
+ if (!ObjectUtil.equals(branch, context.getBranch()))
+ {
+ continue;
+ }
+
+ List<InternalCDORevision> list = entry.getValue();
+ if (list.isEmpty())
+ {
+ continue;
+ }
+
+ InternalCDORevision revision = list.get(0);
+ if (revision instanceof SyntheticCDORevision)
+ {
+ continue;
+ }
+
+ if (!revision.isResourceNode())
+ {
+ continue;
+ }
+
+ revision = getRevision(list, context);
+ if (revision == null || revision instanceof DetachedCDORevision)
+ {
+ continue;
+ }
+
+ CDOID revisionFolder = (CDOID)revision.data().getContainerID();
+ if (!CDOIDUtil.equals(revisionFolder, folderID))
+ {
+ continue;
+ }
+
+ String revisionName = (String)revision.data().get(resourceNameFeature, 0);
+ boolean useEquals = exactMatch || revisionName == null || name == null;
+ boolean match = useEquals ? ObjectUtil.equals(revisionName, name) : revisionName.startsWith(name);
+
+ if (match)
+ {
+ if (!context.addResource(revision.getID()))
+ {
+ // No more results allowed
+ break;
+ }
+ }
+ }
+ }
+
+ public synchronized void queryXRefs(QueryXRefsContext context)
+ {
+ Set<CDOID> targetIDs = context.getTargetObjects().keySet();
+ Map<EClass, List<EReference>> sourceCandidates = context.getSourceCandidates();
+
+ for (Entry<Object, List<InternalCDORevision>> entry : revisions.entrySet())
+ {
+ CDOBranch branch = getBranch(entry.getKey());
+ if (!ObjectUtil.equals(branch, context.getBranch()))
+ {
+ continue;
+ }
+
+ List<InternalCDORevision> list = entry.getValue();
+ if (list.isEmpty())
+ {
+ continue;
+ }
+
+ InternalCDORevision revision = getRevision(list, context);
+ if (revision == null || revision instanceof SyntheticCDORevision)
+ {
+ continue;
+ }
+
+ EClass eClass = revision.getEClass();
+ CDOID sourceID = revision.getID();
+
+ List<EReference> eReferences = sourceCandidates.get(eClass);
+ if (eReferences != null)
+ {
+ for (EReference eReference : eReferences)
+ {
+ Object value = revision.getValue(eReference);
+ if (value != null)
+ {
+ if (eReference.isMany())
+ {
+ @SuppressWarnings("unchecked")
+ List<CDOID> ids = (List<CDOID>)value;
+ int index = 0;
+ for (CDOID id : ids)
+ {
+ if (!queryXRefs(context, targetIDs, id, sourceID, eReference, index++))
+ {
+ return;
+ }
+ }
+ }
+ else
+ {
+ CDOID id = (CDOID)value;
+ if (!queryXRefs(context, targetIDs, id, sourceID, eReference, 0))
+ {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean queryXRefs(QueryXRefsContext context, Set<CDOID> targetIDs, CDOID targetID, CDOID sourceID,
+ EReference sourceReference, int index)
+ {
+ for (CDOID id : targetIDs)
+ {
+ if (id.equals(targetID))
+ {
+ if (!context.addXRef(targetID, sourceID, sourceReference, index))
+ {
+ // No more results allowed
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public synchronized void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime,
+ long toCommitTime)
+ {
+ // TODO: implement MEMStore.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime)
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor)
+ {
+ // TODO: implement MEMStore.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor)
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void rawDelete(CDOID id, int version, CDOBranch branch)
+ {
+ Object listKey = getListKey(id, branch);
+ List<InternalCDORevision> list = revisions.get(listKey);
+ if (list != null)
+ {
+ for (Iterator<InternalCDORevision> it = list.iterator(); it.hasNext();)
+ {
+ InternalCDORevision rev = it.next();
+ if (rev.getVersion() == version)
+ {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ public synchronized LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ return createLockArea(null, userID, branchPoint, readOnly, locks);
+ }
+
+ public synchronized LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint,
+ boolean readOnly, Map<CDOID, LockGrade> locks)
+ {
+ if (durableLockingID != null)
+ {
+ // If the caller is specifying the ID, make sure there is no area with this ID yet
+ if (lockAreas.containsKey(durableLockingID))
+ {
+ throw new LockAreaAlreadyExistsException(durableLockingID);
+ }
+ }
+ else
+ {
+ do
+ {
+ durableLockingID = CDOLockUtil.createDurableLockingID();
+ } while (lockAreas.containsKey(durableLockingID));
+ }
+
+ LockArea area = CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks);
+ lockAreas.put(durableLockingID, area);
+ return area;
+ }
+
+ public synchronized void updateLockArea(LockArea lockArea)
+ {
+ String durableLockingID = lockArea.getDurableLockingID();
+ lockAreas.put(durableLockingID, lockArea);
+ }
+
+ public synchronized LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
+ {
+ LockArea area = lockAreas.get(durableLockingID);
+ if (area == null)
+ {
+ throw new LockAreaNotFoundException(durableLockingID);
+ }
+
+ return area;
+ }
+
+ public synchronized void getLockAreas(String userIDPrefix, Handler handler)
+ {
+ for (LockArea area : lockAreas.values())
+ {
+ String userID = area.getUserID();
+ if (userID == null || userID.startsWith(userIDPrefix))
+ {
+ if (!handler.handleLockArea(area))
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ public synchronized void deleteLockArea(String durableLockingID)
+ {
+ lockAreas.remove(durableLockingID);
+ }
+
+ public synchronized void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock)
+ {
+ LockArea area = getLockArea(durableLockingID);
+ Map<CDOID, LockGrade> locks = area.getLocks();
+
+ InternalLockManager lockManager = getRepository().getLockManager();
+ for (Object objectToLock : objectsToLock)
+ {
+ CDOID id = lockManager.getLockKeyID(objectToLock);
+ LockGrade grade = locks.get(id);
+ if (grade != null)
+ {
+ grade = grade.getUpdated(type, true);
+ }
+ else
+ {
+ grade = LockGrade.get(type);
+ }
+
+ locks.put(id, grade);
+ }
+ }
+
+ public synchronized void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock)
+ {
+ LockArea area = getLockArea(durableLockingID);
+ Map<CDOID, LockGrade> locks = area.getLocks();
+
+ InternalLockManager lockManager = getRepository().getLockManager();
+ for (Object objectToUnlock : objectsToUnlock)
+ {
+ CDOID id = lockManager.getLockKeyID(objectToUnlock);
+ LockGrade grade = locks.get(id);
+ if (grade != null)
+ {
+ grade = grade.getUpdated(type, false);
+ if (grade == LockGrade.NONE)
+ {
+ locks.remove(id);
+ }
+ }
+ }
+ }
+
+ public synchronized void unlock(String durableLockingID)
+ {
+ LockArea area = getLockArea(durableLockingID);
+ Map<CDOID, LockGrade> locks = area.getLocks();
+ locks.clear();
+ }
+
+ public synchronized void queryLobs(List<byte[]> ids)
+ {
+ for (Iterator<byte[]> it = ids.iterator(); it.hasNext();)
+ {
+ byte[] id = it.next();
+ String key = HexUtil.bytesToHex(id);
+ if (!lobs.containsKey(key))
+ {
+ it.remove();
+ }
+ }
+ }
+
+ public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
+ {
+ for (Entry<String, Object> entry : lobs.entrySet())
+ {
+ byte[] id = HexUtil.hexToBytes(entry.getKey());
+ Object lob = entry.getValue();
+ if (lob instanceof byte[])
+ {
+ byte[] blob = (byte[])lob;
+ ByteArrayInputStream in = new ByteArrayInputStream(blob);
+ OutputStream out = handler.handleBlob(id, blob.length);
+ if (out != null)
+ {
+ try
+ {
+ IOUtil.copyBinary(in, out, blob.length);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+ }
+ else
+ {
+ char[] clob = (char[])lob;
+ CharArrayReader in = new CharArrayReader(clob);
+ Writer out = handler.handleClob(id, clob.length);
+ if (out != null)
+ {
+ try
+ {
+ IOUtil.copyCharacter(in, out, clob.length);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+ }
+ }
+ }
+
+ public synchronized void loadLob(byte[] id, OutputStream out) throws IOException
+ {
+ String key = HexUtil.bytesToHex(id);
+ Object lob = lobs.get(key);
+ if (lob == null)
+ {
+ throw new IOException("Lob not found: " + key);
+ }
+
+ if (lob instanceof byte[])
+ {
+ byte[] blob = (byte[])lob;
+ ByteArrayInputStream in = new ByteArrayInputStream(blob);
+ IOUtil.copyBinary(in, out, blob.length);
+ }
+ else
+ {
+ char[] clob = (char[])lob;
+ CharArrayReader in = new CharArrayReader(clob);
+ IOUtil.copyCharacter(in, new OutputStreamWriter(out), clob.length);
+ }
+ }
+
+ public synchronized void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ IOUtil.copyBinary(inputStream, out, size);
+ lobs.put(HexUtil.bytesToHex(id), out.toByteArray());
+ }
+
+ public synchronized void writeClob(byte[] id, long size, Reader reader) throws IOException
+ {
+ CharArrayWriter out = new CharArrayWriter();
+ IOUtil.copyCharacter(reader, out, size);
+ lobs.put(HexUtil.bytesToHex(id), out.toCharArray());
+ }
+
+ @Override
+ public MEMStoreAccessor createReader(ISession session)
+ {
+ return new MEMStoreAccessor(this, session);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public MEMStoreAccessor createWriter(ITransaction transaction)
+ {
+ return new MEMStoreAccessor(this, transaction);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public long getCreationTime()
+ {
+ return creationTime;
+ }
+
+ public void setCreationTime(long creationTime)
+ {
+ this.creationTime = creationTime;
+ }
+
+ public boolean isFirstStart()
+ {
+ return true;
+ }
+
+ public synchronized Map<CDOBranch, List<CDORevision>> getAllRevisions()
+ {
+ Map<CDOBranch, List<CDORevision>> result = new HashMap<CDOBranch, List<CDORevision>>();
+ InternalCDOBranchManager branchManager = getRepository().getBranchManager();
+ result.put(branchManager.getMainBranch(), new ArrayList<CDORevision>());
+
+ for (Integer branchID : branchInfos.keySet())
+ {
+ InternalCDOBranch branch = branchManager.getBranch(branchID);
+ result.put(branch, new ArrayList<CDORevision>());
+ }
+
+ for (List<InternalCDORevision> list : revisions.values())
+ {
+ for (InternalCDORevision revision : list)
+ {
+ CDOBranch branch = revision.getBranch();
+ List<CDORevision> resultList = result.get(branch);
+ resultList.add(revision);
+ }
+ }
+
+ return result;
+ }
+
+ public synchronized EClass getObjectType(CDOID id)
+ {
+ return objectTypes.get(id);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ creationTime = getRepository().getTimeStamp();
+
+ if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT)
+ {
+ setObjectIDTypes(Collections.singleton(CDOID.ObjectType.UUID));
+ }
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ revisions.clear();
+ branchInfos.clear();
+ commitInfos.clear();
+ objectTypes.clear();
+ properties.clear();
+ resourceNameFeature = null;
+ lastBranchID = 0;
+ lastLocalBranchID = 0;
+ super.doDeactivate();
+ }
+
+ @Override
+ protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing)
+ {
+ // Pooling of store accessors not supported
+ return null;
+ }
+
+ @Override
+ protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing)
+ {
+ // Pooling of store accessors not supported
+ return null;
+ }
+
+ private Object getListKey(CDOID id, CDOBranch branch)
+ {
+ if (getRevisionParallelism() == RevisionParallelism.NONE)
+ {
+ return id;
+ }
+
+ return new ListKey(id, branch);
+ }
+
+ private CDOBranch getBranch(Object key)
+ {
+ if (key instanceof ListKey)
+ {
+ return ((ListKey)key).getBranch();
+ }
+
+ return getRepository().getBranchManager().getMainBranch();
+ }
+
+ private int getHighestVersion(List<InternalCDORevision> list)
+ {
+ int version = CDOBranchVersion.UNSPECIFIED_VERSION;
+ for (InternalCDORevision revision : list)
+ {
+ if (revision.getVersion() > version)
+ {
+ version = revision.getVersion();
+ }
+ }
+
+ return version;
+ }
+
+ private InternalCDORevision getRevisionByVersion(List<InternalCDORevision> list, int version)
+ {
+ for (InternalCDORevision revision : list)
+ {
+ if (revision.getVersion() == version)
+ {
+ return revision;
+ }
+ }
+
+ return null;
+ }
+
+ private InternalCDORevision getRevision(List<InternalCDORevision> list, CDOBranchPoint branchPoint)
+ {
+ long timeStamp = branchPoint.getTimeStamp();
+ for (InternalCDORevision revision : list)
+ {
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ if (!revision.isHistorical())
+ {
+ return revision;
+ }
+ }
+ else
+ {
+ if (revision.isValid(timeStamp))
+ {
+ return revision;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private void addRevision(List<InternalCDORevision> list, InternalCDORevision revision, boolean raw)
+ {
+ boolean resource = !(revision instanceof SyntheticCDORevision) && revision.isResource();
+ if (resource && resourceNameFeature == null)
+ {
+ resourceNameFeature = revision.getEClass().getEStructuralFeature(CDOModelConstants.RESOURCE_NODE_NAME_ATTRIBUTE);
+ }
+
+ if (!raw)
+ {
+ // Check version conflict
+ int version = revision.getVersion();
+ InternalCDORevision rev = getRevisionByVersion(list, version);
+ if (rev != null)
+ {
+ rev = getRevisionByVersion(list, version);
+ throw new IllegalStateException("Concurrent modification of " + rev.getEClass().getName() + "@" + rev.getID());
+ }
+
+ // Revise old revision
+ int oldVersion = version - 1;
+ if (oldVersion >= CDORevision.UNSPECIFIED_VERSION)
+ {
+ InternalCDORevision oldRevision = getRevisionByVersion(list, oldVersion);
+ if (oldRevision != null)
+ {
+ if (getRepository().isSupportingAudits())
+ {
+ oldRevision.setRevised(revision.getTimeStamp() - 1);
+ }
+ else
+ {
+ list.remove(oldRevision);
+ }
+ }
+ }
+
+ // Check duplicate resource
+ if (resource)
+ {
+ checkDuplicateResource(revision);
+ }
+ }
+
+ // Adjust the list
+ list.add(revision);
+ if (listLimit != UNLIMITED)
+ {
+ enforceListLimit(list);
+ }
+
+ CDOID id = revision.getID();
+ if (!objectTypes.containsKey(id))
+ {
+ objectTypes.put(id, revision.getEClass());
+ }
+ }
+
+ private void checkDuplicateResource(InternalCDORevision revision)
+ {
+ CDOID revisionFolder = (CDOID)revision.data().getContainerID();
+ String revisionName = (String)revision.data().get(resourceNameFeature, 0);
+
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+
+ CDOID resourceID = accessor.readResourceID(revisionFolder, revisionName, revision);
+ if (!CDOIDUtil.isNull(resourceID))
+ {
+ throw new IllegalStateException("Duplicate resource: name=" + revisionName + ", folderID=" + revisionFolder); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ private void enforceListLimit(List<InternalCDORevision> list)
+ {
+ while (list.size() > listLimit)
+ {
+ list.remove(0);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class ListKey
+ {
+ private CDOID id;
+
+ private CDOBranch branch;
+
+ public ListKey(CDOID id, CDOBranch branch)
+ {
+ this.id = id;
+ this.branch = branch;
+ }
+
+ public CDOID getID()
+ {
+ return id;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branch;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return id.hashCode() ^ branch.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (obj instanceof ListKey)
+ {
+ ListKey that = (ListKey)obj;
+ return ObjectUtil.equals(id, that.getID()) && ObjectUtil.equals(branch, that.getBranch());
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("{0}:{1}", id, branch.getID());
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class CommitInfo
+ {
+ private CDOBranch branch;
+
+ private long timeStamp;
+
+ private long previousTimeStamp;
+
+ private String userID;
+
+ private String comment;
+
+ public CommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment)
+ {
+ this.branch = branch;
+ this.timeStamp = timeStamp;
+ this.previousTimeStamp = previousTimeStamp;
+ this.userID = userID;
+ this.comment = comment;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branch;
+ }
+
+ public long getTimeStamp()
+ {
+ return timeStamp;
+ }
+
+ public void handle(InternalCDOCommitInfoManager manager, CDOCommitInfoHandler handler)
+ {
+ CDOCommitInfo commitInfo = manager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, null);
+ handler.handleCommitInfo(commitInfo);
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}]", branch, timeStamp, previousTimeStamp, userID,
+ comment);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java
new file mode 100644
index 0000000000..55da263437
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java
@@ -0,0 +1,502 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.internal.server.mem;
+
+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.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.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+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.server.IQueryContext;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2;
+import org.eclipse.emf.cdo.server.IStoreAccessor.Raw;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor;
+
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+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.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Simon McDuff
+ */
+public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking2
+{
+ private final IQueryHandler testQueryHandler = new IQueryHandler()
+ {
+ public void executeQuery(CDOQueryInfo info, IQueryContext queryContext)
+ {
+ List<Object> filters = new ArrayList<Object>();
+ Object context = info.getParameters().get("context"); //$NON-NLS-1$
+ Long sleep = (Long)info.getParameters().get("sleep"); //$NON-NLS-1$
+ if (context != null)
+ {
+ if (context instanceof EClass)
+ {
+ final EClass eClass = (EClass)context;
+ filters.add(new Object()
+ {
+ @Override
+ public int hashCode()
+ {
+ return eClass.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ InternalCDORevision revision = (InternalCDORevision)obj;
+ return revision.getEClass().equals(eClass);
+ }
+ });
+ }
+ }
+
+ for (InternalCDORevision revision : getStore().getCurrentRevisions())
+ {
+ if (sleep != null)
+ {
+ try
+ {
+ Thread.sleep(sleep);
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+
+ boolean valid = true;
+
+ for (Object filter : filters)
+ {
+ if (!filter.equals(revision))
+ {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid)
+ {
+ if (!queryContext.addResult(revision))
+ {
+ // No more results allowed
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ private List<InternalCDORevision> newRevisions = new ArrayList<InternalCDORevision>();
+
+ public MEMStoreAccessor(MEMStore store, ISession session)
+ {
+ super(store, session);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public MEMStoreAccessor(MEMStore store, ITransaction transaction)
+ {
+ super(store, transaction);
+ }
+
+ @Override
+ public MEMStore getStore()
+ {
+ return (MEMStore)super.getStore();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public MEMStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature)
+ {
+ return new MEMStoreChunkReader(this, revision, feature);
+ }
+
+ public Collection<InternalCDOPackageUnit> readPackageUnits()
+ {
+ return Collections.emptySet();
+ }
+
+ public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
+ {
+ return getStore().createBranch(branchID, branchInfo);
+ }
+
+ public BranchInfo loadBranch(int branchID)
+ {
+ return getStore().loadBranch(branchID);
+ }
+
+ public SubBranchInfo[] loadSubBranches(int branchID)
+ {
+ return getStore().loadSubBranches(branchID);
+ }
+
+ public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler)
+ {
+ return getStore().loadBranches(startID, endID, branchHandler);
+ }
+
+ public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
+ {
+ getStore().loadCommitInfos(branch, startTime, endTime, handler);
+ }
+
+ public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments)
+ {
+ return getStore().readChangeSet(segments);
+ }
+
+ public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
+ CDORevisionCacheAdder cache)
+ {
+ return getStore().getRevision(id, branchPoint);
+ }
+
+ public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
+ CDORevisionCacheAdder cache)
+ {
+ return getStore().getRevisionByVersion(id, branchVersion);
+ }
+
+ public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler)
+ {
+ getStore().handleRevisions(eClass, branch, timeStamp, exactTime, handler);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ protected void doCommit(OMMonitor monitor)
+ {
+ // Do nothing
+ }
+
+ @Override
+ public void doWrite(InternalCommitContext context, OMMonitor monitor)
+ {
+ MEMStore store = getStore();
+ synchronized (store)
+ {
+ super.doWrite(context, monitor);
+ }
+ }
+
+ @Override
+ protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
+ String comment, OMMonitor monitor)
+ {
+ getStore().addCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment);
+ }
+
+ @Override
+ protected void doRollback(CommitContext context)
+ {
+ MEMStore store = getStore();
+ synchronized (store)
+ {
+ for (InternalCDORevision revision : newRevisions)
+ {
+ store.rollbackRevision(revision);
+ }
+ }
+ }
+
+ public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ // Do nothing
+ }
+
+ @Override
+ protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor)
+ {
+ for (InternalCDORevision revision : revisions)
+ {
+ writeRevision(revision);
+ }
+ }
+
+ protected void writeRevision(InternalCDORevision revision)
+ {
+ newRevisions.add(revision);
+ getStore().addRevision(revision, false);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created,
+ OMMonitor monitor)
+ {
+ for (InternalCDORevisionDelta revisionDelta : revisionDeltas)
+ {
+ writeRevisionDelta(revisionDelta, branch, created);
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, CDOBranch branch, long created)
+ {
+ CDOID id = revisionDelta.getID();
+ CDOBranchVersion version = revisionDelta.getBranch().getVersion(revisionDelta.getVersion());
+ InternalCDORevision revision = getStore().getRevisionByVersion(id, version);
+ if (revision.getVersion() != revisionDelta.getVersion())
+ {
+ throw new ConcurrentModificationException("Trying to update object " + id //$NON-NLS-1$
+ + " that was already modified"); //$NON-NLS-1$
+ }
+
+ InternalCDORevision newRevision = revision.copy();
+ newRevision.adjustForCommit(branch, created);
+
+ revisionDelta.apply(newRevision);
+ writeRevision(newRevision);
+ }
+
+ @Override
+ protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor)
+ {
+ for (CDOID id : detachedObjects)
+ {
+ detachObject(id, branch, timeStamp);
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected void detachObject(CDOID id, CDOBranch branch, long timeStamp)
+ {
+ getStore().detachObject(id, branch, timeStamp);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void queryResources(QueryResourcesContext context)
+ {
+ getStore().queryResources(context);
+ }
+
+ public void queryXRefs(QueryXRefsContext context)
+ {
+ getStore().queryXRefs(context);
+ }
+
+ public IQueryHandler getQueryHandler(CDOQueryInfo info)
+ {
+ if ("TEST".equals(info.getQueryLanguage())) //$NON-NLS-1$
+ {
+ return testQueryHandler;
+ }
+
+ return null;
+ }
+
+ public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ getStore().rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime);
+ }
+
+ public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ getStore().rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor);
+ }
+
+ public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ writePackageUnits(packageUnits, monitor);
+ }
+
+ public void rawStore(InternalCDORevision revision, OMMonitor monitor)
+ {
+ getStore().addRevision(revision, true);
+ }
+
+ 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();
+
+ // getStore().rawDelete(id, version, branch);
+ }
+
+ public void rawCommit(double commitWork, OMMonitor monitor)
+ {
+ // Do nothing
+ }
+
+ public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ return getStore().createLockArea(userID, branchPoint, readOnly, locks);
+ }
+
+ public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ return getStore().createLockArea(durableLockingID, userID, branchPoint, readOnly, locks);
+ }
+
+ public void updateLockArea(LockArea lockArea)
+ {
+ getStore().updateLockArea(lockArea);
+ }
+
+ public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
+ {
+ return getStore().getLockArea(durableLockingID);
+ }
+
+ public void getLockAreas(String userIDPrefix, Handler handler)
+ {
+ getStore().getLockAreas(userIDPrefix, handler);
+ }
+
+ public void deleteLockArea(String durableLockingID)
+ {
+ getStore().deleteLockArea(durableLockingID);
+ }
+
+ public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock)
+ {
+ getStore().lock(durableLockingID, type, objectsToLock);
+ }
+
+ public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock)
+ {
+ getStore().unlock(durableLockingID, type, objectsToUnlock);
+ }
+
+ public void unlock(String durableLockingID)
+ {
+ getStore().unlock(durableLockingID);
+ }
+
+ public void queryLobs(List<byte[]> ids)
+ {
+ getStore().queryLobs(ids);
+ }
+
+ public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
+ {
+ getStore().handleLobs(fromTime, toTime, handler);
+ }
+
+ public void loadLob(byte[] id, OutputStream out) throws IOException
+ {
+ getStore().loadLob(id, out);
+ }
+
+ @Override
+ protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException
+ {
+ getStore().writeBlob(id, size, inputStream);
+ }
+
+ @Override
+ protected void writeClob(byte[] id, long size, Reader reader) throws IOException
+ {
+ getStore().writeClob(id, size, reader);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ // Do nothing
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ newRevisions.clear();
+ }
+
+ @Override
+ protected void doPassivate() throws Exception
+ {
+ // Pooling of store accessors not supported
+ }
+
+ @Override
+ protected void doUnpassivate() throws Exception
+ {
+ // Pooling of store accessors not supported
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java
new file mode 100644
index 0000000000..82e6c634a1
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server.mem;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+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.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.server.StoreChunkReader;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.List;
+
+/**
+ * @author Simon McDuff
+ */
+public class MEMStoreChunkReader extends StoreChunkReader
+{
+ /**
+ * @since 2.0
+ */
+ public MEMStoreChunkReader(IStoreAccessor accessor, CDORevision revision, EStructuralFeature feature)
+ {
+ super(accessor, revision, feature);
+ }
+
+ public List<Chunk> executeRead()
+ {
+ CDOID id = getRevision().getID();
+ CDOBranchVersion branchVersion = getRevision();
+
+ MEMStore store = getAccessor().getStore();
+ List<Chunk> chunks = getChunks();
+ for (Chunk chunk : chunks)
+ {
+ int startIndex = chunk.getStartIndex();
+ InternalCDORevision revision = store.getRevisionByVersion(id, branchVersion);
+ for (int i = 0; i < chunk.size(); i++)
+ {
+ Object object = revision.get(getFeature(), startIndex + i);
+ chunk.add(i, object);
+ }
+ }
+
+ return chunks;
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public MEMStoreAccessor getAccessor()
+ {
+ return (MEMStoreAccessor)super.getAccessor();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java
new file mode 100644
index 0000000000..dd48174921
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java
@@ -0,0 +1,39 @@
+/**
+ * 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:
+ * Simon McDuff - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server.mem;
+
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStoreFactory;
+
+import org.w3c.dom.Element;
+
+import java.util.Map;
+
+/**
+ * @author Simon McDuff
+ */
+public class MEMStoreFactory implements IStoreFactory
+{
+ public MEMStoreFactory()
+ {
+ }
+
+ public String getStoreType()
+ {
+ return MEMStore.TYPE;
+ }
+
+ public IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig)
+ {
+ return new MEMStore();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java
new file mode 100644
index 0000000000..294f584138
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java
@@ -0,0 +1,41 @@
+/**
+ * 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:
+ * Victor Roldan Betancort - initial API and implementation
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.internal.server.messages;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * @author Victor Roldan Betancort
+ */
+public class Messages
+{
+ private static final String BUNDLE_NAME = "org.eclipse.emf.cdo.internal.server.messages.messages"; //$NON-NLS-1$
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+ private Messages()
+ {
+ }
+
+ public static String getString(String key)
+ {
+ try
+ {
+ return RESOURCE_BUNDLE.getString(key);
+ }
+ catch (MissingResourceException e)
+ {
+ return '!' + key + '!';
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties
new file mode 100644
index 0000000000..7dc1b9ef44
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties
@@ -0,0 +1,16 @@
+# 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:
+# Victor Roldan Betancort - initial API and implementation
+# Eike Stepper - maintenance
+
+CDOServerApplication.1=CDO server starting
+CDOServerApplication.6=CDO server started
+CDOServerApplication.7=CDO server stopping
+CDOServerApplication.8=CDO server stopped
+CDOServerApplication.5=CDO server configuration not found:
+CDOServerApplication.3=No CDO repositories configured:
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java
new file mode 100644
index 0000000000..dcfab829f1
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java
@@ -0,0 +1,131 @@
+/**
+ * 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.internal.server.syncing;
+
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalFailoverParticipant;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+/**
+ * @author Eike Stepper
+ */
+public class FailoverParticipant extends SynchronizableRepository implements InternalFailoverParticipant
+{
+ private boolean allowBackupCommits;
+
+ public FailoverParticipant()
+ {
+ setState(OFFLINE);
+ }
+
+ public boolean isAllowBackupCommits()
+ {
+ return allowBackupCommits;
+ }
+
+ public void setAllowBackupCommits(boolean allowBackupCommits)
+ {
+ this.allowBackupCommits = allowBackupCommits;
+ }
+
+ @Override
+ public void setType(Type type)
+ {
+ checkArg(type == MASTER || type == BACKUP, "Type must be MASTER or BACKUP");
+ super.setType(type);
+ }
+
+ @Override
+ protected void changingType(Type oldType, Type newType)
+ {
+ if (isActive())
+ {
+ if (newType == MASTER)
+ {
+ // Switch off synchronizer
+ doStopSynchronization();
+ }
+ else
+ {
+ // Bug 312879
+ setReplicationCountersToLatest();
+
+ // Switch on synchronizer
+ doStartSynchronization();
+ }
+ }
+
+ super.changingType(oldType, newType);
+ }
+
+ @Override
+ protected void initRootResource()
+ {
+ if (getType() == BACKUP)
+ {
+ super.initRootResource();
+ }
+ else
+ {
+ doInitRootResource();
+ }
+ }
+
+ protected void doStartSynchronization()
+ {
+ super.startSynchronization();
+ }
+
+ protected void doStopSynchronization()
+ {
+ super.stopSynchronization();
+ }
+
+ @Override
+ protected void startSynchronization()
+ {
+ if (getType() == BACKUP)
+ {
+ doStartSynchronization();
+ }
+ }
+
+ @Override
+ protected void stopSynchronization()
+ {
+ if (getType() == BACKUP)
+ {
+ doStopSynchronization();
+ }
+ }
+
+ @Override
+ public InternalCommitContext createCommitContext(InternalTransaction transaction)
+ {
+ if (getType() == BACKUP)
+ {
+ if (getState() != ONLINE)
+ {
+ throw new IllegalStateException("Backup repository is not online");
+ }
+
+ if (allowBackupCommits || transaction.getSession() == getReplicatorSession())
+ {
+ return createWriteThroughCommitContext(transaction);
+ }
+
+ throw new IllegalStateException(
+ "Only the repository synchronizer is allowed to commit transactions to a backup repository");
+ }
+
+ return createNormalCommitContext(transaction);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java
new file mode 100644
index 0000000000..d551fc52e9
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.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.internal.server.syncing;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOChangeKind;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitData;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.internal.server.TransactionCommitContext;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+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.CDOChangeKindCache;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+import org.eclipse.net4j.util.collection.IndexedList;
+import org.eclipse.net4j.util.om.monitor.Monitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ */
+public class OfflineClone extends SynchronizableRepository
+{
+ public OfflineClone()
+ {
+ setState(OFFLINE);
+ }
+
+ @Override
+ public final Type getType()
+ {
+ return CLONE;
+ }
+
+ @Override
+ public final void setType(Type type)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public InternalCommitContext createCommitContext(InternalTransaction transaction)
+ {
+ CDOBranch branch = transaction.getBranch();
+ if (branch.isLocal())
+ {
+ return createNormalCommitContext(transaction);
+ }
+
+ if (getState() != ONLINE)
+ {
+ return createBranchingCommitContext(transaction, branch);
+ }
+
+ return createWriteThroughCommitContext(transaction);
+ }
+
+ protected InternalCommitContext createBranchingCommitContext(InternalTransaction transaction, CDOBranch branch)
+ {
+ long[] times = createCommitTimeStamp(new Monitor());
+ CDOBranch offlineBranch = createOfflineBranch(branch, times[0] - 1L);
+ transaction.setBranchPoint(offlineBranch.getHead());
+ return new BranchingCommitContext(transaction, times);
+ }
+
+ protected CDOBranch createOfflineBranch(CDOBranch baseBranch, long baseTimeStamp)
+ {
+ try
+ {
+ StoreThreadLocal.setSession(getReplicatorSession());
+ InternalCDOBranchManager branchManager = getBranchManager();
+ return branchManager.createBranch(NEW_LOCAL_BRANCH,
+ "Offline-" + baseTimeStamp, (InternalCDOBranch)baseBranch, baseTimeStamp); //$NON-NLS-1$
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ protected static final class CommitContextData implements CDOCommitData
+ {
+ private InternalCommitContext commitContext;
+
+ private CDOChangeKindCache changeKindCache;
+
+ public CommitContextData(InternalCommitContext commitContext)
+ {
+ this.commitContext = commitContext;
+ }
+
+ public boolean isEmpty()
+ {
+ return false;
+ }
+
+ public CDOChangeSetData copy()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void merge(CDOChangeSetData changeSetData)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public List<CDOPackageUnit> getNewPackageUnits()
+ {
+ final InternalCDOPackageUnit[] newPackageUnits = commitContext.getNewPackageUnits();
+ return new IndexedList<CDOPackageUnit>()
+ {
+ @Override
+ public CDOPackageUnit get(int index)
+ {
+ return newPackageUnits[index];
+ }
+
+ @Override
+ public int size()
+ {
+ return newPackageUnits.length;
+ }
+ };
+ }
+
+ public List<CDOIDAndVersion> getNewObjects()
+ {
+ final InternalCDORevision[] newObjects = commitContext.getNewObjects();
+ return new IndexedList<CDOIDAndVersion>()
+ {
+ @Override
+ public CDOIDAndVersion get(int index)
+ {
+ return newObjects[index];
+ }
+
+ @Override
+ public int size()
+ {
+ return newObjects.length;
+ }
+ };
+ }
+
+ public List<CDORevisionKey> getChangedObjects()
+ {
+ final InternalCDORevisionDelta[] changedObjects = commitContext.getDirtyObjectDeltas();
+ return new IndexedList<CDORevisionKey>()
+ {
+ @Override
+ public CDORevisionKey get(int index)
+ {
+ return changedObjects[index];
+ }
+
+ @Override
+ public int size()
+ {
+ return changedObjects.length;
+ }
+ };
+ }
+
+ public List<CDOIDAndVersion> getDetachedObjects()
+ {
+ final CDOID[] detachedObjects = commitContext.getDetachedObjects();
+ return new IndexedList<CDOIDAndVersion>()
+ {
+ @Override
+ public CDOIDAndVersion get(int index)
+ {
+ return CDOIDUtil.createIDAndVersion(detachedObjects[index], CDOBranchVersion.UNSPECIFIED_VERSION);
+ }
+
+ @Override
+ public int size()
+ {
+ return detachedObjects.length;
+ }
+ };
+ }
+
+ public synchronized Map<CDOID, CDOChangeKind> getChangeKinds()
+ {
+ if (changeKindCache == null)
+ {
+ changeKindCache = new CDOChangeKindCache(this);
+ }
+
+ return changeKindCache;
+ }
+
+ public CDOChangeKind getChangeKind(CDOID id)
+ {
+ return getChangeKinds().get(id);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ protected final class BranchingCommitContext extends TransactionCommitContext
+ {
+ private long[] times;
+
+ public BranchingCommitContext(InternalTransaction transaction, long[] times)
+ {
+ super(transaction);
+ this.times = times;
+ }
+
+ @Override
+ protected void lockObjects() throws InterruptedException
+ {
+ // Do nothing
+ }
+
+ @Override
+ protected long[] createTimeStamp(OMMonitor monitor)
+ {
+ return times;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java
new file mode 100644
index 0000000000..e69b5a6858
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java
@@ -0,0 +1,162 @@
+/**
+ * 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.internal.server.syncing;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.internal.server.TransactionCommitContext;
+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.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.List;
+
+/**
+ * TODO Optimize createCommitInfo()
+ *
+ * @author Eike Stepper
+ */
+public final class ReplicatorCommitContext extends TransactionCommitContext
+{
+ private final CDOCommitInfo commitInfo;
+
+ public ReplicatorCommitContext(InternalTransaction transaction, CDOCommitInfo commitInfo)
+ {
+ super(transaction);
+ this.commitInfo = commitInfo;
+
+ setCommitComment(commitInfo.getComment());
+
+ InternalCDOPackageUnit[] newPackageUnits = getNewPackageUnits(commitInfo, getPackageRegistry());
+ setNewPackageUnits(newPackageUnits);
+
+ InternalCDORevision[] newObjects = getNewObjects(commitInfo);
+ setNewObjects(newObjects);
+
+ InternalCDORevisionDelta[] dirtyObjectDeltas = getDirtyObjectDeltas(commitInfo);
+ setDirtyObjectDeltas(dirtyObjectDeltas);
+
+ CDOID[] detachedObjects = getDetachedObjects(commitInfo);
+ setDetachedObjects(detachedObjects);
+ }
+
+ @Override
+ public String getUserID()
+ {
+ return commitInfo.getUserID();
+ }
+
+ @Override
+ protected long[] createTimeStamp(OMMonitor monitor)
+ {
+ InternalRepository repository = getTransaction().getSession().getManager().getRepository();
+
+ long commitTimeStamp = commitInfo.getTimeStamp();
+ if (commitTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ commitTimeStamp = repository.getTimeStamp();
+ }
+
+ return repository.forceCommitTimeStamp(commitInfo.getTimeStamp(), monitor);
+ }
+
+ @Override
+ protected void adjustForCommit()
+ {
+ // Do nothing
+ }
+
+ @Override
+ public void applyIDMappings(OMMonitor monitor)
+ {
+ monitor.done();
+ }
+
+ @Override
+ protected void lockObjects() throws InterruptedException
+ {
+ // Do nothing
+ }
+
+ @Override
+ protected void checkXRefs()
+ {
+ // Do nothing
+ }
+
+ private static InternalCDOPackageUnit[] getNewPackageUnits(CDOCommitInfo commitInfo,
+ InternalCDOPackageRegistry packageRegistry)
+ {
+ List<CDOPackageUnit> list = commitInfo.getNewPackageUnits();
+ InternalCDOPackageUnit[] result = new InternalCDOPackageUnit[list.size()];
+
+ int i = 0;
+ for (CDOPackageUnit packageUnit : list)
+ {
+ result[i] = (InternalCDOPackageUnit)packageUnit;
+ packageRegistry.putPackageUnit(result[i]);
+ ++i;
+ }
+
+ return result;
+ }
+
+ private static InternalCDORevision[] getNewObjects(CDOCommitInfo commitInfo)
+ {
+ List<CDOIDAndVersion> list = commitInfo.getNewObjects();
+ InternalCDORevision[] result = new InternalCDORevision[list.size()];
+
+ int i = 0;
+ for (CDOIDAndVersion revision : list)
+ {
+ result[i++] = (InternalCDORevision)revision;
+ }
+
+ return result;
+ }
+
+ private static InternalCDORevisionDelta[] getDirtyObjectDeltas(CDOCommitInfo commitInfo)
+ {
+ List<CDORevisionKey> list = commitInfo.getChangedObjects();
+ InternalCDORevisionDelta[] result = new InternalCDORevisionDelta[list.size()];
+
+ int i = 0;
+ for (CDORevisionKey delta : list)
+ {
+ result[i++] = (InternalCDORevisionDelta)delta;
+ }
+
+ return result;
+ }
+
+ private static CDOID[] getDetachedObjects(CDOCommitInfo commitInfo)
+ {
+ List<CDOIDAndVersion> list = commitInfo.getDetachedObjects();
+ CDOID[] result = new CDOID[list.size()];
+
+ int i = 0;
+ for (CDOIDAndVersion key : list)
+ {
+ result[i++] = key.getID();
+ }
+
+ return result;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java
new file mode 100644
index 0000000000..dfb44b894a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java
@@ -0,0 +1,650 @@
+/**
+ * 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.internal.server.syncing;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode;
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchCreatedEvent;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.internal.common.revision.NOOPRevisionCache;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.session.CDOSessionConfiguration;
+import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory;
+import org.eclipse.emf.cdo.session.CDOSessionInvalidationEvent;
+import org.eclipse.emf.cdo.session.CDOSessionLocksChangedEvent;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache;
+import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer;
+import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository;
+
+import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
+import org.eclipse.net4j.util.concurrent.QueueRunner;
+import org.eclipse.net4j.util.event.IEvent;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.ILifecycleEvent;
+import org.eclipse.net4j.util.om.monitor.NotifyingMonitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.PriorityBlockingQueue;
+
+/**
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public class RepositorySynchronizer extends QueueRunner implements InternalRepositorySynchronizer
+{
+ public static final int DEFAULT_RETRY_INTERVAL = 3;
+
+ public static final int DEFAULT_MAX_RECOMMITS = 10;
+
+ public static final int DEFAULT_RECOMMIT_INTERVAL = 1;
+
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REPOSITORY, RepositorySynchronizer.class);
+
+ private static final Integer CONNECT_PRIORITY = 0;
+
+ private static final Integer REPLICATE_PRIORITY = 1;
+
+ private static final Integer BRANCH_PRIORITY = 2;
+
+ private static final Integer COMMIT_PRIORITY = 3;
+
+ private static final Integer LOCKS_PRIORITY = COMMIT_PRIORITY;
+
+ private int retryInterval = DEFAULT_RETRY_INTERVAL;
+
+ private Object connectLock = new Object();
+
+ private InternalSynchronizableRepository localRepository;
+
+ /**
+ * The session that connects to the master; used passively to receive notifications, and actively to request
+ * replications.
+ */
+ private InternalCDOSession remoteSession;
+
+ private RemoteSessionListener remoteSessionListener = new RemoteSessionListener();
+
+ private CDOSessionConfigurationFactory remoteSessionConfigurationFactory;
+
+ private boolean rawReplication;
+
+ private int maxRecommits = DEFAULT_MAX_RECOMMITS;
+
+ private int recommitInterval = DEFAULT_RECOMMIT_INTERVAL;
+
+ private Timer recommitTimer;
+
+ public RepositorySynchronizer()
+ {
+ setDaemon(true);
+ }
+
+ public int getRetryInterval()
+ {
+ return retryInterval;
+ }
+
+ public void setRetryInterval(int retryInterval)
+ {
+ this.retryInterval = retryInterval;
+ }
+
+ public InternalSynchronizableRepository getLocalRepository()
+ {
+ return localRepository;
+ }
+
+ public void setLocalRepository(InternalSynchronizableRepository localRepository)
+ {
+ checkInactive();
+ this.localRepository = localRepository;
+ }
+
+ public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory()
+ {
+ return remoteSessionConfigurationFactory;
+ }
+
+ public void setRemoteSessionConfigurationFactory(CDOSessionConfigurationFactory masterSessionConfigurationFactory)
+ {
+ checkArg(masterSessionConfigurationFactory, "remoteSessionConfigurationFactory"); //$NON-NLS-1$
+ remoteSessionConfigurationFactory = masterSessionConfigurationFactory;
+ }
+
+ public InternalCDOSession getRemoteSession()
+ {
+ return remoteSession;
+ }
+
+ public boolean isRawReplication()
+ {
+ return rawReplication;
+ }
+
+ public void setRawReplication(boolean rawReplication)
+ {
+ checkInactive();
+ this.rawReplication = rawReplication;
+ }
+
+ public int getMaxRecommits()
+ {
+ return maxRecommits;
+ }
+
+ public void setMaxRecommits(int maxRecommits)
+ {
+ this.maxRecommits = maxRecommits;
+ }
+
+ public int getRecommitInterval()
+ {
+ return recommitInterval;
+ }
+
+ public void setRecommitInterval(int recommitInterval)
+ {
+ this.recommitInterval = recommitInterval;
+ }
+
+ @Override
+ protected String getThreadName()
+ {
+ return "RepositorySynchronizer"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected BlockingQueue<Runnable> createQueue()
+ {
+ return new PriorityBlockingQueue<Runnable>();
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ checkState(remoteSessionConfigurationFactory, "remoteSessionConfigurationFactory"); //$NON-NLS-1$
+ checkState(localRepository, "localRepository"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doAfterActivate() throws Exception
+ {
+ super.doAfterActivate();
+ scheduleConnect();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ if (recommitTimer != null)
+ {
+ recommitTimer.cancel();
+ recommitTimer = null;
+ }
+
+ if (remoteSession != null)
+ {
+ remoteSession.removeListener(remoteSessionListener);
+ remoteSession.getBranchManager().removeListener(remoteSessionListener);
+ remoteSession.close();
+ remoteSession = null;
+ }
+
+ super.doDeactivate();
+ }
+
+ private void handleDisconnect()
+ {
+ OM.LOG.info("Disconnected from master.");
+ if (localRepository.getRootResourceID() == null)
+ {
+ localRepository.setState(CDOCommonRepository.State.INITIAL);
+ }
+ else
+ {
+ localRepository.setState(CDOCommonRepository.State.OFFLINE);
+ }
+
+ remoteSession.getBranchManager().removeListener(remoteSessionListener);
+ remoteSession.removeListener(remoteSessionListener);
+ remoteSession = null;
+
+ reconnect();
+ }
+
+ private void reconnect()
+ {
+ clearQueue();
+ if (isActive())
+ {
+ scheduleConnect();
+ }
+ }
+
+ private void scheduleConnect()
+ {
+ synchronized (connectLock)
+ {
+ if (localRepository.getState().isConnected())
+ {
+ return;
+ }
+
+ if (isActive())
+ {
+ addWork(new ConnectRunnable());
+ }
+ }
+ }
+
+ private void scheduleReplicate()
+ {
+ if (isActive())
+ {
+ addWork(new ReplicateRunnable());
+ }
+ }
+
+ private void sleepRetryInterval()
+ {
+ long end = System.currentTimeMillis() + 1000L * retryInterval;
+
+ for (;;)
+ {
+ long now = System.currentTimeMillis();
+ if (now >= end || !isActive())
+ {
+ break;
+ }
+
+ ConcurrencyUtil.sleep(Math.min(100L, end - now));
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class RemoteSessionListener implements IListener
+ {
+ public void notifyEvent(IEvent event)
+ {
+ if (!isActive())
+ {
+ return;
+ }
+
+ if (event instanceof CDOBranchCreatedEvent)
+ {
+ CDOBranchCreatedEvent e = (CDOBranchCreatedEvent)event;
+ addWork(new BranchRunnable(e.getBranch()));
+ }
+ else if (event instanceof CDOSessionInvalidationEvent)
+ {
+ CDOSessionInvalidationEvent e = (CDOSessionInvalidationEvent)event;
+ if (e.isRemote())
+ {
+ addWork(new CommitRunnable(e));
+ }
+ }
+ else if (event instanceof CDOSessionLocksChangedEvent)
+ {
+ CDOSessionLocksChangedEvent e = (CDOSessionLocksChangedEvent)event;
+ addWork(new LocksRunnable(e));
+ }
+ else if (event instanceof ILifecycleEvent)
+ {
+ ILifecycleEvent e = (ILifecycleEvent)event;
+ if (e.getKind() == ILifecycleEvent.Kind.DEACTIVATED && e.getSource() == remoteSession)
+ {
+ handleDisconnect();
+ }
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static abstract class QueueRunnable implements Runnable, Comparable<QueueRunnable>
+ {
+ public int compareTo(QueueRunnable o)
+ {
+ return getPriority().compareTo(o.getPriority());
+ }
+
+ protected abstract Integer getPriority();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class ConnectRunnable extends QueueRunnable
+ {
+ public ConnectRunnable()
+ {
+ }
+
+ public void run()
+ {
+ synchronized (connectLock)
+ {
+ checkActive();
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Connecting to master..."); //$NON-NLS-1$
+ }
+
+ try
+ {
+ CDOSessionConfiguration masterConfiguration = remoteSessionConfigurationFactory.createSessionConfiguration();
+ masterConfiguration.setPassiveUpdateMode(PassiveUpdateMode.ADDITIONS);
+ masterConfiguration.setLockNotificationMode(LockNotificationMode.ALWAYS);
+
+ remoteSession = (InternalCDOSession)masterConfiguration.openSession();
+
+ ensureNOOPRevisionCache();
+ setRootResourceID();
+ }
+ catch (Exception ex)
+ {
+ if (isActive())
+ {
+ OM.LOG.warn("Connection attempt failed. Retrying in " + retryInterval + " seconds...", ex);
+ sleepRetryInterval();
+ reconnect();
+ }
+
+ return;
+ }
+
+ OM.LOG.info("Connected to master.");
+ scheduleReplicate();
+
+ remoteSession.addListener(remoteSessionListener);
+ remoteSession.getBranchManager().addListener(remoteSessionListener);
+ }
+ }
+
+ @Override
+ protected Integer getPriority()
+ {
+ return CONNECT_PRIORITY;
+ }
+
+ private void setRootResourceID()
+ {
+ if (localRepository.getState() == CDOCommonRepository.State.INITIAL)
+ {
+ CDOID rootResourceID = remoteSession.getRepositoryInfo().getRootResourceID();
+ localRepository.setRootResourceID(rootResourceID);
+ localRepository.setState(CDOCommonRepository.State.OFFLINE);
+ }
+ }
+
+ private void ensureNOOPRevisionCache()
+ {
+ // Ensure that incoming revisions are not cached!
+ InternalCDORevisionCache cache = remoteSession.getRevisionManager().getCache();
+ if (!(cache instanceof NOOPRevisionCache))
+ {
+ throw new IllegalStateException("Master session does not use a NOOPRevisionCache: "
+ + cache.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class ReplicateRunnable extends QueueRunnable
+ {
+ public ReplicateRunnable()
+ {
+ }
+
+ public void run()
+ {
+ try
+ {
+ checkActive();
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Synchronizing with master..."); //$NON-NLS-1$
+ }
+
+ localRepository.setState(CDOCommonRepository.State.SYNCING);
+
+ CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol();
+ OMMonitor monitor = new NotifyingMonitor("Synchronizing", getListeners());
+
+ if (isRawReplication())
+ {
+ sessionProtocol.replicateRepositoryRaw(localRepository, monitor);
+ }
+ else
+ {
+ sessionProtocol.replicateRepository(localRepository, monitor);
+ }
+
+ localRepository.setState(CDOCommonRepository.State.ONLINE);
+ OM.LOG.info("Synchronized with master.");
+ }
+ catch (RuntimeException ex)
+ {
+ if (isActive())
+ {
+ OM.LOG.warn("Replication attempt failed. Retrying in " + retryInterval + " seconds...", ex);
+ sleepRetryInterval();
+ handleDisconnect();
+ }
+ }
+ }
+
+ @Override
+ protected Integer getPriority()
+ {
+ return REPLICATE_PRIORITY;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class BranchRunnable extends QueueRunnable
+ {
+ private CDOBranch branch;
+
+ public BranchRunnable(CDOBranch branch)
+ {
+ this.branch = branch;
+ }
+
+ public void run()
+ {
+ localRepository.handleBranch(branch);
+ }
+
+ @Override
+ public int compareTo(QueueRunnable o)
+ {
+ int result = super.compareTo(o);
+ if (result == 0)
+ {
+ result = branch.compareTo(((BranchRunnable)o).branch);
+ }
+
+ return result;
+ }
+
+ @Override
+ protected Integer getPriority()
+ {
+ return BRANCH_PRIORITY;
+ }
+ }
+
+ private final class CommitRunnable extends RetryingRunnable
+ {
+ private CDOCommitInfo commitInfo;
+
+ public CommitRunnable(CDOCommitInfo commitInfo)
+ {
+ this.commitInfo = commitInfo;
+ }
+
+ @Override
+ protected void doRun()
+ {
+ localRepository.handleCommitInfo(commitInfo);
+ }
+
+ @Override
+ public int compareTo(QueueRunnable o)
+ {
+ int result = super.compareTo(o);
+ if (result == 0)
+ {
+ Long timeStamp = commitInfo.getTimeStamp();
+ Long timeStamp2 = ((CommitRunnable)o).commitInfo.getTimeStamp();
+ result = timeStamp < timeStamp2 ? -1 : timeStamp == timeStamp2 ? 0 : 1;
+ }
+
+ return result;
+ }
+
+ @Override
+ protected Integer getPriority()
+ {
+ return COMMIT_PRIORITY;
+ }
+
+ @Override
+ protected String getErrorMessage()
+ {
+ return "Replication of master commit failed:" + commitInfo;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private abstract class RetryingRunnable extends QueueRunnable
+ {
+ private List<Exception> failedRuns;
+
+ protected abstract void doRun();
+
+ protected abstract String getErrorMessage();
+
+ public void run()
+ {
+ try
+ {
+ doRun();
+ }
+ catch (Exception ex)
+ {
+ if (failedRuns == null)
+ {
+ failedRuns = new ArrayList<Exception>();
+ }
+
+ failedRuns.add(ex);
+ if (failedRuns.size() <= maxRecommits)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Replication of master commit failed. Trying again in {0} seconds...", recommitInterval); //$NON-NLS-1$
+ }
+
+ if (recommitTimer == null)
+ {
+ recommitTimer = new Timer("RecommitTimer-" + RepositorySynchronizer.this);
+ }
+
+ recommitTimer.schedule(new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ addWork(this);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error("CommitRunnableTask failed", ex);
+ }
+ }
+ }, recommitInterval * 1000L);
+ }
+ else
+ {
+ OM.LOG.error(getErrorMessage(), ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * @author Caspar De Groot
+ */
+ private final class LocksRunnable extends RetryingRunnable
+ {
+ private CDOLockChangeInfo lockChangeInfo;
+
+ public LocksRunnable(CDOLockChangeInfo lockChangeInfo)
+ {
+ this.lockChangeInfo = lockChangeInfo;
+ }
+
+ @Override
+ protected Integer getPriority()
+ {
+ return LOCKS_PRIORITY;
+ }
+
+ @Override
+ protected void doRun()
+ {
+ try
+ {
+ StoreThreadLocal.setSession(localRepository.getReplicatorSession());
+ localRepository.handleLockChangeInfo(lockChangeInfo);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ @Override
+ protected String getErrorMessage()
+ {
+ return "Replication of master lock changes failed:" + lockChangeInfo;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java
new file mode 100644
index 0000000000..2276dc9b99
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java
@@ -0,0 +1,788 @@
+/**
+ * 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.internal.server.syncing;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitData;
+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.CDOLob;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation;
+import org.eclipse.emf.cdo.common.lock.CDOLockOwner;
+import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.common.util.CDOException;
+import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl;
+import org.eclipse.emf.cdo.internal.server.Repository;
+import org.eclipse.emf.cdo.internal.server.TransactionCommitContext;
+import org.eclipse.emf.cdo.internal.server.syncing.OfflineClone.CommitContextData;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+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.InternalCDOCommitInfoManager;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
+import org.eclipse.emf.cdo.spi.server.InternalStore;
+import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository;
+import org.eclipse.emf.cdo.spi.server.InternalTransaction;
+import org.eclipse.emf.cdo.spi.server.InternalView;
+import org.eclipse.emf.cdo.spi.server.SyncingUtil;
+
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.Monitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.transaction.TransactionException;
+
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult;
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+/**
+ * TODO:
+ * <ul>
+ * <li>Handle new package units that had been committed during offline (testDisconnectAndCommitAndMergeWithNewPackages).
+ * <li>Make CDOIDs of new objects temporary when merging out of temp branch.
+ * <li>Provide custom branching strategies.
+ * <li>Consider non-auditing masters.
+ * <li>Test out-of-order commits.
+ * <li>Don't create branches table if branching not supported.
+ * <li>Implement raw replication for NUMERIC and DECIMAL.
+ * <li>Notify new branches during raw replication.
+ * </ul>
+ *
+ * @author Eike Stepper
+ */
+public abstract class SynchronizableRepository extends Repository.Default implements InternalSynchronizableRepository
+{
+ protected static final CDOCommonRepository.Type MASTER = CDOCommonRepository.Type.MASTER;
+
+ protected static final CDOCommonRepository.Type BACKUP = CDOCommonRepository.Type.BACKUP;
+
+ protected static final CDOCommonRepository.Type CLONE = CDOCommonRepository.Type.CLONE;
+
+ protected static final CDOCommonRepository.State INITIAL = CDOCommonRepository.State.INITIAL;
+
+ protected static final CDOCommonRepository.State OFFLINE = CDOCommonRepository.State.OFFLINE;
+
+ protected static final CDOCommonRepository.State SYNCING = CDOCommonRepository.State.SYNCING;
+
+ protected static final CDOCommonRepository.State ONLINE = CDOCommonRepository.State.ONLINE;
+
+ private static final String PROP_LAST_REPLICATED_BRANCH_ID = "org.eclipse.emf.cdo.server.lastReplicatedBranchID"; //$NON-NLS-1$
+
+ private static final String PROP_LAST_REPLICATED_COMMIT_TIME = "org.eclipse.emf.cdo.server.lastReplicatedCommitTime"; //$NON-NLS-1$
+
+ private static final String PROP_GRACEFULLY_SHUT_DOWN = "org.eclipse.emf.cdo.server.gracefullyShutDown"; //$NON-NLS-1$
+
+ private InternalRepositorySynchronizer synchronizer;
+
+ private InternalSession replicatorSession;
+
+ private int lastReplicatedBranchID = CDOBranch.MAIN_BRANCH_ID;
+
+ private long lastReplicatedCommitTime = CDOBranchPoint.UNSPECIFIED_DATE;
+
+ private int lastTransactionID;
+
+ private ReadLock writeThroughCommitLock;
+
+ private WriteLock handleCommitInfoLock;
+
+ public SynchronizableRepository()
+ {
+ ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
+ writeThroughCommitLock = rwLock.readLock();
+ handleCommitInfoLock = rwLock.writeLock();
+ }
+
+ public InternalRepositorySynchronizer getSynchronizer()
+ {
+ return synchronizer;
+ }
+
+ public void setSynchronizer(InternalRepositorySynchronizer synchronizer)
+ {
+ checkInactive();
+ this.synchronizer = synchronizer;
+ }
+
+ public InternalSession getReplicatorSession()
+ {
+ return replicatorSession;
+ }
+
+ @Override
+ public Object[] getElements()
+ {
+ List<Object> list = Arrays.asList(super.getElements());
+ list.add(synchronizer);
+ return list.toArray();
+ }
+
+ public int getLastReplicatedBranchID()
+ {
+ return lastReplicatedBranchID;
+ }
+
+ public void setLastReplicatedBranchID(int lastReplicatedBranchID)
+ {
+ if (this.lastReplicatedBranchID < lastReplicatedBranchID)
+ {
+ this.lastReplicatedBranchID = lastReplicatedBranchID;
+ }
+ }
+
+ public long getLastReplicatedCommitTime()
+ {
+ return lastReplicatedCommitTime;
+ }
+
+ public void setLastReplicatedCommitTime(long lastReplicatedCommitTime)
+ {
+ if (this.lastReplicatedCommitTime < lastReplicatedCommitTime)
+ {
+ this.lastReplicatedCommitTime = lastReplicatedCommitTime;
+ }
+ }
+
+ public String[] getLockAreaIDs()
+ {
+ try
+ {
+ StoreThreadLocal.setSession(replicatorSession);
+ final List<String> areaIDs = new LinkedList<String>();
+ getLockManager().getLockAreas(null, new LockArea.Handler()
+ {
+ public boolean handleLockArea(LockArea area)
+ {
+ areaIDs.add(area.getDurableLockingID());
+ return true;
+ }
+ });
+ return areaIDs.toArray(new String[areaIDs.size()]);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ public void handleBranch(CDOBranch branch)
+ {
+ if (branch.isLocal())
+ {
+ return;
+ }
+
+ int branchID = branch.getID();
+ String name = branch.getName();
+
+ CDOBranchPoint base = branch.getBase();
+ InternalCDOBranch baseBranch = (InternalCDOBranch)base.getBranch();
+ long baseTimeStamp = base.getTimeStamp();
+
+ InternalCDOBranchManager branchManager = getBranchManager();
+ branchManager.createBranch(branchID, name, baseBranch, baseTimeStamp);
+ setLastReplicatedBranchID(branchID);
+ }
+
+ public void handleCommitInfo(CDOCommitInfo commitInfo)
+ {
+ CDOBranch branch = commitInfo.getBranch();
+ if (branch.isLocal())
+ {
+ return;
+ }
+
+ long timeStamp = commitInfo.getTimeStamp();
+ CDOBranchPoint head = branch.getHead();
+
+ InternalTransaction transaction = replicatorSession.openTransaction(++lastTransactionID, head);
+ ReplicatorCommitContext commitContext = new ReplicatorCommitContext(transaction, commitInfo);
+ commitContext.preWrite();
+ boolean success = false;
+
+ try
+ {
+ handleCommitInfoLock.lock();
+
+ commitContext.write(new Monitor());
+ commitContext.commit(new Monitor());
+
+ setLastCommitTimeStamp(timeStamp);
+ setLastReplicatedCommitTime(timeStamp);
+ success = true;
+ }
+ finally
+ {
+ handleCommitInfoLock.unlock();
+ commitContext.postCommit(success);
+ transaction.close();
+ }
+ }
+
+ public void handleLockChangeInfo(CDOLockChangeInfo lockChangeInfo)
+ {
+ CDOLockOwner owner = lockChangeInfo.getLockOwner();
+ String durableLockingID = owner.getDurableLockingID();
+ CDOBranch viewedBranch = lockChangeInfo.getBranch();
+ InternalLockManager lockManager = getLockManager();
+ LockType lockType = lockChangeInfo.getLockType();
+
+ InternalView view = null;
+
+ try
+ {
+ view = SyncingUtil.openViewWithLockArea(replicatorSession, lockManager, viewedBranch, durableLockingID);
+ List<Object> lockables = new LinkedList<Object>();
+
+ for (CDOLockState lockState : lockChangeInfo.getLockStates())
+ {
+ lockables.add(lockState.getLockedObject());
+ }
+
+ if (lockChangeInfo.getOperation() == Operation.LOCK)
+ {
+ // If we can't lock immediately, there's a conflict, which means we're in big
+ // trouble: somehow locks were obtained on the clone but not on the master. What to do?
+ // TODO (CD) Consider this problem further
+ //
+ long timeout = 0;
+ super.lock(view, lockType, lockables, null, timeout);
+ }
+ else if (lockChangeInfo.getOperation() == Operation.UNLOCK)
+ {
+ super.doUnlock(view, lockType, lockables);
+ }
+ else
+ {
+ throw new IllegalStateException("Unexpected: " + lockChangeInfo.getOperation());
+ }
+ }
+ finally
+ {
+ LifecycleUtil.deactivate(view);
+ }
+ }
+
+ public boolean handleLockArea(LockArea area)
+ {
+ try
+ {
+ StoreThreadLocal.setSession(replicatorSession);
+ getLockManager().updateLockArea(area);
+
+ // TODO (CD) getSessionManager().sendLockNotification(sender, lockChangeInfo);
+ return true;
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ public void replicateRaw(CDODataInput in, OMMonitor monitor) throws IOException
+ {
+ try
+ {
+ int fromBranchID = lastReplicatedBranchID + 1;
+ int toBranchID = in.readInt();
+ long fromCommitTime = lastReplicatedCommitTime + 1L;
+ long toCommitTime = in.readLong();
+
+ StoreThreadLocal.setSession(replicatorSession);
+ IStoreAccessor.Raw accessor = (IStoreAccessor.Raw)StoreThreadLocal.getAccessor();
+ accessor.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor);
+
+ replicateRawReviseRevisions();
+ replicateRawNotifyClients(lastReplicatedCommitTime, toCommitTime);
+
+ setLastReplicatedBranchID(toBranchID);
+ setLastReplicatedCommitTime(toCommitTime);
+ setLastCommitTimeStamp(toCommitTime);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ public void goOnline()
+ {
+ if (getState() == OFFLINE)
+ {
+ LifecycleUtil.activate(synchronizer);
+ // Do not set the state to ONLINE yet; the synchronizer will set it to SYNCING first,
+ // and then to ONLINE after a succesful replication.
+ }
+ }
+
+ public void goOffline()
+ {
+ if (getState() != OFFLINE)
+ {
+ LifecycleUtil.deactivate(synchronizer);
+ setState(OFFLINE);
+ }
+ }
+
+ private void replicateRawReviseRevisions()
+ {
+ InternalCDORevisionCache cache = getRevisionManager().getCache();
+ for (CDORevision revision : cache.getCurrentRevisions())
+ {
+ cache.removeRevision(revision.getID(), revision);
+ }
+ }
+
+ private void replicateRawNotifyClients(long fromCommitTime, long toCommitTime)
+ {
+ InternalCDOCommitInfoManager manager = getCommitInfoManager();
+ InternalSessionManager sessionManager = getSessionManager();
+
+ Map<CDOBranch, TimeRange> branches = replicateRawGetBranches(fromCommitTime, toCommitTime);
+ for (Entry<CDOBranch, TimeRange> entry : branches.entrySet())
+ {
+ CDOBranch branch = entry.getKey();
+ TimeRange range = entry.getValue();
+ fromCommitTime = range.getTime1();
+ toCommitTime = range.getTime2();
+
+ CDOBranchPoint startPoint = branch.getPoint(fromCommitTime);
+ CDOBranchPoint endPoint = branch.getPoint(toCommitTime);
+ CDOChangeSetData changeSet = getChangeSet(startPoint, endPoint);
+
+ List<CDOPackageUnit> newPackages = Collections.emptyList(); // TODO Notify about new packages
+ List<CDOIDAndVersion> newObjects = changeSet.getNewObjects();
+ List<CDORevisionKey> changedObjects = changeSet.getChangedObjects();
+ List<CDOIDAndVersion> detachedObjects = changeSet.getDetachedObjects();
+ CDOCommitData data = new CDOCommitDataImpl(newPackages, newObjects, changedObjects, detachedObjects);
+
+ String comment = "<replicate raw commits>"; //$NON-NLS-1$
+ CDOCommitInfo commitInfo = manager.createCommitInfo(branch, toCommitTime, fromCommitTime, SYSTEM_USER_ID,
+ comment, data);
+ sessionManager.sendCommitNotification(replicatorSession, commitInfo);
+ }
+ }
+
+ private Map<CDOBranch, TimeRange> replicateRawGetBranches(long fromCommitTime, long toCommitTime)
+ {
+ final Map<CDOBranch, TimeRange> branches = new HashMap<CDOBranch, TimeRange>();
+ CDOCommitInfoHandler handler = new CDOCommitInfoHandler()
+ {
+ public void handleCommitInfo(CDOCommitInfo commitInfo)
+ {
+ CDOBranch branch = commitInfo.getBranch();
+ long timeStamp = commitInfo.getTimeStamp();
+ TimeRange range = branches.get(branch);
+ if (range == null)
+ {
+ branches.put(branch, new TimeRange(timeStamp));
+ }
+ else
+ {
+ range.update(timeStamp);
+ }
+ }
+ };
+
+ getCommitInfoManager().getCommitInfos(null, fromCommitTime, toCommitTime, handler);
+ return branches;
+ }
+
+ @Override
+ public abstract InternalCommitContext createCommitContext(InternalTransaction transaction);
+
+ protected InternalCommitContext createNormalCommitContext(InternalTransaction transaction)
+ {
+ return super.createCommitContext(transaction);
+ }
+
+ protected InternalCommitContext createWriteThroughCommitContext(InternalTransaction transaction)
+ {
+ return new WriteThroughCommitContext(transaction);
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ checkState(synchronizer, "synchronizer"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ InternalStore store = getStore();
+ if (!store.isFirstStart())
+ {
+ Map<String, String> map = store.getPersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN));
+ if (!map.containsKey(PROP_GRACEFULLY_SHUT_DOWN))
+ {
+ setReplicationCountersToLatest();
+ }
+ else
+ {
+ Set<String> names = new HashSet<String>();
+ names.add(PROP_LAST_REPLICATED_BRANCH_ID);
+ names.add(PROP_LAST_REPLICATED_COMMIT_TIME);
+
+ map = store.getPersistentProperties(names);
+ setLastReplicatedBranchID(Integer.valueOf(map.get(PROP_LAST_REPLICATED_BRANCH_ID)));
+ setLastReplicatedCommitTime(Long.valueOf(map.get(PROP_LAST_REPLICATED_COMMIT_TIME)));
+ }
+ }
+
+ store.removePersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN));
+
+ if (getType() != MASTER)
+ {
+ startSynchronization();
+ }
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ stopSynchronization();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(PROP_LAST_REPLICATED_BRANCH_ID, Integer.toString(lastReplicatedBranchID));
+ map.put(PROP_LAST_REPLICATED_COMMIT_TIME, Long.toString(lastReplicatedCommitTime));
+ map.put(PROP_GRACEFULLY_SHUT_DOWN, Boolean.TRUE.toString());
+
+ InternalStore store = getStore();
+ store.setPersistentProperties(map);
+
+ super.doDeactivate();
+ }
+
+ protected void startSynchronization()
+ {
+ replicatorSession = getSessionManager().openSession(null);
+ replicatorSession.options().setPassiveUpdateEnabled(false);
+ replicatorSession.options().setLockNotificationMode(LockNotificationMode.OFF);
+
+ synchronizer.setLocalRepository(this);
+ synchronizer.activate();
+ }
+
+ protected void stopSynchronization()
+ {
+ if (synchronizer != null)
+ {
+ synchronizer.deactivate();
+ }
+ }
+
+ protected void setReplicationCountersToLatest()
+ {
+ setLastReplicatedBranchID(getStore().getLastBranchID());
+ setLastReplicatedCommitTime(getStore().getLastNonLocalCommitTime());
+ }
+
+ protected void doInitRootResource()
+ {
+ super.initRootResource();
+ }
+
+ @Override
+ protected void initRootResource()
+ {
+ setState(INITIAL);
+ }
+
+ @Override
+ public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, long timeout)
+ {
+ if (view.getBranch().isLocal())
+ {
+ return super.lock(view, lockType, revisionKeys, timeout);
+ }
+
+ if (getState() != ONLINE)
+ {
+ throw new CDOException("Cannot lock in a non-local branch when clone is not connected to master");
+ }
+
+ return lockThrough(view, lockType, revisionKeys, timeout);
+ }
+
+ private LockObjectsResult lockOnMaster(InternalView view, LockType type, List<CDORevisionKey> revKeys, long timeout)
+ throws InterruptedException
+ {
+ // Delegate locking to the master
+ InternalCDOSession remoteSession = getSynchronizer().getRemoteSession();
+ CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol();
+
+ String areaID = view.getDurableLockingID();
+ if (areaID == null)
+ {
+ throw new IllegalStateException("Durable locking is not enabled.");
+ }
+
+ LockObjectsResult masterLockingResult = sessionProtocol.delegateLockObjects(areaID, revKeys, view.getBranch(),
+ type, timeout);
+
+ if (masterLockingResult.isSuccessful() && masterLockingResult.isWaitForUpdate())
+ {
+ if (!getSynchronizer().getRemoteSession().options().isPassiveUpdateEnabled())
+ {
+ throw new AssertionError(
+ "Master lock result requires clone to wait, but clone does not have passiveUpdates enabled.");
+ }
+
+ long requiredTimestamp = masterLockingResult.getRequiredTimestamp();
+ remoteSession.waitForUpdate(requiredTimestamp);
+ }
+
+ return masterLockingResult;
+ }
+
+ private LockObjectsResult lockThrough(InternalView view, LockType type, List<CDORevisionKey> keys, long timeout)
+ {
+ try
+ {
+ LockObjectsResult masterLockingResult = lockOnMaster(view, type, keys, timeout);
+ if (!masterLockingResult.isSuccessful())
+ {
+ return masterLockingResult;
+ }
+
+ LockObjectsResult localLockingResult = super.lock(view, type, keys, timeout);
+ return localLockingResult;
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+
+ @Override
+ public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs)
+ {
+ if (view.getBranch().isLocal())
+ {
+ super.unlock(view, lockType, objectIDs);
+ }
+
+ if (getState() != ONLINE)
+ {
+ throw new CDOException("Cannot unlock in a non-local branch when clone is not connected to master");
+ }
+
+ return unlockThrough(view, lockType, objectIDs);
+ }
+
+ private void unlockOnMaster(InternalView view, LockType lockType, List<CDOID> objectIDs)
+ {
+ InternalCDOSession remoteSession = getSynchronizer().getRemoteSession();
+ CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol();
+
+ String lockAreaID = view.getDurableLockingID();
+ if (lockAreaID == null)
+ {
+ throw new IllegalStateException("Durable locking is not enabled.");
+ }
+
+ sessionProtocol.delegateUnlockObjects(lockAreaID, objectIDs, lockType);
+ }
+
+ private UnlockObjectsResult unlockThrough(InternalView view, LockType lockType, List<CDOID> objectIDs)
+ {
+ unlockOnMaster(view, lockType, objectIDs);
+ return super.unlock(view, lockType, objectIDs);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class TimeRange
+ {
+ private long time1;
+
+ private long time2;
+
+ public TimeRange(long time)
+ {
+ time1 = time;
+ time2 = time;
+ }
+
+ public void update(long time)
+ {
+ if (time < time1)
+ {
+ time1 = time;
+ }
+
+ if (time > time2)
+ {
+ time2 = time;
+ }
+ }
+
+ public long getTime1()
+ {
+ return time1;
+ }
+
+ public long getTime2()
+ {
+ return time2;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[" + CDOCommonUtil.formatTimeStamp(time1) + " - " + CDOCommonUtil.formatTimeStamp(time1) + "]";
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ protected final class WriteThroughCommitContext extends TransactionCommitContext
+ {
+ public WriteThroughCommitContext(InternalTransaction transaction)
+ {
+ super(transaction);
+ }
+
+ @Override
+ public void preWrite()
+ {
+ // Do nothing
+ }
+
+ @Override
+ public void write(OMMonitor monitor)
+ {
+ // Do nothing
+ }
+
+ @Override
+ public void commit(OMMonitor monitor)
+ {
+ InternalTransaction transaction = getTransaction();
+
+ // Prepare commit to the master
+ CDOBranch branch = transaction.getBranch();
+ String userID = getUserID();
+ String comment = getCommitComment();
+ CDOCommitData commitData = new CommitContextData(this);
+ Collection<CDOLob<?>> lobs = Collections.emptySet();
+
+ // Delegate commit to the master
+ CDOSessionProtocol sessionProtocol = getSynchronizer().getRemoteSession().getSessionProtocol();
+ CommitTransactionResult result = sessionProtocol.commitDelegation(branch, userID, comment, commitData,
+ getDetachedObjectTypes(), lobs, monitor);
+
+ // Stop if commit to master failed
+ String rollbackMessage = result.getRollbackMessage();
+ if (rollbackMessage != null)
+ {
+ throw new TransactionException(rollbackMessage);
+ }
+
+ // Prepare data needed for commit result and commit notifications
+ long timeStamp = result.getTimeStamp();
+ setTimeStamp(timeStamp);
+ addIDMappings(result.getIDMappings());
+ applyIDMappings(new Monitor());
+
+ try
+ {
+ writeThroughCommitLock.lock();
+
+ // Commit to the local repository
+ super.preWrite();
+ super.write(new Monitor());
+ super.commit(new Monitor());
+ }
+ finally
+ {
+ writeThroughCommitLock.unlock();
+ }
+
+ // Remember commit time in the local repository
+ setLastCommitTimeStamp(timeStamp);
+ setLastReplicatedCommitTime(timeStamp);
+
+ // Remember commit time in the replicator session.
+ getSynchronizer().getRemoteSession().setLastUpdateTime(timeStamp);
+ }
+
+ @Override
+ protected long[] createTimeStamp(OMMonitor monitor)
+ {
+ // Already set after commit to the master.
+ // Do not call getTimeStamp() of the enclosing Repo class!!!
+ InternalRepository repository = getTransaction().getSession().getManager().getRepository();
+ return repository.forceCommitTimeStamp(WriteThroughCommitContext.this.getTimeStamp(), monitor);
+ }
+
+ @Override
+ protected void lockObjects() throws InterruptedException
+ {
+ // Do nothing
+ }
+
+ private void addIDMappings(Map<CDOID, CDOID> idMappings)
+ {
+ for (Map.Entry<CDOID, CDOID> idMapping : idMappings.entrySet())
+ {
+ CDOID oldID = idMapping.getKey();
+ CDOID newID = idMapping.getValue();
+ addIDMapping(oldID, newID);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java
new file mode 100644
index 0000000000..ed7ec5ec4c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java
@@ -0,0 +1,1220 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+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.revision.CDOAllRevisionsProvider;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil.AllRevisionsDumper;
+import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.internal.server.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.emf.cdo.spi.common.revision.DetachedCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.concurrent.Worker;
+import org.eclipse.net4j.util.container.ContainerEventAdapter;
+import org.eclipse.net4j.util.container.IContainer;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class CDOServerBrowser extends Worker
+{
+ private static final String REQUEST_PREFIX = "GET ";
+
+ private static final String REQUEST_SUFFIX = " HTTP/1.1";
+
+ private ThreadLocal<Map<String, String>> params = new InheritableThreadLocal<Map<String, String>>()
+ {
+ @Override
+ protected Map<String, String> initialValue()
+ {
+ return new HashMap<String, String>();
+ }
+ };
+
+ private int port = 7777;
+
+ private ServerSocket serverSocket;
+
+ private Map<String, InternalRepository> repositories;
+
+ private List<Page> pages = new ArrayList<Page>();
+
+ public CDOServerBrowser(Map<String, InternalRepository> repositories)
+ {
+ this.repositories = repositories;
+ setDaemon(true);
+ }
+
+ public Map<String, InternalRepository> getRepositories()
+ {
+ return repositories;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ @Override
+ protected void work(WorkContext context) throws Exception
+ {
+ Socket socket = null;
+
+ try
+ {
+ socket = serverSocket.accept();
+ BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ OutputStream out = new BufferedOutputStream(socket.getOutputStream());
+ PrintStream pout = new PrintStream(out);
+ printHeader(pout);
+
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ if (line.startsWith(REQUEST_PREFIX) && line.endsWith(REQUEST_SUFFIX))
+ {
+ String request = line.substring(REQUEST_PREFIX.length(), line.length() - REQUEST_SUFFIX.length()).trim();
+ String resource = request;
+ String params = "";
+ int pos = request.indexOf('?');
+ if (pos != -1)
+ {
+ resource = request.substring(0, pos);
+ params = request.substring(pos + 1);
+ }
+
+ initParams(params);
+ if ("/".equals(resource))
+ {
+ showMenu(pout);
+ }
+ else
+ {
+ String pageName = resource.substring(1);
+ for (Page page : pages)
+ {
+ if (page.getName().equals(pageName))
+ {
+ showPage(pout, page);
+ }
+ }
+ }
+ }
+
+ out.flush();
+ return;
+ }
+ }
+ catch (Exception ex)
+ {
+ if (isActive())
+ {
+ ex.printStackTrace();
+ }
+ }
+ finally
+ {
+ params.remove();
+ if (socket != null)
+ {
+ socket.close();
+ }
+ }
+ }
+
+ protected void initParams(String params)
+ {
+ Map<String, String> map = this.params.get();
+ for (String param : params.split("&"))
+ {
+ if (param.length() != 0)
+ {
+ String[] keyValue = param.split("=");
+ map.put(keyValue[0], keyValue[1]);
+ }
+ }
+ }
+
+ protected void clearParams()
+ {
+ Map<String, String> map = params.get();
+ map.clear();
+ }
+
+ public void removeParam(String key)
+ {
+ Map<String, String> map = params.get();
+ map.remove(key);
+ }
+
+ public String getParam(String key)
+ {
+ Map<String, String> map = params.get();
+ return map.get(key);
+ }
+
+ public String href(String label, String resource, String... params)
+ {
+ Map<String, String> map = new HashMap<String, String>(this.params.get());
+ for (int i = 0; i < params.length;)
+ {
+ map.put(params[i++], params[i++]);
+ }
+
+ List<String> list = new ArrayList<String>(map.keySet());
+ Collections.sort(list);
+
+ StringBuilder builder = new StringBuilder();
+ for (String key : list)
+ {
+ String value = map.get(key);
+ if (value != null)
+ {
+ if (builder.length() != 0)
+ {
+ builder.append("&");
+ }
+
+ builder.append(key);
+ builder.append("=");
+ builder.append(value);
+ }
+ }
+
+ return "<a href=\"/" + escape(resource) + "?" + escape(builder.toString()) + "\">" + escape(label) + "</a>";
+ }
+
+ public String escape(String raw)
+ {
+ if (raw == null)
+ {
+ return "null";
+ }
+
+ return raw.replace("<", "&lt;");
+ }
+
+ protected void printHeader(PrintStream pout)
+ {
+ pout.print("HTTP/1.1 200 OK\r\n");
+ pout.print("Content-Type: text/html\r\n");
+ pout.print("Date: " + new Date() + "\r\n");
+ pout.print("Server: DBBrowser 3.0\r\n");
+ pout.print("\r\n");
+ }
+
+ protected void showMenu(PrintStream pout)
+ {
+ clearParams();
+ pout.print("<h1>CDO Server Browser 4.0</h1><hr>\r\n");
+
+ for (Page page : pages)
+ {
+ pout.println("<h3>" + href(page.getLabel(), page.getName()) + "</h3>");
+ }
+ }
+
+ protected void showPage(PrintStream pout, Page page)
+ {
+ String repo = getParam("repo");
+
+ List<String> repoNames = new ArrayList<String>(getRepositoryNames());
+ Collections.sort(repoNames);
+
+ pout.print("<h3><a href=\"/\">" + page.getLabel() + "</a>:&nbsp;&nbsp;");
+ for (String repoName : repoNames)
+ {
+ InternalRepository repository = getRepository(repoName);
+ if (!page.canDisplay(repository))
+ {
+ continue;
+ }
+
+ if (repo == null)
+ {
+ repo = repoName;
+ }
+
+ if (repoName.equals(repo))
+ {
+ pout.print("<b>" + escape(repoName) + "</b>&nbsp;&nbsp;");
+ }
+ else
+ {
+ pout.print(href(repoName, page.getName(), "repo", repoName) + "&nbsp;&nbsp;");
+ }
+ }
+
+ pout.print("</h3>");
+
+ InternalRepository repository = getRepository(repo);
+ if (repository != null)
+ {
+ pout.print("<p>\r\n");
+ page.display(this, repository, pout);
+ }
+ }
+
+ protected Set<String> getRepositoryNames()
+ {
+ return repositories.keySet();
+ }
+
+ protected InternalRepository getRepository(String name)
+ {
+ return repositories.get(name);
+ }
+
+ @Override
+ protected String getThreadName()
+ {
+ return "DBBrowser";
+ }
+
+ protected void initPages(List<Page> pages)
+ {
+ pages.add(new PackagesPage());
+ pages.add(new RevisionsPage.FromCache());
+ pages.add(new RevisionsPage.FromStore());
+ pages.add(new LobsPage());
+ pages.add(new HistoryPage());
+
+ IPluginContainer container = IPluginContainer.INSTANCE;
+ Set<String> factoryTypes = container.getFactoryTypes(Page.PRODUCT_GROUP);
+ for (String factoryType : factoryTypes)
+ {
+ Page page = (Page)container.getElement(Page.PRODUCT_GROUP, factoryType, null);
+ pages.add(page);
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ initPages(pages);
+
+ try
+ {
+ serverSocket = new ServerSocket(port);
+ }
+ catch (Exception ex)
+ {
+ throw new IllegalStateException("Could not open socket on port " + port, ex);
+ }
+
+ super.doActivate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ serverSocket.close();
+ super.doDeactivate();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class ContainerBased extends CDOServerBrowser
+ {
+ private IContainer<?> container;
+
+ private IListener containerListener = new ContainerEventAdapter<Object>()
+ {
+ @Override
+ protected void onAdded(IContainer<Object> container, Object element)
+ {
+ addElement(element);
+ }
+
+ @Override
+ protected void onRemoved(IContainer<Object> container, Object element)
+ {
+ removeElement(element);
+ }
+ };
+
+ public ContainerBased(IContainer<?> container)
+ {
+ super(new HashMap<String, InternalRepository>());
+ this.container = container;
+ }
+
+ public ContainerBased()
+ {
+ this(IPluginContainer.INSTANCE);
+ }
+
+ public IContainer<?> getContainer()
+ {
+ return container;
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ for (Object element : container.getElements())
+ {
+ addElement(element);
+ }
+
+ container.addListener(containerListener);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ container.removeListener(containerListener);
+ super.doDeactivate();
+ }
+
+ private void addElement(Object element)
+ {
+ if (element instanceof InternalRepository)
+ {
+ InternalRepository repository = (InternalRepository)element;
+ getRepositories().put(repository.getName(), repository);
+ }
+ }
+
+ private void removeElement(Object element)
+ {
+ if (element instanceof InternalRepository)
+ {
+ InternalRepository repository = (InternalRepository)element;
+ getRepositories().remove(repository.getName());
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends org.eclipse.net4j.util.factory.Factory
+ {
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.browsers";
+
+ public static final String TYPE = "default";
+
+ private IContainer<?> container;
+
+ public Factory()
+ {
+ this(IPluginContainer.INSTANCE);
+ }
+
+ public Factory(IContainer<?> container)
+ {
+ super(PRODUCT_GROUP, TYPE);
+ this.container = container;
+ }
+
+ public CDOServerBrowser.ContainerBased create(String description) throws ProductCreationException
+ {
+ CDOServerBrowser.ContainerBased browser = new CDOServerBrowser.ContainerBased(container);
+
+ try
+ {
+ if (!StringUtil.isEmpty(description))
+ {
+ browser.setPort(Integer.valueOf(description));
+ }
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.warn(ex);
+ }
+
+ return browser;
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static interface Page
+ {
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.browserPages";
+
+ public String getName();
+
+ public String getLabel();
+
+ public boolean canDisplay(InternalRepository repository);
+
+ public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class AbstractPage implements Page
+ {
+ private String name;
+
+ private String label;
+
+ public AbstractPage(String name, String label)
+ {
+ this.name = name;
+ this.label = label;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getLabel()
+ {
+ return label;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class PackagesPage extends AbstractPage
+ {
+ public static final String NAME = "packages";
+
+ public PackagesPage()
+ {
+ super(NAME, "Packages and Classes");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return true;
+ }
+
+ public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out)
+ {
+ String param = browser.getParam("classifier");
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ for (InternalCDOPackageUnit unit : packageRegistry.getPackageUnits())
+ {
+ param = showPackage(unit.getTopLevelPackageInfo(), packageRegistry, browser, param, out, "&nbsp;&nbsp;");
+ }
+ }
+
+ protected String showPackage(InternalCDOPackageInfo info, InternalCDOPackageRegistry packageRegistry,
+ CDOServerBrowser browser, String param, PrintStream out, String prefix)
+ {
+ EPackage ePackage = info.getEPackage();
+ out.println("<h3>" + prefix + ePackage.getName() + "&nbsp;&nbsp;[" + ePackage.getNsURI() + "]</h3>");
+
+ for (EClassifier classifier : ePackage.getEClassifiers())
+ {
+ String name = classifier.getName();
+ if (param == null)
+ {
+ param = name;
+ }
+
+ String label = name.equals(param) ? name : browser.href(name, getName(), "classifier", name);
+ out.print(prefix + "&nbsp;&nbsp;<b>" + label);
+
+ if (classifier instanceof EEnum)
+ {
+ EEnum eenum = (EEnum)classifier;
+ out.print("&nbsp;&nbsp;" + eenum.getELiterals());
+ }
+ else if (classifier instanceof EDataType)
+ {
+ EDataType eDataType = (EDataType)classifier;
+ out.print("&nbsp;&nbsp;" + eDataType.getInstanceClassName());
+ }
+
+ out.println("</b><br>");
+ }
+
+ for (EPackage sub : ePackage.getESubpackages())
+ {
+ InternalCDOPackageInfo subInfo = packageRegistry.getPackageInfo(sub);
+ param = showPackage(subInfo, packageRegistry, browser, param, out, prefix + "&nbsp;&nbsp;");
+ }
+
+ return param;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class RevisionsPage extends AbstractPage
+ {
+ public RevisionsPage(String name, String label)
+ {
+ super(name, label);
+ }
+
+ public void display(final CDOServerBrowser browser, InternalRepository repository, PrintStream out)
+ {
+ Map<CDOBranch, List<CDORevision>> allRevisions = getAllRevisions(repository);
+ Map<CDOID, List<CDORevision>> ids = getAllIDs(allRevisions);
+
+ out.print("<table border=\"0\">\r\n");
+ out.print("<tr>\r\n");
+
+ out.print("<td valign=\"top\">\r\n");
+ out.print("<table border=\"1\" cellpadding=\"2\"><tr><td>\r\n");
+ final String[] revision = { browser.getParam("revision") };
+ new AllRevisionsDumper.Stream.Html(allRevisions, out)
+ {
+ private StringBuilder versionsBuilder;
+
+ private CDORevision lastRevision;
+
+ @Override
+ protected void dumpEnd(List<CDOBranch> branches)
+ {
+ dumpLastRevision();
+ super.dumpEnd(branches);
+ }
+
+ @Override
+ protected void dumpBranch(CDOBranch branch)
+ {
+ dumpLastRevision();
+ super.dumpBranch(branch);
+ }
+
+ @Override
+ protected void dumpRevision(CDORevision rev)
+ {
+ CDOID id = rev.getID();
+ if (lastRevision != null && !id.equals(lastRevision.getID()))
+ {
+ dumpLastRevision();
+ }
+
+ if (versionsBuilder == null)
+ {
+ versionsBuilder = new StringBuilder();
+ }
+ else
+ {
+ versionsBuilder.append(" ");
+ if (versionsBuilder.length() > 64)
+ {
+ versionsBuilder.append("<br>");
+ }
+ }
+
+ String key = CDORevisionUtil.formatRevisionKey(rev);
+ if (revision[0] == null)
+ {
+ revision[0] = key;
+ }
+
+ String version = getVersionPrefix(rev) + rev.getVersion();
+ if (key.equals(revision[0]))
+ {
+ versionsBuilder.append("<b>" + version + "</b>");
+ }
+ else
+ {
+ versionsBuilder.append(browser.href(version, getName(), "revision", key));
+ }
+
+ lastRevision = rev;
+ }
+
+ protected void dumpLastRevision()
+ {
+ if (versionsBuilder != null)
+ {
+ PrintStream out = out();
+ out.println("<tr>");
+ out.println("<td valign=\"top\">&nbsp;&nbsp;&nbsp;&nbsp;");
+ out.println(getCDOIDLabel(lastRevision));
+ out.println("&nbsp;&nbsp;&nbsp;&nbsp;</td>");
+
+ out.println("<td>");
+ out.println(versionsBuilder.toString());
+ out.println("</td>");
+ out.println("</tr>");
+
+ lastRevision = null;
+ versionsBuilder = null;
+ }
+ }
+ }.dump();
+
+ out.print("</td></tr></table></td>\r\n");
+ out.print("<td>&nbsp;&nbsp;&nbsp;</td>\r\n");
+
+ if (revision[0] != null)
+ {
+ out.print("<td valign=\"top\">\r\n");
+ showRevision(out, browser, allRevisions, ids, revision[0], repository);
+ out.print("</td>\r\n");
+ }
+
+ out.print("</tr>\r\n");
+ out.print("</table>\r\n");
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void showRevision(PrintStream pout, CDOServerBrowser browser,
+ Map<CDOBranch, List<CDORevision>> allRevisions, Map<CDOID, List<CDORevision>> ids, String key,
+ InternalRepository repository)
+ {
+ CDORevisionKey revisionKey = CDORevisionUtil.parseRevisionKey(key, repository.getBranchManager());
+ for (CDORevision revision : allRevisions.get(revisionKey.getBranch()))
+ {
+ if (revision.getVersion() == revisionKey.getVersion() && revision.getID().equals(revisionKey.getID()))
+ {
+ showRevision(pout, browser, ids, (InternalCDORevision)revision);
+ return;
+ }
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void showRevision(PrintStream pout, CDOServerBrowser browser, Map<CDOID, List<CDORevision>> ids,
+ InternalCDORevision revision)
+ {
+ String className = revision.getEClass().toString();
+ className = className.substring(className.indexOf(' '));
+ className = StringUtil.replace(className, new String[] { "(", ")", "," }, new String[] { "<br>", "", "<br>" });
+ className = className.substring("<br>".length() + 1);
+
+ String created = CDOCommonUtil.formatTimeStamp(revision.getTimeStamp());
+ String commitInfo = browser.href(created, HistoryPage.NAME, "time", String.valueOf(revision.getTimeStamp()));
+
+ pout.print("<table border=\"1\" cellpadding=\"2\">\r\n");
+ showKeyValue(pout, true, "type", "<b>" + revision.getClass().getSimpleName() + "</b>");
+ showKeyValue(pout, true, "class", className);
+ showKeyValue(pout, true, "id", getRevisionValue(revision.getID(), browser, ids, revision));
+ showKeyValue(pout, true, "branch", revision.getBranch().getName() + "[" + revision.getBranch().getID() + "]");
+ showKeyValue(pout, true, "version", revision.getVersion());
+ showKeyValue(pout, true, "created", commitInfo);
+ showKeyValue(pout, true, "revised", CDOCommonUtil.formatTimeStamp(revision.getRevised()));
+ if (!(revision instanceof SyntheticCDORevision))
+ {
+ showKeyValue(pout, true, "resource", getRevisionValue(revision.getResourceID(), browser, ids, revision));
+ showKeyValue(pout, true, "container", getRevisionValue(revision.getContainerID(), browser, ids, revision));
+ showKeyValue(pout, true, "feature", revision.getContainingFeatureID());
+
+ for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures())
+ {
+ Object value = revision.getValue(feature);
+ showKeyValue(pout, false, feature.getName(), getRevisionValue(value, browser, ids, revision));
+ }
+ }
+
+ pout.print("</table>\r\n");
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected Object getRevisionValue(Object value, CDOServerBrowser browser, Map<CDOID, List<CDORevision>> ids,
+ InternalCDORevision context)
+ {
+ if (value instanceof CDOID)
+ {
+ List<CDORevision> revisions = ids.get(value);
+ if (revisions != null)
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getCDOIDLabel(revisions.get(0)));
+
+ if (browser != null)
+ {
+ builder.append("&nbsp;&nbsp;");
+ for (CDORevision revision : revisions)
+ {
+ String label = getVersionPrefix(revision) + revision.getVersion();
+ String branchName = revision.getBranch().getName();
+ if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName))
+ {
+ label += "[" + branchName + "]";
+ }
+
+ builder.append(" ");
+ if (revision == context)
+ {
+ builder.append(label);
+ }
+ else
+ {
+ builder.append(browser.href(label, getName(), "revision", CDORevisionUtil.formatRevisionKey(revision)));
+ }
+ }
+ }
+
+ return builder.toString();
+ }
+ }
+
+ if (value instanceof Collection)
+ {
+ StringBuilder builder = new StringBuilder();
+ for (Object element : (Collection<?>)value)
+ {
+ builder.append(builder.length() == 0 ? "" : "<br>");
+ builder.append(getRevisionValue(element, browser, ids, context));
+ }
+
+ return builder.toString();
+ }
+
+ return value;
+ }
+
+ private String getVersionPrefix(CDORevision revision)
+ {
+ if (revision instanceof PointerCDORevision)
+ {
+ return "p";
+ }
+
+ if (revision instanceof DetachedCDORevision)
+ {
+ return "d";
+ }
+
+ return "v";
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void showKeyValue(PrintStream pout, boolean bg, String key, Object value)
+ {
+ String color = bg ? "EEEEEE" : "FFFFFF";
+ pout.print("<tr bgcolor=\"" + color + "\">\r\n");
+ pout.print("<td valign=\"top\"><b>" + key + "</b></td>\r\n");
+ pout.print("<td valign=\"top\">");
+ pout.print(value);
+ pout.print("</td>\r\n");
+ pout.print("</tr>\r\n");
+ }
+
+ protected abstract Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository);
+
+ private Map<CDOID, List<CDORevision>> getAllIDs(Map<CDOBranch, List<CDORevision>> allRevisions)
+ {
+ Map<CDOID, List<CDORevision>> ids = new HashMap<CDOID, List<CDORevision>>();
+ for (List<CDORevision> list : allRevisions.values())
+ {
+ for (CDORevision revision : list)
+ {
+ CDOID id = revision.getID();
+ List<CDORevision> revisions = ids.get(id);
+ if (revisions == null)
+ {
+ revisions = new ArrayList<CDORevision>();
+ ids.put(id, revisions);
+ }
+
+ revisions.add(revision);
+ }
+ }
+
+ return ids;
+ }
+
+ protected String getCDOIDLabel(CDORevision revision)
+ {
+ String label = revision.toString();
+ return label.substring(0, label.indexOf(':'));
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class FromCache extends RevisionsPage
+ {
+ public static final String NAME = "crevisions";
+
+ public FromCache()
+ {
+ super(NAME, "Revisions From Cache");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return true;
+ }
+
+ @Override
+ protected Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository)
+ {
+ return repository.getRevisionManager().getCache().getAllRevisions();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class FromStore extends RevisionsPage
+ {
+ public static final String NAME = "srevisions";
+
+ public FromStore()
+ {
+ super(NAME, "Revisions From Store");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return repository.getStore() instanceof CDOAllRevisionsProvider;
+ }
+
+ @Override
+ protected Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository)
+ {
+ return ((CDOAllRevisionsProvider)repository.getStore()).getAllRevisions();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class LobsPage extends AbstractPage
+ {
+ public static final String NAME = "lobs";
+
+ public LobsPage()
+ {
+ super(NAME, "Large Objects");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return true;
+ }
+
+ public void display(final CDOServerBrowser browser, InternalRepository repository, final PrintStream out)
+ {
+ out.print("<table border=\"0\">\r\n");
+ out.print("<tr>\r\n");
+ out.print("<td valign=\"top\">\r\n");
+
+ IStoreAccessor accessor = repository.getStore().getReader(null);
+ StoreThreadLocal.setAccessor(accessor);
+
+ final String param = browser.getParam("id");
+ final Object[] details = { null, null, null };
+
+ try
+ {
+ repository.handleLobs(0, 0, new CDOLobHandler()
+ {
+ public OutputStream handleBlob(byte[] id, long size)
+ {
+ if (showLob(out, "Blob", id, size, browser, param))
+ {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ details[0] = result;
+ details[1] = param;
+ details[2] = size;
+ return result;
+ }
+
+ return null;
+ }
+
+ public Writer handleClob(byte[] id, long size)
+ {
+ if (showLob(out, "Clob", id, size, browser, param))
+ {
+ CharArrayWriter result = new CharArrayWriter();
+ details[0] = result;
+ details[1] = param;
+ details[2] = size;
+ return result;
+ }
+
+ return null;
+ }
+ });
+ }
+ catch (IOException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+
+ out.print("</td>\r\n");
+
+ if (details[0] != null)
+ {
+ out.print("<td>&nbsp;&nbsp;&nbsp;</td>\r\n");
+ out.print("<td valign=\"top\">\r\n");
+ if (details[0] instanceof ByteArrayOutputStream)
+ {
+ ByteArrayOutputStream baos = (ByteArrayOutputStream)details[0];
+ String hex = HexUtil.bytesToHex(baos.toByteArray());
+
+ out.println("<h3>Blob " + details[1] + " (" + details[2] + ")</h3>");
+ out.println("<pre>\r\n");
+ for (int i = 0; i < hex.length(); i++)
+ {
+ out.print(hex.charAt(i));
+ if ((i + 1) % 32 == 0)
+ {
+ out.print("\r\n");
+ }
+ else if ((i + 1) % 16 == 0)
+ {
+ out.print(" ");
+ }
+ else if ((i + 1) % 2 == 0)
+ {
+ out.print(" ");
+ }
+ }
+
+ out.println("</pre>\r\n");
+ }
+ else
+ {
+ CharArrayWriter caw = (CharArrayWriter)details[0];
+ out.println("<h3>Clob " + details[1] + " (" + details[2] + ")</h3>");
+ out.println("<pre>" + caw + "</pre>");
+ }
+
+ out.print("</td>\r\n");
+ }
+
+ out.print("</tr>\r\n");
+ out.print("</table>\r\n");
+ }
+
+ protected boolean showLob(PrintStream out, String type, byte[] id, long size, CDOServerBrowser browser, String param)
+ {
+ String hex = HexUtil.bytesToHex(id);
+ boolean selected = hex.equals(param);
+ String label = selected ? hex : browser.href(hex, getName(), "id", hex);
+ out.println(type + " " + label + " (" + size + ")");
+ return selected;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class HistoryPage extends AbstractPage
+ {
+ public static final String NAME = "history";
+
+ public HistoryPage()
+ {
+ super(NAME, "Commit Infos");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return true;
+ }
+
+ public void display(final CDOServerBrowser browser, InternalRepository repository, final PrintStream out)
+ {
+ out.print("<table border=\"0\">\r\n");
+ out.print("<tr>\r\n");
+ out.print("<td valign=\"top\">\r\n");
+
+ IStoreAccessor accessor = repository.getStore().getReader(null);
+ StoreThreadLocal.setAccessor(accessor);
+
+ final String param = browser.getParam("time");
+
+ out.print("<table border=\"1\" cellpadding=\"2\">\r\n");
+ out.print("<tr>\r\n");
+ out.print("<td valign=\"top\">Time</td>\r\n");
+ out.print("<td valign=\"top\">Branch</td>\r\n");
+ out.print("<td valign=\"top\">User</td>\r\n");
+ out.print("<td valign=\"top\">Comment</td>\r\n");
+ out.print("</tr>\r\n");
+
+ final CDOCommitInfo[] details = { null };
+
+ try
+ {
+ final boolean auditing = repository.isSupportingAudits();
+ repository.getCommitInfoManager().getCommitInfos(null, 0L, 0L, new CDOCommitInfoHandler()
+ {
+ public void handleCommitInfo(CDOCommitInfo commitInfo)
+ {
+ if (showCommitInfo(out, commitInfo, browser, param, auditing))
+ {
+ details[0] = commitInfo;
+ }
+ }
+ });
+
+ out.print("</table>\r\n");
+ out.print("</td>\r\n");
+ out.print("<td>&nbsp;&nbsp;&nbsp;</td>\r\n");
+ out.print("<td valign=\"top\">\r\n");
+
+ if (auditing)
+ {
+ CDOCommitInfo commitInfo = details[0];
+ if (commitInfo != null)
+ {
+ out.print("<h3>Commit Info " + commitInfo.getTimeStamp() + "</h3>\r\n");
+ showCommitData(out, commitInfo, browser);
+ }
+ }
+ else
+ {
+ out.print("<h3>No audit data available in this repository.</h3>\r\n");
+ }
+
+ out.print("</td>\r\n");
+ out.print("</tr>\r\n");
+ out.print("</table>\r\n");
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+ }
+
+ protected boolean showCommitInfo(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser, String param,
+ boolean auditing)
+ {
+ String timeStamp = String.valueOf(commitInfo.getTimeStamp());
+ boolean selected = timeStamp.equals(param);
+
+ String formatted = CDOCommonUtil.formatTimeStamp(commitInfo.getTimeStamp()).replaceAll(" ", "&nbsp;");
+ String label = formatted;
+ if (!selected && auditing)
+ {
+ label = browser.href(formatted, getName(), "time", timeStamp);
+ }
+
+ out.print("<tr>\r\n");
+ out.print("<td valign=\"top\">\r\n");
+ out.print(label);
+ out.print("</td>\r\n");
+
+ CDOBranch branch = commitInfo.getBranch();
+ out.print("<td valign=\"top\">\r\n");
+ out.print(branch.getName() + "[" + branch.getID() + "]");
+ out.print("</td>\r\n");
+
+ String userID = commitInfo.getUserID();
+ out.print("<td valign=\"top\">\r\n");
+ out.print(StringUtil.isEmpty(userID) ? "&nbsp;" : browser.escape(userID));
+ out.print("</td>\r\n");
+
+ String comment = commitInfo.getComment();
+ out.print("<td valign=\"top\">\r\n");
+ out.print(StringUtil.isEmpty(comment) ? "&nbsp;" : browser.escape(comment));
+ out.print("</td>\r\n");
+
+ out.print("</tr>\r\n");
+ return selected;
+ }
+
+ protected void showCommitData(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser)
+ {
+ out.print("<h4>New Objects:</h4>\r\n");
+ out.print("<ul>\r\n");
+ for (CDOIDAndVersion key : commitInfo.getNewObjects())
+ {
+ CDORevision newObject = (CDORevision)key;
+ out.print("<li>"
+ + browser.href(newObject.toString(), RevisionsPage.FromStore.NAME, "revision",
+ CDORevisionUtil.formatRevisionKey(newObject)) + "<br>\r\n");
+ }
+
+ out.print("</ul>\r\n");
+ out.print("<h4>Changed Objects:</h4>\r\n");
+ out.print("<ul>\r\n");
+ for (CDORevisionKey key : commitInfo.getChangedObjects())
+ {
+ CDORevisionDelta changedObject = (CDORevisionDelta)key;
+ out.print("<li>" + changedObject.toString() + "<br>\r\n");
+ }
+
+ out.print("</ul>\r\n");
+ out.print("<h4>Detached Objects:</h4>\r\n");
+ out.print("<ul>\r\n");
+ for (CDOIDAndVersion key : commitInfo.getDetachedObjects())
+ {
+ out.print("<li>" + key.toString() + "<br>\r\n");
+ }
+
+ out.print("</ul>\r\n");
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java
new file mode 100644
index 0000000000..cc9a2adedb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java
@@ -0,0 +1,695 @@
+/**
+ * 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;
+
+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.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.lob.CDOBlob;
+import org.eclipse.emf.cdo.common.lob.CDOClob;
+import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.model.CDOClassInfo;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
+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.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.io.XMLOutput;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMap;
+import org.eclipse.emf.ecore.util.FeatureMapUtil;
+
+import org.xml.sax.SAXException;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public abstract class CDOServerExporter<OUT>
+{
+ private InternalRepository repository;
+
+ public CDOServerExporter(IRepository repository)
+ {
+ this.repository = (InternalRepository)repository;
+ }
+
+ public final IRepository getRepository()
+ {
+ return repository;
+ }
+
+ public final void exportRepository(OutputStream out) throws Exception
+ {
+ boolean wasActive = LifecycleUtil.isActive(repository);
+ if (!wasActive)
+ {
+ LifecycleUtil.activate(repository);
+ }
+
+ InternalSession session = repository.getSessionManager().openSession(null);
+ StoreThreadLocal.setSession(session);
+
+ try
+ {
+ OUT output = createOutput(out);
+ exportAll(output);
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ if (!wasActive)
+ {
+ LifecycleUtil.deactivate(repository);
+ }
+
+ repository = null;
+ }
+ }
+
+ protected abstract OUT createOutput(OutputStream out) throws Exception;
+
+ protected void exportAll(final OUT out) throws Exception
+ {
+ try
+ {
+ exportPackages(out);
+ exportBranches(out);
+ exportLobs(out);
+ exportCommits(out);
+ }
+ catch (WrappedException ex)
+ {
+ throw WrappedException.unwrap(ex);
+ }
+ }
+
+ protected void exportPackages(OUT out) throws Exception
+ {
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ InternalCDOPackageUnit[] packageUnits = packageRegistry.getPackageUnits(false);
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ String id = packageUnit.getID();
+ CDOPackageUnit.Type type = packageUnit.getOriginalType();
+ long time = packageUnit.getTimeStamp();
+
+ EPackage ePackage = packageUnit.getTopLevelPackageInfo().getEPackage();
+ String data = new String(EMFUtil.getEPackageBytes(ePackage, false, packageRegistry));
+
+ startPackageUnit(out, id, type, time, data);
+ for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos())
+ {
+ String packageURI = packageInfo.getPackageURI();
+ exportPackageInfo(out, packageURI);
+ }
+
+ endPackageUnit(out);
+ }
+ }
+
+ protected abstract void startPackageUnit(OUT out, String id, CDOPackageUnit.Type type, long time, String data)
+ throws Exception;
+
+ protected abstract void endPackageUnit(OUT out) throws Exception;
+
+ protected abstract void exportPackageInfo(OUT out, String packageURI) throws Exception;
+
+ protected void exportBranches(final OUT out) throws Exception
+ {
+ InternalCDOBranchManager branchManager = repository.getBranchManager();
+ exportBranch(out, branchManager.getMainBranch());
+
+ if (repository.isSupportingBranches())
+ {
+ branchManager.getBranches(0, 0, new CDOBranchHandler()
+ {
+ public void handleBranch(CDOBranch branch)
+ {
+ try
+ {
+ exportBranch(out, branch);
+ }
+ catch (Exception ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ });
+ }
+ }
+
+ protected void exportBranch(OUT out, CDOBranch branch) throws Exception
+ {
+ exportRevisions(out, branch);
+ }
+
+ protected void exportRevisions(final OUT out, CDOBranch branch) throws Exception
+ {
+ repository.handleRevisions(null, branch, true, CDOBranchPoint.INVALID_DATE, false,
+ new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler()
+ {
+ public boolean handleRevision(CDORevision revision)
+ {
+ try
+ {
+ exportRevision(out, revision);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ }));
+ }
+
+ protected abstract void exportRevision(OUT out, CDORevision revision) throws Exception;
+
+ protected void exportLobs(final OUT out) throws Exception
+ {
+ repository.handleLobs(0, 0, new CDOLobHandler()
+ {
+ public OutputStream handleBlob(byte[] id, long size)
+ {
+ try
+ {
+ return startBlob(out, id, size);
+ }
+ catch (Exception ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+
+ public Writer handleClob(byte[] id, long size)
+ {
+ try
+ {
+ return startClob(out, id, size);
+ }
+ catch (Exception ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ });
+ }
+
+ protected abstract OutputStream startBlob(OUT out, byte[] id, long size) throws Exception;
+
+ protected abstract Writer startClob(OUT out, byte[] id, long size) throws Exception;
+
+ protected void exportCommits(final OUT out) throws Exception
+ {
+ InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager();
+ commitInfoManager.getCommitInfos(null, 0L, 0L, new CDOCommitInfoHandler()
+ {
+ public void handleCommitInfo(CDOCommitInfo commitInfo)
+ {
+ try
+ {
+ exportCommit(out, commitInfo);
+ }
+ catch (Exception ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ });
+ }
+
+ protected abstract void exportCommit(OUT out, CDOCommitInfo commitInfo) throws Exception;
+
+ /**
+ * @author Eike Stepper
+ */
+ public static interface XMLConstants
+ {
+ public static final String REPOSITORY = "repository";
+
+ public static final String REPOSITORY_NAME = "name";
+
+ public static final String REPOSITORY_UUID = "uuid";
+
+ public static final String REPOSITORY_ROOT = "root";
+
+ public static final String REPOSITORY_CREATED = "created";
+
+ public static final String REPOSITORY_COMMITTED = "committed";
+
+ public static final String MODELS = "models";
+
+ public static final String PACKAGE_UNIT = "packageUnit";
+
+ public static final String PACKAGE_UNIT_ID = "id";
+
+ public static final String PACKAGE_UNIT_TYPE = "type";
+
+ public static final String PACKAGE_UNIT_TIME = "time";
+
+ public static final String PACKAGE_UNIT_DATA = "data";
+
+ public static final String PACKAGE_INFO = "packageInfo";
+
+ public static final String PACKAGE_INFO_URI = "uri";
+
+ public static final String INSTANCES = "instances";
+
+ public static final String BRANCH = "branch";
+
+ public static final String BRANCH_ID = "id";
+
+ public static final String BRANCH_NAME = "name";
+
+ public static final String BRANCH_TIME = "time";
+
+ public static final String BRANCH_PARENT = "parent";
+
+ public static final String REVISION = "revision";
+
+ public static final String REVISION_ID = "id";
+
+ public static final String REVISION_CLASS = "class";
+
+ public static final String REVISION_VERSION = "version";
+
+ public static final String REVISION_TIME = "time";
+
+ public static final String REVISION_REVISED = "revised";
+
+ public static final String REVISION_RESOURCE = "resource";
+
+ public static final String REVISION_CONTAINER = "container";
+
+ public static final String REVISION_FEATURE = "feature";
+
+ public static final String FEATURE = "feature";
+
+ public static final String FEATURE_NAME = "name";
+
+ public static final String FEATURE_TYPE = "type";
+
+ public static final String FEATURE_INNER_FEATURE = "innerFeature";
+
+ public static final String FEATURE_INNER_TYPE = "innerType";
+
+ public static final String FEATURE_VALUE = "value";
+
+ public static final String FEATURE_ID = "id";
+
+ public static final String FEATURE_SIZE = "size";
+
+ public static final String TYPE_BLOB = "Blob";
+
+ public static final String TYPE_CLOB = "Clob";
+
+ public static final String TYPE_FEATURE_MAP = "FeatureMap";
+
+ public static final String LOBS = "lobs";
+
+ public static final String LOB_ID = "id";
+
+ public static final String LOB_SIZE = "size";
+
+ public static final String BLOB = "blob";
+
+ public static final String CLOB = "clob";
+
+ public static final String COMMITS = "commits";
+
+ public static final String COMMIT = "commit";
+
+ public static final String COMMIT_TIME = "time";
+
+ public static final String COMMIT_PREVIOUS = "previous";
+
+ public static final String COMMIT_BRANCH = "branch";
+
+ public static final String COMMIT_USER = "user";
+
+ public static final String COMMIT_COMMENT = "comment";
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class XML extends CDOServerExporter<XMLOutput> implements XMLConstants
+ {
+ public XML(IRepository repository)
+ {
+ super(repository);
+ }
+
+ @Override
+ protected final XMLOutput createOutput(OutputStream out) throws Exception
+ {
+ return new XMLOutput(out);
+ }
+
+ @Override
+ protected void exportAll(XMLOutput out) throws Exception
+ {
+ out.element(REPOSITORY);
+ out.attribute(REPOSITORY_NAME, getRepository().getName());
+ out.attribute(REPOSITORY_UUID, getRepository().getUUID());
+ out.attribute(REPOSITORY_ROOT, str(getRepository().getRootResourceID()));
+ out.attribute(REPOSITORY_CREATED, getRepository().getStore().getCreationTime());
+ out.attribute(REPOSITORY_COMMITTED, getRepository().getLastCommitTimeStamp());
+
+ out.push();
+ super.exportAll(out);
+ out.done();
+ }
+
+ @Override
+ protected void exportPackages(XMLOutput out) throws Exception
+ {
+ out.element(MODELS);
+
+ out.push();
+ super.exportPackages(out);
+ out.pop();
+ }
+
+ @Override
+ protected void startPackageUnit(XMLOutput out, String id, CDOPackageUnit.Type type, long time, String data)
+ throws Exception
+ {
+ out.element(PACKAGE_UNIT);
+ out.attribute(PACKAGE_UNIT_ID, id);
+ out.attribute(PACKAGE_UNIT_TYPE, type);
+ out.attribute(PACKAGE_UNIT_TIME, time);
+ out.attribute(PACKAGE_UNIT_DATA, data);
+ out.push();
+ }
+
+ @Override
+ protected void endPackageUnit(XMLOutput out) throws Exception
+ {
+ out.pop();
+ }
+
+ @Override
+ protected void exportPackageInfo(XMLOutput out, String uri) throws Exception
+ {
+ out.element(PACKAGE_INFO);
+ out.attribute(PACKAGE_INFO_URI, uri);
+ }
+
+ @Override
+ protected void exportBranches(XMLOutput out) throws Exception
+ {
+ out.element(INSTANCES);
+
+ out.push();
+ super.exportBranches(out);
+ out.pop();
+ }
+
+ @Override
+ protected void exportBranch(XMLOutput out, CDOBranch branch) throws Exception
+ {
+ out.element(BRANCH);
+ out.attribute(BRANCH_ID, branch.getID());
+ out.attribute(BRANCH_NAME, branch.getName());
+ out.attribute(BRANCH_TIME, branch.getBase().getTimeStamp());
+ if (!branch.isMainBranch())
+ {
+ out.attribute(BRANCH_PARENT, branch.getBase().getBranch().getID());
+ }
+
+ out.push();
+ super.exportBranch(out, branch);
+ out.pop();
+
+ }
+
+ @Override
+ protected void exportRevision(XMLOutput out, CDORevision revision) throws Exception
+ {
+ InternalCDORevision rev = (InternalCDORevision)revision;
+
+ out.element(REVISION);
+ out.attribute(REVISION_ID, str(rev.getID()));
+ out.attribute(REVISION_CLASS, new CDOClassifierRef(rev.getEClass()).getURI());
+ out.attribute(REVISION_VERSION, rev.getVersion());
+ out.attribute(REVISION_TIME, rev.getTimeStamp());
+
+ long revised = rev.getRevised();
+ if (revised != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ out.attribute(REVISION_REVISED, revised);
+ }
+
+ CDOID resourceID = rev.getResourceID();
+ if (!CDOIDUtil.isNull(resourceID))
+ {
+ out.attribute(REVISION_RESOURCE, str(resourceID));
+ }
+
+ CDOID containerID = (CDOID)rev.getContainerID();
+ if (!CDOIDUtil.isNull(containerID))
+ {
+ out.attribute(REVISION_CONTAINER, str(containerID));
+ }
+
+ int containingFeatureID = rev.getContainingFeatureID();
+ if (containingFeatureID != 0)
+ {
+ out.attribute(REVISION_FEATURE, containingFeatureID);
+ }
+
+ out.push();
+ CDOClassInfo classInfo = rev.getClassInfo();
+ for (EStructuralFeature feature : classInfo.getAllPersistentFeatures())
+ {
+ if (feature.isMany())
+ {
+ @SuppressWarnings("unchecked")
+ List<Object> list = (List<Object>)rev.getValue(feature);
+ if (list != null)
+ {
+ for (Object value : list)
+ {
+ exportFeature(out, feature, value);
+ }
+ }
+ }
+ else
+ {
+ Object value = rev.getValue(feature);
+ if (value != null)
+ {
+ exportFeature(out, feature, value);
+ }
+ }
+ }
+
+ out.pop();
+ }
+
+ protected void exportFeature(XMLOutput out, EStructuralFeature feature, Object value) throws Exception
+ {
+ out.element(FEATURE);
+ out.attribute(FEATURE_NAME, feature.getName());
+ exportFeature(out, feature, FEATURE_TYPE, value);
+ }
+
+ protected void exportFeature(XMLOutput out, EStructuralFeature feature, String featureType, Object value)
+ throws SAXException
+ {
+ if (value instanceof CDOID)
+ {
+ out.attribute(featureType, Object.class.getSimpleName());
+ out.attribute(FEATURE_VALUE, str((CDOID)value));
+ }
+ else if (value instanceof CDOBlob)
+ {
+ CDOBlob blob = (CDOBlob)value;
+ out.attribute(featureType, TYPE_BLOB);
+ out.attribute(FEATURE_ID, HexUtil.bytesToHex(blob.getID()));
+ out.attribute(FEATURE_SIZE, blob.getSize());
+ }
+ else if (value instanceof CDOClob)
+ {
+ CDOClob clob = (CDOClob)value;
+ out.attribute(featureType, TYPE_CLOB);
+ out.attribute(FEATURE_ID, HexUtil.bytesToHex(clob.getID()));
+ out.attribute(FEATURE_SIZE, clob.getSize());
+ }
+ else if (value instanceof Date)
+ {
+ Date date = (Date)value;
+ out.attribute(featureType, Date.class.getSimpleName());
+ out.attribute(FEATURE_VALUE, date.getTime());
+ }
+ else if (FeatureMapUtil.isFeatureMap(feature))
+ {
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature innerFeature = entry.getEStructuralFeature();
+ Object innerValue = entry.getValue();
+
+ out.attribute(featureType, TYPE_FEATURE_MAP);
+ out.attribute(FEATURE_INNER_FEATURE, innerFeature.getName());
+ exportFeature(out, innerFeature, FEATURE_INNER_TYPE, innerValue);
+ }
+ else
+ {
+ if (!(value instanceof String))
+ {
+ out.attribute(featureType, type(value));
+ }
+
+ out.attributeOrNull(FEATURE_VALUE, value);
+ }
+ }
+
+ @Override
+ protected void exportLobs(XMLOutput out) throws Exception
+ {
+ out.element(LOBS);
+
+ out.push();
+ super.exportLobs(out);
+ out.pop();
+ }
+
+ @Override
+ protected OutputStream startBlob(XMLOutput out, byte[] id, long size) throws Exception
+ {
+ out.element(BLOB);
+ out.attribute(LOB_ID, HexUtil.bytesToHex(id));
+ out.attribute(LOB_SIZE, size);
+ return out.bytes();
+ }
+
+ @Override
+ protected Writer startClob(XMLOutput out, byte[] id, long size) throws Exception
+ {
+ out.element(CLOB);
+ out.attribute(LOB_ID, HexUtil.bytesToHex(id));
+ out.attribute(LOB_SIZE, size);
+ return out.characters();
+ }
+
+ @Override
+ protected void exportCommits(XMLOutput out) throws Exception
+ {
+ out.element(COMMITS);
+
+ out.push();
+ super.exportCommits(out);
+ out.pop();
+ }
+
+ @Override
+ protected void exportCommit(XMLOutput out, CDOCommitInfo commitInfo) throws Exception
+ {
+ out.element(COMMIT);
+ out.attribute(COMMIT_TIME, commitInfo.getTimeStamp());
+ long previous = commitInfo.getPreviousTimeStamp();
+ if (previous != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ out.attribute(COMMIT_PREVIOUS, previous);
+ }
+
+ int branch = commitInfo.getBranch().getID();
+ if (branch != CDOBranch.MAIN_BRANCH_ID)
+ {
+ out.attribute(COMMIT_BRANCH, branch);
+ }
+
+ out.attribute(COMMIT_USER, commitInfo.getUserID());
+ out.attribute(COMMIT_COMMENT, commitInfo.getComment());
+ }
+
+ protected final String str(CDOID id)
+ {
+ StringBuilder builder = new StringBuilder();
+ CDOIDUtil.write(builder, id);
+ return builder.toString();
+ }
+
+ protected String type(Object value)
+ {
+ if (value instanceof Boolean)
+ {
+ return Boolean.class.getSimpleName();
+ }
+
+ if (value instanceof Character)
+ {
+ return Character.class.getSimpleName();
+ }
+
+ if (value instanceof Byte)
+ {
+ return Byte.class.getSimpleName();
+ }
+
+ if (value instanceof Short)
+ {
+ return Short.class.getSimpleName();
+ }
+
+ if (value instanceof Integer)
+ {
+ return Integer.class.getSimpleName();
+ }
+
+ if (value instanceof Long)
+ {
+ return Long.class.getSimpleName();
+ }
+
+ if (value instanceof Float)
+ {
+ return Float.class.getSimpleName();
+ }
+
+ if (value instanceof Double)
+ {
+ return Double.class.getSimpleName();
+ }
+
+ if (value instanceof String)
+ {
+ return String.class.getSimpleName();
+ }
+
+ throw new IllegalArgumentException("Invalid type: " + value.getClass().getName());
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java
new file mode 100644
index 0000000000..2441d7138e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java
@@ -0,0 +1,663 @@
+/**
+ * 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;
+
+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.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.lob.CDOLobUtil;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit.Type;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.model.EMFUtil.ExtResourceSet;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+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.model.InternalCDOPackageInfo;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageLoader;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.io.AsyncOutputStream;
+import org.eclipse.net4j.util.io.AsyncWriter;
+import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.Monitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ * @apiviz.has {@link CDOServerImporter.Handler}
+ */
+public abstract class CDOServerImporter
+{
+ private InternalRepository repository;
+
+ public CDOServerImporter(IRepository repository)
+ {
+ this.repository = (InternalRepository)repository;
+ init();
+ }
+
+ private void init()
+ {
+ LifecycleUtil.checkInactive(repository);
+ repository.setSkipInitialization(true);
+ repository.getStore().setDropAllDataOnActivate(true);
+ LifecycleUtil.activate(repository);
+ }
+
+ protected final InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ public void importRepository(InputStream in) throws Exception
+ {
+ try
+ {
+ FlushHandler handler = new FlushHandler();
+ importAll(in, handler);
+ handler.flush();
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ repository = null;
+ }
+ }
+
+ protected abstract void importAll(InputStream in, Handler handler) throws Exception;
+
+ /**
+ * @author Eike Stepper
+ */
+ public static interface Handler extends CDORevisionHandler, CDOLobHandler
+ {
+ public void handleRepository(String name, String uuid, CDOID root, long created, long committed);
+
+ public InternalCDOPackageUnit handlePackageUnit(String id, Type type, long time, String data);
+
+ public InternalCDOPackageInfo handlePackageInfo(String packageURI);
+
+ public InternalCDOPackageRegistry handleModels();
+
+ public InternalCDOBranch handleBranch(int id, String name, long time, int parentID);
+
+ public void handleCommitInfo(long time, long previous, int branch, String user, String comment);
+
+ public void flush();
+
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class FlushHandler implements Handler
+ {
+ private OMMonitor monitor = new Monitor();
+
+ private IStoreAccessor.Raw accessor;
+
+ private Map<String, String> models = new HashMap<String, String>();
+
+ private LinkedList<InternalCDOPackageUnit> packageUnits = new LinkedList<InternalCDOPackageUnit>();
+
+ private List<InternalCDOPackageInfo> packageInfos;
+
+ private InternalCDOPackageRegistry packageRegistry = getRepository().getPackageRegistry(false);
+
+ public FlushHandler()
+ {
+ }
+
+ public void handleRepository(String name, String uuid, CDOID root, long created, long committed)
+ {
+ repository.getStore().setCreationTime(created);
+ repository.getStore().setLastCommitTime(committed);
+
+ InternalCDOBranchManager branchManager = repository.getBranchManager();
+ repository.initMainBranch(branchManager, created);
+ LifecycleUtil.activate(branchManager);
+
+ repository.initSystemPackages();
+ repository.setRootResourceID(root);
+
+ // InternalSession session = repository.getSessionManager().openSession(null);
+ // StoreThreadLocal.setSession(session);
+
+ accessor = (IStoreAccessor.Raw)repository.getStore().getWriter(null);
+ StoreThreadLocal.setAccessor(accessor);
+ }
+
+ public InternalCDOPackageUnit handlePackageUnit(String id, Type type, long time, String data)
+ {
+ collectPackageInfos();
+
+ InternalCDOPackageUnit packageUnit = packageRegistry.createPackageUnit();
+ packageUnit.setOriginalType(type);
+ packageUnit.setTimeStamp(time);
+
+ models.put(id, data);
+ packageUnits.add(packageUnit);
+ packageInfos = new ArrayList<InternalCDOPackageInfo>();
+ return packageUnit;
+ }
+
+ public InternalCDOPackageInfo handlePackageInfo(String packageURI)
+ {
+ InternalCDOPackageInfo packageInfo = (InternalCDOPackageInfo)CDOModelUtil.createPackageInfo();
+ packageInfo.setPackageURI(packageURI);
+ packageInfos.add(packageInfo);
+ return packageInfo;
+ }
+
+ public InternalCDOPackageRegistry handleModels()
+ {
+ collectPackageInfos();
+ InternalCDOPackageUnit[] array = packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]);
+ packageUnits = null;
+
+ final ExtResourceSet resourceSet = EMFUtil.createExtResourceSet(packageRegistry, false, false);
+
+ PackageLoader loader = new PackageLoader()
+ {
+ public EPackage[] loadPackages(CDOPackageUnit packageUnit)
+ {
+ String id = packageUnit.getID();
+ String data = models.get(id);
+
+ EPackage ePackage = EMFUtil.createEPackage(id, data.getBytes(), false, resourceSet, true);
+ return EMFUtil.getAllPackages(ePackage);
+ }
+ };
+
+ packageRegistry.putPackageUnits(array, CDOPackageUnit.State.PROXY);
+ for (InternalCDOPackageUnit packageUnit : array)
+ {
+ packageUnit.load(loader, false);
+ }
+
+ // Before we resolve, we configure the resourceSet to start delegating, which means
+ // it will consult the packageRegistry for packages it didn't just load -- such as the Ecore
+ // package
+ resourceSet.setDelegating(true);
+ EMFUtil.safeResolveAll(resourceSet);
+
+ accessor.rawStore(array, monitor);
+
+ return packageRegistry;
+ }
+
+ public InternalCDOBranch handleBranch(int id, String name, long time, int parentID)
+ {
+ InternalCDOBranchManager branchManager = repository.getBranchManager();
+ if (id == CDOBranch.MAIN_BRANCH_ID)
+ {
+ return branchManager.getMainBranch();
+ }
+
+ InternalCDOBranch parent = branchManager.getBranch(parentID);
+ return branchManager.createBranch(id, name, parent, time);
+ }
+
+ public boolean handleRevision(CDORevision revision)
+ {
+ accessor.rawStore((InternalCDORevision)revision, monitor);
+ return true;
+ }
+
+ public OutputStream handleBlob(final byte[] id, final long size) throws IOException
+ {
+ return new AsyncOutputStream()
+ {
+ @Override
+ protected void asyncWrite(InputStream in) throws IOException
+ {
+ accessor.rawStore(id, size, in);
+ }
+ };
+ }
+
+ public Writer handleClob(final byte[] id, final long size) throws IOException
+ {
+ return new AsyncWriter()
+ {
+ @Override
+ protected void asyncWrite(Reader in) throws IOException
+ {
+ accessor.rawStore(id, size, in);
+ }
+ };
+ }
+
+ public void handleCommitInfo(long time, long previous, int branchID, String user, String comment)
+ {
+ CDOBranch branch = repository.getBranchManager().getBranch(branchID);
+ accessor.rawStore(branch, time, previous, user, comment, monitor);
+ }
+
+ public void flush()
+ {
+ accessor.rawCommit(1.0, monitor);
+ }
+
+ private void collectPackageInfos()
+ {
+ if (packageInfos != null)
+ {
+ InternalCDOPackageUnit packageUnit = packageUnits.getLast();
+ packageUnit.setPackageInfos(packageInfos.toArray(new InternalCDOPackageInfo[packageInfos.size()]));
+ packageInfos = null;
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class XML extends CDOServerImporter implements CDOServerExporter.XMLConstants
+ {
+ public XML(IRepository repository)
+ {
+ super(repository);
+ }
+
+ @Override
+ protected void importAll(InputStream in, final Handler handler) throws Exception
+ {
+ DefaultHandler xmlHandler = new XMLHandler(handler);
+
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser saxParser = factory.newSAXParser();
+ saxParser.parse(in, xmlHandler);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class XMLHandler extends DefaultHandler
+ {
+ private Handler handler;
+
+ private InternalCDOPackageRegistry packageRegistry;
+
+ private InternalCDOBranch branch;
+
+ private InternalCDORevision revision;
+
+ private Character blobChar;
+
+ private OutputStream blob;
+
+ private Writer clob;
+
+ private XMLHandler(Handler handler)
+ {
+ this.handler = handler;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
+ {
+ if (REPOSITORY.equals(qName))
+ {
+ String name = attributes.getValue(REPOSITORY_NAME);
+ String uuid = attributes.getValue(REPOSITORY_UUID);
+ CDOID root = id(attributes.getValue(REPOSITORY_ROOT));
+ long created = Long.parseLong(attributes.getValue(REPOSITORY_CREATED));
+ long committed = Long.parseLong(attributes.getValue(REPOSITORY_COMMITTED));
+ handler.handleRepository(name, uuid, root, created, committed);
+ }
+ else if (PACKAGE_UNIT.equals(qName))
+ {
+ String id = attributes.getValue(PACKAGE_UNIT_ID);
+ Type type = CDOPackageUnit.Type.valueOf(attributes.getValue(PACKAGE_UNIT_TYPE));
+ long time = Long.parseLong(attributes.getValue(PACKAGE_UNIT_TIME));
+ String data = attributes.getValue(PACKAGE_UNIT_DATA);
+ handler.handlePackageUnit(id, type, time, data);
+ }
+ else if (PACKAGE_INFO.equals(qName))
+ {
+ String packageURI = attributes.getValue(PACKAGE_INFO_URI);
+ handler.handlePackageInfo(packageURI);
+ }
+ else if (BRANCH.equals(qName))
+ {
+ int id = Integer.parseInt(attributes.getValue(BRANCH_ID));
+ String name = attributes.getValue(BRANCH_NAME);
+ long time = Long.parseLong(attributes.getValue(BRANCH_TIME));
+ String parent = attributes.getValue(BRANCH_PARENT);
+ int parentID = parent == null ? 0 : Integer.parseInt(parent);
+ branch = handler.handleBranch(id, name, time, parentID);
+ }
+ else if (REVISION.equals(qName))
+ {
+ CDOClassifierRef classifierRef = new CDOClassifierRef(attributes.getValue(REVISION_CLASS));
+ EClass eClass = (EClass)classifierRef.resolve(packageRegistry);
+ revision = (InternalCDORevision)CDORevisionFactory.DEFAULT.createRevision(eClass);
+ revision.setID(id(attributes.getValue(REVISION_ID)));
+ revision.setBranchPoint(branch.getPoint(Long.parseLong(attributes.getValue(REVISION_TIME))));
+ revision.setVersion(Integer.parseInt(attributes.getValue(REVISION_VERSION)));
+ String revised = attributes.getValue(REVISION_REVISED);
+ if (revised != null)
+ {
+ revision.setRevised(Long.parseLong(revised));
+ }
+
+ String resourceID = attributes.getValue(REVISION_RESOURCE);
+ if (resourceID != null)
+ {
+ revision.setResourceID(id(resourceID));
+ }
+
+ String containerID = attributes.getValue(REVISION_CONTAINER);
+ if (containerID != null)
+ {
+ revision.setContainerID(id(containerID));
+ }
+
+ String featureID = attributes.getValue(REVISION_FEATURE);
+ if (featureID != null)
+ {
+ revision.setContainingFeatureID(Integer.parseInt(featureID));
+ }
+ }
+ else if (FEATURE.equals(qName))
+ {
+ String name = attributes.getValue(FEATURE_NAME);
+ Object value = value(attributes);
+
+ EClass eClass = revision.getEClass();
+ EStructuralFeature feature = eClass.getEStructuralFeature(name);
+ if (feature == null)
+ {
+ throw new IllegalStateException("Feature " + name + " not found in class " + eClass.getName());
+ }
+
+ if (feature.isMany())
+ {
+ CDOList list = revision.getList(feature);
+ list.add(value);
+ }
+ else
+ {
+ if (value != null)
+ {
+ revision.setValue(feature, value);
+ }
+ }
+ }
+ else if (BLOB.equals(qName))
+ {
+ try
+ {
+ byte[] id = HexUtil.hexToBytes(attributes.getValue(LOB_ID));
+ long size = Long.parseLong(attributes.getValue(LOB_SIZE));
+ blob = handler.handleBlob(id, size);
+ }
+ catch (IOException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ else if (CLOB.equals(qName))
+ {
+ try
+ {
+ byte[] id = HexUtil.hexToBytes(attributes.getValue(LOB_ID));
+ long size = Long.parseLong(attributes.getValue(LOB_SIZE));
+ clob = handler.handleClob(id, size);
+ }
+ catch (IOException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ else if (COMMIT.equals(qName))
+ {
+ long time = Long.parseLong(attributes.getValue(COMMIT_TIME));
+
+ String value = attributes.getValue(COMMIT_PREVIOUS);
+ long previous = value == null ? CDOBranchPoint.UNSPECIFIED_DATE : Long.parseLong(value);
+
+ value = attributes.getValue(COMMIT_BRANCH);
+ int branch = value == null ? CDOBranch.MAIN_BRANCH_ID : Integer.parseInt(value);
+
+ String user = attributes.getValue(COMMIT_USER);
+ String comment = attributes.getValue(COMMIT_COMMENT);
+
+ handler.handleCommitInfo(time, previous, branch, user, comment);
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException
+ {
+ if (blob != null)
+ {
+ try
+ {
+ if (blobChar != null)
+ {
+ char[] firstChars = { blobChar, ch[start] };
+ blobChar = null;
+
+ byte[] firstByte = HexUtil.hexToBytes(new String(firstChars));
+ blob.write(firstByte, 0, 1);
+
+ ++start;
+ --length;
+ }
+
+ if ((length & 1) == 1) // odd length?
+ {
+ --length;
+ blobChar = ch[length];
+ }
+
+ if (start != 0 || length != ch.length)
+ {
+ char[] tmp = new char[length];
+ System.arraycopy(ch, start, tmp, 0, length);
+ ch = tmp;
+ }
+
+ byte[] buf = HexUtil.hexToBytes(new String(ch));
+ blob.write(buf);
+ }
+ catch (IOException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ else if (clob != null)
+ {
+ try
+ {
+ clob.write(ch, start, length);
+ }
+ catch (IOException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException
+ {
+ if (MODELS.equals(qName))
+ {
+ packageRegistry = handler.handleModels();
+ }
+ else if (BRANCH.equals(qName))
+ {
+ branch = null;
+ }
+ else if (REVISION.equals(qName))
+ {
+ handler.handleRevision(revision);
+ revision = null;
+ }
+ else if (BLOB.equals(qName))
+ {
+ IOUtil.close(blob);
+ blob = null;
+ }
+ else if (CLOB.equals(qName))
+ {
+ IOUtil.close(clob);
+ clob = null;
+ }
+ }
+
+ protected final CDOID id(String str)
+ {
+ return CDOIDUtil.read(str);
+ }
+
+ protected Object value(Attributes attributes)
+ {
+ String type = attributes.getValue(FEATURE_TYPE);
+
+ if (TYPE_BLOB.equals(type))
+ {
+ byte[] id = HexUtil.hexToBytes(attributes.getValue(FEATURE_ID));
+ long size = Long.parseLong(attributes.getValue(FEATURE_SIZE));
+ return CDOLobUtil.createBlob(id, size);
+ }
+
+ if (TYPE_CLOB.equals(type))
+ {
+ byte[] id = HexUtil.hexToBytes(attributes.getValue(FEATURE_ID));
+ long size = Long.parseLong(attributes.getValue(FEATURE_SIZE));
+ return CDOLobUtil.createClob(id, size);
+ }
+
+ if (TYPE_FEATURE_MAP.equals(type))
+ {
+ String innerFeatureName = attributes.getValue(FEATURE_INNER_FEATURE);
+ EStructuralFeature innerFeature = revision.getEClass().getEStructuralFeature(innerFeatureName);
+
+ String innerType = attributes.getValue(FEATURE_INNER_TYPE);
+ Object innerValue = value(attributes, innerType);
+
+ return CDORevisionUtil.createFeatureMapEntry(innerFeature, innerValue);
+ }
+
+ return value(attributes, type);
+ }
+
+ protected Object value(Attributes attributes, String type)
+ {
+ String str = attributes.getValue(FEATURE_VALUE);
+ if (str == null)
+ {
+ return null;
+ }
+
+ if (type == null || String.class.getSimpleName().equals(type))
+ {
+ return str;
+ }
+
+ if (Object.class.getSimpleName().equals(type))
+ {
+ return id(str);
+ }
+
+ if (Boolean.class.getSimpleName().equals(type))
+ {
+ return Boolean.valueOf(str);
+ }
+
+ if (Character.class.getSimpleName().equals(type))
+ {
+ return str.charAt(0);
+ }
+
+ if (Byte.class.getSimpleName().equals(type))
+ {
+ return Byte.valueOf(str);
+ }
+
+ if (Short.class.getSimpleName().equals(type))
+ {
+ return Short.valueOf(str);
+ }
+
+ if (Integer.class.getSimpleName().equals(type))
+ {
+ return Integer.valueOf(str);
+ }
+
+ if (Long.class.getSimpleName().equals(type))
+ {
+ return Long.valueOf(str);
+ }
+
+ if (Float.class.getSimpleName().equals(type))
+ {
+ return Float.valueOf(str);
+ }
+
+ if (Double.class.getSimpleName().equals(type))
+ {
+ return Double.valueOf(str);
+ }
+
+ if (Date.class.getSimpleName().equals(type))
+ {
+ return new Date(Long.valueOf(str));
+ }
+
+ throw new IllegalArgumentException("Invalid type: " + type);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java
new file mode 100644
index 0000000000..e609c536b1
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java
@@ -0,0 +1,299 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
+import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
+import org.eclipse.emf.cdo.internal.server.Repository;
+import org.eclipse.emf.cdo.internal.server.ServerCDOView;
+import org.eclipse.emf.cdo.internal.server.SessionManager;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration;
+import org.eclipse.emf.cdo.internal.server.syncing.FailoverParticipant;
+import org.eclipse.emf.cdo.internal.server.syncing.OfflineClone;
+import org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer;
+import org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration;
+import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory;
+import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
+import org.eclipse.emf.cdo.spi.common.revision.ManagedRevisionProvider;
+import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+import org.eclipse.emf.cdo.spi.server.InternalStore;
+import org.eclipse.emf.cdo.spi.server.RepositoryFactory;
+import org.eclipse.emf.cdo.view.CDOView;
+
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.OMPlatform;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.exclude
+ */
+public final class CDOServerUtil
+{
+ private CDOServerUtil()
+ {
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled,
+ CDORevisionProvider revisionProvider)
+ {
+ return new ServerCDOView((InternalSession)session, branchPoint, legacyModeEnabled, revisionProvider);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled)
+ {
+ CDORevisionManager revisionManager = session.getManager().getRepository().getRevisionManager();
+ CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branchPoint);
+ return new ServerCDOView((InternalSession)session, branchPoint, legacyModeEnabled, revisionProvider);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static CDOView openView(IView view, boolean legacyModeEnabled)
+ {
+ ISession session = view.getSession();
+ CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view);
+ return openView(session, branchPoint, legacyModeEnabled, view);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static CDOView openView(IStoreAccessor.CommitContext commitContext, boolean legacyModeEnabled)
+ {
+ ISession session = commitContext.getTransaction().getSession();
+ CDOBranchPoint branchPoint = commitContext.getBranchPoint();
+ return openView(session, branchPoint, legacyModeEnabled, commitContext);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static CDOSessionConfiguration createSessionConfiguration()
+ {
+ return new EmbeddedClientSessionConfiguration();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static ISessionManager createSessionManager()
+ {
+ return new SessionManager();
+ }
+
+ public static IRepository createRepository(String name, IStore store, Map<String, String> props)
+ {
+ Repository repository = new Repository.Default();
+ initRepository(repository, name, store, props);
+ return repository;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static IRepositorySynchronizer createRepositorySynchronizer(
+ CDOSessionConfigurationFactory remoteSessionConfigurationFactory)
+ {
+ RepositorySynchronizer synchronizer = new RepositorySynchronizer();
+ synchronizer.setRemoteSessionConfigurationFactory(remoteSessionConfigurationFactory);
+ return synchronizer;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static ISynchronizableRepository createOfflineClone(String name, IStore store, Map<String, String> props,
+ IRepositorySynchronizer synchronizer)
+ {
+ OfflineClone repository = new OfflineClone();
+ initRepository(repository, name, store, props);
+ repository.setSynchronizer((InternalRepositorySynchronizer)synchronizer);
+ return repository;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static ISynchronizableRepository createFailoverParticipant(String name, IStore store,
+ Map<String, String> props, IRepositorySynchronizer synchronizer, boolean master, boolean allowBackupCommits)
+ {
+ FailoverParticipant repository = new FailoverParticipant();
+ initRepository(repository, name, store, props);
+ repository.setSynchronizer((InternalRepositorySynchronizer)synchronizer);
+ repository.setType(master ? CDOCommonRepository.Type.MASTER : CDOCommonRepository.Type.BACKUP);
+ return repository;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static ISynchronizableRepository createFailoverParticipant(String name, IStore store,
+ Map<String, String> props, IRepositorySynchronizer synchronizer, boolean master)
+ {
+ return createFailoverParticipant(name, store, props, synchronizer, master, false);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static ISynchronizableRepository createFailoverParticipant(String name, IStore store,
+ Map<String, String> props, IRepositorySynchronizer synchronizer)
+ {
+ return createFailoverParticipant(name, store, props, synchronizer, false);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, Map<String, String> props)
+ {
+ return createFailoverParticipant(name, store, props, null);
+ }
+
+ private static void initRepository(Repository repository, String name, IStore store, Map<String, String> props)
+ {
+ repository.setName(name);
+ repository.setStore((InternalStore)store);
+ repository.setProperties(props);
+ }
+
+ public static void addRepository(IManagedContainer container, IRepository repository)
+ {
+ container.putElement(RepositoryFactory.PRODUCT_GROUP, RepositoryFactory.TYPE, repository.getName(), repository);
+ LifecycleUtil.activate(repository);
+ }
+
+ public static IRepository getRepository(IManagedContainer container, String name)
+ {
+ return RepositoryFactory.get(container, name);
+ }
+
+ public static Element getRepositoryConfig(String repositoryName) throws ParserConfigurationException, SAXException,
+ IOException
+ {
+ File configFile = OMPlatform.INSTANCE.getConfigFile("cdo-server.xml"); //$NON-NLS-1$
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(configFile);
+ NodeList elements = document.getElementsByTagName("repository"); //$NON-NLS-1$
+ for (int i = 0; i < elements.getLength(); i++)
+ {
+ Node node = elements.item(i);
+ if (node instanceof Element)
+ {
+ Element element = (Element)node;
+ String name = element.getAttribute("name"); //$NON-NLS-1$
+ if (ObjectUtil.equals(name, repositoryName))
+ {
+ return element;
+ }
+ }
+ }
+
+ throw new IllegalStateException("Repository config not found: " + repositoryName); //$NON-NLS-1$
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ * @apiviz.exclude
+ */
+ public static abstract class RepositoryReadAccessValidator implements IRepository.ReadAccessHandler
+ {
+ public RepositoryReadAccessValidator()
+ {
+ }
+
+ public void handleRevisionsBeforeSending(ISession session, CDORevision[] revisions,
+ List<CDORevision> additionalRevisions) throws RuntimeException
+ {
+ List<String> violations = new ArrayList<String>();
+ for (CDORevision revision : revisions)
+ {
+ String violation = validate(session, revision);
+ if (violation != null)
+ {
+ violations.add(violation);
+ }
+ }
+
+ if (!violations.isEmpty())
+ {
+ throwException(session, violations);
+ }
+
+ for (Iterator<CDORevision> it = additionalRevisions.iterator(); it.hasNext();)
+ {
+ CDORevision revision = it.next();
+ String violation = validate(session, revision);
+ if (violation != null)
+ {
+ OM.LOG.info("Revision can not be delivered to " + session + ": " + violation); //$NON-NLS-1$ //$NON-NLS-2$
+ it.remove();
+ }
+ }
+ }
+
+ protected void throwException(ISession session, List<String> violations) throws RuntimeException
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Revisions can not be delivered to "); //$NON-NLS-1$
+ builder.append(session);
+ builder.append(":"); //$NON-NLS-1$
+ for (String violation : violations)
+ {
+ builder.append("\n- "); //$NON-NLS-1$
+ builder.append(violation);
+ }
+
+ throwException(builder.toString());
+ }
+
+ protected void throwException(String message) throws RuntimeException
+ {
+ throw new IllegalStateException(message);
+ }
+
+ protected abstract String validate(ISession session, CDORevision revision);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java
new file mode 100644
index 0000000000..0a71e68c00
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java
@@ -0,0 +1,39 @@
+/**
+ * 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;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class ContainmentCycleDetectedException extends IllegalStateException
+{
+ private static final long serialVersionUID = 1L;
+
+ public ContainmentCycleDetectedException()
+ {
+ }
+
+ public ContainmentCycleDetectedException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public ContainmentCycleDetectedException(String s)
+ {
+ super(s);
+ }
+
+ public ContainmentCycleDetectedException(Throwable cause)
+ {
+ super(cause);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java
new file mode 100644
index 0000000000..d73f1ec0b8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.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;
+
+/**
+ * A simple in-memory store.
+ *
+ * @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.
+ * @deprecated Use {@link org.eclipse.emf.cdo.server.mem.IMEMStore}
+ * @apiviz.exclude
+ */
+@Deprecated
+public interface IMEMStore extends org.eclipse.emf.cdo.server.mem.IMEMStore
+{
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java
new file mode 100644
index 0000000000..9752d12981
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java
@@ -0,0 +1,41 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+
+/**
+ * @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.
+ * @apiviz.exclude
+ */
+public interface IQueryContext extends CDOBranchPoint
+{
+ public IView getView();
+
+ /**
+ * @since 4.0
+ */
+ public int getResultCount();
+
+ /**
+ * Adds the given object to the results of the associated query.
+ *
+ * @param object
+ * Support many primitives, CDOID and CDORevision. CDORevision are converted in CDOID and only CDOID are
+ * transfered to the client.
+ * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise
+ * (i.e. maxResults has been reached or an asynchronous query has been canceled).
+ */
+ public boolean addResult(Object object);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java
new file mode 100644
index 0000000000..65e2795182
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java
@@ -0,0 +1,25 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public interface IQueryHandler
+{
+ /**
+ * @since 3.0
+ */
+ public void executeQuery(CDOQueryInfo info, IQueryContext context);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java
new file mode 100644
index 0000000000..944d2d7251
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.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;
+
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ * @apiviz.uses {@link IQueryHandler} - - provides
+ */
+public interface IQueryHandlerProvider
+{
+ /**
+ * @since 3.0
+ */
+ public IQueryHandler getQueryHandler(CDOQueryInfo info);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java
new file mode 100644
index 0000000000..a9d8de729f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java
@@ -0,0 +1,264 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.branch.CDOBranchManager;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager;
+import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
+
+import org.eclipse.net4j.util.container.IContainer;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EPackage.Registry;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @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.
+ * @apiviz.landmark
+ * @apiviz.has {@link IStore}
+ * @apiviz.has {@link java.util.Map} oneway - - properties
+ * @apiviz.has {@link org.eclipse.emf.cdo.common.model.CDOPackageRegistry}
+ * @apiviz.has {@link org.eclipse.emf.cdo.common.branch.CDOBranchManager}
+ * @apiviz.has {@link org.eclipse.emf.cdo.common.revision.CDORevisionManager}
+ * @apiviz.has {@link org.eclipse.emf.cdo.common.lock.IDurableLockingManager}
+ * @apiviz.has {@link ISessionManager}
+ * @apiviz.has {@link IQueryHandlerProvider}
+ * @apiviz.composedOf {@link org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler}
+ * @apiviz.composedOf {@link IRepository.Handler} - - accessHandlers
+ */
+public interface IRepository extends CDOCommonRepository, IQueryHandlerProvider, IContainer<Object>
+{
+ /**
+ * @since 3.0
+ */
+ public static final String SYSTEM_USER_ID = "CDO_SYSTEM"; //$NON-NLS-1$
+
+ public IStore getStore();
+
+ public Map<String, String> getProperties();
+
+ /**
+ * Returns the EMF {@link Registry package registry} that is used by this repository.
+ *
+ * @since 2.0
+ */
+ public CDOPackageRegistry getPackageRegistry();
+
+ /**
+ * @since 3.0
+ */
+ public CDOBranchManager getBranchManager();
+
+ /**
+ * @since 3.0
+ */
+ public CDORevisionManager getRevisionManager();
+
+ public ISessionManager getSessionManager();
+
+ /**
+ * @since 4.0
+ */
+ public IDurableLockingManager getLockManager();
+
+ /**
+ * @since 2.0
+ */
+ public IQueryHandlerProvider getQueryHandlerProvider();
+
+ /**
+ * Returns the time stamp of the last commit operation.
+ *
+ * @since 3.0
+ */
+ public long getLastCommitTimeStamp();
+
+ /**
+ * Blocks the calling thread until the next commit operation has succeeded and returns the last (highest) commit time
+ * stamp.
+ *
+ * @since 3.0
+ */
+ public long waitForCommit(long timeout);
+
+ /**
+ * Validates the given timeStamp against the repository time.
+ *
+ * @throws IllegalArgumentException
+ * if the given timeStamp is less than the repository creation time or greater than the current repository
+ * time.
+ * @since 2.0
+ */
+ public void validateTimeStamp(long timeStamp) throws IllegalArgumentException;
+
+ /**
+ * @since 4.0
+ */
+ public void addCommitInfoHandler(CDOCommitInfoHandler handler);
+
+ /**
+ * @since 4.0
+ */
+ public void removeCommitInfoHandler(CDOCommitInfoHandler handler);
+
+ /**
+ * @since 2.0
+ */
+ public void addHandler(Handler handler);
+
+ /**
+ * @since 2.0
+ */
+ public void removeHandler(Handler handler);
+
+ /**
+ * @since 4.0
+ */
+ public void setInitialPackages(EPackage... initialPackages);
+
+ /**
+ * A marker interface to indicate valid arguments to {@link IRepository#addHandler(Handler)} and
+ * {@link IRepository#removeHandler(Handler)}.
+ *
+ * @see ReadAccessHandler
+ * @see WriteAccessHandler
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public interface Handler
+ {
+ }
+
+ /**
+ * Provides a way to handle revisions that are to be sent to the client.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public interface ReadAccessHandler extends Handler
+ {
+ /**
+ * Provides a way to handle revisions that are to be sent to the client.
+ *
+ * @param session
+ * The session that is going to send the revisions.
+ * @param revisions
+ * The revisions that are requested by the client. If the client must not see any of these revisions an
+ * unchecked exception must be thrown.
+ * @param additionalRevisions
+ * The additional revisions that are to be sent to the client because internal optimizers believe that they
+ * will be needed soon. If the client must not see any of these revisions they should be removed from the
+ * list.
+ * @throws RuntimeException
+ * to indicate that none of the revisions must be sent to the client. This exception will be visible at
+ * the client side!
+ */
+ public void handleRevisionsBeforeSending(ISession session, CDORevision[] revisions,
+ List<CDORevision> additionalRevisions) throws RuntimeException;
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public interface WriteAccessHandler extends Handler
+ {
+ /**
+ * Provides a way to handle transactions that are to be committed to the backend store.
+ *
+ * @param transaction
+ * The transaction that is going to be committed.
+ * @param commitContext
+ * The context of the commit operation that is to be executed against the backend store. The context can be
+ * used to introspect all aspects of the current commit operation. <b>Note that you must not alter the
+ * internal state of the commit context in any way!</b>
+ * @param monitor
+ * A monitor that should be used by the implementor to avoid timeouts.
+ * @throws RuntimeException
+ * to indicate that the commit operation must not be executed against the backend store. This exception
+ * will be visible at the client side!
+ */
+ public void handleTransactionBeforeCommitting(ITransaction transaction, IStoreAccessor.CommitContext commitContext,
+ OMMonitor monitor) throws RuntimeException;
+
+ /**
+ * Provides a way to handle transactions after they have been committed to the backend store.
+ *
+ * @param transaction
+ * The transaction that has been committed.
+ * @param commitContext
+ * The context of the commit operation that was executed against the backend store. The context can be used
+ * to introspect all aspects of the current commit operation. <b>Note that you must not alter the internal
+ * state of the commit context in any way!</b>
+ * @param monitor
+ * A monitor that should be used by the implementor to avoid timeouts.
+ * @since 3.0
+ */
+ public void handleTransactionAfterCommitted(ITransaction transaction, IStoreAccessor.CommitContext commitContext,
+ OMMonitor monitor);
+ }
+
+ /**
+ * @author Eike Stepper
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ * @apiviz.exclude
+ */
+ public interface Props
+ {
+ /**
+ * Used to override the automatic UUID generation during first startup of a repository. Passing the empty string
+ * causes the UUID of the repository to be set to its {@link IRepository#getName() name}.
+ *
+ * @since 2.0
+ */
+ public static final String OVERRIDE_UUID = "overrideUUID"; //$NON-NLS-1$
+
+ /**
+ * @since 2.0
+ */
+ public static final String SUPPORTING_AUDITS = "supportingAudits"; //$NON-NLS-1$
+
+ /**
+ * @since 3.0
+ */
+ public static final String SUPPORTING_BRANCHES = "supportingBranches"; //$NON-NLS-1$
+
+ /**
+ * @since 4.0
+ */
+ public static final String SUPPORTING_ECORE = "supportingEcore"; //$NON-NLS-1$
+
+ /**
+ * @since 3.0
+ */
+ public static final String ENSURE_REFERENTIAL_INTEGRITY = "ensureReferentialIntegrity"; //$NON-NLS-1$
+
+ /**
+ * @since 4.0
+ */
+ public static final String ALLOW_INTERRUPT_RUNNING_QUERIES = "allowInterruptRunningQueries"; //$NON-NLS-1$
+
+ /**
+ * @since 4.1
+ */
+ public static final String ID_GENERATION_LOCATION = "idGenerationLocation"; //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java
new file mode 100644
index 0000000000..9e2d276c5c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java
@@ -0,0 +1,27 @@
+/**
+ * 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;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.uses {@link IRepository} - - creates
+ */
+public interface IRepositoryFactory
+{
+ /**
+ * @since 2.0
+ */
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.repositories"; //$NON-NLS-1$
+
+ public String getRepositoryType();
+
+ public IRepository createRepository();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java
new file mode 100644
index 0000000000..336775093f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java
@@ -0,0 +1,20 @@
+/**
+ * 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;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.uses {@link IRepository} - - provides
+ */
+public interface IRepositoryProvider
+{
+ public IRepository getRepository(String name);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java
new file mode 100644
index 0000000000..cae1cc836a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.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
+ */
+package org.eclipse.emf.cdo.server;
+
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory;
+
+import org.eclipse.net4j.util.event.INotifier;
+
+/**
+ * @author Eike Stepper
+ * @since 3.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @apiviz.landmark
+ * @apiviz.has {@link org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory} oneway - - remote
+ */
+public interface IRepositorySynchronizer extends INotifier
+{
+ public int getRetryInterval();
+
+ public void setRetryInterval(int retryInterval);
+
+ public ISynchronizableRepository getLocalRepository();
+
+ public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory();
+
+ public CDOSession getRemoteSession();
+
+ public boolean isRawReplication();
+
+ /**
+ * @since 4.0
+ */
+ public void setRawReplication(boolean rawReplication);
+
+ public int getMaxRecommits();
+
+ public void setMaxRecommits(int maxRecommits);
+
+ public int getRecommitInterval();
+
+ public void setRecommitInterval(int recommitInterval);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java
new file mode 100644
index 0000000000..386af3d069
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java
@@ -0,0 +1,60 @@
+/**
+ * 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
+ * Simon McDuff - bug 230832
+ */
+package org.eclipse.emf.cdo.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonSession;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.spi.server.ISessionProtocol;
+
+import org.eclipse.net4j.util.container.IContainer;
+
+/**
+ * @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.
+ * @apiviz.landmark
+ * @apiviz.has {@link org.eclipse.emf.cdo.spi.server.ISessionProtocol}
+ * @apiviz.composedOf {@link IView} - - views
+ * @apiviz.composedOf {@link ITransaction} - - transactions
+ */
+public interface ISession extends CDOCommonSession, IContainer<IView>
+{
+ /**
+ * @since 3.0
+ */
+ public ISessionManager getManager();
+
+ /**
+ * @since 3.0
+ */
+ public ISessionProtocol getProtocol();
+
+ /**
+ * @since 4.0
+ */
+ public long getLastUpdateTime();
+
+ /**
+ * @since 2.0
+ */
+ public boolean isSubscribed();
+
+ /**
+ * @since 3.0
+ */
+ public IView openView(int viewID, CDOBranchPoint branchPoint);
+
+ /**
+ * @since 3.0
+ */
+ public ITransaction openTransaction(int viewID, CDOBranchPoint branchPoint);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java
new file mode 100644
index 0000000000..795a835614
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java
@@ -0,0 +1,35 @@
+/**
+ * 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;
+
+import org.eclipse.net4j.util.container.IContainer;
+
+/**
+ * @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.
+ * @apiviz.landmark
+ * @apiviz.composedOf {@link ISession}
+ */
+public interface ISessionManager extends IContainer<ISession>
+{
+ /**
+ * @since 2.0
+ */
+ public IRepository getRepository();
+
+ public ISession[] getSessions();
+
+ /**
+ * @since 2.0
+ */
+ public ISession getSession(int sessionID);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java
new file mode 100644
index 0000000000..a94202dc8c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java
@@ -0,0 +1,213 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+
+import org.eclipse.net4j.util.om.monitor.ProgressDistributor;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.landmark
+ * @apiviz.has {@link IStore.ChangeFormat}
+ * @apiviz.has {@link IStore.RevisionTemporality}
+ * @apiviz.has {@link IStore.RevisionParallelism}
+ * @apiviz.uses {@link IStoreAccessor} - - creates
+ */
+public interface IStore
+{
+ /**
+ * @since 2.0
+ */
+ public IRepository getRepository();
+
+ /**
+ * @since 2.0
+ */
+ public String getType();
+
+ /**
+ * @since 3.0
+ */
+ public Set<CDOID.ObjectType> getObjectIDTypes();
+
+ /**
+ * @since 4.0
+ */
+ public CDOID createObjectID(String val);
+
+ /**
+ * @since 2.0
+ */
+ public Set<ChangeFormat> getSupportedChangeFormats();
+
+ /**
+ * @since 2.0
+ */
+ public Set<RevisionTemporality> getSupportedRevisionTemporalities();
+
+ /**
+ * @since 2.0
+ */
+ public Set<RevisionParallelism> getSupportedRevisionParallelisms();
+
+ /**
+ * @since 2.0
+ */
+ public RevisionTemporality getRevisionTemporality();
+
+ /**
+ * @since 2.0
+ */
+ public RevisionParallelism getRevisionParallelism();
+
+ /**
+ * Returns <code>true</code>if this store was activated for the first time, <code>false</code> otherwise.
+ *
+ * @since 4.0
+ */
+ public boolean isFirstStart();
+
+ /**
+ * Returns the store creation time.
+ *
+ * @since 2.0
+ */
+ public long getCreationTime();
+
+ /**
+ * Returns the id of the last branch that has been created in this store.
+ *
+ * @since 3.0
+ */
+ public int getLastBranchID();
+
+ /**
+ * Returns the id of the last local branch that has been created in this store.
+ *
+ * @since 3.0
+ */
+ public int getLastLocalBranchID();
+
+ /**
+ * Returns the time stamp of the last successful commit operation.
+ *
+ * @since 3.0
+ */
+ public long getLastCommitTime();
+
+ /**
+ * Returns the time stamp of the last successful commit operation to a non-local {@link CDOBranch branch}.
+ *
+ * @since 3.0
+ */
+ public long getLastNonLocalCommitTime();
+
+ /**
+ * Returns a map filled with the property entries for the requested property <code>names</code> if names is not
+ * <code>null</code> and not {@link Collection#isEmpty() empty}, all existing property entries otherwise.
+ *
+ * @since 4.0
+ */
+ public Map<String, String> getPersistentProperties(Set<String> names);
+
+ /**
+ * @since 4.0
+ */
+ public void setPersistentProperties(Map<String, String> properties);
+
+ /**
+ * @since 4.0
+ */
+ public void removePersistentProperties(Set<String> names);
+
+ /**
+ * Returns a reader that can be used to read from this store in the context of the given session.
+ *
+ * @param session
+ * The session that should be used as a context for read access or <code>null</code>. The store implementor
+ * is free to interpret and use the session in a manner suitable for him or ignore it at all. It is meant
+ * only as a hint. Implementor can use it as a key into a cache and/or register a
+ * {@link org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter LifecycleEventAdapter} with it to intercept
+ * cleanup on session close. Note however that the session can be <code>null</code>, for example during
+ * startup of the server while the repositories are initialized but before any user session has been opened.
+ * @return a reader that can be used to read from this store in the context of the given session, never
+ * <code>null</code>.
+ * @since 2.0
+ */
+ public IStoreAccessor getReader(ISession session);
+
+ /**
+ * Returns a writer that can be used to write to this store in the context of the given view. The given view is always
+ * marked as a transaction.
+ *
+ * @param transaction
+ * The view that must be used as a context for write access. The store implementor is free to interpret and
+ * use the view in a manner suitable for him or ignore it at all. It is meant only as a hint. Implementor can
+ * use it as a key into a cache and/or register a
+ * {@link org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter LifecycleEventAdapter} with it to intercept
+ * cleanup on view close.
+ * @return a writer that can be used to write to this store in the context of the given view, never <code>null</code>.
+ * @since 2.0
+ */
+ public IStoreAccessor getWriter(ITransaction transaction);
+
+ /**
+ * @since 2.0
+ */
+ public ProgressDistributor getIndicatingCommitDistributor();
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public enum ChangeFormat
+ {
+ REVISION, DELTA
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public enum RevisionTemporality
+ {
+ NONE, AUDITING
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+ public enum RevisionParallelism
+ {
+ NONE, BRANCHING
+ }
+
+ /**
+ * A marker interface for {@link IStore stores} that can handle {@link CDOID IDs} assigned by a
+ * {@link IDGenerationLocation#CLIENT client}, typically {@link ObjectType#UUID UUIDs}.
+ *
+ * @author Eike Stepper
+ * @since 4.1
+ * @apiviz.exclude
+ */
+ public interface CanHandleClientAssignedIDs
+ {
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java
new file mode 100644
index 0000000000..960a813c42
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java
@@ -0,0 +1,729 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+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.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDReference;
+import org.eclipse.emf.cdo.common.lob.CDOBlob;
+import org.eclipse.emf.cdo.common.lob.CDOClob;
+import org.eclipse.emf.cdo.common.lob.CDOLob;
+import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+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.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager.CommitInfoLoader;
+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.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState;
+import org.eclipse.net4j.util.io.ExtendedDataInputStream;
+import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.uses {@link IStoreChunkReader} - - creates
+ */
+public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, CommitInfoLoader
+{
+ /**
+ * Returns the store this accessor is associated with.
+ */
+ public IStore getStore();
+
+ /**
+ * Returns the session this accessor is associated with.
+ *
+ * @since 3.0
+ */
+ public InternalSession getSession();
+
+ /**
+ * Returns the transaction this accessor is associated with if {@link #isReader()} returns <code>false</code>,
+ * <code>null</code> otherwise.
+ *
+ * @since 2.0
+ */
+ public ITransaction getTransaction();
+
+ /**
+ * Returns <code>true</code> if this accessor has been configured for read-only access to the back-end,
+ * <code>false</code> otherwise.
+ *
+ * @since 2.0
+ */
+ public boolean isReader();
+
+ /**
+ * @since 2.0
+ */
+ public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature);
+
+ /**
+ * @since 2.0
+ */
+ public Collection<InternalCDOPackageUnit> readPackageUnits();
+
+ /**
+ * Demand loads a given package proxy that has been created on startup of the repository.
+ * <p>
+ * This method must only load the given package, <b>not</b> possible contained packages.
+ *
+ * @since 2.0
+ */
+ public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit);
+
+ /**
+ * Reads a revision from the back-end that was valid at the given timeStamp in the given branch.
+ *
+ * @since 4.0
+ */
+ public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
+ CDORevisionCacheAdder cache);
+
+ /**
+ * Reads a revision with the given version in the given branch from the back-end.
+ *
+ * @since 4.0
+ */
+ public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
+ CDORevisionCacheAdder cache);
+
+ /**
+ * 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><b>One</b> of the following conditions is met:
+ * <ul>
+ * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#INVALID_DATE INVALID}.
+ * <li>The <code>exactTime</code> parameter is <code>true</code> and the <code>timeStamp</code> parameter is
+ * {@link CDOBranchPoint#UNSPECIFIED_DATE UNSPECIFIED} or equal to <code>revision.getTimeStamp()</code>.
+ * <li>The <code>exactTime</code> parameter is <code>false</code> and the <code>timeStamp</code> parameter is between
+ * <code>revision.getTimeStamp()</code> and <code>revision.getRevised()</code>.
+ * </ul>
+ * </ul>
+ *
+ * @since 4.0
+ */
+ public void handleRevisions(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!
+ *
+ * @since 4.0
+ */
+ public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments);
+
+ /**
+ * Returns the <code>CDOID</code> of the resource node with the given folderID and name if a resource with this
+ * folderID and name exists in the store, <code>null</code> otherwise.
+ *
+ * @since 3.0
+ */
+ public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint);
+
+ /**
+ * @since 2.0
+ */
+ public void queryResources(QueryResourcesContext context);
+
+ /**
+ * @since 3.0
+ */
+ public void queryXRefs(QueryXRefsContext context);
+
+ /**
+ * Determines which of the large objects identified by the given {@link CDOLob#getID() IDs} are known in the backend
+ * represented by this {@link IStoreAccessor} by removing the unknown IDs from the passed collection.
+ * <p>
+ * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object.
+ * <p>
+ * <b>Usage context:</b> This method is only called in the context of a commit operation of a client transaction if
+ * that transaction contains additions of or changes to large objects.
+ *
+ * @param ids
+ * the collection of large object IDs that the unknown IDs are supposed to be removed from.
+ * @since 4.0
+ */
+ public void queryLobs(List<byte[]> ids);
+
+ /**
+ * Serializes the content of the large object identified by the given {@link CDOLob#getID() ID} to the given
+ * <i>stream</i>.
+ * <p>
+ * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object.
+ *
+ * @param id
+ * the ID of the large object whose content is to be written to the <i>stream</i>.
+ * @throws IOException
+ * if the <i>stream</i> could not be written to.
+ * @since 4.0
+ */
+ public void loadLob(byte[] id, OutputStream out) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException;
+
+ /**
+ * @since 2.0
+ */
+ public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
+
+ /**
+ * Called before committing. An instance of this accessor represents an instance of a back-end transaction. Could be
+ * called multiple times before commit it called. {@link IStoreAccessor#commit(OMMonitor)} or
+ * {@link IStoreAccessor#rollback()} will be called after any numbers of
+ * {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)}.
+ * <p>
+ * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and
+ * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads.
+ *
+ * @since 3.0
+ */
+ public void write(InternalCommitContext context, OMMonitor monitor);
+
+ /**
+ * Flushes to the back-end and makes available the data for others.
+ * <p>
+ * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and
+ * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads.
+ * <p>
+ * <b>Note</b>: Implementors should detect if dirty write occurred. In this case it should throw an exception.
+ *
+ * <pre>
+ * if (revision.getVersion() != revisionDelta.getOriginVersion())
+ * {
+ * throw new ConcurrentModificationException(&quot;Trying to update object &quot; + revisionDelta.getID()
+ * + &quot; that was already modified&quot;);
+ * }
+ * </pre>
+ *
+ * @since 2.0
+ */
+ public void commit(OMMonitor monitor);
+
+ /**
+ * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and {@link IStoreAccessor#rollback()}
+ * could be called from different threads.
+ *
+ * @since 2.0
+ */
+ public void rollback();
+
+ public void release();
+
+ /**
+ * Represents the state of a single, logical commit operation which is driven through multiple calls to several
+ * methods on the {@link IStoreAccessor} API. All these method calls get the same <code>CommitContext</code> instance
+ * passed so that the implementor of the {@link IStoreAccessor} can track the state and progress of the commit
+ * operation.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @apiviz.exclude
+ */
+ public interface CommitContext extends CDORevisionProvider
+ {
+ /**
+ * Returns the transactional view (<code>ITransaction</code>) which is the scope of the commit operation represented
+ * by this <code>CommitContext</code>.
+ *
+ * @since 4.0
+ */
+ public ITransaction getTransaction();
+
+ /**
+ * Returns the branch ID and timestamp of this commit operation.
+ *
+ * @since 3.0
+ */
+ public CDOBranchPoint getBranchPoint();
+
+ /**
+ * @since 4.0
+ */
+ public long getPreviousTimeStamp();
+
+ /**
+ * @since 3.0
+ */
+ public String getUserID();
+
+ /**
+ * @since 3.0
+ */
+ public String getCommitComment();
+
+ /**
+ * @since 3.0
+ */
+ public boolean isAutoReleaseLocksEnabled();
+
+ /**
+ * Returns the temporary, transactional package manager associated with the commit operation represented by this
+ * <code>CommitContext</code>. In addition to the packages registered with the session this package manager also
+ * contains the new packages that are part of this commit operation.
+ */
+ public InternalCDOPackageRegistry getPackageRegistry();
+
+ /**
+ * Returns an array of the new package units that are part of the commit operation represented by this
+ * <code>CommitContext</code>.
+ */
+ public InternalCDOPackageUnit[] getNewPackageUnits();
+
+ /**
+ * Returns an array of the new objects that are part of the commit operation represented by this
+ * <code>CommitContext</code>.
+ */
+ public InternalCDORevision[] getNewObjects();
+
+ /**
+ * Returns an array of the dirty objects that are part of the commit operation represented by this
+ * <code>CommitContext</code>.
+ */
+ public InternalCDORevision[] getDirtyObjects();
+
+ /**
+ * Returns an array of the dirty object deltas that are part of the commit operation represented by this
+ * <code>CommitContext</code>.
+ */
+ public InternalCDORevisionDelta[] getDirtyObjectDeltas();
+
+ /**
+ * Returns an array of the removed object that are part of the commit operation represented by this
+ * <code>CommitContext</code>.
+ *
+ * @since 2.0
+ */
+ public CDOID[] getDetachedObjects();
+
+ /**
+ * Returns a map with an {@link EClass} value per {@link CDOID} type.
+ *
+ * @since 4.0
+ */
+ public Map<CDOID, EClass> getDetachedObjectTypes();
+
+ /**
+ * Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the
+ * stream is:
+ * <p>
+ * <ol>
+ * <li> {@link ExtendedDataInputStream#readInt() int}: the number of lobs to be read from the stream.
+ * <li>The following data can be read from the stream in a loop with one iteration per lob in the stream:
+ * <ol>
+ * <li> {@link ExtendedDataInputStream#readByteArray() int + byte[]}: the id of the lob (prepended by the size of the
+ * id).
+ * <li> {@link ExtendedDataInputStream#readLong() long}: the size of the lob. The foollowing interpretation applies:
+ * <ul>
+ * <li>A positive size indicates a {@link CDOBlob blob} and means the number of bytes that can be
+ * {@link IOUtil#copyBinary(java.io.InputStream, java.io.OutputStream) read}.
+ * <li>A negative size indicates a {@link CDOClob clob} and means the number of characters that can be
+ * {@link IOUtil#copyCharacter(java.io.Reader, java.io.Writer) read}.
+ * </ul>
+ * </ol>
+ * </ol>
+ *
+ * @since 4.0
+ */
+ public ExtendedDataInputStream getLobs();
+
+ /**
+ * Returns an unmodifiable map from all temporary IDs to their persistent counter parts.
+ */
+ public Map<CDOID, CDOID> getIDMappings();
+
+ /**
+ * @since 4.0
+ */
+ public CDOCommitInfo createCommitInfo();
+
+ /**
+ * @since 3.0
+ */
+ public String getRollbackMessage();
+
+ /**
+ * @since 4.0
+ */
+ public List<CDOIDReference> getXRefs();
+
+ /**
+ * @since 4.1
+ */
+ public List<LockState<Object, IView>> getPostCommmitLockStates();
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @apiviz.exclude
+ */
+ public interface QueryResourcesContext extends CDOBranchPoint
+ {
+ public CDOID getFolderID();
+
+ public String getName();
+
+ public boolean exactMatch();
+
+ /**
+ * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no
+ * limitation.
+ */
+ public int getMaxResults();
+
+ /**
+ * Adds the CDOID of one resource to the results of the underlying query.
+ *
+ * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise
+ * (i.e. maxResults has been reached or an asynchronous query has been canceled).
+ */
+ public boolean addResource(CDOID resourceID);
+
+ /**
+ * @author Eike Stepper
+ * @since 2.0
+ * @apiviz.exclude
+ */
+ public interface ExactMatch extends QueryResourcesContext
+ {
+ public CDOID getResourceID();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @apiviz.exclude
+ */
+ public interface QueryXRefsContext extends CDOBranchPoint
+ {
+ /**
+ * @since 4.0
+ */
+ public Map<CDOID, EClass> getTargetObjects();
+
+ public EReference[] getSourceReferences();
+
+ /**
+ * @since 4.0
+ */
+ public Map<EClass, List<EReference>> getSourceCandidates();
+
+ /**
+ * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no
+ * limitation.
+ */
+ public int getMaxResults();
+
+ /**
+ * Adds the data of one cross reference to the results of the underlying query.
+ *
+ * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise
+ * (i.e. maxResults has been reached or an asynchronous query has been canceled).
+ */
+ public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex);
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ * @apiviz.exclude
+ */
+ public interface Raw extends IStoreAccessor
+ {
+ /**
+ * Serializes all backend data within the given ranges such that it can be deserialized by the
+ * {@link #rawImport(CDODataInput, int, int, long, long, OMMonitor) rawImport()} method of a different instance of
+ * the same implementation of {@link IStoreAccessor.Raw raw store accessor}.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only
+ * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store
+ * accessor}.
+ * <p>
+ * <b>Usage context:</b> This method is only called in the context of a
+ * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered
+ * from {@link IRepositorySynchronizer}.
+ *
+ * @param out
+ * the <i>stream</i> to serialize the data to.
+ * @param fromBranchID
+ * the {@link CDOBranch#getID() ID} of the first branch to be exported.
+ * @param toBranchID
+ * the {@link CDOBranch#getID() ID} of the last branch to be exported.
+ * @param fromCommitTime
+ * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
+ * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
+ * etc...) to be exported.
+ * @param toCommitTime
+ * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
+ * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
+ * etc...) to be exported.
+ * @throws IOException
+ * if the <i>stream</i> could not be written to.
+ * @throws UnsupportedOperationException
+ * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching.
+ */
+ public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ /**
+ * Deserializes backend data that has been serialized by the {@link #rawExport(CDODataOutput, int, int, long, long)
+ * rawExport()} method of a different instance of the same implementation of {@link IStoreAccessor.Raw raw store
+ * accessor}.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only
+ * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store
+ * accessor}.
+ * <p>
+ * <b>Usage context:</b> This method is only called in the context of a
+ * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered
+ * from {@link IRepositorySynchronizer}.
+ *
+ * @param in
+ * the <i>stream</i> to deserialize the data from.
+ * @param fromBranchID
+ * the {@link CDOBranch#getID() ID} of the first branch to be imported.
+ * @param toBranchID
+ * the {@link CDOBranch#getID() ID} of the last branch to be imported.
+ * @param fromCommitTime
+ * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
+ * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
+ * etc...) to be imported.
+ * @param toCommitTime
+ * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
+ * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
+ * etc...) to be imported.
+ * @throws IOException
+ * if the <i>stream</i> could not be read from.
+ * @throws UnsupportedOperationException
+ * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching.
+ */
+ public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException;
+
+ /**
+ * Stores the given {@link CDOPackageUnit package units} in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor} without going through a regular
+ * {@link IStoreAccessor #commit(OMMonitor) commit}. A regular commit operation would assign new
+ * {@link CDOPackageUnit#getTimeStamp() time stamps}, which is not desired in the context of a replication
+ * operation.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
+ * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
+ * where the accumulated backend changes can be committed atomically.
+ *
+ * @param packageUnits
+ * the package units to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store
+ * accessor}.
+ * @param monitor
+ * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and
+ * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly
+ * to prevent timeouts from expiring in the caller.
+ * @see #rawCommit(double, OMMonitor)
+ */
+ public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
+
+ /**
+ * Stores the given {@link CDORevision revision} in the backend represented by this {@link IStoreAccessor.Raw raw
+ * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. A regular commit
+ * operation would assign new {@link CDORevisionKey#getID() IDs} and {@link CDOBranchPoint#getTimeStamp() time
+ * stamps}, which is not desired in the context of a replication operation.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
+ * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
+ * where the accumulated backend changes can be committed atomically.
+ *
+ * @param revision
+ * the revision to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store
+ * accessor}.
+ * @param monitor
+ * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and
+ * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly
+ * to prevent timeouts from expiring in the caller.
+ * @see #rawCommit(double, OMMonitor)
+ */
+ public void rawStore(InternalCDORevision revision, OMMonitor monitor);
+
+ /**
+ * Stores the given {@link CDOBlob blob} in the backend represented by this {@link IStoreAccessor.Raw raw store
+ * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
+ * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
+ * where the accumulated backend changes can be committed atomically.
+ *
+ * @param id
+ * the {@link CDOBlob#getID() ID} of the blob to be stored in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor}.
+ * @param size
+ * the {@link CDOBlob#getSize() size} of the blob to be stored in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor}.
+ * @param inputStream
+ * the {@link CDOBlob#getContents() contents} of the blob to be stored in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor}.
+ * @see #rawCommit(double, OMMonitor)
+ */
+ public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException;
+
+ /**
+ * Stores the given {@link CDOClob clob} in the backend represented by this {@link IStoreAccessor.Raw raw store
+ * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
+ * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
+ * where the accumulated backend changes can be committed atomically.
+ *
+ * @param id
+ * the {@link CDOClob#getID() ID} of the clob to be stored in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor}.
+ * @param size
+ * the {@link CDOClob#getSize() size} of the clob to be stored in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor}.
+ * @param reader
+ * the {@link CDOClob#getContents() contents} of the clob to be stored in the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor}.
+ * @see #rawCommit(double, OMMonitor)
+ */
+ public void rawStore(byte[] id, long size, Reader reader) throws IOException;
+
+ /**
+ * Stores the given {@link CDOCommitInfo commit} in the backend represented by this {@link IStoreAccessor.Raw raw
+ * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
+ * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
+ * where the accumulated backend changes can be committed atomically.
+ *
+ * @param branch
+ * the {@link CDOCommitInfo#getBranch() branch} of the commit info to be stored in the backend represented
+ * by this {@link IStoreAccessor.Raw raw store accessor}.
+ * @param timeStamp
+ * the {@link CDOCommitInfo#getTimeStamp() time stamp} of the commit info to be stored in the backend
+ * represented by this {@link IStoreAccessor.Raw raw store accessor}.
+ * @param previousTimeStamp
+ * the {@link CDOCommitInfo#getPreviousTimeStamp() previous time stamp} of the commit info to be stored in
+ * the backend represented by this {@link IStoreAccessor.Raw raw store accessor}.
+ * @param userID
+ * the {@link CDOCommitInfo#getUserID() user ID} of the commit info to be stored in the backend represented
+ * by this {@link IStoreAccessor.Raw raw store accessor}.
+ * @param comment
+ * the {@link CDOCommitInfo#getComment() comment} of the commit info to be stored in the backend
+ * represented by this {@link IStoreAccessor.Raw raw store accessor}.
+ * @see #rawCommit(double, OMMonitor)
+ */
+ public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
+ OMMonitor monitor);
+
+ /**
+ * Deletes the revision identified by the given {@link CDORevisionKey key} from the backend represented by this
+ * {@link IStoreAccessor.Raw raw store accessor} without going through a regular
+ * {@link IStoreAccessor#commit(OMMonitor) commit}.
+ * <p>
+ * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
+ * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
+ * where the accumulated backend changes can be committed atomically.
+ *
+ * @see #rawCommit(double, OMMonitor)
+ * @deprecated Not used anymore
+ */
+ @Deprecated
+ public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor);
+
+ /**
+ * Atomically commits the accumulated backend changes resulting from previous calls to the rawStore() methods.
+ *
+ * @param commitWork
+ * the amount of work to use up from the monitor while executing the commit.
+ * @param monitor
+ * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and
+ * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly
+ * to prevent timeouts from expiring in the caller.
+ * @see #rawStore(InternalCDOPackageUnit[], OMMonitor)
+ * @see #rawStore(InternalCDORevision, OMMonitor)
+ * @see #rawStore(byte[], long, InputStream)
+ * @see #rawStore(byte[], long, Reader)
+ * @see #rawStore(CDOBranch, long, long, String, String, OMMonitor)
+ */
+ public void rawCommit(double commitWork, OMMonitor monitor);
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ * @apiviz.exclude
+ */
+ public interface DurableLocking extends IDurableLockingManager
+ {
+ public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock);
+
+ public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock);
+
+ public void unlock(String durableLockingID);
+ }
+
+ /**
+ * @author Caspar De Groot
+ * @since 4.1
+ * @apiviz.exclude
+ */
+ public interface DurableLocking2 extends DurableLocking
+ {
+ LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks);
+
+ public void updateLockArea(LockArea lockArea);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java
new file mode 100644
index 0000000000..a27d25fd05
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java
@@ -0,0 +1,119 @@
+/**
+ * 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
+ * Simon McDuff - bug 210868
+ */
+package org.eclipse.emf.cdo.server;
+
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.uses {@link IStoreChunkReader.Chunk} - - reads
+ */
+public interface IStoreChunkReader
+{
+ /**
+ * @since 2.0
+ */
+ public IStoreAccessor getAccessor();
+
+ public CDORevision getRevision();
+
+ /**
+ * @since 2.0
+ */
+ public EStructuralFeature getFeature();
+
+ public void addSimpleChunk(int index);
+
+ /**
+ * @param fromIndex
+ * Inclusive value.
+ * @param toIndex
+ * Exclusive value.
+ */
+ public void addRangedChunk(int fromIndex, int toIndex);
+
+ public List<Chunk> executeRead();
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Chunk
+ {
+ private int startIndex;
+
+ private Object ids;
+
+ public Chunk(int startIndex)
+ {
+ this.startIndex = startIndex;
+ }
+
+ public Chunk(int startIndex, int size)
+ {
+ this(startIndex);
+ ids = new Object[size];
+ }
+
+ public int getStartIndex()
+ {
+ return startIndex;
+ }
+
+ public int size()
+ {
+ return ids instanceof Object[] ? ((Object[])ids).length : 1;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public Object get(int indexInChunk)
+ {
+ if (ids instanceof Object[])
+ {
+ return ((Object[])ids)[indexInChunk];
+ }
+
+ if (indexInChunk == 0)
+ {
+ return ids;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(indexInChunk);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void add(int indexInChunk, Object value)
+ {
+ if (ids instanceof Object[])
+ {
+ ((Object[])ids)[indexInChunk] = value;
+ }
+ else
+ {
+ if (indexInChunk == 0)
+ {
+ ids = value;
+ return;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(indexInChunk);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java
new file mode 100644
index 0000000000..daaa452533
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java
@@ -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
+ */
+package org.eclipse.emf.cdo.server;
+
+import org.w3c.dom.Element;
+
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @apiviz.uses {@link IStore} - - creates
+ */
+public interface IStoreFactory
+{
+ public String getStoreType();
+
+ /**
+ * @since 4.0
+ */
+ public IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java
new file mode 100644
index 0000000000..c692607c54
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.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;
+
+/**
+ * A repository with the ability to {@link IRepositorySynchronizer synchronize} its content with another repository.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @apiviz.landmark
+ * @apiviz.has {@link IRepositorySynchronizer}
+ * @apiviz.has {@link ISession} oneway - - replicatorSession
+ */
+public interface ISynchronizableRepository extends IRepository
+{
+ public IRepositorySynchronizer getSynchronizer();
+
+ public ISession getReplicatorSession();
+
+ public int getLastReplicatedBranchID();
+
+ public long getLastReplicatedCommitTime();
+
+ /**
+ * @since 4.1
+ */
+ public void goOnline();
+
+ /**
+ * @since 4.1
+ */
+ public void goOffline();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java
new file mode 100644
index 0000000000..021d214593
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java
@@ -0,0 +1,24 @@
+/**
+ * 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
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonTransaction;
+
+/**
+ * @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.
+ * @apiviz.landmark
+ */
+public interface ITransaction extends IView, CDOCommonTransaction
+{
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java
new file mode 100644
index 0000000000..dc5eea35cd
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java
@@ -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
+ */
+package org.eclipse.emf.cdo.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonView;
+
+/**
+ * @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.
+ * @apiviz.landmark
+ */
+public interface IView extends CDOCommonView
+{
+ /**
+ * @since 2.0
+ */
+ public IRepository getRepository();
+
+ public ISession getSession();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java
new file mode 100644
index 0000000000..bce4b76255
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java
@@ -0,0 +1,37 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.common.util.CDOException;
+
+/**
+ * @author Eike Stepper
+ */
+public class RepositoryNotFoundException extends CDOException
+{
+ private static final long serialVersionUID = 1L;
+
+ public RepositoryNotFoundException(String repositoryName)
+ {
+ super(repositoryName);
+ }
+
+ public String getRepositoryName()
+ {
+ return super.getMessage();
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return "Repository not found: " + getRepositoryName(); //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java
new file mode 100644
index 0000000000..dd0ac05814
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java
@@ -0,0 +1,118 @@
+/**
+ * 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;
+
+import org.eclipse.emf.cdo.spi.server.InternalSession;
+
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ * @apiviz.exclude
+ */
+public final class StoreThreadLocal
+{
+ private static final ThreadLocal<InternalSession> SESSION = new InheritableThreadLocal<InternalSession>();
+
+ private static final ThreadLocal<IStoreAccessor> ACCESSOR = new InheritableThreadLocal<IStoreAccessor>();
+
+ private static final ThreadLocal<IStoreAccessor.CommitContext> COMMIT_CONTEXT = new InheritableThreadLocal<IStoreAccessor.CommitContext>();
+
+ private StoreThreadLocal()
+ {
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static void setSession(InternalSession session)
+ {
+ SESSION.set(session);
+ ACCESSOR.set(null);
+ }
+
+ /**
+ * Returns the session associated with the current thread.
+ *
+ * @return Never <code>null</code>.
+ * @throws IllegalStateException
+ * if no session is associated with the current thread.
+ * @since 3.0
+ */
+ public static InternalSession getSession()
+ {
+ InternalSession session = SESSION.get();
+ if (session == null)
+ {
+ throw new IllegalStateException("session == null"); //$NON-NLS-1$
+ }
+
+ return session;
+ }
+
+ public static void setAccessor(IStoreAccessor accessor)
+ {
+ // IStoreAccessor old = ACCESSOR.get();
+ // if (old != null && old != accessor)
+ // {
+ // throw new IllegalStateException("Attempt to overwrite accessor");
+ // }
+
+ SESSION.set(accessor == null ? null : accessor.getSession());
+ ACCESSOR.set(accessor);
+ }
+
+ public static IStoreAccessor getAccessor()
+ {
+ IStoreAccessor accessor = ACCESSOR.get();
+ if (accessor == null)
+ {
+ ISession session = getSession();
+ IStore store = session.getManager().getRepository().getStore();
+ accessor = store.getReader(session);
+ ACCESSOR.set(accessor);
+ }
+
+ return accessor;
+ }
+
+ public static void setCommitContext(IStoreAccessor.CommitContext commitContext)
+ {
+ COMMIT_CONTEXT.set(commitContext);
+ }
+
+ public static IStoreAccessor.CommitContext getCommitContext()
+ {
+ return COMMIT_CONTEXT.get();
+ }
+
+ public static void release()
+ {
+ try
+ {
+ IStoreAccessor accessor = ACCESSOR.get();
+ if (accessor != null)
+ {
+ if (LifecycleUtil.isActive(accessor))
+ {
+ accessor.release();
+ }
+ }
+ }
+ finally
+ {
+ ACCESSOR.set(null);
+ SESSION.set(null);
+ COMMIT_CONTEXT.set(null);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java
new file mode 100644
index 0000000000..9d68ad8233
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java
@@ -0,0 +1,21 @@
+/**
+ * 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.embedded;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 CDOSession extends org.eclipse.emf.cdo.session.CDOSession
+{
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java
new file mode 100644
index 0000000000..4481992c2c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java
@@ -0,0 +1,33 @@
+/**
+ * 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.embedded;
+
+import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
+import org.eclipse.emf.cdo.server.IRepository;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 CDOSessionConfiguration extends org.eclipse.emf.cdo.session.CDOSessionConfiguration
+{
+ public IRepository getRepository();
+
+ public void setRepository(IRepository repository);
+
+ public CDORevisionManager getRevisionManager();
+
+ public void setRevisionManager(CDORevisionManager revisionManager);
+
+ public org.eclipse.emf.cdo.server.embedded.CDOSession openSession();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/package-info.java
new file mode 100644
index 0000000000..e4b6b8e8e9
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/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 embedded sessions.
+ */
+package org.eclipse.emf.cdo.server.embedded;
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java
new file mode 100644
index 0000000000..ef73bbdbd8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java
@@ -0,0 +1,56 @@
+/**
+ * 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.mem;
+
+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.server.IStore;
+import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs;
+
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+
+import org.eclipse.emf.ecore.EClass;
+
+/**
+ * A simple in-memory store.
+ *
+ * @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.
+ * @since 4.0
+ */
+public interface IMEMStore extends IStore, CDOAllRevisionsProvider, CanHandleClientAssignedIDs
+{
+ public static final int UNLIMITED = -1;
+
+ /**
+ * Returns the number of {@link CDORevision revisions} per {@link CDOID} that are stored.
+ */
+ public int getListLimit();
+
+ /**
+ * Limits the number of {@link CDORevision revisions} per {@link CDOID} to the given value.
+ * <p>
+ * A value of 2, for example, stores the current and the immediately preceding revisions whereas older revisions are
+ * dropped from thids store. A value of 1 only stores the current revisions. A value of {@link #UNLIMITED} does not
+ * limit the number of revisions to be stored for any id.
+ * <p>
+ * The list limit can be set and enforced at any time before or after the {@link LifecycleUtil#activate(Object)
+ * activation} of this store.
+ */
+ public void setListLimit(int listLimit);
+
+ /**
+ * @since 3.0
+ */
+ public EClass getObjectType(CDOID id);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java
new file mode 100644
index 0000000000..358a29dba0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.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.mem;
+
+import org.eclipse.emf.cdo.internal.server.mem.MEMStore;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public final class MEMStoreUtil
+{
+ private MEMStoreUtil()
+ {
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static IMEMStore createMEMStore()
+ {
+ return new MEMStore();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java
new file mode 100644
index 0000000000..d6b75391c6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/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 in-memory stores.
+ */
+package org.eclipse.emf.cdo.server.mem;
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java
new file mode 100644
index 0000000000..af751e4894
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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 repositories and stores.
+ *
+ * @apiviz.exclude .*\.CDOServerBrowser.*
+ * @apiviz.exclude .*\.CommitInfoLoader
+ * @apiviz.exclude .*\.BranchLoader
+ * @apiviz.exclude .*\.IContainer
+ * @apiviz.exclude .*\.INotifier
+ * @apiviz.exclude .*Exception
+ */
+package org.eclipse.emf.cdo.server;
+
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java
new file mode 100644
index 0000000000..3d44fe9600
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.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
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.IQueryHandlerProvider;
+
+import org.eclipse.net4j.util.container.IManagedContainer;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class ContainerQueryHandlerProvider implements IQueryHandlerProvider
+{
+ private IManagedContainer container;
+
+ public ContainerQueryHandlerProvider(IManagedContainer container)
+ {
+ this.container = container;
+ }
+
+ public IManagedContainer getContainer()
+ {
+ return container;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public IQueryHandler getQueryHandler(CDOQueryInfo info)
+ {
+ return (IQueryHandler)container.getElement(QueryHandlerFactory.PRODUCT_GROUP, info.getQueryLanguage(), null);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java
new file mode 100644
index 0000000000..5323ff826d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java
@@ -0,0 +1,49 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IRepositoryProvider;
+
+import org.eclipse.net4j.util.container.IManagedContainer;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class ContainerRepositoryProvider implements IRepositoryProvider
+{
+ private IManagedContainer container;
+
+ public ContainerRepositoryProvider(IManagedContainer container)
+ {
+ this.container = container;
+ }
+
+ public IManagedContainer getContainer()
+ {
+ return container;
+ }
+
+ public IRepository getRepository(String name)
+ {
+ try
+ {
+ return RepositoryFactory.get(container, name);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ return null;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java
new file mode 100644
index 0000000000..40c3826844
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java
@@ -0,0 +1,108 @@
+/**
+ * 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.spi.server;
+
+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.lock.CDOLockUtil;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @deprecated Use {@link CDOLockUtil#createLockArea(String, String, CDOBranchPoint, boolean, Map)} instead
+ */
+@Deprecated
+public class DurableLockArea implements LockArea
+{
+ public static final int DEFAULT_DURABLE_LOCKING_ID_BYTES = 32;
+
+ private String durableLockingID;
+
+ private String userID;
+
+ private CDOBranchPoint branchPoint;
+
+ private boolean readOnly;
+
+ private Map<CDOID, LockGrade> locks;
+
+ public DurableLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ this.durableLockingID = durableLockingID;
+ this.userID = userID;
+ this.branchPoint = branchPoint;
+ this.readOnly = readOnly;
+ this.locks = locks;
+ }
+
+ public String getDurableLockingID()
+ {
+ return durableLockingID;
+ }
+
+ public String getUserID()
+ {
+ return userID;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public boolean isReadOnly()
+ {
+ return readOnly;
+ }
+
+ public Map<CDOID, LockGrade> getLocks()
+ {
+ return locks;
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("DurableLockArea\nid={0}\nuser={1}\nbranchPoint={2}\nreadOnly={3}\nlocks={4}",
+ durableLockingID, userID, branchPoint, readOnly, locks);
+ }
+
+ public static String createDurableLockingID()
+ {
+ return CDOLockUtil.createDurableLockingID();
+ }
+
+ public static String createDurableLockingID(int bytes)
+ {
+ return CDOLockUtil.createDurableLockingID(bytes);
+ }
+
+ /**
+ * @since 4.1
+ */
+ public boolean isMissing()
+ {
+ return false;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java
new file mode 100644
index 0000000000..19f7a2b41b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java
@@ -0,0 +1,76 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.IQueryHandlerProvider;
+
+import org.eclipse.net4j.util.factory.IFactory;
+import org.eclipse.net4j.util.registry.HashMapRegistry;
+import org.eclipse.net4j.util.registry.IRegistry;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class FactoriesQueryHandlerProvider implements IQueryHandlerProvider
+{
+ private IRegistry<String, IFactory> registry;
+
+ public FactoriesQueryHandlerProvider()
+ {
+ }
+
+ public FactoriesQueryHandlerProvider(IRegistry<String, IFactory> registry)
+ {
+ setRegistry(registry);
+ }
+
+ public FactoriesQueryHandlerProvider(IFactory factory)
+ {
+ addFactory(factory);
+ }
+
+ public IRegistry<String, IFactory> getRegistry()
+ {
+ if (registry == null)
+ {
+ registry = new HashMapRegistry<String, IFactory>();
+ }
+
+ return registry;
+ }
+
+ public void setRegistry(IRegistry<String, IFactory> registry)
+ {
+ this.registry = registry;
+ }
+
+ public void addFactory(IFactory factory)
+ {
+ getRegistry().put(factory.getKey().getType(), factory);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public IQueryHandler getQueryHandler(CDOQueryInfo info)
+ {
+ IFactory factory = registry.get(info.getQueryLanguage());
+ if (factory != null)
+ {
+ return (IQueryHandler)factory.create(null);
+ }
+
+ return null;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java
new file mode 100644
index 0000000000..b828bd1692
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.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.spi.server;
+
+import java.io.File;
+
+/**
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public interface IAppExtension
+{
+ public static final String EXT_POINT = "appExtensions"; //$NON-NLS-1$
+
+ public void start(File configFile) throws Exception;
+
+ public void stop() throws Exception;
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java
new file mode 100644
index 0000000000..1562ae0689
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java
@@ -0,0 +1,63 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 ISessionProtocol extends CDOProtocol
+{
+ /**
+ * @since 4.0
+ */
+ public CDOAuthenticationResult sendAuthenticationChallenge(byte[] randomToken) throws Exception;
+
+ public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType)
+ throws Exception;
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState)
+ throws Exception;
+
+ /**
+ * @since 4.1
+ */
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState,
+ CDOID rootResourceID) throws Exception;
+
+ public void sendBranchNotification(InternalCDOBranch branch) throws Exception;
+
+ public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception;
+
+ public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception;
+
+ public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception;
+
+ /**
+ * @since 4.1
+ */
+ public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception;
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java
new file mode 100644
index 0000000000..135b6b4d47
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java
@@ -0,0 +1,103 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.net4j.util.io.ExtendedDataInputStream;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.monitor.ProgressDistributable;
+import org.eclipse.net4j.util.om.monitor.ProgressDistributor;
+
+import org.eclipse.emf.ecore.EClass;
+
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalCommitContext extends IStoreAccessor.CommitContext
+{
+ @SuppressWarnings("unchecked")
+ public static final ProgressDistributable<InternalCommitContext>[] OPS = ProgressDistributor.array( //
+ new ProgressDistributable.Default<InternalCommitContext>()
+ {
+ public void runLoop(int index, InternalCommitContext commitContext, OMMonitor monitor) throws Exception
+ {
+ commitContext.write(monitor.fork());
+ }
+ }, //
+
+ new ProgressDistributable.Default<InternalCommitContext>()
+ {
+ public void runLoop(int index, InternalCommitContext commitContext, OMMonitor monitor) throws Exception
+ {
+ if (commitContext.getRollbackMessage() == null)
+ {
+ commitContext.commit(monitor.fork());
+ }
+ else
+ {
+ monitor.worked();
+ }
+ }
+ });
+
+ public InternalTransaction getTransaction();
+
+ public void preWrite();
+
+ public void write(OMMonitor monitor);
+
+ public void commit(OMMonitor monitor);
+
+ public void rollback(String message);
+
+ public void postCommit(boolean success);
+
+ /**
+ * @since 4.0
+ */
+ public InternalCDORevision[] getDetachedRevisions();
+
+ public void setNewPackageUnits(InternalCDOPackageUnit[] newPackageUnits);
+
+ public void setNewObjects(InternalCDORevision[] newObjects);
+
+ public void setDirtyObjectDeltas(InternalCDORevisionDelta[] dirtyObjectDeltas);
+
+ public void setDetachedObjects(CDOID[] detachedObjects);
+
+ /**
+ * @since 4.0
+ */
+ public void setDetachedObjectTypes(Map<CDOID, EClass> detachedObjectTypes);
+
+ public void setAutoReleaseLocksEnabled(boolean on);
+
+ public void setCommitComment(String comment);
+
+ /**
+ * @since 4.0
+ */
+ public void setLobs(ExtendedDataInputStream in);
+
+ public void addIDMapping(CDOID oldID, CDOID newID);
+
+ public void applyIDMappings(OMMonitor monitor);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java
new file mode 100644
index 0000000000..6ebefcf239
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.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
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalCommitManager
+{
+ public InternalRepository getRepository();
+
+ public void setRepository(InternalRepository repository);
+
+ /**
+ * Create a future to execute commitContext in a different thread.
+ */
+ public void preCommit(InternalCommitContext commitContext, OMMonitor monitor);
+
+ /**
+ * Called after a commitContext is done successfully or not.
+ */
+ public void remove(InternalCommitContext commitContext);
+
+ public void rollback(InternalCommitContext commitContext);
+
+ /**
+ * Waiting for a commit to be done.
+ */
+ public void waitForTermination(InternalTransaction transaction) throws InterruptedException, ExecutionException;
+
+ public InternalCommitContext get(InternalTransaction transaction);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java
new file mode 100644
index 0000000000..71a476bfac
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java
@@ -0,0 +1,24 @@
+/**
+ * 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.spi.server;
+
+/**
+ * @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 InternalFailoverParticipant extends InternalSynchronizableRepository
+{
+ public boolean isAllowBackupCommits();
+
+ public void setAllowBackupCommits(boolean allowBackupCommits);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java
new file mode 100644
index 0000000000..ab8d9a2c37
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java
@@ -0,0 +1,139 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IView;
+
+import org.eclipse.net4j.util.concurrent.IRWOLockManager;
+import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The type of the to-be-locked objects is either {@link CDOIDAndBranch} or {@link CDOID}, depending on whether
+ * branching is supported by the repository or not.
+ *
+ * @author Eike Stepper
+ * @since 3.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 InternalLockManager extends IRWOLockManager<Object, IView>, IDurableLockingManager
+{
+ public InternalRepository getRepository();
+
+ public void setRepository(InternalRepository repository);
+
+ /**
+ * @since 4.0
+ */
+ public Object getLockEntryObject(Object key);
+
+ /**
+ * @since 4.0
+ */
+ public Object getLockKey(CDOID id, CDOBranch branch);
+
+ /**
+ * @since 4.0
+ */
+ public CDOID getLockKeyID(Object key);
+
+ /**
+ * @since 4.0
+ */
+ public Map<CDOID, LockGrade> getLocks(IView view);
+
+ /**
+ * @since 4.0
+ */
+ @Deprecated
+ public void lock(boolean explicit, LockType type, IView context, Collection<? extends Object> objectsToLock,
+ long timeout) throws InterruptedException;
+
+ /**
+ * @since 4.1
+ */
+ public List<LockState<Object, IView>> lock2(boolean explicit, LockType type, IView context,
+ Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException;
+
+ /**
+ * Attempts to release for a given locktype, view and objects.
+ *
+ * @throws IllegalMonitorStateException
+ * Unlocking objects without lock.
+ * @since 4.0
+ */
+ @Deprecated
+ public void unlock(boolean explicit, LockType type, IView context, Collection<? extends Object> objectsToUnlock);
+
+ /**
+ * @since 4.1
+ */
+ public List<LockState<Object, IView>> unlock2(boolean explicit, LockType type, IView context,
+ Collection<? extends Object> objectsToUnlock);
+
+ /**
+ * Attempts to release all locks(read and write) for a given view.
+ *
+ * @since 4.0
+ */
+ @Deprecated
+ public void unlock(boolean explicit, IView context);
+
+ /**
+ * @since 4.1
+ */
+ public List<LockState<Object, IView>> unlock2(boolean explicit, IView context);
+
+ /**
+ * @since 4.0
+ */
+ public LockArea createLockArea(InternalView view);
+
+ /**
+ * @since 4.1
+ */
+ public LockArea createLockArea(InternalView view, String lockAreaID);
+
+ /**
+ * @since 4.1
+ */
+ // TODO (CD) I've also added this to DurableLocking2 Refactoring opportunity?
+ public void updateLockArea(LockArea lockArea);
+
+ /**
+ * @since 4.0
+ */
+ public IView openView(ISession session, int viewID, boolean readOnly, String durableLockingID);
+
+ /**
+ * @since 4.1
+ */
+ public LockGrade getLockGrade(Object key);
+
+ /**
+ * @since 4.1
+ */
+ public LockState<Object, IView> getLockState(Object key);
+
+ /**
+ * @since 4.1
+ */
+ public void setLockState(Object key, LockState<Object, IView> lockState);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java
new file mode 100644
index 0000000000..633026a217
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.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.spi.server;
+
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalQueryManager
+{
+ public InternalRepository getRepository();
+
+ public void setRepository(InternalRepository repository);
+
+ public InternalQueryResult execute(InternalView view, CDOQueryInfo queryInfo);
+
+ public boolean isRunning(int queryID);
+
+ public void cancel(int queryID);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java
new file mode 100644
index 0000000000..6c9479cc2e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.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.spi.server;
+
+import org.eclipse.emf.cdo.common.util.BlockingCloseableIterator;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.common.util.CDOQueryQueue;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalQueryResult extends BlockingCloseableIterator<Object>
+{
+ public int getQueryID();
+
+ public CDOQueryInfo getQueryInfo();
+
+ public InternalView getView();
+
+ public CDOQueryQueue<Object> getQueue();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
new file mode 100644
index 0000000000..eb8c0b50ce
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
@@ -0,0 +1,230 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.server.IQueryHandlerProvider;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.spi.common.CDOReplicationContext;
+import org.eclipse.emf.cdo.spi.common.CDOReplicationInfo;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader;
+import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager.CommitInfoLoader;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageLoader;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageProcessor;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager.RevisionLoader;
+
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalRepository extends IRepository, PackageProcessor, PackageLoader, BranchLoader, RevisionLoader,
+ CommitInfoLoader
+{
+ public void setName(String name);
+
+ public void setType(Type type);
+
+ public void setState(State state);
+
+ public InternalStore getStore();
+
+ public void setStore(InternalStore store);
+
+ public void setProperties(Map<String, String> properties);
+
+ public InternalCDOBranchManager getBranchManager();
+
+ public void setBranchManager(InternalCDOBranchManager branchManager);
+
+ /**
+ * @since 4.1
+ */
+ public Semaphore getPackageRegistryCommitLock();
+
+ /**
+ * Same as calling {@link #getPackageRegistry(boolean) getPackageRegistry(true)}.
+ */
+ public InternalCDOPackageRegistry getPackageRegistry();
+
+ public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext);
+
+ public InternalCDORevisionManager getRevisionManager();
+
+ public void setRevisionManager(InternalCDORevisionManager revisionManager);
+
+ public InternalCDOCommitInfoManager getCommitInfoManager();
+
+ public InternalSessionManager getSessionManager();
+
+ public void setSessionManager(InternalSessionManager sessionManager);
+
+ public InternalLockManager getLockManager();
+
+ public InternalQueryManager getQueryManager();
+
+ public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider);
+
+ public InternalCommitManager getCommitManager();
+
+ public InternalCommitContext createCommitContext(InternalTransaction transaction);
+
+ /**
+ * Returns a commit time stamp that is guaranteed to be unique in this repository. At index 1 of the returned
+ * <code>long</code> array is the previous commit time.
+ *
+ * @since 4.0
+ */
+ public long[] createCommitTimeStamp(OMMonitor monitor);
+
+ /**
+ * Like {@link #createCommitTimeStamp(OMMonitor)}, but forces the repository to use the timestamp value passed in as
+ * the argument. This should be called only to force the timestamp of the first commit of a new repository to be equal
+ * to its creation time.
+ *
+ * @since 4.0
+ */
+ public long[] forceCommitTimeStamp(long timestamp, OMMonitor monitor);
+
+ /**
+ * Notifies the repository of the completion of a commit. The value passed in must be a value obtained earlier through
+ * {@link #createCommitTimeStamp(OMMonitor)}
+ *
+ * @since 4.0
+ */
+ public void endCommit(long timeStamp);
+
+ /**
+ * Notifies the repository of the failure of a commit. The value passed in must be a value obtained earlier through
+ * {@link #createCommitTimeStamp(OMMonitor)}
+ *
+ * @since 4.0
+ */
+ public void failCommit(long timeStamp);
+
+ /**
+ * @since 4.0
+ */
+ public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo);
+
+ public void setRootResourceID(CDOID rootResourceID);
+
+ /**
+ * @since 4.0
+ */
+ public void setLastCommitTimeStamp(long commitTimeStamp);
+
+ public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart,
+ int chunkEnd);
+
+ public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions,
+ List<CDORevision> additionalRevisions);
+
+ public void notifyWriteAccessHandlers(ITransaction transaction, IStoreAccessor.CommitContext commitContext,
+ boolean beforeCommit, OMMonitor monitor);
+
+ public void replicate(CDOReplicationContext context);
+
+ public CDOReplicationInfo replicateRaw(CDODataOutput out, int lastReplicatedBranchID, long lastReplicatedCommitTime)
+ throws IOException;
+
+ public CDOChangeSetData getChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint);
+
+ /**
+ * @since 4.0
+ */
+ public Set<CDOID> getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo,
+ CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor);
+
+ /**
+ * @since 4.0
+ */
+ public void queryLobs(List<byte[]> ids);
+
+ /**
+ * @since 4.0
+ */
+ public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public void loadLob(byte[] id, OutputStream out) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler);
+
+ /**
+ * @since 4.0
+ */
+ public boolean isSkipInitialization();
+
+ /**
+ * @since 4.0
+ */
+ public void setSkipInitialization(boolean skipInitialization);
+
+ /**
+ * @since 4.0
+ */
+ public void initSystemPackages();
+
+ /**
+ * @since 4.0
+ */
+ public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp);
+
+ /**
+ * @since 4.1
+ */
+ public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, long timeout);
+
+ /**
+ * @since 4.1
+ */
+ public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java
new file mode 100644
index 0000000000..20151f3e8e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java
@@ -0,0 +1,35 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.server.IRepositorySynchronizer;
+import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory;
+
+import org.eclipse.net4j.util.lifecycle.ILifecycle;
+
+import org.eclipse.emf.spi.cdo.InternalCDOSession;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalRepositorySynchronizer extends IRepositorySynchronizer, ILifecycle
+{
+ public InternalSynchronizableRepository getLocalRepository();
+
+ public void setLocalRepository(InternalSynchronizableRepository localRepository);
+
+ public void setRemoteSessionConfigurationFactory(CDOSessionConfigurationFactory remoteSessionConfigurationFactory);
+
+ public InternalCDOSession getRemoteSession();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java
new file mode 100644
index 0000000000..9195c68c5c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java
@@ -0,0 +1,86 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.CDOCommonSession;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDProvider;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalSession extends ISession, CDOIDProvider, CDOCommonSession.Options
+{
+ public static final int TEMP_VIEW_ID = 0;
+
+ public InternalSessionManager getManager();
+
+ public InternalView[] getViews();
+
+ public InternalView getView(int viewID);
+
+ public InternalView openView(int viewID, CDOBranchPoint branchPoint);
+
+ public InternalTransaction openTransaction(int viewID, CDOBranchPoint branchPoint);
+
+ public void viewClosed(InternalView view);
+
+ public void setSubscribed(boolean subscribed);
+
+ public void collectContainedRevisions(InternalCDORevision revision, CDOBranchPoint branchPoint, int referenceChunk,
+ Set<CDOID> revisions, List<CDORevision> additionalRevisions);
+
+ public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType)
+ throws Exception;
+
+ /**
+ * @deprecated use
+ * {@link #sendRepositoryStateNotification(org.eclipse.emf.cdo.common.CDOCommonRepository.State, org.eclipse.emf.cdo.common.CDOCommonRepository.State, CDOID)}
+ * instead
+ */
+ @Deprecated
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState)
+ throws Exception;
+
+ /**
+ * @since 4.1
+ */
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState,
+ CDOID rootResourceID) throws Exception;
+
+ public void sendBranchNotification(InternalCDOBranch branch) throws Exception;
+
+ public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception;
+
+ public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception;
+
+ public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception;
+
+ /**
+ * @since 4.1
+ */
+ public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception;
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java
new file mode 100644
index 0000000000..435b78e680
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java
@@ -0,0 +1,79 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.server.ISessionManager;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+
+import org.eclipse.net4j.util.security.IUserManager;
+
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalSessionManager extends ISessionManager
+{
+ public InternalRepository getRepository();
+
+ public void setRepository(InternalRepository repository);
+
+ public void setUserManager(IUserManager userManager);
+
+ public InternalSession[] getSessions();
+
+ public InternalSession getSession(int sessionID);
+
+ /**
+ * @return Never <code>null</code>
+ */
+ public InternalSession openSession(ISessionProtocol sessionProtocol);
+
+ public void sessionClosed(InternalSession session);
+
+ public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType);
+
+ /**
+ * @deprecated use
+ * {@link #sendRepositoryStateNotification(org.eclipse.emf.cdo.common.CDOCommonRepository.State, org.eclipse.emf.cdo.common.CDOCommonRepository.State, CDOID)}
+ * instead
+ */
+ @Deprecated
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState);
+
+ /**
+ * @since 4.1
+ */
+ public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState,
+ CDOID rootResourceID);
+
+ public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch);
+
+ public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo);
+
+ /**
+ * @since 4.1
+ */
+ public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo);
+
+ public void sendRemoteSessionNotification(InternalSession sender, byte opcode);
+
+ public List<Integer> sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message,
+ int[] recipients);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java
new file mode 100644
index 0000000000..af99668712
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java
@@ -0,0 +1,112 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStore;
+
+import org.eclipse.net4j.util.lifecycle.ILifecycle;
+
+/**
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public interface InternalStore extends IStore, ILifecycle
+{
+ public InternalRepository getRepository();
+
+ public void setRepository(IRepository repository);
+
+ public void setRevisionTemporality(RevisionTemporality revisionTemporality);
+
+ public void setRevisionParallelism(RevisionParallelism revisionParallelism);
+
+ public int getNextBranchID();
+
+ public int getNextLocalBranchID();
+
+ public void setLastBranchID(int lastBranchID);
+
+ public void setLastLocalBranchID(int lastLocalBranchID);
+
+ public void setLastCommitTime(long lastCommitTime);
+
+ public void setLastNonLocalCommitTime(long lastNonLocalCommitTime);
+
+ /**
+ * @since 4.0
+ */
+ public boolean isLocal(CDOID id);
+
+ /**
+ * @since 4.0
+ */
+ public boolean isDropAllDataOnActivate();
+
+ /**
+ * @since 4.0
+ */
+ public void setDropAllDataOnActivate(boolean dropAllDataOnActivate);
+
+ /**
+ * @since 4.0
+ */
+ public void setCreationTime(long creationTime);
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+ public interface NoExternalReferences
+ {
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+ public interface NoQueryXRefs
+ {
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+ public interface NoLargeObjects
+ {
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+ public interface NoFeatureMaps
+ {
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+ public interface NoHandleRevisions
+ {
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+ public interface NoRawAccess
+ {
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java
new file mode 100644
index 0000000000..f4c3298dc0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java
@@ -0,0 +1,36 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfoHandler;
+import org.eclipse.emf.cdo.server.ISynchronizableRepository;
+import org.eclipse.emf.cdo.spi.common.CDORawReplicationContext;
+import org.eclipse.emf.cdo.spi.common.CDOReplicationContext;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalSynchronizableRepository extends ISynchronizableRepository, InternalRepository,
+ CDOReplicationContext, CDORawReplicationContext, CDOLockChangeInfoHandler
+{
+ public InternalRepositorySynchronizer getSynchronizer();
+
+ public void setSynchronizer(InternalRepositorySynchronizer synchronizer);
+
+ public InternalSession getReplicatorSession();
+
+ public void setLastReplicatedBranchID(int lastReplicatedBranchID);
+
+ public void setLastReplicatedCommitTime(long lastReplicatedCommitTime);
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java
new file mode 100644
index 0000000000..2f51ae2db1
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java
@@ -0,0 +1,24 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.server.ITransaction;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalTransaction extends ITransaction, InternalView
+{
+ public InternalCommitContext createCommitContext();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java
new file mode 100644
index 0000000000..8adca8fe50
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java
@@ -0,0 +1,56 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
+import org.eclipse.emf.cdo.server.IView;
+
+import org.eclipse.net4j.util.lifecycle.ILifecycle;
+
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 3.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 InternalView extends IView, ILifecycle
+{
+ public InternalSession getSession();
+
+ public InternalRepository getRepository();
+
+ public void setBranchPoint(CDOBranchPoint branchPoint);
+
+ /**
+ * @since 4.0
+ */
+ public void setDurableLockingID(String durableLockingID);
+
+ /**
+ * @since 4.0
+ */
+ public void changeTarget(CDOBranchPoint branchPoint, List<CDOID> invalidObjects,
+ List<CDORevisionDelta> allChangedObjects, List<CDOID> allDetachedObjects);
+
+ public void subscribe(CDOID id);
+
+ public void unsubscribe(CDOID id);
+
+ public boolean hasSubscription(CDOID id);
+
+ public void clearChangeSubscription();
+
+ public void doClose();
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java
new file mode 100644
index 0000000000..908e5baa5a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java
@@ -0,0 +1,120 @@
+/**
+ * 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.spi.server;
+
+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.revision.CDORevision;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class LongIDStore extends Store
+{
+ /**
+ * @since 3.0
+ */
+ public static final Set<ObjectType> OBJECT_ID_TYPES = Collections.singleton(CDOID.ObjectType.LONG);
+
+ /**
+ * @since 3.0
+ */
+ public static final long NULL = CDOIDUtil.getLong(CDOID.NULL);
+
+ @ExcludeFromDump
+ private transient AtomicLong lastObjectID = new AtomicLong();
+
+ @ExcludeFromDump
+ private transient AtomicLong nextLocalObjectID = new AtomicLong(Long.MAX_VALUE);
+
+ public LongIDStore(String type, Set<ChangeFormat> supportedChangeFormats,
+ Set<RevisionTemporality> supportedRevisionTemporalities, Set<RevisionParallelism> supportedRevisionParallelisms)
+ {
+ super(type, OBJECT_ID_TYPES, supportedChangeFormats, supportedRevisionTemporalities, supportedRevisionParallelisms);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public CDOID createObjectID(String val)
+ {
+ Long id = Long.valueOf(val);
+ return CDOIDUtil.createLong(id);
+ }
+
+ public long getLastObjectID()
+ {
+ return lastObjectID.get();
+ }
+
+ public void setLastObjectID(long lastObjectID)
+ {
+ this.lastObjectID.set(lastObjectID);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public long getNextLocalObjectID()
+ {
+ return nextLocalObjectID.get();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setNextLocalObjectID(long nextLocalObjectID)
+ {
+ this.nextLocalObjectID.set(nextLocalObjectID);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public CDOID getNextCDOID(LongIDStoreAccessor accessor, CDORevision revision)
+ {
+ if (revision.getBranch().isLocal())
+ {
+ return CDOIDUtil.createLong(nextLocalObjectID.getAndDecrement());
+ }
+
+ return CDOIDUtil.createLong(lastObjectID.incrementAndGet());
+ }
+
+ /**
+ * @since 4.0
+ */
+ public boolean isLocal(CDOID id)
+ {
+ long value = CDOIDUtil.getLong(id);
+ return value > nextLocalObjectID.get();
+ }
+
+ /**
+ * @since 4.0
+ */
+ public void ensureLastObjectID(CDOID id)
+ {
+ long addedID = CDOIDUtil.getLong(id);
+ if (addedID > getLastObjectID())
+ {
+ setLastObjectID(addedID);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java
new file mode 100644
index 0000000000..803368ead7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java
@@ -0,0 +1,50 @@
+/**
+ * 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
+ * Simon McDuff - bug 201266
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.ITransaction;
+
+/**
+ * @since 2.0
+ * @author Eike Stepper
+ */
+public abstract class LongIDStoreAccessor extends StoreAccessor
+{
+ protected LongIDStoreAccessor(Store store, ISession session)
+ {
+ super(store, session);
+ }
+
+ protected LongIDStoreAccessor(Store store, ITransaction transaction)
+ {
+ super(store, transaction);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ public LongIDStore getStore()
+ {
+ return (LongIDStore)super.getStore();
+ }
+
+ @Override
+ protected CDOID getNextCDOID(CDORevision revision)
+ {
+ return getStore().getNextCDOID(this, revision);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java
new file mode 100644
index 0000000000..8db6cb5246
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java
@@ -0,0 +1,159 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.server.CDOServerUtil;
+import org.eclipse.emf.cdo.server.IRepository.WriteAccessHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.util.CDOUtil;
+import org.eclipse.emf.cdo.view.CDOView;
+
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class ObjectWriteAccessHandler implements WriteAccessHandler
+{
+ private boolean legacyModeEnabled;
+
+ private IStoreAccessor.CommitContext commitContext;
+
+ private CDOView view;
+
+ private EObject[] newObjects;
+
+ private EObject[] dirtyObjects;
+
+ public ObjectWriteAccessHandler()
+ {
+ }
+
+ public ObjectWriteAccessHandler(boolean legacyModeEnabled)
+ {
+ this.legacyModeEnabled = legacyModeEnabled;
+ }
+
+ public final boolean isLegacyModeEnabled()
+ {
+ return legacyModeEnabled;
+ }
+
+ protected final IStoreAccessor.CommitContext getCommitContext()
+ {
+ return commitContext;
+ }
+
+ protected final ITransaction getTransaction()
+ {
+ return commitContext.getTransaction();
+ }
+
+ protected final CDOView getView()
+ {
+ if (view == null)
+ {
+ view = CDOServerUtil.openView(commitContext, legacyModeEnabled);
+ }
+
+ return view;
+ }
+
+ protected final EObject[] getNewObjects()
+ {
+ if (newObjects == null)
+ {
+ InternalCDORevision[] newRevisions = commitContext.getNewObjects();
+ newObjects = new EObject[newRevisions.length];
+ CDOView view = getView();
+
+ for (int i = 0; i < newRevisions.length; i++)
+ {
+ InternalCDORevision newRevision = newRevisions[i];
+ CDOObject newObject = view.getObject(newRevision.getID());
+ newObjects[i] = CDOUtil.getEObject(newObject);
+ }
+ }
+
+ return newObjects;
+ }
+
+ protected final EObject[] getDirtyObjects()
+ {
+ if (dirtyObjects == null)
+ {
+ InternalCDORevision[] dirtyRevisions = commitContext.getDirtyObjects();
+ dirtyObjects = new EObject[dirtyRevisions.length];
+ CDOView view = getView();
+
+ for (int i = 0; i < dirtyRevisions.length; i++)
+ {
+ InternalCDORevision dirtyRevision = dirtyRevisions[i];
+ CDOObject dirtyObject = view.getObject(dirtyRevision.getID());
+ dirtyObjects[i] = CDOUtil.getEObject(dirtyObject);
+ }
+ }
+
+ return dirtyObjects;
+ }
+
+ public final void handleTransactionBeforeCommitting(ITransaction transaction,
+ IStoreAccessor.CommitContext commitContext, OMMonitor monitor) throws RuntimeException
+ {
+ try
+ {
+ this.commitContext = commitContext;
+ handleTransactionBeforeCommitting(monitor);
+ }
+ finally
+ {
+ LifecycleUtil.deactivate(view);
+ view = null;
+ dirtyObjects = null;
+ newObjects = null;
+ this.commitContext = null;
+ }
+ }
+
+ public final void handleTransactionAfterCommitted(ITransaction transaction, CommitContext commitContext,
+ OMMonitor monitor)
+ {
+ try
+ {
+ this.commitContext = commitContext;
+ handleTransactionAfterCommitted(monitor);
+ }
+ finally
+ {
+ LifecycleUtil.deactivate(view);
+ view = null;
+ dirtyObjects = null;
+ newObjects = null;
+ this.commitContext = null;
+ }
+ }
+
+ protected void handleTransactionBeforeCommitting(OMMonitor monitor) throws RuntimeException
+ {
+ }
+
+ protected void handleTransactionAfterCommitted(OMMonitor monitor)
+ {
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java
new file mode 100644
index 0000000000..c084dce1fb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java
@@ -0,0 +1,27 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.net4j.util.container.IPluginContainer;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public final class PluginRepositoryProvider extends ContainerRepositoryProvider
+{
+ public static final PluginRepositoryProvider INSTANCE = new PluginRepositoryProvider();
+
+ private PluginRepositoryProvider()
+ {
+ super(IPluginContainer.INSTANCE);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java
new file mode 100644
index 0000000000..10aa1d8e09
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java
@@ -0,0 +1,35 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.server.IQueryHandler;
+
+import org.eclipse.net4j.util.factory.Factory;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class QueryHandlerFactory extends Factory
+{
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.queryHandlerFactories"; //$NON-NLS-1$
+
+ public QueryHandlerFactory(String language)
+ {
+ super(PRODUCT_GROUP, language);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public abstract IQueryHandler create(String description) throws ProductCreationException;
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java
new file mode 100644
index 0000000000..e58dc7918c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java
@@ -0,0 +1,346 @@
+/**
+ * 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
+ * Lothar Werzinger - support for configuring user managers
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.internal.server.SessionManager;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.CDOServerUtil;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IRepositoryFactory;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStoreFactory;
+
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+import org.eclipse.net4j.util.security.IUserManager;
+
+import org.eclipse.emf.ecore.EPackage;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class RepositoryConfigurator
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REPOSITORY, RepositoryConfigurator.class);
+
+ private IManagedContainer container;
+
+ private Map<String, IRepositoryFactory> repositoryFactories = new HashMap<String, IRepositoryFactory>();
+
+ private Map<String, IStoreFactory> storeFactories = new HashMap<String, IStoreFactory>();
+
+ public RepositoryConfigurator()
+ {
+ this(null);
+ }
+
+ public RepositoryConfigurator(IManagedContainer container)
+ {
+ this.container = container;
+ }
+
+ public IManagedContainer getContainer()
+ {
+ return container;
+ }
+
+ public Map<String, IRepositoryFactory> getRepositoryFactories()
+ {
+ return repositoryFactories;
+ }
+
+ public Map<String, IStoreFactory> getStoreFactories()
+ {
+ return storeFactories;
+ }
+
+ public IRepository[] configure(File configFile) throws ParserConfigurationException, SAXException, IOException,
+ CoreException
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Configuring CDO server from " + configFile.getAbsolutePath()); //$NON-NLS-1$
+ }
+
+ List<IRepository> repositories = new ArrayList<IRepository>();
+ Document document = getDocument(configFile);
+ NodeList elements = document.getElementsByTagName("repository"); //$NON-NLS-1$
+ for (int i = 0; i < elements.getLength(); i++)
+ {
+ Element repositoryConfig = (Element)elements.item(i);
+ IRepository repository = getRepository(repositoryConfig);
+ repositories.add(repository);
+
+ if (container != null)
+ {
+ CDOServerUtil.addRepository(container, repository);
+ }
+ }
+
+ return repositories.toArray(new IRepository[repositories.size()]);
+ }
+
+ protected Document getDocument(File configFile) throws ParserConfigurationException, SAXException, IOException
+ {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ return builder.parse(configFile);
+ }
+
+ protected IRepositoryFactory getRepositoryFactory(String type) throws CoreException
+ {
+ IRepositoryFactory factory = repositoryFactories.get(type);
+ if (factory == null)
+ {
+ factory = createExecutableExtension("repositoryFactories", "repositoryFactory", //$NON-NLS-1$ //$NON-NLS-2$
+ "repositoryType", type); //$NON-NLS-1$
+ }
+
+ if (factory == null)
+ {
+ throw new IllegalStateException("CDORepositoryInfo factory not found: " + type); //$NON-NLS-1$
+ }
+
+ return factory;
+ }
+
+ protected IRepository getRepository(Element repositoryConfig) throws CoreException
+ {
+ String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$
+ if (StringUtil.isEmpty(repositoryName))
+ {
+ throw new IllegalArgumentException("CDORepositoryInfo name is missing or empty"); //$NON-NLS-1$
+ }
+
+ String repositoryType = repositoryConfig.getAttribute("type"); //$NON-NLS-1$
+ if (StringUtil.isEmpty(repositoryType))
+ {
+ repositoryType = RepositoryFactory.TYPE;
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Configuring repository {0} (type={1})", repositoryName, repositoryType); //$NON-NLS-1$
+ }
+
+ Map<String, String> properties = getProperties(repositoryConfig, 1);
+
+ Element storeConfig = getStoreConfig(repositoryConfig);
+ IStore store = createStore(repositoryName, properties, storeConfig);
+
+ InternalRepository repository = (InternalRepository)getRepository(repositoryType);
+ repository.setName(repositoryName);
+ repository.setStore((InternalStore)store);
+ repository.setProperties(properties);
+
+ Element userManagerConfig = getUserManagerConfig(repositoryConfig);
+ if (userManagerConfig != null)
+ {
+ IUserManager userManager = getUserManager(userManagerConfig);
+ if (userManager != null)
+ {
+ InternalSessionManager sessionManager = repository.getSessionManager();
+ if (sessionManager == null)
+ {
+ sessionManager = new SessionManager();
+ repository.setSessionManager(sessionManager);
+ }
+
+ sessionManager.setUserManager(userManager);
+ }
+ }
+
+ EPackage[] initialPackages = getInitialPackages(repositoryConfig);
+ if (initialPackages.length != 0)
+ {
+ repository.setInitialPackages(initialPackages);
+ }
+
+ return repository;
+ }
+
+ protected IRepository getRepository(String repositoryType) throws CoreException
+ {
+ IRepositoryFactory factory = getRepositoryFactory(repositoryType);
+ return factory.createRepository();
+ }
+
+ protected Element getUserManagerConfig(Element repositoryConfig)
+ {
+ NodeList userManagerConfig = repositoryConfig.getElementsByTagName("userManager"); //$NON-NLS-1$
+ if (userManagerConfig.getLength() > 1)
+ {
+ String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$
+ throw new IllegalStateException("At most one user manager must be configured for repository " + repositoryName); //$NON-NLS-1$
+ }
+
+ return (Element)(userManagerConfig.getLength() > 0 ? userManagerConfig.item(0) : null);
+ }
+
+ protected IUserManager getUserManager(Element userManagerConfig) throws CoreException
+ {
+ String type = userManagerConfig.getAttribute("type"); //$NON-NLS-1$
+ String description = userManagerConfig.getAttribute("description"); //$NON-NLS-1$
+ return getUserManager(type, description);
+ }
+
+ protected IUserManager getUserManager(String type, String description) throws CoreException
+ {
+ IUserManager userManager = (IUserManager)container.getElement("org.eclipse.net4j.userManagers", type, description); //$NON-NLS-1$
+ if (userManager == null)
+ {
+ throw new IllegalStateException("UserManager factory not found: " + type); //$NON-NLS-1$
+ }
+
+ return userManager;
+ }
+
+ protected EPackage[] getInitialPackages(Element repositoryConfig)
+ {
+ List<EPackage> result = new ArrayList<EPackage>();
+
+ NodeList initialPackagesConfig = repositoryConfig.getElementsByTagName("initialPackage"); //$NON-NLS-1$
+ for (int i = 0; i < initialPackagesConfig.getLength(); i++)
+ {
+ Element initialPackageConfig = (Element)initialPackagesConfig.item(i);
+ String nsURI = initialPackageConfig.getAttribute("nsURI"); //$NON-NLS-1$
+ if (nsURI == null)
+ {
+ throw new IllegalStateException("nsURI missing for initialPackage element"); //$NON-NLS-1$
+ }
+
+ EPackage initialPackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
+ if (initialPackage == null)
+ {
+ throw new IllegalStateException("Initial package not found in global package registry: " + nsURI); //$NON-NLS-1$
+ }
+
+ result.add(initialPackage);
+ }
+
+ return result.toArray(new EPackage[result.size()]);
+ }
+
+ protected Element getStoreConfig(Element repositoryConfig)
+ {
+ NodeList storeConfigs = repositoryConfig.getElementsByTagName("store"); //$NON-NLS-1$
+ if (storeConfigs.getLength() == 0)
+ {
+ String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$
+ throw new IllegalStateException("A store must be configured for repository " + repositoryName); //$NON-NLS-1$
+ }
+
+ return (Element)storeConfigs.item(0);
+ }
+
+ protected IStoreFactory getStoreFactory(String type) throws CoreException
+ {
+ IStoreFactory factory = storeFactories.get(type);
+ if (factory == null)
+ {
+ factory = createExecutableExtension("storeFactories", "storeFactory", "storeType", type); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ if (factory == null)
+ {
+ throw new IllegalStateException("Store factory not found: " + type); //$NON-NLS-1$
+ }
+
+ return factory;
+ }
+
+ protected IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig)
+ throws CoreException
+ {
+ String type = storeConfig.getAttribute("type"); //$NON-NLS-1$
+ IStoreFactory storeFactory = getStoreFactory(type);
+ return storeFactory.createStore(repositoryName, repositoryProperties, storeConfig);
+ }
+
+ public static Map<String, String> getProperties(Element element, int levels)
+ {
+ Map<String, String> properties = new HashMap<String, String>();
+ collectProperties(element, "", properties, levels); //$NON-NLS-1$
+ return properties;
+ }
+
+ private static void collectProperties(Element element, String prefix, Map<String, String> properties, int levels)
+ {
+ if ("property".equals(element.getNodeName())) //$NON-NLS-1$
+ {
+ String name = element.getAttribute("name"); //$NON-NLS-1$
+ String value = element.getAttribute("value"); //$NON-NLS-1$
+ properties.put(prefix + name, value);
+ prefix += name + "."; //$NON-NLS-1$
+ }
+
+ if (levels > 0)
+ {
+ NodeList childNodes = element.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++)
+ {
+ Node childNode = childNodes.item(i);
+ if (childNode instanceof Element)
+ {
+ collectProperties((Element)childNode, prefix, properties, levels - 1);
+ }
+ }
+ }
+ }
+
+ private static <T> T createExecutableExtension(String extPointName, String elementName, String attributeName,
+ String type) throws CoreException
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, extPointName);
+ for (IConfigurationElement element : elements)
+ {
+ if (ObjectUtil.equals(element.getName(), elementName))
+ {
+ String storeType = element.getAttribute(attributeName);
+ if (ObjectUtil.equals(storeType, type))
+ {
+ @SuppressWarnings("unchecked")
+ T result = (T)element.createExecutableExtension("class"); //$NON-NLS-1$
+ return result;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java
new file mode 100644
index 0000000000..58ea25cc69
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.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
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.internal.server.Repository;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IRepositoryFactory;
+
+import org.eclipse.net4j.util.container.IManagedContainer;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class RepositoryFactory implements IRepositoryFactory
+{
+ public static final String TYPE = "default"; //$NON-NLS-1$
+
+ public RepositoryFactory()
+ {
+ }
+
+ public String getRepositoryType()
+ {
+ return TYPE;
+ }
+
+ public IRepository createRepository()
+ {
+ return new Repository.Default();
+ }
+
+ public static IRepository get(IManagedContainer container, String name)
+ {
+ return (IRepository)container.getElement(PRODUCT_GROUP, TYPE, name);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java
new file mode 100644
index 0000000000..5b7d424350
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java
@@ -0,0 +1,147 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.server.CDOServerUtil;
+import org.eclipse.emf.cdo.server.IRepository;
+
+import org.eclipse.net4j.util.container.IElementProcessor;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.security.IUserManager;
+import org.eclipse.net4j.util.security.SecurityUtil;
+import org.eclipse.net4j.util.security.UserManagerFactory;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public abstract class RepositoryUserManager extends Lifecycle implements IUserManager
+{
+ private IManagedContainer container;
+
+ private String repositoryName;
+
+ protected RepositoryUserManager()
+ {
+ }
+
+ private void setContainer(IManagedContainer container)
+ {
+ this.container = container;
+ }
+
+ private void setRepositoryName(String repositoryName)
+ {
+ this.repositoryName = repositoryName;
+ }
+
+ public void addUser(String userID, char[] password)
+ {
+ // Cann be overridden in subclasses.
+ }
+
+ public void removeUser(String userID)
+ {
+ // Cann be overridden in subclasses.
+ }
+
+ public byte[] encrypt(String userID, byte[] data, String algorithmName, byte[] salt, int count)
+ throws SecurityException
+ {
+ IRepository repository = getRepository(container, repositoryName);
+ if (repository == null)
+ {
+ throw new SecurityException("Repository not found: " + repositoryName); //$NON-NLS-1$
+ }
+
+ char[] password = getPassword(repository, userID);
+ if (password == null)
+ {
+ throw new SecurityException("No such user: " + userID); //$NON-NLS-1$
+ }
+
+ try
+ {
+ return SecurityUtil.encrypt(data, password, algorithmName, salt, count);
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ throw new SecurityException(ex);
+ }
+ }
+
+ protected IRepository getRepository(IManagedContainer container, String repositoryName)
+ {
+ return CDOServerUtil.getRepository(container, repositoryName);
+ }
+
+ protected abstract char[] getPassword(IRepository repository, String userID);
+
+ public static void prepareContainer(IManagedContainer container, RepositoryUserManagerFactory factory)
+ {
+ container.registerFactory(factory);
+ container.addPostProcessor(new RepositoryInjector());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class RepositoryUserManagerFactory extends UserManagerFactory
+ {
+ protected RepositoryUserManagerFactory(String type)
+ {
+ super(type);
+ }
+
+ public final Object create(String description) throws ProductCreationException
+ {
+ RepositoryUserManager userManager = doCreate(description);
+ String repositoryName = getRepositoryName(description);
+ userManager.setRepositoryName(repositoryName);
+ return userManager;
+ }
+
+ protected String getRepositoryName(String description)
+ {
+ return description;
+ }
+
+ protected abstract RepositoryUserManager doCreate(String description) throws ProductCreationException;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class RepositoryInjector implements IElementProcessor
+ {
+ public RepositoryInjector()
+ {
+ }
+
+ public Object process(IManagedContainer container, String productGroup, String factoryType, String description,
+ Object element)
+ {
+ if (element instanceof RepositoryUserManager)
+ {
+ RepositoryUserManager userManager = (RepositoryUserManager)element;
+ userManager.setContainer(container);
+ }
+
+ return element;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java
new file mode 100644
index 0000000000..407f50c409
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java
@@ -0,0 +1,549 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonView;
+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.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
+import org.eclipse.emf.cdo.internal.server.Repository;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.ISessionManager;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.container.IContainerDelta.Kind;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.ProgressDistributor;
+
+import org.eclipse.emf.ecore.EClass;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class Store extends Lifecycle implements InternalStore
+{
+ /**
+ * @since 3.0
+ * @deprecated Use CDOBranchPoint.UNSPECIFIED_DATE
+ */
+ @Deprecated
+ public static final long UNSPECIFIED_DATE = CDOBranchPoint.UNSPECIFIED_DATE;
+
+ private String type;
+
+ private Set<ObjectType> objectIDTypes;
+
+ private Set<ChangeFormat> supportedChangeFormats;
+
+ private Set<RevisionTemporality> supportedRevisionTemporalities;
+
+ private Set<RevisionParallelism> supportedRevisionParallelisms;
+
+ private RevisionTemporality revisionTemporality = RevisionTemporality.NONE;
+
+ private RevisionParallelism revisionParallelism = RevisionParallelism.NONE;
+
+ private InternalRepository repository;
+
+ private boolean dropAllDataOnActivate;
+
+ /**
+ * Is protected against concurrent thread access through {@link Repository#createBranchLock}.
+ */
+ @ExcludeFromDump
+ private transient int lastBranchID;
+
+ /**
+ * Is protected against concurrent thread access through {@link Repository#createBranchLock}.
+ */
+ @ExcludeFromDump
+ private transient int lastLocalBranchID;
+
+ @ExcludeFromDump
+ private transient long lastCommitTime;
+
+ @ExcludeFromDump
+ private transient Object lastCommitTimeLock = new Object();
+
+ @ExcludeFromDump
+ private transient long lastNonLocalCommitTime;
+
+ @ExcludeFromDump
+ private transient Object lastNonLocalCommitTimeLock = new Object();
+
+ /**
+ * @since 3.0
+ */
+ @ExcludeFromDump
+ private transient ProgressDistributor indicatingCommitDistributor = new ProgressDistributor.Geometric()
+ {
+ @Override
+ public String toString()
+ {
+ String result = "indicatingCommitDistributor"; //$NON-NLS-1$
+ if (repository != null)
+ {
+ result += ": " + repository.getName(); //$NON-NLS-1$
+ }
+
+ return result;
+ }
+ };
+
+ /**
+ * @since 3.0
+ */
+ public Store(String type, Set<CDOID.ObjectType> objectIDTypes, Set<ChangeFormat> supportedChangeFormats,
+ Set<RevisionTemporality> supportedRevisionTemporalities, Set<RevisionParallelism> supportedRevisionParallelisms)
+ {
+ checkArg(!StringUtil.isEmpty(type), "Empty type"); //$NON-NLS-1$
+ this.type = type;
+ this.objectIDTypes = objectIDTypes;
+
+ checkArg(supportedChangeFormats != null && !supportedChangeFormats.isEmpty(), "Empty supportedChangeFormats"); //$NON-NLS-1$
+ this.supportedChangeFormats = supportedChangeFormats;
+
+ checkArg(supportedRevisionTemporalities != null && !supportedRevisionTemporalities.isEmpty(),
+ "Empty supportedRevisionTemporalities"); //$NON-NLS-1$
+ this.supportedRevisionTemporalities = supportedRevisionTemporalities;
+
+ checkArg(supportedRevisionParallelisms != null && !supportedRevisionParallelisms.isEmpty(),
+ "Empty supportedRevisionParallelisms"); //$NON-NLS-1$
+ this.supportedRevisionParallelisms = supportedRevisionParallelisms;
+ }
+
+ public final String getType()
+ {
+ return type;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public Set<CDOID.ObjectType> getObjectIDTypes()
+ {
+ return objectIDTypes;
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void setObjectIDTypes(Set<ObjectType> objectIDTypes)
+ {
+ this.objectIDTypes = objectIDTypes;
+ }
+
+ public Set<ChangeFormat> getSupportedChangeFormats()
+ {
+ return supportedChangeFormats;
+ }
+
+ public Set<RevisionTemporality> getSupportedRevisionTemporalities()
+ {
+ return supportedRevisionTemporalities;
+ }
+
+ public final Set<RevisionParallelism> getSupportedRevisionParallelisms()
+ {
+ return supportedRevisionParallelisms;
+ }
+
+ public RevisionTemporality getRevisionTemporality()
+ {
+ return revisionTemporality;
+ }
+
+ public void setRevisionTemporality(RevisionTemporality revisionTemporality)
+ {
+ checkInactive();
+ checkState(supportedRevisionTemporalities.contains(revisionTemporality), "Revision temporality not supported: " //$NON-NLS-1$
+ + revisionTemporality);
+ this.revisionTemporality = revisionTemporality;
+ }
+
+ public RevisionParallelism getRevisionParallelism()
+ {
+ return revisionParallelism;
+ }
+
+ public void setRevisionParallelism(RevisionParallelism revisionParallelism)
+ {
+ checkInactive();
+ checkState(supportedRevisionParallelisms.contains(revisionParallelism), "Revision parallelism not supported: " //$NON-NLS-1$
+ + revisionParallelism);
+ this.revisionParallelism = revisionParallelism;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public InternalRepository getRepository()
+ {
+ return repository;
+ }
+
+ public void setRepository(IRepository repository)
+ {
+ this.repository = (InternalRepository)repository;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public boolean isDropAllDataOnActivate()
+ {
+ return dropAllDataOnActivate;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public void setDropAllDataOnActivate(boolean dropAllDataOnActivate)
+ {
+ this.dropAllDataOnActivate = dropAllDataOnActivate;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public int getLastBranchID()
+ {
+ return lastBranchID;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setLastBranchID(int lastBranchID)
+ {
+ this.lastBranchID = lastBranchID;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public int getNextBranchID()
+ {
+ return ++lastBranchID;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public int getLastLocalBranchID()
+ {
+ return lastLocalBranchID;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setLastLocalBranchID(int lastLocalBranchID)
+ {
+ this.lastLocalBranchID = lastLocalBranchID;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public int getNextLocalBranchID()
+ {
+ return --lastLocalBranchID;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public long getLastCommitTime()
+ {
+ synchronized (lastCommitTimeLock)
+ {
+ return lastCommitTime;
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setLastCommitTime(long lastCommitTime)
+ {
+ synchronized (lastCommitTimeLock)
+ {
+ if (this.lastCommitTime < lastCommitTime)
+ {
+ this.lastCommitTime = lastCommitTime;
+ }
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public long getLastNonLocalCommitTime()
+ {
+ synchronized (lastNonLocalCommitTimeLock)
+ {
+ return lastNonLocalCommitTime;
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setLastNonLocalCommitTime(long lastNonLocalCommitTime)
+ {
+ synchronized (lastNonLocalCommitTimeLock)
+ {
+ if (this.lastNonLocalCommitTime < lastNonLocalCommitTime)
+ {
+ this.lastNonLocalCommitTime = lastNonLocalCommitTime;
+ }
+ }
+ }
+
+ public IStoreAccessor getReader(ISession session)
+ {
+ IStoreAccessor reader = null;
+ StoreAccessorPool pool = getReaderPool(session, false);
+ if (pool != null)
+ {
+ reader = pool.removeStoreAccessor(session);
+ }
+
+ if (reader == null && session != null)
+ {
+ CDOCommonView[] views = session.getViews();
+ for (CDOCommonView view : views)
+ {
+ pool = getWriterPool((IView)view, false);
+ if (pool != null)
+ {
+ reader = pool.removeStoreAccessor(view);
+ if (reader != null)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ if (reader == null)
+ {
+ reader = createReader(session);
+ LifecycleUtil.activate(reader);
+ }
+
+ return reader;
+ }
+
+ public IStoreAccessor getWriter(ITransaction transaction)
+ {
+ IStoreAccessor writer = null;
+ StoreAccessorPool pool = getWriterPool(transaction, false);
+ if (pool != null)
+ {
+ writer = pool.removeStoreAccessor(transaction);
+ }
+
+ if (writer == null)
+ {
+ writer = createWriter(transaction);
+ LifecycleUtil.activate(writer);
+ }
+
+ return writer;
+ }
+
+ public ProgressDistributor getIndicatingCommitDistributor()
+ {
+ return indicatingCommitDistributor;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public InternalCDORevision createRevision(EClass eClass, CDOID id)
+ {
+ CDORevisionFactory factory = repository.getRevisionManager().getFactory();
+ InternalCDORevision revision = (InternalCDORevision)factory.createRevision(eClass);
+ revision.setID(id);
+ return revision;
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void releaseAccessor(StoreAccessorBase accessor)
+ {
+ StoreAccessorPool pool = null;
+ if (accessor.isReader())
+ {
+ pool = getReaderPool(accessor.getSession(), true);
+ }
+ else
+ {
+ pool = getWriterPool(accessor.getTransaction(), true);
+ }
+
+ if (pool != null)
+ {
+ pool.addStoreAccessor(accessor);
+ }
+ else
+ {
+ accessor.deactivate();
+ }
+ }
+
+ /**
+ * Returns a {@link StoreAccessorPool pool} that may contain {@link IStoreAccessor} instances that are compatible with
+ * the given session. The implementor may return <code>null</code> to indicate that no pooling occurs. It's also left
+ * to the implementors choice how to determine the appropriate pool instance to be used for the given session, for
+ * example it could always return the same pool instance, regardless of the given session.
+ * <p>
+ * If the implementor of this method decides to create pools that are only compatible with certain sessions or views,
+ * then it is his responsibility to listen to {@link Kind#REMOVED REMOVED} events sent by either the
+ * {@link ISessionManager} (indicating that a session is closed) or any of its sessions (indicating that a view is
+ * closed). <b>Note:</b> Closing a session <em>implies</em> that all contained views are closed sliently without
+ * firing respective events!
+ *
+ * @param session
+ * The context which the pool must be compatible with. Must not be <code>null</code>.
+ * @param forReleasing
+ * Enables lazy pool creation. The implementor is not supposed to create a new pool if <code>false</code> is
+ * passed. If <code>true</code> is passed it's up to the implementor whether to create a new pool or not.
+ */
+ protected abstract StoreAccessorPool getReaderPool(ISession session, boolean forReleasing);
+
+ /**
+ * Returns a {@link StoreAccessorPool pool} that may contain {@link IStoreAccessor} instances that are compatible with
+ * the given session. The implementor may return <code>null</code> to indicate that no pooling occurs. It's also left
+ * to the implementors choice how to determine the appropriate pool instance to be used for the given session, for
+ * example it could always return the same pool instance, regardless of the given session.
+ * <p>
+ * If the implementor of this method decides to create pools that are only compatible with certain sessions or views,
+ * then it is his responsibility to listen to {@link Kind#REMOVED REMOVED} events sent by either the
+ * {@link ISessionManager} (indicating that a session is closed) or any of its sessions (indicating that a view is
+ * closed). <b>Note:</b> Closing a session <em>implies</em> that all contained views are closed sliently without
+ * firing respective events!
+ *
+ * @param view
+ * The context which the pool must be compatible with. Must not be <code>null</code>.
+ * @param forReleasing
+ * Enables lazy pool creation. The implementor is not supposed to create a new pool if <code>false</code> is
+ * passed. If <code>true</code> is passed it's up to the implementor whether to create a new pool or not.
+ */
+ protected abstract StoreAccessorPool getWriterPool(IView view, boolean forReleasing);
+
+ /**
+ * Creates and returns a <b>new</b> {@link IStoreAccessor} instance. The caller of this method is responsible for
+ * {@link Lifecycle#activate() activating} the new instance.
+ */
+ protected abstract IStoreAccessor createReader(ISession session);
+
+ /**
+ * Creates and returns a <b>new</b> {@link IStoreAccessor} instance. The caller of this method is responsible for
+ * {@link Lifecycle#activate() activating} the new instance.
+ */
+ protected abstract IStoreAccessor createWriter(ITransaction transaction);
+
+ protected static <T> Set<T> set(T... elements)
+ {
+ return Collections.unmodifiableSet(new HashSet<T>(Arrays.asList(elements)));
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static String idToString(CDOID id)
+ {
+ StringBuilder builder = new StringBuilder();
+ CDOIDUtil.write(builder, id);
+ return builder.toString();
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static CDOID stringToID(String string)
+ {
+ return CDOIDUtil.read(string);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static IStoreAccessor.QueryResourcesContext.ExactMatch createExactMatchContext(final CDOID folderID,
+ final String name, final CDOBranchPoint branchPoint)
+ {
+ return new IStoreAccessor.QueryResourcesContext.ExactMatch()
+ {
+ private CDOID resourceID;
+
+ public CDOID getResourceID()
+ {
+ return resourceID;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public CDOID getFolderID()
+ {
+ return folderID;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public boolean exactMatch()
+ {
+ return true;
+ }
+
+ public int getMaxResults()
+ {
+ return 1;
+ }
+
+ public boolean addResource(CDOID resourceID)
+ {
+ this.resourceID = resourceID;
+ return false;
+ }
+ };
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java
new file mode 100644
index 0000000000..5b988af6e9
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java
@@ -0,0 +1,180 @@
+/**
+ * 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
+ * Simon McDuff - bug 201266
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.io.ExtendedDataInputStream;
+import org.eclipse.net4j.util.io.LimitedInputStream;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class StoreAccessor extends StoreAccessorBase
+{
+ protected StoreAccessor(Store store, ISession session)
+ {
+ super(store, session);
+ }
+
+ protected StoreAccessor(Store store, ITransaction transaction)
+ {
+ super(store, transaction);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void doWrite(InternalCommitContext context, OMMonitor monitor)
+ {
+ CDOBranch branch = context.getBranchPoint().getBranch();
+ long timeStamp = context.getBranchPoint().getTimeStamp();
+ long previousTimeStamp = context.getPreviousTimeStamp();
+ String userID = context.getUserID();
+ String commitComment = context.getCommitComment();
+
+ boolean deltas = getStore().getSupportedChangeFormats().contains(IStore.ChangeFormat.DELTA);
+
+ InternalCDOPackageUnit[] newPackageUnits = context.getNewPackageUnits();
+ InternalCDORevision[] newObjects = context.getNewObjects();
+ CDOID[] detachedObjects = context.getDetachedObjects();
+ int dirtyCount = deltas ? context.getDirtyObjectDeltas().length : context.getDirtyObjects().length;
+
+ try
+ {
+ monitor.begin(1 + newPackageUnits.length + 2 + newObjects.length + detachedObjects.length + dirtyCount);
+ writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, monitor.fork());
+
+ if (newPackageUnits.length != 0)
+ {
+ writePackageUnits(newPackageUnits, monitor.fork(newPackageUnits.length));
+ }
+
+ if (getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
+ {
+ addIDMappings(context, monitor.fork());
+ }
+
+ applyIDMappings(context, monitor);
+
+ if (detachedObjects.length != 0)
+ {
+ detachObjects(detachedObjects, branch, timeStamp, monitor.fork(detachedObjects.length));
+ }
+
+ if (newObjects.length != 0)
+ {
+ writeRevisions(newObjects, branch, monitor.fork(newObjects.length));
+ }
+
+ if (dirtyCount != 0)
+ {
+ if (deltas)
+ {
+ writeRevisionDeltas(context.getDirtyObjectDeltas(), branch, timeStamp, monitor.fork(dirtyCount));
+ }
+ else
+ {
+ writeRevisions(context.getDirtyObjects(), branch, monitor.fork(dirtyCount));
+ }
+ }
+
+ ExtendedDataInputStream in = context.getLobs();
+ if (in != null)
+ {
+ try
+ {
+ int count = in.readInt();
+ for (int i = 0; i < count; i++)
+ {
+ byte[] id = in.readByteArray();
+ long size = in.readLong();
+ if (size > 0)
+ {
+ writeBlob(id, size, new LimitedInputStream(in, size));
+ }
+ else
+ {
+ writeClob(id, -size, new InputStreamReader(new LimitedInputStream(in, -size)));
+ }
+ }
+ }
+ catch (IOException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor)
+ {
+ context.applyIDMappings(monitor.fork());
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected abstract void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
+ String comment, OMMonitor monitor);
+
+ /**
+ * @since 3.0
+ */
+ protected abstract void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor);
+
+ /**
+ * @since 3.0
+ */
+ protected abstract void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch,
+ long created, OMMonitor monitor);
+
+ /**
+ * @since 3.0
+ */
+ protected abstract void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor);
+
+ /**
+ * @since 4.0
+ */
+ protected abstract void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ protected abstract void writeClob(byte[] id, long size, Reader reader) throws IOException;
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java
new file mode 100644
index 0000000000..7be7a2a3a8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java
@@ -0,0 +1,508 @@
+/**
+ * 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
+ * Simon McDuff - bug 201266
+ * Simon McDuff - bug 213402
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOCommitData;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDTemp;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+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.CDORevisionKey;
+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.CDORemoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.ITransaction;
+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.CDOFeatureDeltaVisitorImpl;
+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.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public abstract class StoreAccessorBase extends Lifecycle implements IStoreAccessor
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, StoreAccessorBase.class);
+
+ private Store store;
+
+ private Object context;
+
+ private boolean reader;
+
+ private List<CommitContext> commitContexts = new ArrayList<CommitContext>();
+
+ private StoreAccessorBase(Store store, Object context, boolean reader)
+ {
+ this.store = store;
+ this.context = context;
+ this.reader = reader;
+ }
+
+ protected StoreAccessorBase(Store store, ISession session)
+ {
+ this(store, session, true);
+ }
+
+ protected StoreAccessorBase(Store store, ITransaction transaction)
+ {
+ this(store, transaction, false);
+ }
+
+ void setContext(Object context)
+ {
+ this.context = context;
+ }
+
+ public Store getStore()
+ {
+ return store;
+ }
+
+ public boolean isReader()
+ {
+ return reader;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public InternalSession getSession()
+ {
+ if (context instanceof ITransaction)
+ {
+ return (InternalSession)((ITransaction)context).getSession();
+ }
+
+ return (InternalSession)context;
+ }
+
+ public ITransaction getTransaction()
+ {
+ if (context instanceof ITransaction)
+ {
+ return (ITransaction)context;
+ }
+
+ return null;
+ }
+
+ public void release()
+ {
+ store.releaseAccessor(this);
+ commitContexts.clear();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public final void write(InternalCommitContext context, OMMonitor monitor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing transaction: {0}", getTransaction()); //$NON-NLS-1$
+ }
+
+ commitContexts.add(context);
+ doWrite(context, monitor);
+ }
+
+ protected abstract void doWrite(InternalCommitContext context, OMMonitor monitor);
+
+ /**
+ * @since 3.0
+ */
+ public final void commit(OMMonitor monitor)
+ {
+ doCommit(monitor);
+
+ long latest = CDORevision.UNSPECIFIED_DATE;
+ long latestNonLocal = CDORevision.UNSPECIFIED_DATE;
+ for (CommitContext commitContext : commitContexts)
+ {
+ CDOBranchPoint branchPoint = commitContext.getBranchPoint();
+ long timeStamp = branchPoint.getTimeStamp();
+ if (timeStamp > latest)
+ {
+ latest = timeStamp;
+ }
+
+ CDOBranch branch = branchPoint.getBranch();
+ if (!branch.isLocal())
+ {
+ if (timeStamp > latestNonLocal)
+ {
+ latestNonLocal = timeStamp;
+ }
+ }
+ }
+
+ getStore().setLastCommitTime(latest);
+ getStore().setLastNonLocalCommitTime(latestNonLocal);
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected abstract void doCommit(OMMonitor monitor);
+
+ public final void rollback()
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Rolling back transaction: {0}", getTransaction()); //$NON-NLS-1$
+ }
+
+ for (CommitContext commitContext : commitContexts)
+ {
+ doRollback(commitContext);
+ }
+ }
+
+ protected abstract void doRollback(CommitContext commitContext);
+
+ /**
+ * @since 3.0
+ */
+ public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint)
+ {
+ QueryResourcesContext.ExactMatch context = Store.createExactMatchContext(folderID, name, branchPoint);
+ queryResources(context);
+ return context.getResourceID();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public CDOCommitData loadCommitData(long timeStamp)
+ {
+ CommitDataRevisionHandler handler = new CommitDataRevisionHandler(this, timeStamp);
+ return handler.getCommitData();
+ }
+
+ /**
+ * Add ID mappings for all new objects of a transaction to the commit context. The implementor must, for each new
+ * object of the commit context, determine a permanent CDOID and make it known to the context by calling
+ * {@link InternalCommitContext#addIDMapping(CDOID, CDOID)}.
+ *
+ * @since 3.0
+ */
+ public void addIDMappings(InternalCommitContext commitContext, OMMonitor monitor)
+ {
+ try
+ {
+ CDORevision[] newObjects = commitContext.getNewObjects();
+ monitor.begin(newObjects.length);
+ for (CDORevision revision : newObjects)
+ {
+ CDOID id = revision.getID();
+ if (id instanceof CDOIDTemp)
+ {
+ CDOIDTemp oldID = (CDOIDTemp)id;
+ CDOID newID = getNextCDOID(revision);
+ if (CDOIDUtil.isNull(newID) || newID.isTemporary())
+ {
+ throw new IllegalStateException("newID=" + newID); //$NON-NLS-1$
+ }
+
+ commitContext.addIDMapping(oldID, newID);
+ }
+
+ monitor.worked();
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ protected abstract CDOID getNextCDOID(CDORevision revision);
+
+ protected void doPassivate() throws Exception
+ {
+ }
+
+ protected void doUnpassivate() throws Exception
+ {
+ }
+
+ /**
+ * @author Eike Stepper
+ * @since 3.0
+ */
+ public static class CommitDataRevisionHandler implements CDORevisionHandler
+ {
+ private IStoreAccessor storeAccessor;
+
+ private long timeStamp;
+
+ private InternalCDORevisionManager revisionManager;
+
+ private List<CDOPackageUnit> newPackageUnits = new ArrayList<CDOPackageUnit>();
+
+ private List<CDOIDAndVersion> newObjects = new ArrayList<CDOIDAndVersion>();
+
+ private List<CDORevisionKey> changedObjects = new ArrayList<CDORevisionKey>();
+
+ private DetachCounter detachCounter = new DetachCounter();
+
+ public CommitDataRevisionHandler(IStoreAccessor storeAccessor, long timeStamp)
+ {
+ this.storeAccessor = storeAccessor;
+ this.timeStamp = timeStamp;
+
+ InternalStore store = (InternalStore)storeAccessor.getStore();
+ InternalRepository repository = store.getRepository();
+ revisionManager = repository.getRevisionManager();
+
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ InternalCDOPackageUnit[] packageUnits = packageRegistry.getPackageUnits(timeStamp, timeStamp);
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ if (!packageUnit.isSystem())
+ {
+ newPackageUnits.add(packageUnit);
+ }
+ }
+ }
+
+ public CDOCommitData getCommitData()
+ {
+ storeAccessor.handleRevisions(null, null, timeStamp, true, new CDORevisionHandler.Filtered.Undetached(this));
+
+ List<CDOIDAndVersion> detachedObjects = detachCounter.getDetachedObjects();
+ return new CDOCommitDataImpl(newPackageUnits, newObjects, changedObjects, detachedObjects);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public boolean handleRevision(CDORevision rev)
+ {
+ if (rev.getTimeStamp() != timeStamp)
+ {
+ throw new IllegalArgumentException("Invalid revision time stamp: "
+ + CDOCommonUtil.formatTimeStamp(rev.getTimeStamp()));
+ }
+
+ if (rev instanceof DetachedCDORevision)
+ {
+ // Do nothing. Detached objects are handled by detachCounter.
+ }
+ else
+ {
+ InternalCDORevision revision = (InternalCDORevision)rev;
+ CDOID id = revision.getID();
+ CDOBranch branch = revision.getBranch();
+ int version = revision.getVersion();
+ if (version > CDOBranchVersion.FIRST_VERSION)
+ {
+ CDOBranchVersion oldVersion = branch.getVersion(version - 1);
+ InternalCDORevision oldRevision = revisionManager.getRevisionByVersion(id, oldVersion, CDORevision.UNCHUNKED,
+ true);
+ InternalCDORevisionDelta delta = revision.compare(oldRevision);
+ changedObjects.add(delta);
+
+ detachCounter.update(oldRevision, delta);
+ }
+ else
+ {
+ InternalCDORevision oldRevision = getRevisionFromBase(id, branch);
+ if (oldRevision != null)
+ {
+ InternalCDORevisionDelta delta = revision.compare(oldRevision);
+ changedObjects.add(delta);
+ }
+ else
+ {
+ InternalCDORevision newRevision = revision.copy();
+ newRevision.setRevised(CDOBranchPoint.UNSPECIFIED_DATE);
+ newObjects.add(newRevision);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private InternalCDORevision getRevisionFromBase(CDOID id, CDOBranch branch)
+ {
+ if (branch.isMainBranch())
+ {
+ return null;
+ }
+
+ CDOBranchPoint base = branch.getBase();
+ InternalCDORevision revision = revisionManager.getRevision(id, base, CDORevision.UNCHUNKED,
+ CDORevision.DEPTH_NONE, true);
+ if (revision == null)
+ {
+ revision = getRevisionFromBase(id, base.getBranch());
+ }
+
+ return revision;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class DetachCounter extends CDOFeatureDeltaVisitorImpl
+ {
+ private Map<CDOID, AtomicInteger> counters = new HashMap<CDOID, AtomicInteger>();
+
+ private InternalCDORevision oldRevision;
+
+ public DetachCounter()
+ {
+ }
+
+ public void update(InternalCDORevision oldRevision, InternalCDORevisionDelta delta)
+ {
+ try
+ {
+ this.oldRevision = oldRevision;
+ delta.accept(this);
+ }
+ finally
+ {
+ this.oldRevision = null;
+ }
+ }
+
+ public List<CDOIDAndVersion> getDetachedObjects()
+ {
+ List<CDOIDAndVersion> result = new ArrayList<CDOIDAndVersion>();
+ for (Entry<CDOID, AtomicInteger> entry : counters.entrySet())
+ {
+ int value = entry.getValue().get();
+ if (value == -1)
+ {
+ CDOID id = entry.getKey();
+ result.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ if (isContainment(delta.getFeature()))
+ {
+ handleContainment(delta.getValue(), 1);
+ }
+ }
+
+ @Override
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ if (isContainment(delta.getFeature()))
+ {
+ handleContainment(delta.getValue(), -1);
+ }
+ }
+
+ @Override
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ if (isContainment(delta.getFeature()))
+ {
+ handleContainment(delta.getValue(), 1);
+ }
+ }
+
+ @Override
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ EStructuralFeature feature = delta.getFeature();
+ if (isContainment(feature))
+ {
+ Object value = oldRevision.getValue(feature);
+ handleContainment(value, -1);
+ }
+ }
+
+ @Override
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ EStructuralFeature feature = delta.getFeature();
+ if (isContainment(feature))
+ {
+ CDOList list = oldRevision.getList(feature);
+ for (Object value : list)
+ {
+ handleContainment(value, -1);
+ }
+ }
+ }
+
+ private void handleContainment(Object value, int delta)
+ {
+ CDOID id = (CDOID)value;
+ AtomicInteger counter = counters.get(id);
+ if (counter == null)
+ {
+ counter = new AtomicInteger();
+ counters.put(id, counter);
+ }
+
+ counter.addAndGet(delta);
+ }
+
+ private static boolean isContainment(EStructuralFeature feature)
+ {
+ if (feature instanceof EReference)
+ {
+ EReference reference = (EReference)feature;
+ return reference.isContainment();
+ }
+
+ return false;
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java
new file mode 100644
index 0000000000..4b71389999
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java
@@ -0,0 +1,121 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IView;
+
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.log.OMLogger;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class StoreAccessorPool
+{
+ /**
+ * The {@link IStore store} instance that manages this pool.
+ */
+ private IStore store;
+
+ /**
+ * The pooling context of this pool. An instance of either {@link ISession} or {@link IView}, or <code>null</code> if
+ * this pool is not contextual.
+ */
+ private Object context;
+
+ private ConcurrentLinkedQueue<StoreAccessorBase> accessors = new ConcurrentLinkedQueue<StoreAccessorBase>();
+
+ public StoreAccessorPool(IStore store, Object context)
+ {
+ this.store = store;
+ this.context = context;
+ }
+
+ public IStore getStore()
+ {
+ return store;
+ }
+
+ public Object getContext()
+ {
+ return context;
+ }
+
+ /**
+ * Passivates the given {@link StoreAccessor store accessor} and adds it to this pool.
+ *
+ * @since 4.0
+ */
+ public void addStoreAccessor(StoreAccessorBase storeAccessor)
+ {
+ try
+ {
+ storeAccessor.doPassivate();
+ accessors.add(storeAccessor);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+
+ /**
+ * Returns a {@link StoreAccessor store accessor} from this pool if one is available, or <code>null</code> otherwise.
+ * If a store accessor is available it is removed from this pool and its unpassivate method is called.
+ *
+ * @since 4.0
+ */
+ public StoreAccessorBase removeStoreAccessor(Object context)
+ {
+ StoreAccessorBase accessor = accessors.poll();
+ if (accessor != null)
+ {
+ try
+ {
+ accessor.doUnpassivate();
+ accessor.setContext(context);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ return null;
+ }
+ }
+
+ return accessor;
+ }
+
+ /**
+ * Deactivates all contained {@link StoreAccessor store accessors} and clears this pool.
+ */
+ public void dispose()
+ {
+ for (;;)
+ {
+ StoreAccessorBase accessor = accessors.poll();
+ if (accessor == null)
+ {
+ break;
+ }
+
+ LifecycleUtil.deactivate(accessor, OMLogger.Level.WARN);
+ }
+
+ context = null;
+ store = null;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java
new file mode 100644
index 0000000000..36b7dc1a32
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java
@@ -0,0 +1,72 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreChunkReader;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class StoreChunkReader implements IStoreChunkReader
+{
+ private IStoreAccessor accessor;
+
+ private CDORevision revision;
+
+ private EStructuralFeature feature;
+
+ private List<Chunk> chunks = new ArrayList<Chunk>(0);
+
+ public StoreChunkReader(IStoreAccessor accessor, CDORevision revision, EStructuralFeature feature)
+ {
+ this.accessor = accessor;
+ this.revision = revision;
+ this.feature = feature;
+ }
+
+ public IStoreAccessor getAccessor()
+ {
+ return accessor;
+ }
+
+ public CDORevision getRevision()
+ {
+ return revision;
+ }
+
+ public EStructuralFeature getFeature()
+ {
+ return feature;
+ }
+
+ public List<Chunk> getChunks()
+ {
+ return chunks;
+ }
+
+ public void addSimpleChunk(int index)
+ {
+ chunks.add(new Chunk(index));
+ }
+
+ public void addRangedChunk(int fromIndex, int toIndex)
+ {
+ chunks.add(new Chunk(fromIndex, toIndex - fromIndex));
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java
new file mode 100644
index 0000000000..a6eef7052e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java
@@ -0,0 +1,60 @@
+/**
+ * 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.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException;
+
+import org.eclipse.net4j.util.CheckUtil;
+
+/**
+ * Static methods that may help with classes related to repository synchronization.
+ *
+ * @author Eike Stepper
+ * @since 4.1
+ */
+public final class SyncingUtil
+{
+ private SyncingUtil()
+ {
+ }
+
+ public static InternalView openViewWithLockArea(InternalSession session, InternalLockManager lockManager,
+ CDOBranch viewedBranch, String lockAreaID)
+ {
+ LockArea lockArea;
+ InternalView view;
+
+ try
+ {
+ lockArea = lockManager.getLockArea(lockAreaID);
+
+ // If we get here, the lockArea already exists.
+ view = (InternalView)lockManager.openView(session, InternalSession.TEMP_VIEW_ID, true, lockAreaID);
+ }
+ catch (LockAreaNotFoundException e)
+ {
+ // If we get here, the lockArea does not yet exist, so we open
+ // a view without a lockArea first, then create a lockArea with the given ID,
+ // and associate it with the view.
+ view = session.openView(InternalSession.TEMP_VIEW_ID, viewedBranch.getHead());
+ lockArea = lockManager.createLockArea(view, lockAreaID);
+ view.setDurableLockingID(lockAreaID);
+ }
+
+ CheckUtil.checkNull(lockAreaID, "lockAreaID");
+ CheckUtil.checkNull(lockArea, "lockArea");
+ CheckUtil.checkState(lockAreaID.equals(lockArea.getDurableLockingID()), "lockAreaID has incorrect value");
+
+ return view;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java
new file mode 100644
index 0000000000..f320267eb8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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 service provider interfaces and useful base implementations.
+ *
+ * @apiviz.exclude .*
+ */
+package org.eclipse.emf.cdo.spi.server;
+

Back to the top