diff --git a/bundles/cnf/ext/repositories.bnd b/bundles/cnf/ext/repositories.bnd index 4c1d8ea45..16874f134 100644 --- a/bundles/cnf/ext/repositories.bnd +++ b/bundles/cnf/ext/repositories.bnd @@ -1,5 +1,5 @@ --plugin: \ - aQute.bnd.deployer.repository.LocalIndexedRepo;name=Release;local=${workspace}/cnf/releaserepo;pretty=true,\ - aQute.bnd.deployer.repository.LocalIndexedRepo;name=Local;local=${workspace}/cnf/localrepo;pretty=true,\ - aQute.lib.deployer.FileRepo;name=Build;location=${workspace}/cnf/buildrepo;latest=false --releaserepo: Release +-plugin: \ + aQute.bnd.deployer.repository.LocalIndexedRepo;name=Release;local=${workspace}/cnf/releaserepo;pretty=true,\ + aQute.bnd.deployer.repository.LocalIndexedRepo;name=Local;local=${workspace}/cnf/localrepo;pretty=true,\ + aQute.lib.deployer.FileRepo;name=Build;location=${workspace}/cnf/buildrepo;latest=false +-releaserepo: Release diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ant/org.eclipse.emf.ant-2.8.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ant/org.eclipse.emf.ant-2.8.0.jar deleted file mode 100644 index 720c4f3eb..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ant/org.eclipse.emf.ant-2.8.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.admin.source/org.eclipse.emf.cdo.admin.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.admin.source/org.eclipse.emf.cdo.admin.source-4.1.400.jar deleted file mode 100644 index ddd955ce8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.admin.source/org.eclipse.emf.cdo.admin.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.admin/org.eclipse.emf.cdo.admin-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.admin/org.eclipse.emf.cdo.admin-4.1.400.jar deleted file mode 100644 index fe6420eb7..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.admin/org.eclipse.emf.cdo.admin-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.db.source/org.eclipse.emf.cdo.common.db.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.db.source/org.eclipse.emf.cdo.common.db.source-3.0.500.jar deleted file mode 100644 index ab0e49c6e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.db.source/org.eclipse.emf.cdo.common.db.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.db/org.eclipse.emf.cdo.common.db-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.db/org.eclipse.emf.cdo.common.db-3.0.500.jar deleted file mode 100644 index cc6db10f2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.db/org.eclipse.emf.cdo.common.db-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.source/org.eclipse.emf.cdo.common.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.source/org.eclipse.emf.cdo.common.source-4.5.0.jar deleted file mode 100644 index a767a1554..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common.source/org.eclipse.emf.cdo.common.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common/org.eclipse.emf.cdo.common-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.common/org.eclipse.emf.cdo.common-4.5.0.jar deleted file mode 100644 index 0f6980106..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common/org.eclipse.emf.cdo.common-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.common/org.eclipse.emf.cdo.common-4.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.common/org.eclipse.emf.cdo.common-4.7.0.jar new file mode 100644 index 000000000..410fe8fbb Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.cdo.common/org.eclipse.emf.cdo.common-4.7.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.compare.source/org.eclipse.emf.cdo.compare.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.compare.source/org.eclipse.emf.cdo.compare.source-4.3.100.jar deleted file mode 100644 index 3e70525cf..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.compare.source/org.eclipse.emf.cdo.compare.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.compare/org.eclipse.emf.cdo.compare-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.compare/org.eclipse.emf.cdo.compare-4.3.100.jar deleted file mode 100644 index 7aae0fbbe..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.compare/org.eclipse.emf.cdo.compare-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit.source-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit.source-2.0.300.jar deleted file mode 100644 index 715dd7139..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit.source-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit-2.0.300.jar deleted file mode 100644 index fa4c107f9..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.edit-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor.source-2.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor.source-2.0.400.jar deleted file mode 100644 index 0839e0d3d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor.source-2.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor-2.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor-2.0.400.jar deleted file mode 100644 index 49d389004..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.editor-2.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit.source-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit.source-1.0.300.jar deleted file mode 100644 index abc7ad1ab..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit.source-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit-1.0.300.jar deleted file mode 100644 index 7e8a958e0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.edit-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.source-1.0.400.jar deleted file mode 100644 index 46a4c9a4a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui.source-1.0.400.jar deleted file mode 100644 index 9a37ef995..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui-1.0.400.jar deleted file mode 100644 index 7fe4202d8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf.ui-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf-1.0.400.jar deleted file mode 100644 index 1353f7c1c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit.source-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit.source-1.0.300.jar deleted file mode 100644 index f28c40bb7..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit.source-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit-1.0.300.jar deleted file mode 100644 index a457d738f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.edit-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.source-1.0.400.jar deleted file mode 100644 index 886f4b6dd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui.source-1.0.400.jar deleted file mode 100644 index 5b21f909f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui-1.0.400.jar deleted file mode 100644 index 16a51fd43..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf.ui-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf-1.0.400.jar deleted file mode 100644 index 5f9886691..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.gmf-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.source-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.source-2.0.300.jar deleted file mode 100644 index 874981edc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.source/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.source-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel-2.0.300.jar deleted file mode 100644 index 9680fa950..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.source/org.eclipse.emf.cdo.dawn.codegen.source-1.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.source/org.eclipse.emf.cdo.dawn.codegen.source-1.0.500.jar deleted file mode 100644 index a033b0a35..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen.source/org.eclipse.emf.cdo.dawn.codegen.source-1.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen/org.eclipse.emf.cdo.dawn.codegen-1.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen/org.eclipse.emf.cdo.dawn.codegen-1.0.500.jar deleted file mode 100644 index e5e7b7a67..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.codegen/org.eclipse.emf.cdo.dawn.codegen-1.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecore.editor.dawn.source/org.eclipse.emf.cdo.dawn.ecore.editor.dawn.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecore.editor.dawn.source/org.eclipse.emf.cdo.dawn.ecore.editor.dawn.source-1.0.400.jar deleted file mode 100644 index 8a314a2cd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecore.editor.dawn.source/org.eclipse.emf.cdo.dawn.ecore.editor.dawn.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecore.editor.dawn/org.eclipse.emf.cdo.dawn.ecore.editor.dawn-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecore.editor.dawn/org.eclipse.emf.cdo.dawn.ecore.editor.dawn-1.0.400.jar deleted file mode 100644 index 2efbece7a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecore.editor.dawn/org.eclipse.emf.cdo.dawn.ecore.editor.dawn-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn.source/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn.source/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn.source-1.0.400.jar deleted file mode 100644 index 1b9dc297f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn.source/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn-1.0.400.jar deleted file mode 100644 index 9f0216e1b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn/org.eclipse.emf.cdo.dawn.ecoretools.diagram.dawn-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.emf.source/org.eclipse.emf.cdo.dawn.emf.source-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.emf.source/org.eclipse.emf.cdo.dawn.emf.source-2.0.300.jar deleted file mode 100644 index a854fc141..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.emf.source/org.eclipse.emf.cdo.dawn.emf.source-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.emf/org.eclipse.emf.cdo.dawn.emf-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.emf/org.eclipse.emf.cdo.dawn.emf-2.0.300.jar deleted file mode 100644 index dfb9fc700..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.emf/org.eclipse.emf.cdo.dawn.emf-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn.source-1.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn.source-1.0.500.jar deleted file mode 100644 index 5f24f1130..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn.source-1.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn-1.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn-1.0.500.jar deleted file mode 100644 index 295c26cee..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn/org.eclipse.emf.cdo.dawn.examples.acore.diagram.dawn-1.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.source/org.eclipse.emf.cdo.dawn.examples.acore.diagram.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.source/org.eclipse.emf.cdo.dawn.examples.acore.diagram.source-1.0.400.jar deleted file mode 100644 index 454559ce4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram.source/org.eclipse.emf.cdo.dawn.examples.acore.diagram.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram/org.eclipse.emf.cdo.dawn.examples.acore.diagram-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram/org.eclipse.emf.cdo.dawn.examples.acore.diagram-1.0.400.jar deleted file mode 100644 index c84ecc118..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.diagram/org.eclipse.emf.cdo.dawn.examples.acore.diagram-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.edit.source/org.eclipse.emf.cdo.dawn.examples.acore.edit.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.edit.source/org.eclipse.emf.cdo.dawn.examples.acore.edit.source-1.0.400.jar deleted file mode 100644 index 71fa2af94..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.edit.source/org.eclipse.emf.cdo.dawn.examples.acore.edit.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.edit/org.eclipse.emf.cdo.dawn.examples.acore.edit-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.edit/org.eclipse.emf.cdo.dawn.examples.acore.edit-1.0.400.jar deleted file mode 100644 index 224f48b1c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.edit/org.eclipse.emf.cdo.dawn.examples.acore.edit-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn.source-1.0.400.jar deleted file mode 100644 index f5db69347..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn-1.0.400.jar deleted file mode 100644 index bf9074ddc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn/org.eclipse.emf.cdo.dawn.examples.acore.editor.dawn-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.source/org.eclipse.emf.cdo.dawn.examples.acore.editor.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.source/org.eclipse.emf.cdo.dawn.examples.acore.editor.source-1.0.400.jar deleted file mode 100644 index 47bf9f1d3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor.source/org.eclipse.emf.cdo.dawn.examples.acore.editor.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor/org.eclipse.emf.cdo.dawn.examples.acore.editor-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor/org.eclipse.emf.cdo.dawn.examples.acore.editor-1.0.400.jar deleted file mode 100644 index 38d404f91..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.editor/org.eclipse.emf.cdo.dawn.examples.acore.editor-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn.source-1.0.400.jar deleted file mode 100644 index c8c1cb50e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn.source/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn-1.0.400.jar deleted file mode 100644 index 7721c81ad..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.dawn-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.source/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.source-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.source/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.source-1.0.300.jar deleted file mode 100644 index a888d840b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.source/org.eclipse.emf.cdo.dawn.examples.acore.graphiti.source-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti/org.eclipse.emf.cdo.dawn.examples.acore.graphiti-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti/org.eclipse.emf.cdo.dawn.examples.acore.graphiti-1.0.300.jar deleted file mode 100644 index ef56b0d25..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.graphiti/org.eclipse.emf.cdo.dawn.examples.acore.graphiti-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.source/org.eclipse.emf.cdo.dawn.examples.acore.source-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.source/org.eclipse.emf.cdo.dawn.examples.acore.source-1.0.400.jar deleted file mode 100644 index 5ffe40b1a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore.source/org.eclipse.emf.cdo.dawn.examples.acore.source-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore/org.eclipse.emf.cdo.dawn.examples.acore-1.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore/org.eclipse.emf.cdo.dawn.examples.acore-1.0.400.jar deleted file mode 100644 index 414855286..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.acore/org.eclipse.emf.cdo.dawn.examples.acore-1.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.source/org.eclipse.emf.cdo.dawn.examples.source-1.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.source/org.eclipse.emf.cdo.dawn.examples.source-1.0.500.jar deleted file mode 100644 index 5e0e7f55a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples.source/org.eclipse.emf.cdo.dawn.examples.source-1.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples/org.eclipse.emf.cdo.dawn.examples-1.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples/org.eclipse.emf.cdo.dawn.examples-1.0.500.jar deleted file mode 100644 index 32beb7bcd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.examples/org.eclipse.emf.cdo.dawn.examples-1.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.gmf.source/org.eclipse.emf.cdo.dawn.gmf.source-2.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.gmf.source/org.eclipse.emf.cdo.dawn.gmf.source-2.1.300.jar deleted file mode 100644 index caf66d11b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.gmf.source/org.eclipse.emf.cdo.dawn.gmf.source-2.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.gmf/org.eclipse.emf.cdo.dawn.gmf-2.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.gmf/org.eclipse.emf.cdo.dawn.gmf-2.1.300.jar deleted file mode 100644 index bbddf7fbd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.gmf/org.eclipse.emf.cdo.dawn.gmf-2.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.graphiti.source/org.eclipse.emf.cdo.dawn.graphiti.source-2.1.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.graphiti.source/org.eclipse.emf.cdo.dawn.graphiti.source-2.1.200.jar deleted file mode 100644 index ed96660ca..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.graphiti.source/org.eclipse.emf.cdo.dawn.graphiti.source-2.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.graphiti/org.eclipse.emf.cdo.dawn.graphiti-2.1.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.graphiti/org.eclipse.emf.cdo.dawn.graphiti-2.1.200.jar deleted file mode 100644 index 7072143f3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.graphiti/org.eclipse.emf.cdo.dawn.graphiti-2.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.sdk.source/org.eclipse.emf.cdo.dawn.sdk.source-2.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.sdk.source/org.eclipse.emf.cdo.dawn.sdk.source-2.0.400.jar deleted file mode 100644 index 40967d4e2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.sdk.source/org.eclipse.emf.cdo.dawn.sdk.source-2.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.sdk/org.eclipse.emf.cdo.dawn.sdk-2.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.sdk/org.eclipse.emf.cdo.dawn.sdk-2.0.400.jar deleted file mode 100644 index 2b6bf1fa6..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.sdk/org.eclipse.emf.cdo.dawn.sdk-2.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.source/org.eclipse.emf.cdo.dawn.source-2.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.source/org.eclipse.emf.cdo.dawn.source-2.0.400.jar deleted file mode 100644 index fffca6c86..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.source/org.eclipse.emf.cdo.dawn.source-2.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.tests.source/org.eclipse.emf.cdo.dawn.tests.source-1.1.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.tests.source/org.eclipse.emf.cdo.dawn.tests.source-1.1.200.jar deleted file mode 100644 index d1c326dfa..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.tests.source/org.eclipse.emf.cdo.dawn.tests.source-1.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.tests/org.eclipse.emf.cdo.dawn.tests-1.1.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.tests/org.eclipse.emf.cdo.dawn.tests-1.1.200.jar deleted file mode 100644 index 89b65a005..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.tests/org.eclipse.emf.cdo.dawn.tests-1.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ui.source/org.eclipse.emf.cdo.dawn.ui.source-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ui.source/org.eclipse.emf.cdo.dawn.ui.source-2.0.300.jar deleted file mode 100644 index 1897bfa2a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ui.source/org.eclipse.emf.cdo.dawn.ui.source-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ui/org.eclipse.emf.cdo.dawn.ui-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ui/org.eclipse.emf.cdo.dawn.ui-2.0.300.jar deleted file mode 100644 index dfb1d44e5..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.ui/org.eclipse.emf.cdo.dawn.ui-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.util.source/org.eclipse.emf.cdo.dawn.util.source-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.util.source/org.eclipse.emf.cdo.dawn.util.source-2.0.300.jar deleted file mode 100644 index a52306456..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.util.source/org.eclipse.emf.cdo.dawn.util.source-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.util/org.eclipse.emf.cdo.dawn.util-2.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.util/org.eclipse.emf.cdo.dawn.util-2.0.300.jar deleted file mode 100644 index 24badf950..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn.util/org.eclipse.emf.cdo.dawn.util-2.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn/org.eclipse.emf.cdo.dawn-2.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn/org.eclipse.emf.cdo.dawn-2.0.400.jar deleted file mode 100644 index f0b4943bf..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.dawn/org.eclipse.emf.cdo.dawn-2.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.defs.source/org.eclipse.emf.cdo.defs.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.defs.source/org.eclipse.emf.cdo.defs.source-4.0.500.jar deleted file mode 100644 index 8596f8439..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.defs.source/org.eclipse.emf.cdo.defs.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.defs/org.eclipse.emf.cdo.defs-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.defs/org.eclipse.emf.cdo.defs-4.0.500.jar deleted file mode 100644 index 009699b12..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.defs/org.eclipse.emf.cdo.defs-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.doc.source/org.eclipse.emf.cdo.doc.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.doc.source/org.eclipse.emf.cdo.doc.source-4.1.400.jar deleted file mode 100644 index 973f77598..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.doc.source/org.eclipse.emf.cdo.doc.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.doc/org.eclipse.emf.cdo.doc-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.doc/org.eclipse.emf.cdo.doc-4.1.400.jar deleted file mode 100644 index 78c8dedb3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.doc/org.eclipse.emf.cdo.doc-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore.retrofit.source/org.eclipse.emf.cdo.ecore.retrofit.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore.retrofit.source/org.eclipse.emf.cdo.ecore.retrofit.source-4.2.300.jar deleted file mode 100644 index 83fa82408..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore.retrofit.source/org.eclipse.emf.cdo.ecore.retrofit.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore.retrofit/org.eclipse.emf.cdo.ecore.retrofit-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore.retrofit/org.eclipse.emf.cdo.ecore.retrofit-4.2.300.jar deleted file mode 100644 index eba022eaa..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore.retrofit/org.eclipse.emf.cdo.ecore.retrofit-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore/org.eclipse.emf.cdo.ecore-1.0.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore/org.eclipse.emf.cdo.ecore-1.0.0.jar new file mode 100644 index 000000000..df7092816 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ecore/org.eclipse.emf.cdo.ecore-1.0.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.edit.source/org.eclipse.emf.cdo.edit.source-4.4.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.edit.source/org.eclipse.emf.cdo.edit.source-4.4.0.jar deleted file mode 100644 index 278dfc6c3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.edit.source/org.eclipse.emf.cdo.edit.source-4.4.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.edit/org.eclipse.emf.cdo.edit-4.4.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.edit/org.eclipse.emf.cdo.edit-4.4.0.jar deleted file mode 100644 index bb29ad0ff..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.edit/org.eclipse.emf.cdo.edit-4.4.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.edit.source/org.eclipse.emf.cdo.examples.company.edit.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.edit.source/org.eclipse.emf.cdo.examples.company.edit.source-4.0.400.jar deleted file mode 100644 index 3e32f1091..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.edit.source/org.eclipse.emf.cdo.examples.company.edit.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.edit/org.eclipse.emf.cdo.examples.company.edit-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.edit/org.eclipse.emf.cdo.examples.company.edit-4.0.400.jar deleted file mode 100644 index 60b5423fd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.edit/org.eclipse.emf.cdo.examples.company.edit-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.source/org.eclipse.emf.cdo.examples.company.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.source/org.eclipse.emf.cdo.examples.company.source-4.0.400.jar deleted file mode 100644 index a457401e7..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company.source/org.eclipse.emf.cdo.examples.company.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company/org.eclipse.emf.cdo.examples.company-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company/org.eclipse.emf.cdo.examples.company-4.0.400.jar deleted file mode 100644 index c3e3d8d2c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.company/org.eclipse.emf.cdo.examples.company-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.client.source/org.eclipse.emf.cdo.examples.hibernate.client.source-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.client.source/org.eclipse.emf.cdo.examples.hibernate.client.source-4.1.300.jar deleted file mode 100644 index 78d5cb38f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.client.source/org.eclipse.emf.cdo.examples.hibernate.client.source-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.client/org.eclipse.emf.cdo.examples.hibernate.client-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.client/org.eclipse.emf.cdo.examples.hibernate.client-4.1.300.jar deleted file mode 100644 index 7271cc216..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.client/org.eclipse.emf.cdo.examples.hibernate.client-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.log4j.source/org.eclipse.emf.cdo.examples.hibernate.log4j.source-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.log4j.source/org.eclipse.emf.cdo.examples.hibernate.log4j.source-4.0.300.jar deleted file mode 100644 index 7e633a2f8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.log4j.source/org.eclipse.emf.cdo.examples.hibernate.log4j.source-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.log4j/org.eclipse.emf.cdo.examples.hibernate.log4j-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.log4j/org.eclipse.emf.cdo.examples.hibernate.log4j-4.0.300.jar deleted file mode 100644 index c26f71eed..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.log4j/org.eclipse.emf.cdo.examples.hibernate.log4j-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.server.source/org.eclipse.emf.cdo.examples.hibernate.server.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.server.source/org.eclipse.emf.cdo.examples.hibernate.server.source-4.1.400.jar deleted file mode 100644 index 5af013b75..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.server.source/org.eclipse.emf.cdo.examples.hibernate.server.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.server/org.eclipse.emf.cdo.examples.hibernate.server-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.server/org.eclipse.emf.cdo.examples.hibernate.server-4.1.400.jar deleted file mode 100644 index f1a60ea88..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.hibernate.server/org.eclipse.emf.cdo.examples.hibernate.server-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.installer.source/org.eclipse.emf.cdo.examples.installer.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.installer.source/org.eclipse.emf.cdo.examples.installer.source-4.1.400.jar deleted file mode 100644 index 28137bd09..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.installer.source/org.eclipse.emf.cdo.examples.installer.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.installer/org.eclipse.emf.cdo.examples.installer-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.installer/org.eclipse.emf.cdo.examples.installer-4.1.400.jar deleted file mode 100644 index 58c6bb3c8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.installer/org.eclipse.emf.cdo.examples.installer-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.server.source/org.eclipse.emf.cdo.examples.server.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.server.source/org.eclipse.emf.cdo.examples.server.source-4.0.400.jar deleted file mode 100644 index 2bf25c780..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.server.source/org.eclipse.emf.cdo.examples.server.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.server/org.eclipse.emf.cdo.examples.server-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.server/org.eclipse.emf.cdo.examples.server-4.0.400.jar deleted file mode 100644 index ed76b755c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.server/org.eclipse.emf.cdo.examples.server-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.source/org.eclipse.emf.cdo.examples.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.source/org.eclipse.emf.cdo.examples.source-4.0.500.jar deleted file mode 100644 index 1e781253a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples.source/org.eclipse.emf.cdo.examples.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples/org.eclipse.emf.cdo.examples-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples/org.eclipse.emf.cdo.examples-4.0.500.jar deleted file mode 100644 index 23be4bfa2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.examples/org.eclipse.emf.cdo.examples-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.source/org.eclipse.emf.cdo.explorer.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.source/org.eclipse.emf.cdo.explorer.source-4.5.0.jar deleted file mode 100644 index 47be15504..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.source/org.eclipse.emf.cdo.explorer.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.ui.source/org.eclipse.emf.cdo.explorer.ui.source-4.4.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.ui.source/org.eclipse.emf.cdo.explorer.ui.source-4.4.100.jar deleted file mode 100644 index d7b107678..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.ui.source/org.eclipse.emf.cdo.explorer.ui.source-4.4.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.ui/org.eclipse.emf.cdo.explorer.ui-4.4.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.ui/org.eclipse.emf.cdo.explorer.ui-4.4.100.jar deleted file mode 100644 index d153a4b59..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer.ui/org.eclipse.emf.cdo.explorer.ui-4.4.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer/org.eclipse.emf.cdo.explorer-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer/org.eclipse.emf.cdo.explorer-4.5.0.jar deleted file mode 100644 index 72c00e78d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.explorer/org.eclipse.emf.cdo.explorer-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.edit.source/org.eclipse.emf.cdo.expressions.edit.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.edit.source/org.eclipse.emf.cdo.expressions.edit.source-4.3.100.jar deleted file mode 100644 index c82879a90..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.edit.source/org.eclipse.emf.cdo.expressions.edit.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.edit/org.eclipse.emf.cdo.expressions.edit-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.edit/org.eclipse.emf.cdo.expressions.edit-4.3.100.jar deleted file mode 100644 index 14f328e9e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.edit/org.eclipse.emf.cdo.expressions.edit-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.editor.source/org.eclipse.emf.cdo.expressions.editor.source-4.3.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.editor.source/org.eclipse.emf.cdo.expressions.editor.source-4.3.200.jar deleted file mode 100644 index 4279605a0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.editor.source/org.eclipse.emf.cdo.expressions.editor.source-4.3.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.editor/org.eclipse.emf.cdo.expressions.editor-4.3.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.editor/org.eclipse.emf.cdo.expressions.editor-4.3.200.jar deleted file mode 100644 index a8a207c38..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.editor/org.eclipse.emf.cdo.expressions.editor-4.3.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.source/org.eclipse.emf.cdo.expressions.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.source/org.eclipse.emf.cdo.expressions.source-4.3.100.jar deleted file mode 100644 index 89e645ad3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions.source/org.eclipse.emf.cdo.expressions.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions/org.eclipse.emf.cdo.expressions-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions/org.eclipse.emf.cdo.expressions-4.3.100.jar deleted file mode 100644 index b3a30203a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.expressions/org.eclipse.emf.cdo.expressions-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.migrator.source/org.eclipse.emf.cdo.migrator.source-3.0.600.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.migrator.source/org.eclipse.emf.cdo.migrator.source-3.0.600.jar deleted file mode 100644 index f78218823..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.migrator.source/org.eclipse.emf.cdo.migrator.source-3.0.600.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.migrator/org.eclipse.emf.cdo.migrator-3.0.600.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.migrator/org.eclipse.emf.cdo.migrator-3.0.600.jar deleted file mode 100644 index 0d41f38a3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.migrator/org.eclipse.emf.cdo.migrator-3.0.600.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.net4j.source/org.eclipse.emf.cdo.net4j.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.net4j.source/org.eclipse.emf.cdo.net4j.source-4.1.400.jar deleted file mode 100644 index 101fe7df6..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.net4j.source/org.eclipse.emf.cdo.net4j.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.net4j/org.eclipse.emf.cdo.net4j-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.net4j/org.eclipse.emf.cdo.net4j-4.1.400.jar deleted file mode 100644 index b92393615..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.net4j/org.eclipse.emf.cdo.net4j-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.apireports.source/org.eclipse.emf.cdo.releng.apireports.source-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.apireports.source/org.eclipse.emf.cdo.releng.apireports.source-1.0.300.jar deleted file mode 100644 index c32fd8167..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.apireports.source/org.eclipse.emf.cdo.releng.apireports.source-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.apireports/org.eclipse.emf.cdo.releng.apireports-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.apireports/org.eclipse.emf.cdo.releng.apireports-1.0.300.jar deleted file mode 100644 index 77748b1a2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.apireports/org.eclipse.emf.cdo.releng.apireports-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.buildstamp.source/org.eclipse.emf.cdo.releng.buildstamp.source-1.0.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.buildstamp.source/org.eclipse.emf.cdo.releng.buildstamp.source-1.0.200.jar deleted file mode 100644 index 872155e68..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.buildstamp.source/org.eclipse.emf.cdo.releng.buildstamp.source-1.0.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.buildstamp/org.eclipse.emf.cdo.releng.buildstamp-1.0.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.buildstamp/org.eclipse.emf.cdo.releng.buildstamp-1.0.200.jar deleted file mode 100644 index 29a78abf0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.buildstamp/org.eclipse.emf.cdo.releng.buildstamp-1.0.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.help.source/org.eclipse.emf.cdo.releng.help.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.help.source/org.eclipse.emf.cdo.releng.help.source-4.1.400.jar deleted file mode 100644 index 568fe18f1..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.help.source/org.eclipse.emf.cdo.releng.help.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.help/org.eclipse.emf.cdo.releng.help-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.help/org.eclipse.emf.cdo.releng.help-4.1.400.jar deleted file mode 100644 index 9762f4a6d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.help/org.eclipse.emf.cdo.releng.help-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.relativepaths.source/org.eclipse.emf.cdo.releng.relativepaths.source-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.relativepaths.source/org.eclipse.emf.cdo.releng.relativepaths.source-1.0.300.jar deleted file mode 100644 index 4869ffaee..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.relativepaths.source/org.eclipse.emf.cdo.releng.relativepaths.source-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.relativepaths/org.eclipse.emf.cdo.releng.relativepaths-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.relativepaths/org.eclipse.emf.cdo.releng.relativepaths-1.0.300.jar deleted file mode 100644 index 001d01346..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.relativepaths/org.eclipse.emf.cdo.releng.relativepaths-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.tasks.source/org.eclipse.emf.cdo.releng.tasks.source-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.tasks.source/org.eclipse.emf.cdo.releng.tasks.source-1.0.300.jar deleted file mode 100644 index 34f204ccf..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.tasks.source/org.eclipse.emf.cdo.releng.tasks.source-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.tasks/org.eclipse.emf.cdo.releng.tasks-1.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.tasks/org.eclipse.emf.cdo.releng.tasks-1.0.300.jar deleted file mode 100644 index 5751fb34a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.releng.tasks/org.eclipse.emf.cdo.releng.tasks-1.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.sdk.source/org.eclipse.emf.cdo.sdk.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.sdk.source/org.eclipse.emf.cdo.sdk.source-4.1.400.jar deleted file mode 100644 index b77c0f2c3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.sdk.source/org.eclipse.emf.cdo.sdk.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.sdk/org.eclipse.emf.cdo.sdk-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.sdk/org.eclipse.emf.cdo.sdk-4.1.400.jar deleted file mode 100644 index 27d9c712c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.sdk/org.eclipse.emf.cdo.sdk-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.edit.source/org.eclipse.emf.cdo.security.edit.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.edit.source/org.eclipse.emf.cdo.security.edit.source-4.3.100.jar deleted file mode 100644 index cd66fb4e4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.edit.source/org.eclipse.emf.cdo.security.edit.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.edit/org.eclipse.emf.cdo.security.edit-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.edit/org.eclipse.emf.cdo.security.edit-4.3.100.jar deleted file mode 100644 index 7e26836e7..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.edit/org.eclipse.emf.cdo.security.edit-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.editor.source/org.eclipse.emf.cdo.security.editor.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.editor.source/org.eclipse.emf.cdo.security.editor.source-4.2.300.jar deleted file mode 100644 index b89822a7f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.editor.source/org.eclipse.emf.cdo.security.editor.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.editor/org.eclipse.emf.cdo.security.editor-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.editor/org.eclipse.emf.cdo.security.editor-4.2.300.jar deleted file mode 100644 index 00f670a06..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.editor/org.eclipse.emf.cdo.security.editor-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.source/org.eclipse.emf.cdo.security.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.source/org.eclipse.emf.cdo.security.source-4.3.100.jar deleted file mode 100644 index 4afe553ca..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.source/org.eclipse.emf.cdo.security.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.ui.source/org.eclipse.emf.cdo.security.ui.source-4.3.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.ui.source/org.eclipse.emf.cdo.security.ui.source-4.3.200.jar deleted file mode 100644 index 6e9a0eda3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.ui.source/org.eclipse.emf.cdo.security.ui.source-4.3.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.ui/org.eclipse.emf.cdo.security.ui-4.3.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.ui/org.eclipse.emf.cdo.security.ui-4.3.200.jar deleted file mode 100644 index dbedea7fc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security.ui/org.eclipse.emf.cdo.security.ui-4.3.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security/org.eclipse.emf.cdo.security-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.security/org.eclipse.emf.cdo.security-4.3.100.jar deleted file mode 100644 index 5755d066e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.security/org.eclipse.emf.cdo.security-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.admin.source/org.eclipse.emf.cdo.server.admin.source-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.admin.source/org.eclipse.emf.cdo.server.admin.source-4.2.200.jar deleted file mode 100644 index c15616a47..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.admin.source/org.eclipse.emf.cdo.server.admin.source-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.admin/org.eclipse.emf.cdo.server.admin-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.admin/org.eclipse.emf.cdo.server.admin-4.2.200.jar deleted file mode 100644 index 5589c2b15..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.admin/org.eclipse.emf.cdo.server.admin-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db.source/org.eclipse.emf.cdo.server.db.source-4.4.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db.source/org.eclipse.emf.cdo.server.db.source-4.4.0.jar deleted file mode 100644 index da04baaac..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db.source/org.eclipse.emf.cdo.server.db.source-4.4.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db/org.eclipse.emf.cdo.server.db-4.4.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db/org.eclipse.emf.cdo.server.db-4.4.0.jar deleted file mode 100644 index e92b6dac7..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db/org.eclipse.emf.cdo.server.db-4.4.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db4o.source/org.eclipse.emf.cdo.server.db4o.source-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db4o.source/org.eclipse.emf.cdo.server.db4o.source-4.0.300.jar deleted file mode 100644 index 922e953b9..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db4o.source/org.eclipse.emf.cdo.server.db4o.source-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db4o/org.eclipse.emf.cdo.server.db4o-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db4o/org.eclipse.emf.cdo.server.db4o-4.0.300.jar deleted file mode 100644 index 917ccc38c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.db4o/org.eclipse.emf.cdo.server.db4o-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.source/org.eclipse.emf.cdo.server.hibernate.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.source/org.eclipse.emf.cdo.server.hibernate.source-4.2.300.jar deleted file mode 100644 index 101ccb0c3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.source/org.eclipse.emf.cdo.server.hibernate.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.teneo.source/org.eclipse.emf.cdo.server.hibernate.teneo.source-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.teneo.source/org.eclipse.emf.cdo.server.hibernate.teneo.source-4.2.200.jar deleted file mode 100644 index f6d27a88f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.teneo.source/org.eclipse.emf.cdo.server.hibernate.teneo.source-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.teneo/org.eclipse.emf.cdo.server.hibernate.teneo-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.teneo/org.eclipse.emf.cdo.server.hibernate.teneo-4.2.200.jar deleted file mode 100644 index 593f250d9..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate.teneo/org.eclipse.emf.cdo.server.hibernate.teneo-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate/org.eclipse.emf.cdo.server.hibernate-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate/org.eclipse.emf.cdo.server.hibernate-4.2.300.jar deleted file mode 100644 index 7f95f2202..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.hibernate/org.eclipse.emf.cdo.server.hibernate-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.lissome.source/org.eclipse.emf.cdo.server.lissome.source-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.lissome.source/org.eclipse.emf.cdo.server.lissome.source-4.2.200.jar deleted file mode 100644 index bc9ce27f4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.lissome.source/org.eclipse.emf.cdo.server.lissome.source-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.lissome/org.eclipse.emf.cdo.server.lissome-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.lissome/org.eclipse.emf.cdo.server.lissome-4.2.200.jar deleted file mode 100644 index dd023745d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.lissome/org.eclipse.emf.cdo.server.lissome-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.mongodb.source/org.eclipse.emf.cdo.server.mongodb.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.mongodb.source/org.eclipse.emf.cdo.server.mongodb.source-4.0.400.jar deleted file mode 100644 index 27dbb5f51..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.mongodb.source/org.eclipse.emf.cdo.server.mongodb.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.mongodb/org.eclipse.emf.cdo.server.mongodb-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.mongodb/org.eclipse.emf.cdo.server.mongodb-4.0.400.jar deleted file mode 100644 index 8821e5689..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.mongodb/org.eclipse.emf.cdo.server.mongodb-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j.source/org.eclipse.emf.cdo.server.net4j.source-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j.source/org.eclipse.emf.cdo.server.net4j.source-4.1.300.jar deleted file mode 100644 index a5e61cc22..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j.source/org.eclipse.emf.cdo.server.net4j.source-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j/org.eclipse.emf.cdo.server.net4j-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j/org.eclipse.emf.cdo.server.net4j-4.1.300.jar deleted file mode 100644 index 03b2110cb..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j/org.eclipse.emf.cdo.server.net4j-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j/org.eclipse.emf.cdo.server.net4j-4.1.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j/org.eclipse.emf.cdo.server.net4j-4.1.500.jar new file mode 100644 index 000000000..32d1041d1 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.net4j/org.eclipse.emf.cdo.server.net4j-4.1.500.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.source/org.eclipse.emf.cdo.server.objectivity.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.source/org.eclipse.emf.cdo.server.objectivity.source-4.0.400.jar deleted file mode 100644 index 50edc70e0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.source/org.eclipse.emf.cdo.server.objectivity.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.stub.source/org.eclipse.emf.cdo.server.objectivity.stub.source-4.3.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.stub.source/org.eclipse.emf.cdo.server.objectivity.stub.source-4.3.0.jar deleted file mode 100644 index 191effd62..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.stub.source/org.eclipse.emf.cdo.server.objectivity.stub.source-4.3.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.stub/org.eclipse.emf.cdo.server.objectivity.stub-4.3.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.stub/org.eclipse.emf.cdo.server.objectivity.stub-4.3.0.jar deleted file mode 100644 index af9118ac2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity.stub/org.eclipse.emf.cdo.server.objectivity.stub-4.3.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity/org.eclipse.emf.cdo.server.objectivity-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity/org.eclipse.emf.cdo.server.objectivity-4.0.400.jar deleted file mode 100644 index db3d6cb9b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.objectivity/org.eclipse.emf.cdo.server.objectivity-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl.source/org.eclipse.emf.cdo.server.ocl.source-4.2.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl.source/org.eclipse.emf.cdo.server.ocl.source-4.2.100.jar deleted file mode 100644 index c6d5f61fc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl.source/org.eclipse.emf.cdo.server.ocl.source-4.2.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl/org.eclipse.emf.cdo.server.ocl-4.2.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl/org.eclipse.emf.cdo.server.ocl-4.2.100.jar deleted file mode 100644 index f5ee9e8be..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl/org.eclipse.emf.cdo.server.ocl-4.2.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl/org.eclipse.emf.cdo.server.ocl-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl/org.eclipse.emf.cdo.server.ocl-4.2.300.jar new file mode 100644 index 000000000..48f205cda Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.ocl/org.eclipse.emf.cdo.server.ocl-4.2.300.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.product.source/org.eclipse.emf.cdo.server.product.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.product.source/org.eclipse.emf.cdo.server.product.source-4.1.400.jar deleted file mode 100644 index 1657e8476..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.product.source/org.eclipse.emf.cdo.server.product.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.product/org.eclipse.emf.cdo.server.product-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.product/org.eclipse.emf.cdo.server.product-4.1.400.jar deleted file mode 100644 index 5ee832ba6..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.product/org.eclipse.emf.cdo.server.product-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.security.source/org.eclipse.emf.cdo.server.security.source-4.3.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.security.source/org.eclipse.emf.cdo.server.security.source-4.3.200.jar deleted file mode 100644 index 3c0d16762..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.security.source/org.eclipse.emf.cdo.server.security.source-4.3.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.security/org.eclipse.emf.cdo.server.security-4.3.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.security/org.eclipse.emf.cdo.server.security-4.3.200.jar deleted file mode 100644 index 98c96b243..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.security/org.eclipse.emf.cdo.server.security-4.3.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/META-INF/MANIFEST.MF b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/META-INF/MANIFEST.MF new file mode 100644 index 000000000..021296c37 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.9.6 +Created-By: 1.8.0_121-b13 (Oracle Corporation) +Bundle-SymbolicName: org.eclipse.emf.cdo.server.source +Bundle-ManifestVersion: 2 +Bundle-Vendor: Eclipse Modeling Project +Eclipse-SourceBundle: org.eclipse.emf.cdo.server;version="4.6.0.v20170 + 602-1611";roots="." +Bundle-Version: 4.6.0.v20170602-1611 +Bundle-Name: CDO Model Repository Server Source + diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/about.html b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/about.html new file mode 100644 index 000000000..d35d5aed6 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

June 5, 2007

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") 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 http://www.eclipse.org.

+ + + diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org.eclipse.emf.cdo.server.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org.eclipse.emf.cdo.server.source-4.5.0.jar deleted file mode 100644 index ea197e2db..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org.eclipse.emf.cdo.server.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/CommitManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/CommitManager.java new file mode 100644 index 000000000..a65d6b0c7 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/CommitManager.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2008-2012, 2015 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.protocol.CDODataInput; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +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.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.ThreadPool; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.Closeable; +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.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 contextEntries = new ConcurrentHashMap(); + + public CommitManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + } + + public synchronized ExecutorService getExecutors() + { + if (executors == null) + { + executors = ConcurrencyUtil.getExecutorService(repository); + + if (executors == null) + { + shutdownExecutorService = true; + executors = ThreadPool.create(); + } + } + + return executors; + } + + public synchronized void setExecutors(ExecutorService executors) + { + if (shutdownExecutorService) + { + if (this.executors != null) + { + this.executors.shutdown(); + } + + shutdownExecutorService = false; + } + + this.executors = executors; + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + setExecutors(null); + } + + @Deprecated + public void preCommit(InternalCommitContext commitContext, OMMonitor monitor) + { + preCommit(commitContext, null, monitor); + } + + public void preCommit(InternalCommitContext commitContext, CDODataInput in, OMMonitor monitor) + { + TransactionCommitContextEntry contextEntry = new TransactionCommitContextEntry(in, monitor); + contextEntry.setContext(commitContext); + + Future 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 final CDODataInput in; + + private final OMMonitor monitor; + + private InternalCommitContext context; + + private Future future; + + public TransactionCommitContextEntry(CDODataInput in, OMMonitor monitor) + { + this.in = in; + this.monitor = monitor; + } + + public Callable createCallable() + { + return new Callable() + { + public Object call() throws Exception + { + try + { + StoreThreadLocal.setCommitContext(context); + context.write(monitor); + } + finally + { + StoreThreadLocal.setCommitContext(null); + if (in instanceof Closeable) + { + IOUtil.closeSilent((Closeable)in); + } + } + + return null; + } + }; + } + + public InternalCommitContext getContext() + { + return context; + } + + public void setContext(InternalCommitContext context) + { + this.context = context; + } + + public Future getFuture() + { + return future; + } + + public void setFuture(Future future) + { + this.future = future; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java new file mode 100644 index 000000000..9efe84839 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2010-2013, 2016 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.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +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.server.IView; +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.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; + +import org.eclipse.emf.ecore.EClass; + +import java.util.List; +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 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 getDetachedObjectTypes() + { + return getDelegate().getDetachedObjectTypes(); + } + + public CDORevision getRevision(CDOID id) + { + return getDelegate().getRevision(id); + } + + public Map getIDMappings() + { + return getDelegate().getIDMappings(); + } + + public long getPreviousTimeStamp() + { + return getDelegate().getPreviousTimeStamp(); + } + + public long getLastUpdateTime() + { + return getDelegate().getLastUpdateTime(); + } + + public boolean isClearResourcePathCache() + { + return getDelegate().isClearResourcePathCache(); + } + + public boolean isUsingEcore() + { + return getDelegate().isUsingEcore(); + } + + public boolean isUsingEtypes() + { + return getDelegate().isUsingEtypes(); + } + + @Deprecated + public boolean isAutoReleaseLocksEnabled() + { + return getDelegate().isAutoReleaseLocksEnabled(); + } + + public CDOLockState[] getLocksOnNewObjects() + { + return getDelegate().getLocksOnNewObjects(); + } + + public CDOID[] getIDsToUnlock() + { + return getDelegate().getIDsToUnlock(); + } + + public CDOBranchVersion[] getDetachedObjectVersions() + { + return getDelegate().getDetachedObjectVersions(); + } + + public ExtendedDataInputStream getLobs() + { + return getDelegate().getLobs(); + } + + public CDOCommitInfo createCommitInfo() + { + return getDelegate().createCommitInfo(); + } + + public List> getPostCommmitLockStates() + { + return getDelegate().getPostCommmitLockStates(); + } + + public byte getRollbackReason() + { + return getDelegate().getRollbackReason(); + } + + public String getRollbackMessage() + { + return getDelegate().getRollbackMessage(); + } + + public List getXRefs() + { + return getDelegate().getXRefs(); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/InstancesQueryHandler.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/InstancesQueryHandler.java new file mode 100644 index 000000000..a2eeb585f --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/InstancesQueryHandler.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, 2015 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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +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.spi.common.revision.DetachedCDORevision; +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 java.util.List; + +/** + * @author Eike Stepper + */ +public class InstancesQueryHandler implements IQueryHandler +{ + public InstancesQueryHandler() + { + } + + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + EClass type = (EClass)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_INSTANCES_TYPE); + if (type != null) + { + executeQuery(type, context); + + if (!Boolean.TRUE.equals(info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_INSTANCES_EXACT))) + { + List subTypes = context.getView().getRepository().getPackageRegistry().getSubTypes().get(type); + if (subTypes != null && !subTypes.isEmpty()) + { + for (EClass subType : subTypes) + { + if (context.getResultCount() == 0) + { + break; + } + + executeQuery(subType, context); + } + } + } + } + } + + private void executeQuery(EClass type, final IQueryContext context) + { + if (type.isInterface() || type.isAbstract()) + { + return; + } + + CDOBranch branch = context.getBranch(); + long timeStamp = context.getTimeStamp(); + + InternalRepository repository = (InternalRepository)context.getView().getRepository(); + repository.handleRevisions(type, branch, false, timeStamp, false, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + if (revision instanceof DetachedCDORevision) + { + return true; + } + + return context.addResult(revision); + } + }); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends QueryHandlerFactory + { + public Factory() + { + super(CDOProtocolConstants.QUERY_LANGUAGE_INSTANCES); + } + + @Override + public InstancesQueryHandler create(String description) throws ProductCreationException + { + return new InstancesQueryHandler(); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/LockingManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/LockingManager.java new file mode 100644 index 000000000..17b1d6255 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/LockingManager.java @@ -0,0 +1,894 @@ +/* + * Copyright (c) 2011-2013, 2015, 2016 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.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +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.common.revision.ManagedRevisionProvider; +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.collection.ConcurrentArray; +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 org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +import org.eclipse.core.runtime.PlatformObject; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Simon McDuff + * @since 3.0 + */ +public class LockingManager extends RWOLockManager implements InternalLockManager +{ + private InternalRepository repository; + + private Map openDurableViews = new HashMap(); + + private Map durableViews = new HashMap(); + + private ConcurrentArray durableViewHandlers = new ConcurrentArray() + { + @Override + protected DurableViewHandler[] newArray(int length) + { + return new DurableViewHandler[length]; + } + }; + + @ExcludeFromDump + private transient IListener sessionListener = new ContainerEventAdapter() + { + @Override + protected void onRemoved(IContainer container, IView view) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID == null) + { + repository.unlock((InternalView)view, null, null, false); + } + else + { + DurableView durableView = new DurableView(durableLockingID); + changeContext(view, durableView); + unregisterOpenDurableView(durableLockingID); + durableViews.put(durableLockingID, durableView); + } + } + }; + + @ExcludeFromDump + private transient IListener sessionManagerListener = new ContainerEventAdapter() + { + @Override + protected void onAdded(IContainer container, ISession session) + { + session.addListener(sessionListener); + } + + @Override + protected void onRemoved(IContainer container, ISession session) + { + session.removeListener(sessionListener); + } + }; + + public LockingManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + } + + public synchronized Object getLockEntryObject(Object key) + { + LockState 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 getLocks(final IView view) + { + final Map result = CDOIDUtil.createMap(); + + for (LockState 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 objectsToLock, long timeout) throws InterruptedException + { + lock2(explicit, type, view, objectsToLock, false, timeout); + } + + public List> lock2(boolean explicit, LockType type, IView view, Collection objectsToLock, boolean recursive, + long timeout) throws InterruptedException + { + String durableLockingID = null; + DurableLocking accessor = null; + + if (explicit) + { + durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + accessor = getDurableLocking(); + } + } + + long startTime = timeout == WAIT ? 0L : currentTimeMillis(); + + List> newLockStates; + synchronized (this) + { + if (recursive) + { + objectsToLock = createContentSet(objectsToLock, view); + } + + // Adjust timeout for delay we may have incurred on entering this synchronized block + if (timeout != WAIT) + { + timeout -= currentTimeMillis() - startTime; + } + + newLockStates = super.lock2(type, view, objectsToLock, timeout); + } + + if (accessor != null) + { + accessor.lock(durableLockingID, type, objectsToLock); + } + + return newLockStates; + } + + @Override + public void lock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Collection objectsToLock, long timeout) + throws InterruptedException + { + lock2(false, type, context, objectsToLock, false, timeout); + } + + @Override + public void lock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Object objectToLock, long timeout) throws InterruptedException + { + Collection objectsToLock = new LinkedHashSet(); + objectsToLock.add(objectToLock); + lock2(false, type, context, objectsToLock, false, timeout); + } + + @Override + public List> lock2(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, + IView context, Collection objectsToLock, long timeout) throws InterruptedException + { + return lock2(false, type, context, objectsToLock, false, timeout); + } + + private Set createContentSet(Collection objectsToLock, IView view) + { + CDOBranch branch = view.getBranch(); + boolean branching = repository.isSupportingBranches(); + + CDORevisionManager revisionManager = view.getSession().getManager().getRepository().getRevisionManager(); + CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branch.getHead()); + + Set contents = new HashSet(); + + for (Object objectToLock : objectsToLock) + { + contents.add(objectToLock); + + CDOID id = branching ? ((CDOIDAndBranch)objectToLock).getID() : (CDOID)objectToLock; + CDORevision revision = revisionProvider.getRevision(id); + createContentSet(branch, branching, revisionProvider, revision, contents); + } + + return contents; + } + + private void createContentSet(CDOBranch branch, boolean branching, CDORevisionProvider revisionProvider, CDORevision revision, Set contents) + { + for (CDORevision child : CDORevisionUtil.getChildRevisions(revision, revisionProvider)) + { + CDOID childID = child.getID(); + contents.add(branching ? CDOIDUtil.createIDAndBranch(childID, branch) : childID); + createContentSet(branch, branching, revisionProvider, child, contents); + } + } + + @Deprecated + public synchronized void unlock(boolean explicit, LockType type, IView view, Collection objectsToUnlock) + { + unlock2(explicit, type, view, objectsToUnlock, false); + } + + public synchronized List> unlock2(boolean explicit, LockType type, IView view, Collection objects, + boolean recursive) + { + List> newLockStates; + synchronized (this) + { + if (recursive) + { + objects = createContentSet(objects, view); + } + + newLockStates = super.unlock2(type, view, objects); + } + + if (explicit) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + DurableLocking accessor = getDurableLocking(); + accessor.unlock(durableLockingID, type, objects); + } + } + + return newLockStates; + } + + @Deprecated + public synchronized void unlock(boolean explicit, IView view) + { + unlock2(explicit, view); + } + + public synchronized List> unlock2(boolean explicit, IView view) + { + if (explicit) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + DurableLocking accessor = getDurableLocking(); + accessor.unlock(durableLockingID); + } + } + + return super.unlock2(view); + } + + @Override + public synchronized List> unlock2(IView context) + { + return unlock2(false, context); + } + + @Override + public synchronized List> unlock2(IView context, + Collection objectsToUnlock) + { + // If no locktype is specified, use the LockType.WRITE + return unlock2(false, LockType.WRITE, context, objectsToUnlock, false); + } + + @Override + public synchronized List> unlock2( + org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Collection objectsToUnlock) + { + return unlock2(false, type, context, objectsToUnlock, false); + } + + @Override + public synchronized void unlock(IView context) + { + unlock2(context); + } + + @Override + public synchronized void unlock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Collection objectsToUnlock) + { + unlock2(type, context, objectsToUnlock); + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return createLockArea(userID, branchPoint, readOnly, locks, null); + } + + private LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 locks = getLocks(view); + + LockArea area = createLockArea(userID, branchPoint, readOnly, locks, lockAreaID); + synchronized (openDurableViews) + { + openDurableViews.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); + unregisterOpenDurableView(durableLockingID); + } + + public IView openView(ISession session, int viewID, boolean readOnly, final String durableLockingID) + { + synchronized (openDurableViews) + { + InternalView view = openDurableViews.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"); + } + + for (DurableViewHandler handler : durableViewHandlers.get()) + { + try + { + handler.openingView(session, viewID, readOnly, area); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + + if (readOnly) + { + view = (InternalView)session.openView(viewID, area); + } + else + { + view = (InternalView)session.openTransaction(viewID, area); + } + + DurableView durableView = durableViews.get(durableLockingID); + changeContext(durableView, view); + view.setDurableLockingID(durableLockingID); + view.addListener(new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + synchronized (openDurableViews) + { + openDurableViews.remove(durableLockingID); + } + } + }); + + openDurableViews.put(durableLockingID, view); + return view; + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + loadLocks(); + 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()); + } + + public void reloadLocks() + { + DurableLockLoader handler = new DurableLockLoader(); + getLockAreas(null, handler); + } + + private void loadLocks() + { + InternalStore store = repository.getStore(); + IStoreAccessor reader = null; + + try + { + reader = store.getReader(null); + if (reader instanceof DurableLocking) + { + StoreThreadLocal.setAccessor(reader); + reloadLocks(); + } + } + finally + { + StoreThreadLocal.release(); + } + } + + private void unregisterOpenDurableView(String durableLockingID) + { + synchronized (openDurableViews) + { + InternalView view = openDurableViews.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); + } + + public void addDurableViewHandler(DurableViewHandler handler) + { + durableViewHandlers.add(handler); + } + + public void removeDurableViewHandler(DurableViewHandler handler) + { + durableViewHandlers.remove(handler); + } + + public DurableViewHandler[] getDurableViewHandlers() + { + return durableViewHandlers.get(); + } + + /** + * @author Eike Stepper + */ + private final class DurableView extends PlatformObject implements IView, CDOCommonView.Options + { + private String durableLockingID; + + private IRegistry properties; + + public DurableView(String durableLockingID) + { + this.durableLockingID = durableLockingID; + } + + public String getDurableLockingID() + { + return durableLockingID; + } + + public boolean isDurableView() + { + return true; + } + + public int getSessionID() + { + throw new UnsupportedOperationException(); + } + + public int getViewID() + { + throw new UnsupportedOperationException(); + } + + public boolean isReadOnly() + { + throw new UnsupportedOperationException(); + } + + public boolean isHistorical() + { + 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 synchronized IRegistry properties() + { + if (properties == null) + { + properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + } + + return properties; + } + + public boolean isLockNotificationEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + } + } + + /** + * @author Eike Stepper + */ + private final class DurableLockLoader implements LockArea.Handler + { + public DurableLockLoader() + { + } + + private IView getView(String lockAreaID) + { + IView view = openDurableViews.get(lockAreaID); + if (view == null) + { + view = durableViews.get(lockAreaID); + } + + return view; + } + + public boolean handleLockArea(LockArea area) + { + String durableLockingID = area.getDurableLockingID(); + IView view = getView(durableLockingID); + if (view != null) + { + unlock2(view); + } + + if (view == null) + { + view = new DurableView(durableLockingID); + durableViews.put(durableLockingID, (DurableView)view); + } + + Collection readLocks = new ArrayList(); + Collection writeLocks = new ArrayList(); + Collection writeOptions = new ArrayList(); + for (Entry 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 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 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); + new DurableLockLoader().handleLockArea(lockArea); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/QueryManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/QueryManager.java new file mode 100644 index 000000000..ba8561908 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/QueryManager.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2008-2013, 2015, 2016 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.common.util.CDOQueryQueue; +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.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.ThreadPool; +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.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 queryContexts = new ConcurrentHashMap(); + + 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) + { + executors = ConcurrencyUtil.getExecutorService(repository); + + if (executors == null) + { + shutdownExecutorService = true; + executors = ThreadPool.create(); + } + } + + return executors; + } + + public synchronized void setExecutors(ExecutorService executors) + { + if (shutdownExecutorService) + { + if (this.executors != null) + { + this.executors.shutdown(); + } + + shutdownExecutorService = false; + } + + this.executors = executors; + } + + public InternalQueryResult execute(InternalView view, CDOQueryInfo queryInfo) + { + InternalQueryResult queryResult = new QueryResult(view, queryInfo, getNextQueryID()); + 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(QueryContext queryContext) + { + int queryID = queryContext.getQueryResult().getQueryID(); + queryContexts.put(queryID, queryContext); + queryContext.addListener(); + } + + public synchronized void unregister(QueryContext queryContext) + { + int queryID = queryContext.getQueryResult().getQueryID(); + queryContexts.remove(queryID); + queryContext.removeListener(); + } + + public synchronized int getNextQueryID() + { + return ++nextQuery; + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + setExecutors(null); + } + + private Future execute(QueryContext queryContext) + { + register(queryContext); + + Future future = getExecutors().submit(queryContext); + queryContext.setFuture(future); + 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$ + } + + CDOQueryQueue queue = queryResult.getQueue(); + queue.add(object); + + return !cancelled && --resultCount > 0; + } + + public void run() + { + CDOQueryQueue queue = queryResult.getQueue(); + + 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); + + try + { + handler.executeQuery(info, this); + } + catch (Throwable executionException) + { + addResult(executionException); + return; + } + } + catch (Throwable initializationException) + { + queue.setException(initializationException); + } + finally + { + queue.close(); + unregister(this); + StoreThreadLocal.release(); + } + } + + public void addListener() + { + InternalSession session = getQueryResult().getView().getSession(); + session.addListener(sessionListener); + } + + public void removeListener() + { + InternalSession session = getQueryResult().getView().getSession(); + session.removeListener(sessionListener); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/QueryResult.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/QueryResult.java new file mode 100644 index 000000000..d80555641 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/QueryResult.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008-2012, 2015, 2016 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 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Repository.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Repository.java new file mode 100644 index 000000000..f22baaede --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Repository.java @@ -0,0 +1,2605 @@ +/* + * Copyright (c) 2007-2016 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.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.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.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo; +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.CDOTimeProvider; +import org.eclipse.emf.cdo.common.util.CurrentTimeProvider; +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.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.branch.InternalCDOBranchManager.BranchLoader3; +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.BaseCDORevision; +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.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.emf.internal.cdo.object.CDOFactoryImpl; +import org.eclipse.emf.internal.cdo.util.CompletePackageClosure; +import org.eclipse.emf.internal.cdo.util.IPackageClosure; + +import org.eclipse.net4j.util.AdapterUtil; +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.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.IExecutorServiceProvider; +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.IManagedContainer; +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.om.monitor.ProgressDistributor; +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.MergeDataResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + +import org.eclipse.core.runtime.IProgressMonitor; + +import java.io.IOException; +import java.io.OutputStream; +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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class Repository extends Container implements InternalRepository, IExecutorServiceProvider +{ + private static final int UNCHUNKED = CDORevision.UNCHUNKED; + + private static final int NONE = CDORevision.DEPTH_NONE; + + private String name; + + private String uuid; + + private InternalStore store; + + private Type type = Type.MASTER; + + private State state = State.INITIAL; + + private Map properties; + + private boolean supportingAudits; + + private boolean supportingBranches; + + private boolean supportingUnits; + + private boolean serializingCommits; + + private boolean ensuringReferentialIntegrity; + + private IDGenerationLocation idGenerationLocation; + + private CommitInfoStorage commitInfoStorage; + + private long optimisticLockingTimeout = 10000L; + + private CDOTimeProvider timeProvider; + + /** + * Must not be thread-bound to support XA commits. + */ + private final 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 lockingManager; + + private InternalUnitManager unitManager; + + private IQueryHandlerProvider queryHandlerProvider; + + private IManagedContainer container; + + private final List readAccessHandlers = new ArrayList(); + + private final List writeAccessHandlers = new ArrayList(); + + // Bug 297940 + private final TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this); + + @ExcludeFromDump + private final transient Object commitTransactionLock = new Object(); + + @ExcludeFromDump + private final transient Object createBranchLock = new Object(); + + private boolean skipInitialization; + + private EPackage[] initialPackages; + + private CDOID rootResourceID; + + private long lastTreeRestructuringCommit = -1; + + 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 boolean waitWhileInitial(IProgressMonitor monitor) + { + return CDOCommonUtil.waitWhileInitial(this, this, monitor); + } + + public synchronized Map getProperties() + { + if (properties == null) + { + properties = new HashMap(); + } + + return properties; + } + + public synchronized void setProperties(Map properties) + { + this.properties = properties; + } + + public boolean isAuthenticating() + { + if (sessionManager != null) + { + return sessionManager.getAuthenticator() != null; + } + + return false; + } + + public boolean isSupportingAudits() + { + return supportingAudits; + } + + public boolean isSupportingBranches() + { + return supportingBranches; + } + + public boolean isSupportingUnits() + { + return supportingUnits; + } + + @Deprecated + public boolean isSupportingEcore() + { + return true; + } + + public boolean isSerializingCommits() + { + return serializingCommits; + } + + public boolean isEnsuringReferentialIntegrity() + { + return ensuringReferentialIntegrity; + } + + public IDGenerationLocation getIDGenerationLocation() + { + return idGenerationLocation; + } + + public CommitInfoStorage getCommitInfoStorage() + { + return commitInfoStorage; + } + + public long getOptimisticLockingTimeout() + { + return optimisticLockingTimeout; + } + + public void setOptimisticLockingTimeout(long optimisticLockingTimeout) + { + this.optimisticLockingTimeout = optimisticLockingTimeout; + } + + public String getStoreType() + { + return store.getType(); + } + + public Set 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 createBranch(int branchID, BranchInfo branchInfo) + { + if (!isSupportingBranches()) + { + throw new IllegalStateException("Branching is not supported by " + this); + } + + long baseTimeStamp = branchInfo.getBaseTimeStamp(); + long baseTimeStampMax = timeStampAuthority.getMaxBaseTimeForNewBranch(); + + if (baseTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE || baseTimeStamp > baseTimeStampMax) + { + baseTimeStamp = baseTimeStampMax; + 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); + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + if (!isSupportingBranches()) + { + throw new IllegalStateException("Branching is not supported by " + this); + } + + if (branchID == CDOBranch.MAIN_BRANCH_ID) + { + throw new IllegalArgumentException("Renaming of the MAIN branch is not supported"); + } + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + if (!(accessor instanceof BranchLoader3)) + { + throw new UnsupportedOperationException("Branch renaming is not supported by " + this); + } + + synchronized (createBranchLock) + { + ((BranchLoader3)accessor).renameBranch(branchID, oldName, newName); + } + } + + 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 loadRevisions(List 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()) + { + InternalCDORevision target = loadRevisionTarget(id, branchPoint, referenceChunk, accessor); + if (target != null) + { + target = normalizeRevision(target, info, referenceChunk); + + 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(); + + revision = normalizeRevision(revision, info, referenceChunk); + info.setResult(revision); + } + } + + return null; + } + + private InternalCDORevision normalizeRevision(InternalCDORevision revision, RevisionInfo info, int referenceChunk) + { + if (info instanceof RevisionInfo.Available) + { + RevisionInfo.Available availableInfo = (RevisionInfo.Available)info; + + CDOBranchVersion availableBranchVersion = availableInfo.getAvailableBranchVersion(); + if (availableBranchVersion instanceof BaseCDORevision) + { + BaseCDORevision availableRevision = (BaseCDORevision)availableBranchVersion; + if (availableRevision.equals(revision)) + { + ensureChunks(availableRevision, referenceChunk); + return availableRevision; + } + } + } + + if (referenceChunk == UNCHUNKED) + { + revision.setUnchunked(); + } + + return revision; + } + + 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), 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); + } + + public CDOBranchPointRange loadObjectLifetime(CDOID id, CDOBranchPoint branchPoint) + { + CDORevision revision = revisionManager.getRevision(id, branchPoint, UNCHUNKED, NONE, true); + if (revision == null) + { + return null; + } + + CDORevision firstRevision = getFirstRevision(id, revision); + if (firstRevision == null) + { + return null; + } + + CDOBranchPoint lastPoint = getLastBranchPoint(revision, branchPoint); + return CDOBranchUtil.createRange(firstRevision, lastPoint); + } + + private CDORevision getFirstRevision(CDOID id, CDORevision revision) + { + CDOBranch branch = revision.getBranch(); + + for (int version = revision.getVersion() - 1; version >= CDOBranchVersion.FIRST_VERSION; --version) + { + CDORevision rev = revisionManager.getRevisionByVersion(id, branch.getVersion(version), UNCHUNKED, true); + if (rev == null) + { + return revision; + } + + revision = rev; + } + + if (!branch.isMainBranch()) + { + CDOBranchPoint base = branch.getBase(); + CDORevision baseRevision = revisionManager.getRevision(id, base, UNCHUNKED, NONE, true); + if (baseRevision != null) + { + return getFirstRevision(id, baseRevision); + } + } + + return revision; + } + + private CDOBranchPoint getLastBranchPoint(CDORevision revision, CDOBranchPoint branchPoint) + { + CDOBranch branch = branchPoint.getBranch(); + if (revision.getBranch() != branch) + { + return branch.getHead(); + } + + CDOID id = revision.getID(); + for (int version = revision.getVersion() + 1; version <= Integer.MAX_VALUE; ++version) + { + if (revision.getRevised() == CDOBranchPoint.UNSPECIFIED_DATE) + { + break; + } + + CDORevision rev = revisionManager.getRevisionByVersion(id, branch.getVersion(version), UNCHUNKED, true); + if (rev == null) + { + break; + } + + revision = rev; + } + + return branch.getPoint(revision.getRevised()); + } + + /** + * @deprecated Not used. + */ + @Deprecated + protected void ensureChunks(InternalCDORevision revision, int referenceChunk, IStoreAccessor accessor) + { + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + if (feature.isMany()) + { + MoveableList list = revision.getList(feature); + int chunkEnd = Math.min(referenceChunk, list.size()); + accessor = ensureChunk(revision, feature, accessor, list, 0, chunkEnd); + } + } + } + + public void ensureChunks(InternalCDORevision revision) + { + ensureChunks(revision, UNCHUNKED); + } + + public void ensureChunks(InternalCDORevision revision, int chunkSize) + { + if (revision.isUnchunked()) + { + return; + } + + IStoreAccessor accessor = null; + boolean unchunked = true; + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + if (feature.isMany()) + { + MoveableList list = revision.getList(feature); + int size = list.size(); + if (size != 0) + { + int chunkSizeToUse = chunkSize; + if (chunkSizeToUse == UNCHUNKED) + { + chunkSizeToUse = size; + } + + int chunkEnd = Math.min(chunkSizeToUse, size); + accessor = ensureChunk(revision, feature, accessor, list, 0, chunkEnd); + + if (unchunked) + { + for (int i = chunkEnd + 1; i < size; i++) + { + if (list.get(i) == InternalCDOList.UNINITIALIZED) + { + unchunked = false; + break; + } + } + } + } + } + } + + if (unchunked) + { + revision.setUnchunked(); + } + } + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, int chunkEnd) + { + if (!revision.isUnchunked()) + { + MoveableList list = revision.getList(feature); + chunkEnd = Math.min(chunkEnd, list.size()); + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + ensureChunk(revision, feature, accessor, list, chunkStart, chunkEnd); + + // TODO Expensive: if the revision is unchunked all lists/elements must be visited + if (isUnchunked(revision)) + { + revision.setUnchunked(); + } + + return accessor; + } + + return null; + } + + private boolean isUnchunked(InternalCDORevision revision) + { + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + if (feature.isMany()) + { + MoveableList list = revision.getList(feature); + int size = list.size(); + for (int i = 0; i < size; i++) + { + if (list.get(i) == InternalCDOList.UNINITIALIZED) + { + return false; + } + } + } + } + + return true; + } + + protected IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, IStoreAccessor accessor, MoveableList 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 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 CDOTimeProvider getTimeProvider() + { + return timeProvider; + } + + public void setTimeProvider(CDOTimeProvider timeProvider) + { + checkInactive(); + this.timeProvider = timeProvider; + } + + 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 InternalUnitManager getUnitManager() + { + return unitManager; + } + + public void setUnitManager(InternalUnitManager unitManager) + { + checkInactive(); + this.unitManager = unitManager; + } + + 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 + * @deprecated + */ + @Deprecated + public InternalLockManager getLockManager() + { + return getLockingManager(); + } + + public InternalLockManager getLockingManager() + { + return lockingManager; + } + + /** + * @since 2.0 + */ + public void setLockingManager(InternalLockManager lockingManager) + { + checkInactive(); + this.lockingManager = lockingManager; + } + + 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); + } + + public void executeOutsideStartCommit(Runnable runnable) + { + synchronized (timeStampAuthority) + { + runnable.run(); + } + } + + public void commit(InternalCommitContext commitContext, OMMonitor monitor) + { + if (commitContext.isTreeRestructuring()) + { + synchronized (commitTransactionLock) + { + commitContext.setLastTreeRestructuringCommit(lastTreeRestructuringCommit); + commitUnsynced(commitContext, monitor); + lastTreeRestructuringCommit = commitContext.getTimeStamp(); + } + } + else if (serializingCommits) + { + synchronized (commitTransactionLock) + { + commitUnsynced(commitContext, monitor); + } + } + else + { + commitUnsynced(commitContext, monitor); + } + } + + protected void commitUnsynced(InternalCommitContext commitContext, OMMonitor monitor) + { + ProgressDistributor distributor = store.getIndicatingCommitDistributor(); + distributor.run(InternalCommitContext.OPS, commitContext, monitor); + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo) + { + sendCommitNotification(sender, commitInfo, true); + } + + @Deprecated + public CDOCommitInfoHandler[] getCommitInfoHandlers() + { + return commitInfoManager.getCommitInfoHandlers(); + } + + @Deprecated + public void addCommitInfoHandler(CDOCommitInfoHandler handler) + { + commitInfoManager.addCommitInfoHandler(handler); + } + + @Deprecated + public void removeCommitInfoHandler(CDOCommitInfoHandler handler) + { + commitInfoManager.removeCommitInfoHandler(handler); + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo info) + { + CDOCommitInfo commitInfo = info.getCommitInfo(); + boolean isFailureCommitInfo = commitInfo.getBranch() == null; + if (isFailureCommitInfo || !commitInfo.isEmpty()) + { + sessionManager.sendCommitNotification(info); + commitInfoManager.notifyCommitInfoHandlers(commitInfo); + } + } + + /** + * @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_INSTANCES.equals(language)) + { + return new InstancesQueryHandler(); + } + + 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) + { + IManagedContainer container = getContainer(); + queryHandlerProvider = new ContainerQueryHandlerProvider(container); + } + + IQueryHandler handler = queryHandlerProvider.getQueryHandler(info); + if (handler != null) + { + return handler; + } + + return null; + } + + public IManagedContainer getContainer() + { + if (container == null) + { + return IPluginContainer.INSTANCE; + } + + return container; + } + + public void setContainer(IManagedContainer container) + { + this.container = container; + } + + public ExecutorService getExecutorService() + { + IManagedContainer container = getContainer(); + return ConcurrencyUtil.getExecutorService(container); + } + + public Object[] getElements() + { + final Object[] elements = { packageRegistry, branchManager, revisionManager, sessionManager, queryManager, commitManager, commitInfoManager, + getLockingManager(), 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})", //$NON-NLS-1$ + CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(creationTimeStamp))); + } + + long currentTimeStamp = getTimeStamp(); + if (timeStamp > currentTimeStamp) + { + throw new IllegalArgumentException(MessageFormat.format("timeStamp ({0}) > current time ({1})", //$NON-NLS-1$ + CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(currentTimeStamp))); + } + } + + public long getTimeStamp() + { + return timeProvider.getTimeStamp(); + } + + public Set getHandlers() + { + Set handlers = new HashSet(); + + synchronized (readAccessHandlers) + { + handlers.addAll(readAccessHandlers); + } + + synchronized (writeAccessHandlers) + { + handlers.addAll(writeAccessHandlers); + } + + return handlers; + } + + /** + * @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 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 = store.getLastBranchID(); + + final long fromCommitTime = lastReplicatedCommitTime + 1L; + final long toCommitTime = store.getLastCommitTime(); + + out.writeXInt(toBranchID); + out.writeXLong(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); + + getLockingManager().getLockAreas(null, context); + } + + public CDOChangeSetData getChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint) + { + CDOChangeSetSegment[] segments = CDOChangeSetSegment.createFrom(startPoint, endPoint); + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + Set ids = accessor.readChangeSet(new Monitor(), segments); + + return CDORevisionUtil.createChangeSetData(ids, startPoint, endPoint, revisionManager); + } + + @Deprecated + public Set getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, CDORevisionAvailabilityInfo targetBaseInfo, + CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor) + { + MergeDataResult result = getMergeData2(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo, monitor); + Set ids = result.getTargetIDs(); + ids.addAll(result.getSourceIDs()); + return ids; + } + + public MergeDataResult getMergeData2(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(); + + MergeDataResult result = new MergeDataResult(); + Set targetIDs = result.getTargetIDs(); + Set sourceIDs = result.getSourceIDs(); + + if (targetBaseInfo == null && sourceBaseInfo == null) + { + if (CDOBranchUtil.isContainedBy(source, target)) + { + // This is a "compare" case, see CDOSessionImpl.compareRevisions(). + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(source, target))); + } + else if (CDOBranchUtil.isContainedBy(target, source)) + { + // This is a "compare" case, see CDOSessionImpl.compareRevisions(). + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(target, source))); + } + else + { + CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source); + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, target))); + sourceIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, source))); + } + } + else + { + CDOChangeSetSegment[] targetSegments; + CDOChangeSetSegment[] sourceSegments; + + if (targetBaseInfo.getBranchPoint() == CDOBranchUtil.AUTO_BRANCH_POINT) + { + CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source); + targetSegments = CDOChangeSetSegment.createFrom(ancestor, target); + sourceSegments = CDOChangeSetSegment.createFrom(ancestor, source); + + CDOBranchPoint targetBase = ancestor; + CDOBranchPoint sourceBase = ancestor; + long ancestorTime = ancestor.getTimeStamp(); + + CDOBranchPointRange latestTargetMerge = getLatestMerge(targetSegments, sourceSegments, ancestorTime); + if (latestTargetMerge != null) + { + targetBase = latestTargetMerge.getEndPoint(); + sourceBase = latestTargetMerge.getStartPoint(); + + if (!sourceBase.equals(ancestor)) + { + sourceSegments = CDOChangeSetSegment.createFrom(sourceBase, source); + } + } + + CDOBranchPointRange latestSourceMerge = getLatestMerge(sourceSegments, targetSegments, ancestorTime); + if (latestSourceMerge != null) + { + CDOBranchPoint mergeSource = latestSourceMerge.getStartPoint(); + if (targetBase.getTimeStamp() < mergeSource.getTimeStamp()) + { + targetBase = mergeSource; + } + + result.setResultBase(sourceBase); + } + + if (!targetBase.equals(ancestor)) + { + targetSegments = CDOChangeSetSegment.createFrom(targetBase, target); + } + + targetBaseInfo.setBranchPoint(targetBase); + sourceBaseInfo.setBranchPoint(sourceBase); + } + else + { + CDORevisionAvailabilityInfo sourceBaseInfoToUse = sourceBaseInfo == null ? targetBaseInfo : sourceBaseInfo; + targetSegments = CDOChangeSetSegment.createFrom(targetBaseInfo.getBranchPoint(), target); + sourceSegments = CDOChangeSetSegment.createFrom(sourceBaseInfoToUse.getBranchPoint(), source); + } + + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), targetSegments)); + sourceIDs.addAll(accessor.readChangeSet(monitor.fork(), sourceSegments)); + } + + loadMergeData(targetIDs, targetInfo, monitor.fork()); + loadMergeData(sourceIDs, sourceInfo, monitor.fork()); + + if (targetBaseInfo != null) + { + loadMergeData(targetIDs, targetBaseInfo, monitor.fork()); + } + + if (sourceBaseInfo != null && !targetBaseInfo.getBranchPoint().equals(sourceBaseInfo.getBranchPoint())) + { + loadMergeData(sourceIDs, sourceBaseInfo, monitor.fork()); + } + + return result; + } + finally + { + monitor.done(); + } + } + + private CDOBranchPointRange getLatestMerge(CDOChangeSetSegment[] targetSegments, CDOChangeSetSegment[] sourceSegments, long ancestorTime) + { + for (int i = targetSegments.length - 1; i >= 0; --i) + { + CDOChangeSetSegment targetSegment = targetSegments[i]; + CDOBranch targetBranch = targetSegment.getBranch(); + long startTime = targetSegment.getTimeStamp(); + long endTime = targetSegment.getEndTime(); + + while (endTime > startTime || endTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + CDOCommitInfo commitInfo = commitInfoManager.getCommitInfo(targetBranch, endTime, false); + if (commitInfo == null) + { + break; + } + + long timeStamp = commitInfo.getTimeStamp(); + if (timeStamp <= startTime) + { + break; + } + + CDOBranchPoint mergeSource = getMergeSource(commitInfo, sourceSegments, ancestorTime); + if (mergeSource != null) + { + CDOBranchPoint endPoint = CDOBranchUtil.copyBranchPoint(commitInfo); + return CDOBranchUtil.createRange(mergeSource, endPoint); + } + + endTime = timeStamp - 1; + } + } + + return null; + } + + private CDOBranchPoint getMergeSource(CDOCommitInfo commitInfo, CDOChangeSetSegment[] sourceSegments, long ancestorTime) + { + CDOBranchPoint mergeSource = commitInfo.getMergeSource(); + if (mergeSource != null) + { + if (CDOChangeSetSegment.contains(sourceSegments, mergeSource)) + { + return mergeSource; + } + + CDOChangeSetSegment[] targetSegments = CDOChangeSetSegment.createFrom(ancestorTime, mergeSource); + CDOBranchPointRange latestMerge = getLatestMerge(targetSegments, sourceSegments, ancestorTime); + if (latestMerge != null) + { + return latestMerge.getStartPoint(); + } + } + + return null; + } + + private void loadMergeData(Set 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) + { + return revisionManager.getRevision(id, branchPoint, UNCHUNKED, NONE, true); + } + + public void queryLobs(List 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 handled = new HashSet(); + + 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 revisionKeysToObjects(List revisionKeys, CDOBranch viewedBranch, boolean isSupportingBranches) + { + List lockables = new ArrayList(); + 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 revKeys, boolean recursive, long timeout) + { + List lockables = revisionKeysToObjects(revKeys, view.getBranch(), isSupportingBranches()); + return lock(view, lockType, lockables, revKeys, recursive, timeout); + } + + protected LockObjectsResult lock(InternalView view, LockType type, List lockables, List loadedRevs, boolean recursive, long timeout) + { + List> newLockStates = null; + + try + { + newLockStates = getLockingManager().lock2(true, type, view, lockables, recursive, 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 = null; + + try + { + staleRevisionsArray = checkStaleRevisions(view, loadedRevs, lockables, type, requiredTimestamp); + } + catch (IllegalArgumentException e) + { + getLockingManager().unlock2(true, type, view, lockables, recursive); + throw e; + } + + // 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) + { + getLockingManager().unlock2(true, type, view, lockables, recursive); + 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 revisionKeys, List objectsToLock, LockType lockType, + long[] requiredTimestamp) + { + List staleRevisions = new LinkedList(); + 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(), UNCHUNKED, NONE, true); + + if (rev == null) + { + throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id, viewedBranch)); + } + + if (!revKey.equals(rev)) + { + // Send back the *expected* revision keys, so that the client can check that it really has loaded those. + staleRevisions.add(CDORevisionUtil.copyRevisionKey(rev)); + 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> lockStates) + { + CDOLockState[] cdoLockStates = new CDOLockState[lockStates.size()]; + int i = 0; + + for (LockState lockState : lockStates) + { + CDOLockState cdoLockState = CDOLockUtil.createLockState(lockState); + cdoLockStates[i++] = cdoLockState; + } + + return cdoLockStates; + } + + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List objectIDs, boolean recursive) + { + List unlockables = null; + if (objectIDs != null) + { + unlockables = new ArrayList(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, recursive); + } + + protected UnlockObjectsResult doUnlock(InternalView view, LockType lockType, List unlockables, boolean recursive) + { + List> newLockStates = null; + if (lockType == null) // Signals an unlock-all operation + { + newLockStates = getLockingManager().unlock2(true, view); + } + else + { + newLockStates = getLockingManager().unlock2(true, lockType, view, unlockables, recursive); + } + + long timestamp = getTimeStamp(); + CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates); + sendLockNotifications(view, Operation.UNLOCK, lockType, cdoLockStates); + + return new UnlockObjectsResult(cdoLockStates, timestamp); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) + { + return AdapterUtil.adapt(this, adapter, false); + } + + @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() + { + // SUPPORTING_AUDITS + String valueAudits = properties.get(Props.SUPPORTING_AUDITS); + if (valueAudits != null) + { + supportingAudits = Boolean.valueOf(valueAudits); + } + else + { + supportingAudits = store.getRevisionTemporality() == IStore.RevisionTemporality.AUDITING; + } + + // SUPPORTING_BRANCHES + String valueBranches = properties.get(Props.SUPPORTING_BRANCHES); + if (valueBranches != null) + { + supportingBranches = Boolean.valueOf(valueBranches); + } + else + { + supportingBranches = store.getRevisionParallelism() == IStore.RevisionParallelism.BRANCHING; + } + + // SUPPORTING_UNITS + String valueUnits = properties.get(Props.SUPPORTING_UNITS); + if (valueUnits != null) + { + supportingUnits = Boolean.valueOf(valueUnits); + } + + // SERIALIZE_COMMITS + String valueCommits = properties.get(Props.SERIALIZE_COMMITS); + if (valueCommits != null) + { + serializingCommits = Boolean.valueOf(valueCommits); + } + + // ENSURE_REFERENTIAL_INTEGRITY + String valueIntegrity = properties.get(Props.ENSURE_REFERENTIAL_INTEGRITY); + if (valueIntegrity != null) + { + ensuringReferentialIntegrity = Boolean.valueOf(valueIntegrity); + } + + // ID_GENERATION_LOCATION + String valueIDLocation = properties.get(Props.ID_GENERATION_LOCATION); + if (valueIDLocation != null) + { + idGenerationLocation = IDGenerationLocation.valueOf(valueIDLocation); + } + + if (idGenerationLocation == null) + { + idGenerationLocation = IDGenerationLocation.STORE; + } + + // COMMIT_INFO_STORAGE + String valueCommitInfoStorage = properties.get(Props.COMMIT_INFO_STORAGE); + if (valueCommitInfoStorage != null) + { + commitInfoStorage = CommitInfoStorage.valueOf(valueCommitInfoStorage); + } + + if (commitInfoStorage == null) + { + commitInfoStorage = CommitInfoStorage.WITH_MERGE_SOURCE; + } + + if (commitInfoStorage != CommitInfoStorage.NO && !supportingBranches) + { + commitInfoStorage = CommitInfoStorage.YES; + } + + // ENSURE_REFERENTIAL_INTEGRITY + String valueTimeout = properties.get(Props.OPTIMISTIC_LOCKING_TIMEOUT); + if (valueTimeout != null) + { + optimisticLockingTimeout = Long.valueOf(valueTimeout); + } + } + + @Deprecated + public void initSystemPackages() + { + initSystemPackages(true); + } + + public void initSystemPackages(final boolean firstStart) + { + IStoreAccessor writer = store.getWriter(null); + StoreThreadLocal.setAccessor(writer); + + try + { + List list = new ArrayList(); + boolean nonSystemPackages = false; + if (initialPackages != null) + { + for (EPackage initialPackage : initialPackages) + { + if (!packageRegistry.containsKey(initialPackage.getNsURI())) + { + list.add(initPackage(initialPackage)); + nonSystemPackages = true; + } + } + } + + if (nonSystemPackages && !firstStart) + { + OM.LOG.info("Initial packages are about to be pre-registered. They may not become part of replications."); + } + + if (!list.isEmpty()) + { + InternalCDOPackageUnit[] initialUnits = list.toArray(new InternalCDOPackageUnit[list.size()]); + writer.writePackageUnits(initialUnits, new Monitor()); + writer.commit(new Monitor()); + } + } + finally + { + StoreThreadLocal.release(); + } + + fireEvent(new PackagesInitializedEvent() + { + public InternalRepository getSource() + { + return Repository.this; + } + + public boolean isFirstStart() + { + return firstStart; + } + }); + } + + protected InternalCDOPackageUnit initPackage(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 ""; //$NON-NLS-1$ + } + }; + + commitContext.setNewObjects(new InternalCDORevision[] { rootResource }); + commitContext.setNewPackageUnits(getNewPackageUnitsForRootResource(commitContext.getPackageRegistry())); + 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(); + } + + private InternalCDOPackageUnit[] getNewPackageUnitsForRootResource(InternalCDOPackageRegistry commitContextPackageRegistry) + { + Collection newPackageUnitsForRootResource = new ArrayList(); + IPackageClosure closure = new CompletePackageClosure(); + Set ePackages = closure.calculate(Collections. singletonList(EresourcePackage.eINSTANCE)); + for (EPackage ePackage : ePackages) + { + InternalCDOPackageUnit packageUnit = commitContextPackageRegistry.getPackageUnit(ePackage); + if (packageUnit != null) + { + newPackageUnitsForRootResource.add(packageUnit); + } + } + + return newPackageUnitsForRootResource.toArray(new InternalCDOPackageUnit[0]); + } + + 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 packageUnits = reader.readPackageUnits(); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + packageRegistry.putPackageUnit(packageUnit); + } + } + finally + { + StoreThreadLocal.release(); + } + } + + protected void setPostActivateState() + { + setState(State.ONLINE); + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + + checkState(!StringUtil.isEmpty(name), "name is empty"); //$NON-NLS-1$ + checkState(store, "store"); //$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(lockingManager, "lockingManager"); //$NON-NLS-1$ + + packageRegistry.setReplacingDescriptors(true); + + if (packageRegistry.getPackageProcessor() == null) + { + packageRegistry.setPackageProcessor(this); + } + + if (packageRegistry.getPackageLoader() == null) + { + packageRegistry.setPackageLoader(this); + } + + if (branchManager.getRepository() == null) + { + branchManager.setRepository(this); + } + + if (branchManager.getBranchLoader() == null) + { + branchManager.setBranchLoader(this); + } + + if (revisionManager.getRevisionLoader() == null) + { + revisionManager.setRevisionLoader(this); + } + + if (sessionManager.getRepository() == null) + { + sessionManager.setRepository(this); + } + + if (queryManager.getRepository() == null) + { + queryManager.setRepository(this); + } + + if (commitInfoManager.getRepository() == null) + { + commitInfoManager.setRepository(this); + } + + if (commitInfoManager.getCommitInfoLoader() == null) + { + commitInfoManager.setCommitInfoLoader(this); + } + + if (commitManager.getRepository() == null) + { + commitManager.setRepository(this); + } + + if (lockingManager.getRepository() == null) + { + lockingManager.setRepository(this); + } + + if (store.getRepository() == null) + { + 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 (supportingUnits) + { + LifecycleUtil.activate(unitManager); + } + + if (!skipInitialization) + { + long creationTime = store.getCreationTime(); + initMainBranch(branchManager, creationTime); + + long lastCommitTime = Math.max(creationTime, store.getLastCommitTime()); + timeStampAuthority.setLastFinishedTimeStamp(lastCommitTime); + commitInfoManager.setLastCommitOfBranch(null, lastCommitTime); + + if (store.isFirstStart()) + { + initSystemPackages(true); + initRootResource(); + } + else + { + readPackageUnits(); + initSystemPackages(false); + + readRootResource(); + } + } + + LifecycleUtil.activate(lockingManager); // Needs an initialized main branch / branch manager + setPostActivateState(); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(unitManager); + LifecycleUtil.deactivate(lockingManager); + 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 (getTimeProvider() == null) + { + setTimeProvider(createTimeProvider()); + } + + 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) + { + setLockingManager(createLockManager()); + } + + if (getUnitManager() == null) + { + setUnitManager(createUnitManager()); + } + + super.doBeforeActivate(); + } + + protected CDOTimeProvider createTimeProvider() + { + return CurrentTimeProvider.INSTANCE; + } + + 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 InternalUnitManager createUnitManager() + { + return new UnitManager(this); + } + + @Deprecated + protected InternalLockManager createLockManager() + { + return createLockingManager(); + } + + public LockingManager createLockingManager() + { + return new LockingManager(); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java new file mode 100644 index 000000000..6d5f2fba8 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2008-2013 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.server.StoreThreadLocal.NoSessionRegisteredException; +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) + { + try + { + 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); + } + } + catch (NoSessionRegisteredException ex) + { + // View has been closed - do nothing + } + } + + /** + * @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 resourceIDs = new HashSet(); + + public QueryContext(CDOQueryInfo info, IQueryContext context) + { + this.info = info; + this.context = context; + branchPoint = context; + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + this.branchPoint = branchPoint; + } + + public Set 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/ServerCDOView.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/ServerCDOView.java new file mode 100644 index 000000000..17203f52e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/ServerCDOView.java @@ -0,0 +1,1271 @@ +/* + * Copyright (c) 2010-2016 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 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +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.id.CDOIDUtil; +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.CDOProtocol.CommitNotificationInfo; +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.common.security.CDOPermission; +import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.session.CDOCollectionLoadingPolicy; +import org.eclipse.emf.cdo.session.CDORepositoryInfo; +import org.eclipse.emf.cdo.session.CDOSession; +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.CDOUnitManager; +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.emf.internal.cdo.view.CDOLockStateLoadingPolicy; + +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.KeyedReference; +import org.eclipse.net4j.util.ref.ReferenceType; +import org.eclipse.net4j.util.ref.ReferenceValueMap2; +import org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; +import org.eclipse.net4j.util.security.IPasswordCredentialsProvider; + +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.CDOPermissionUpdater; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult.Provider; +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 org.eclipse.emf.spi.cdo.InternalCDOViewSet; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.PlatformObject; + +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, CDORevisionProvider revisionProvider) + { + super(null, branchPoint); + this.session = new ServerCDOSession(session); + this.revisionProvider = revisionProvider; + + InternalCDOViewSet resourceSet = SessionUtil.prepareResourceSet(new ResourceSetImpl()); + setViewSet(resourceSet); + + Map> map = CDOIDUtil.createMap(); + setObjects(new ReferenceValueMap2.Weak(map)); + + 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 InternalCDORevision getRevision(CDOID id, boolean loadOnDemand) + { + return (InternalCDORevision)revisionProvider.getRevision(id); + } + + @Override + protected void excludeNewObject(CDOID id) + { + // Do nothing + } + + public boolean isInvalidationRunnerActive() + { + return false; + } + + public boolean setBranchPoint(CDOBranchPoint branchPoint, IProgressMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public void lockObjects(Collection objects, LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void lockObjects(Collection objects, LockType lockType, long timeout, boolean recursive) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(Collection objects, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(Collection objects, LockType lockType, boolean recursive) + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects() + { + throw new UnsupportedOperationException(); + } + + public boolean waitForUpdate(long updateTime, long timeoutMillis) + { + throw new UnsupportedOperationException(); + } + + public boolean runAfterUpdate(long updateTime, Runnable runnable) + { + throw new UnsupportedOperationException(); + } + + public void setViewID(int viewId) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setSession(InternalCDOSession session) + { + throw new UnsupportedOperationException(); + } + + public int getSessionID() + { + return session.getSessionID(); + } + + public boolean isDurableView() + { + return false; + } + + public String getDurableLockingID() + { + return null; + } + + @Deprecated + public String enableDurableLocking(boolean enable) + { + throw new UnsupportedOperationException(); + } + + public String enableDurableLocking() + { + throw new UnsupportedOperationException(); + } + + public void disableDurableLocking(boolean releaseLocks) + { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + public CDOFeatureAnalyzer getFeatureAnalyzer() + { + return CDOFeatureAnalyzer.NOOP; + } + + @SuppressWarnings("deprecation") + public void setFeatureAnalyzer(CDOFeatureAnalyzer featureAnalyzer) + { + throw new UnsupportedOperationException(); + } + + public InternalCDOTransaction toTransaction() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOBranch branch, long lastUpdateTime, List allChangedObjects, List allDetachedObjects, + Map oldRevisions, boolean async) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOBranch branch, long lastUpdateTime, List allChangedObjects, List allDetachedObjects, + Map oldRevisions, boolean async, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void invalidate(ViewInvalidationData invalidationData) + { + throw new UnsupportedOperationException(); + } + + public void handleLockNotification(InternalCDOView sender, CDOLockChangeInfo lockChangeInfo) + { + // Do nothing + } + + public void resourceLoaded(CDOResourceImpl resource, boolean loaded) + { + // 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 isDetachmentNotificationEnabled() + { + return false; + } + + public void setDetachmentNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isInvalidationNotificationEnabled() + { + return false; + } + + public void setInvalidationNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isLoadNotificationEnabled() + { + return false; + } + + public void setLoadNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isLockNotificationEnabled() + { + return false; + } + + public boolean isLockStatePrefetchEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public CDOLockStateLoadingPolicy getLockStateLoadingPolicy() + { + return null; + } + + public void setLockStateLoadingPolicy(CDOLockStateLoadingPolicy lockStateLoadingPolicy) + { + } + + public void setLockStatePrefetchEnabled(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.DEFAULT; + } + + 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 ids) + { + throw new UnsupportedOperationException(); + } + + public CDOUnitManager getUnitManager() + { + throw new UnsupportedOperationException(); + } + + /** + * @author Eike Stepper + */ + private final class ServerCDOSession extends PlatformObject implements InternalCDOSession, CDORepositoryInfo, org.eclipse.emf.cdo.session.CDOSession.Options + { + private final IRegistry properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + + private boolean generatedPackageEmulationEnabled; + + private InternalSession internalSession; + + private InternalRepository repository; + + public ServerCDOSession(InternalSession internalSession) + { + this.internalSession = internalSession; + repository = internalSession.getManager().getRepository(); + } + + public IRegistry properties() + { + return properties; + } + + public CDOSession getSession() + { + return this; + } + + public String getUserID() + { + return internalSession.getUserID(); + } + + public int getSessionID() + { + return internalSession.getSessionID(); + } + + public CDOView[] getElements() + { + return new ServerCDOView[] { ServerCDOView.this }; + } + + public InternalCDOTransaction getTransaction(int viewID) + { + return null; + } + + public InternalCDOTransaction[] getTransactions() + { + return new InternalCDOTransaction[0]; + } + + public InternalCDOTransaction[] getTransactions(CDOBranch branch) + { + return new InternalCDOTransaction[0]; + } + + public CDOView[] getViews(CDOBranch branch) + { + if (getBranch() == branch) + { + return getViews(); + } + + return new CDOView[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(); + } + + /** + * Server sessions may not be used to change the user's credentials: it must + * be done client-side by interaction with the user. + * + * @since 4.3 + */ + public void changeCredentials() + { + throw new UnsupportedOperationException(); + } + + /** + * Server sessions may not be used to reset a user's credentials: it must + * be done client-side by interaction with an adminstrator. + * + * @since 4.3 + */ + public void resetCredentials(String userID) + { + 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 getObjectIDTypes() + { + return repository.getObjectIDTypes(); + } + + public CDOID getRootResourceID() + { + return repository.getRootResourceID(); + } + + public boolean isAuthenticating() + { + return repository.isAuthenticating(); + } + + public boolean isSupportingAudits() + { + return repository.isSupportingAudits(); + } + + public boolean isSupportingBranches() + { + return repository.isSupportingBranches(); + } + + public boolean isSupportingUnits() + { + return repository.isSupportingUnits(); + } + + @Deprecated + public boolean isSupportingEcore() + { + return repository.isSupportingEcore(); + } + + public boolean isSerializingCommits() + { + return repository.isSerializingCommits(); + } + + public boolean isEnsuringReferentialIntegrity() + { + return repository.isEnsuringReferentialIntegrity(); + } + + public IDGenerationLocation getIDGenerationLocation() + { + return repository.getIDGenerationLocation(); + } + + public CommitInfoStorage getCommitInfoStorage() + { + return repository.getCommitInfoStorage(); + } + + public boolean waitWhileInitial(IProgressMonitor monitor) + { + return repository.waitWhileInitial(monitor); + } + + 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 runAfterUpdate(long updateTime, Runnable runnable) + { + throw new UnsupportedOperationException(); + } + + 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 long refresh(Provider provider) + { + throw new UnsupportedOperationException(); + } + + public long refresh() + { + throw new UnsupportedOperationException(); + } + + public Options options() + { + return this; + } + + 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 ensureChunks(InternalCDORevision revision, int chunkSize) + { + throw new UnsupportedOperationException(); + } + + public void processRefreshSessionResult(RefreshSessionResult result, CDOBranch branch, List branchViews, + Map> viewedRevisions) + { + throw new UnsupportedOperationException(); + } + + public Object startLocalCommit() + { + throw new UnsupportedOperationException(); + } + + public void endLocalCommit(Object token) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender, boolean clearResourcePathCache, byte securityImpact, + Map newPermissions) + { + throw new UnsupportedOperationException(); + } + + public void invalidate(InvalidationData invalidationData) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void handleCommitNotification(CDOCommitInfo commitInfo) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void handleCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void handleCommitNotification(CommitNotificationInfo info) + { + throw new UnsupportedOperationException(); + } + + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo, InternalCDOView sender) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void handleBranchNotification(InternalCDOBranch branch) + { + throw new UnsupportedOperationException(); + } + + public InternalCDORemoteSessionManager getRemoteSessionManager() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public org.eclipse.emf.cdo.common.protocol.CDOAuthenticator getAuthenticator() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void setAuthenticator(org.eclipse.emf.cdo.common.protocol.CDOAuthenticator authenticator) + { + throw new UnsupportedOperationException(); + } + + public IPasswordCredentialsProvider getCredentialsProvider() + { + throw new UnsupportedOperationException(); + } + + public void setCredentialsProvider(IPasswordCredentialsProvider credentialsProvider) + { + 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(); + } + + @Deprecated + public CDORevisionAvailabilityInfo createRevisionAvailabilityInfo(CDOBranchPoint branchPoint) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void cacheRevisions(CDORevisionAvailabilityInfo info) + { + throw new UnsupportedOperationException(); + } + + public MergeData getMergeData(CDOBranchPoint target, CDOBranchPoint source, CDOBranchPoint sourceBase, boolean computeChangeSets) + { + throw new UnsupportedOperationException(); + } + + public MergeData getMergeData(CDOBranchPoint target, CDOBranchPoint source, CDOBranchPoint targetBase, CDOBranchPoint sourceBase, boolean computeChangeSets) + { + throw new UnsupportedOperationException(); + } + + public boolean isPassiveUpdateEnabled() + { + throw new UnsupportedOperationException(); + } + + public void setPassiveUpdateEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public PassiveUpdateMode getPassiveUpdateMode() + { + throw new UnsupportedOperationException(); + } + + public void setPassiveUpdateMode(PassiveUpdateMode mode) + { + throw new UnsupportedOperationException(); + } + + public LockNotificationMode getLockNotificationMode() + { + throw new UnsupportedOperationException(); + } + + public void setLockNotificationMode(LockNotificationMode mode) + { + throw new UnsupportedOperationException(); + } + + public CDOSession getContainer() + { + throw new UnsupportedOperationException(); + } + + public boolean isGeneratedPackageEmulationEnabled() + { + return generatedPackageEmulationEnabled; + } + + public void setGeneratedPackageEmulationEnabled(boolean generatedPackageEmulationEnabled) + { + this.generatedPackageEmulationEnabled = generatedPackageEmulationEnabled; + } + + public CDOCollectionLoadingPolicy getCollectionLoadingPolicy() + { + throw new UnsupportedOperationException(); + } + + public void setCollectionLoadingPolicy(CDOCollectionLoadingPolicy policy) + { + throw new UnsupportedOperationException(); + } + + public CDOLobStore getLobCache() + { + throw new UnsupportedOperationException(); + } + + public void setLobCache(CDOLobStore lobCache) + { + throw new UnsupportedOperationException(); + } + + public CDOPermissionUpdater getPermissionUpdater() + { + throw new UnsupportedOperationException(); + } + + public void setPermissionUpdater(CDOPermissionUpdater permissionUpdater) + { + throw new UnsupportedOperationException(); + } + + public boolean isDelegableViewLockEnabled() + { + throw new UnsupportedOperationException(); + } + + public void setDelegableViewLockEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Session.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Session.java new file mode 100644 index 000000000..0c8f68d62 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Session.java @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2007-2016 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.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +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.protocol.CDOProtocol.CommitNotificationInfo; +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.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.security.CDOPermission; +import org.eclipse.emf.cdo.internal.common.commit.DelegatingCommitInfo; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IPermissionManager; +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.InternalRepository; +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.AdapterUtil; +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.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + */ +public class Session extends Container 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 boolean openOnClientSide; + + private long firstUpdateTime; + + private long lastUpdateTime; + + @ExcludeFromDump + private Object lastUpdateTimeLock = new Object(); + + private Map views = new HashMap(); + + private AtomicInteger lastTempViewID = new AtomicInteger(); + + private final IRegistry properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + + @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; + } + + public final IRegistry properties() + { + return properties; + } + + /** + * @since 2.0 + */ + public CDOCommonSession getContainer() + { + return this; + } + + public InternalSessionManager getManager() + { + return manager; + } + + public CDOBranchManager getBranchManager() + { + return manager.getRepository().getBranchManager(); + } + + public ISessionProtocol getProtocol() + { + return protocol; + } + + public int getSessionID() + { + return sessionID; + } + + /** + * @since 2.0 + */ + public String getUserID() + { + return userID; + } + + public void setUserID(String userID) + { + this.userID = 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; + } + + @Deprecated + public long getLastUpdateTime() + { + synchronized (lastUpdateTimeLock) + { + return lastUpdateTime; + } + } + + public long getFirstUpdateTime() + { + return firstUpdateTime; + } + + public void setFirstUpdateTime(long firstUpdateTime) + { + this.firstUpdateTime = firstUpdateTime; + } + + public boolean isOpenOnClientSide() + { + return openOnClientSide; + } + + public void setOpenOnClientSide() + { + openOnClientSide = true; + manager.openedOnClientSide(this); + } + + public InternalView[] getElements() + { + checkActive(); + return getViews(); + } + + @Override + public boolean isEmpty() + { + checkActive(); + + synchronized (views) + { + return views.isEmpty(); + } + } + + public InternalView[] getViews() + { + checkActive(); + return getViewsArray(); + } + + private InternalView[] getViewsArray() + { + synchronized (views) + { + return views.values().toArray(new InternalView[views.size()]); + } + } + + public InternalView getView(int viewID) + { + checkActive(); + + synchronized (views) + { + 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(); + + synchronized (views) + { + views.put(viewID, view); + } + + fireElementAddedEvent(view); + } + + /** + * @since 2.0 + */ + public void viewClosed(InternalView view) + { + int viewID = view.getViewID(); + InternalView removedView; + + synchronized (views) + { + removedView = views.remove(viewID); + } + + if (removedView == 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 revisions, + List additionalRevisions) + { + InternalCDORevisionManager revisionManager = manager.getRepository().getRevisionManager(); + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + // 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 CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext) + { + IPermissionManager permissionManager = manager.getPermissionManager(); + if (permissionManager != null) + { + return permissionManager.getPermission(revision, securityContext, this); + } + + return CDORevision.PERMISSION_PROVIDER.getPermission(revision, securityContext); + } + + 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); + } + } + + @Deprecated + public void sendBranchNotification(InternalCDOBranch branch) throws Exception + { + sendBranchNotification(branch, ChangeKind.CREATED); + } + + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception + { + if (protocol != null) + { + protocol.sendBranchNotification(branch, changeKind); + } + } + + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo notificationInfo) throws Exception + { + if (protocol == null) + { + return; + } + + if (!isPassiveUpdateEnabled()) + { + return; + } + + byte securityImpact = notificationInfo.getSecurityImpact(); + if (securityImpact == CommitNotificationInfo.IMPACT_PERMISSIONS) + { + IPermissionManager permissionManager = manager.getPermissionManager(); + Set impactedRules = notificationInfo.getImpactedRules(); + + if (!permissionManager.hasAnyRule(this, impactedRules)) + { + securityImpact = CommitNotificationInfo.IMPACT_NONE; + } + } + + CommitInfo sessionCommitInfo = new CommitInfo(notificationInfo); + + CommitNotificationInfo sessionNotificationInfo = new CommitNotificationInfo(); + sessionNotificationInfo.setSender(notificationInfo.getSender()); + sessionNotificationInfo.setCommitInfo(sessionCommitInfo); + sessionNotificationInfo.setRevisionProvider(notificationInfo.getRevisionProvider()); + sessionNotificationInfo.setClearResourcePathCache(notificationInfo.isClearResourcePathCache()); + sessionNotificationInfo.setNewPermissions(sessionCommitInfo.getNewPermissions()); + sessionNotificationInfo.setSecurityImpact(securityImpact); + + CDOLockChangeInfo lockChangeInfo = notificationInfo.getLockChangeInfo(); + if (lockChangeInfo != null) + { + Object lockNotificationRequired = isLockNotificationRequired(lockChangeInfo); + if (lockNotificationRequired != null) + { + sessionNotificationInfo.setLockChangeInfo(lockChangeInfo); + } + } + + protocol.sendCommitNotification(sessionNotificationInfo); + + synchronized (lastUpdateTimeLock) + { + CDOCommitInfo originalCommitInfo = notificationInfo.getCommitInfo(); + lastUpdateTime = originalCommitInfo.getTimeStamp(); + } + } + + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception + { + if (protocol != null) + { + Object lockNotificationRequired = isLockNotificationRequired(lockChangeInfo); + if (lockNotificationRequired == Boolean.TRUE) + { + protocol.sendLockNotification(lockChangeInfo); + return; + } + + if (lockNotificationRequired instanceof InternalView) + { + InternalView view = (InternalView)lockNotificationRequired; + + try + { + protocol.sendLockNotification(lockChangeInfo); + } + catch (Exception ex) + { + if (!view.isClosed()) + { + OM.LOG.warn("A problem occured while notifying view " + view, ex); + } + } + } + } + } + + private Object isLockNotificationRequired(CDOLockChangeInfo lockChangeInfo) + { + LockNotificationMode lockNotificationMode = options().getLockNotificationMode(); + if (lockNotificationMode == LockNotificationMode.ALWAYS) + { + return Boolean.TRUE; + } + + if (lockNotificationMode == LockNotificationMode.IF_REQUIRED_BY_VIEWS) + { + // We send the lockChangeInfo only if this session has one (or more) views configured for this branch. + for (InternalView view : getViews()) + { + if (view.options().isLockNotificationEnabled()) + { + CDOBranch affectedBranch = lockChangeInfo.getBranch(); + if (view.getBranch() == affectedBranch || affectedBranch == null) + { + return view; + } + } + } + } + + return null; + } + + private boolean isDeltaNeeded(CDOID id, InternalView[] views) + { + boolean supportingUnits = manager.getRepository().isSupportingUnits(); + + for (InternalView view : views) + { + try + { + if (view.hasSubscription(id)) + { + return true; + } + + if (supportingUnits && view.isInOpenUnit(id)) + { + return true; + } + } + catch (Exception ex) + { + if (!view.isClosed()) + { + OM.LOG.warn("A problem occured while checking subscriptions of view " + view, ex); + } + } + } + + 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); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) + { + return AdapterUtil.adapt(this, adapter, false); + } + + @Override + public String toString() + { + String name = "unknown"; + if (manager != null) + { + InternalRepository repository = manager.getRepository(); + if (repository != null) + { + name = repository.getName(); + } + } + + if (userID != null && userID.length() != 0) + { + name = userID + "@" + name; + } + + return MessageFormat.format("Session{0} [{1}]", sessionID, name); //$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(); + } + + /** + * @author Eike Stepper + */ + private final class CommitInfo extends DelegatingCommitInfo + { + private final CDOCommitInfo delegate; + + private final CDORevisionProvider revisionProvider; + + private final InternalView[] views; + + private final IPermissionManager permissionManager; + + private final Map newPermissions; + + private final boolean additions; + + private final boolean changes; + + public CommitInfo(CommitNotificationInfo notificationInfo) + { + delegate = notificationInfo.getCommitInfo(); + revisionProvider = notificationInfo.getRevisionProvider(); + + views = getViews(); + permissionManager = manager.getPermissionManager(); + if (permissionManager != null) + { + newPermissions = CDOIDUtil.createMap(); + } + else + { + newPermissions = null; + } + + PassiveUpdateMode passiveUpdateMode = getPassiveUpdateMode(); + additions = passiveUpdateMode == PassiveUpdateMode.ADDITIONS; + changes = additions || passiveUpdateMode == PassiveUpdateMode.CHANGES; + } + + @Override + protected CDOCommitInfo getDelegate() + { + return delegate; + } + + protected void addNewPermission(CDOID id, CDOPermission permission) + { + newPermissions.put(id, permission); + } + + public Map getNewPermissions() + { + return newPermissions; + } + + @Override + public List getNewObjects() + { + final List newObjects = super.getNewObjects(); + return new IndexedList() + { + @Override + public CDOIDAndVersion get(int index) + { + CDORevision revision = (CDORevision)newObjects.get(index); + if (additions) + { + if (permissionManager == null) + { + // Return full revision + return revision; + } + + CDOPermission permission = permissionManager.getPermission(revision, delegate, Session.this); + CDOID id = revision.getID(); + addNewPermission(id, permission); + + if (permission != CDOPermission.NONE) + { + // Return full revision + return revision; + } + } + + // Prevent sending full revision by copying the id and version + return CDOIDUtil.createIDAndVersion(revision); + } + + @Override + public int size() + { + return newObjects.size(); + } + }; + } + + @Override + public List getChangedObjects() + { + final List changedObjects = super.getChangedObjects(); + return new IndexedList() + { + @Override + public CDORevisionKey get(int index) + { + CDORevisionDelta revisionDelta = (CDORevisionDelta)changedObjects.get(index); + CDOID id = revisionDelta.getID(); + + if (changes || isDeltaNeeded(id, views)) + { + if (permissionManager == null) + { + // Return full delta + return revisionDelta; + } + + if (revisionProvider == null) + { + // Return full delta + return revisionDelta; + } + + CDORevision newRevision = revisionProvider.getRevision(id); + CDOPermission permission = permissionManager.getPermission(newRevision, delegate, Session.this); + addNewPermission(id, permission); + + if (permission != CDOPermission.NONE) + { + // Return full delta + return revisionDelta; + } + } + + // Prevent sending full delta by copying the id and version + return CDORevisionUtil.copyRevisionKey(revisionDelta); + } + + @Override + public int size() + { + return changedObjects.size(); + } + }; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/SessionManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/SessionManager.java new file mode 100644 index 000000000..c9bc214cd --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/SessionManager.java @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 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 + * Christian W. Damus (CEA LIST) - bug 399306 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +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.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IPermissionManager; +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.server.IAuthenticationProtocol; +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.ObjectUtil; +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +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.trace.ContextTracer; +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; +import org.eclipse.net4j.util.security.DiffieHellman; +import org.eclipse.net4j.util.security.DiffieHellman.Client.Response; +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IAuthenticator2; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.UserManagerAuthenticator; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +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 implements InternalSessionManager +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SESSION, SessionManager.class); + + private InternalRepository repository; + + private DiffieHellman.Server authenticationServer; + + private IAuthenticator authenticator; + + private IPermissionManager permissionManager; + + private final Map sessions = new HashMap(); + + private final AtomicInteger lastSessionID = new AtomicInteger(); + + private final Map> commitNotificationInfoQueues = new HashMap>(); + + private final IListener sessionListener = new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + synchronized (commitNotificationInfoQueues) + { + commitNotificationInfoQueues.remove(lifecycle); + } + } + }; + + /** + * @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; + } + + @Deprecated + public IUserManager getUserManager() + { + if (authenticator instanceof UserManagerAuthenticator) + { + return ((UserManagerAuthenticator)authenticator).getUserManager(); + } + + return null; + } + + @Deprecated + public void setUserManager(IUserManager userManager) + { + UserManagerAuthenticator userManagerAuthenticator = new UserManagerAuthenticator(); + userManagerAuthenticator.setUserManager(userManager); + + setAuthenticator(userManagerAuthenticator); + } + + public DiffieHellman.Server getAuthenticationServer() + { + return authenticationServer; + } + + public void setAuthenticationServer(DiffieHellman.Server authenticationServer) + { + this.authenticationServer = authenticationServer; + } + + public IAuthenticator getAuthenticator() + { + return authenticator; + } + + public void setAuthenticator(IAuthenticator authenticator) + { + this.authenticator = authenticator; + if (isActive() && authenticator != null) + { + initAuthentication(); + } + } + + public IPermissionManager getPermissionManager() + { + return permissionManager; + } + + public void setPermissionManager(IPermissionManager permissionManager) + { + this.permissionManager = permissionManager; + } + + 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) + { + final int id = lastSessionID.incrementAndGet(); + if (TRACER.isEnabled()) + { + TRACER.trace("Opening session " + id); //$NON-NLS-1$ + } + + String userID = authenticateUser(sessionProtocol); + final InternalSession session = createSession(id, userID, sessionProtocol); + LifecycleUtil.activate(session); + + synchronized (sessions) + { + repository.executeOutsideStartCommit(new Runnable() + { + public void run() + { + long firstUpdateTime = repository.getLastCommitTimeStamp(); + session.setFirstUpdateTime(firstUpdateTime); + + 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 openedOnClientSide(InternalSession session) + { + processQueuedCommitNotifications(session); + } + + 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); + } + } + } + + @Deprecated + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch) + { + sendBranchNotification(sender, branch, ChangeKind.CREATED); + } + + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch, ChangeKind changeKind) + { + for (InternalSession session : getSessions()) + { + if (session != sender) + { + try + { + session.sendBranchNotification(branch, changeKind); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo info) + { + CDOCommonSession sender = info.getSender(); + for (InternalSession session : getSessions()) + { + if (session != sender) + { + if (session.isOpenOnClientSide()) + { + processQueuedCommitNotifications(session); + doSendCommitNotification(session, info); + } + else + { + queueCommitNotification(session, info); + } + } + } + } + + private void doSendCommitNotification(InternalSession session, CommitNotificationInfo info) + { + try + { + session.sendCommitNotification(info); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + + private void queueCommitNotification(InternalSession session, CommitNotificationInfo info) + { + synchronized (commitNotificationInfoQueues) + { + List queue = commitNotificationInfoQueues.get(session); + if (queue == null) + { + queue = new ArrayList(); + commitNotificationInfoQueues.put(session, queue); + + session.addListener(sessionListener); + } + + queue.add(info); + } + } + + private void processQueuedCommitNotifications(InternalSession session) + { + List queue; + synchronized (commitNotificationInfoQueues) + { + queue = commitNotificationInfoQueues.remove(session); + } + + if (queue != null && !session.isClosed()) + { + session.removeListener(sessionListener); + + for (CommitNotificationInfo queuedInfo : queue) + { + doSendCommitNotification(session, queuedInfo); + } + } + } + + 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 sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, int[] recipients) + { + List result = new ArrayList(); + 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) + { + if (session.isClosed()) + { + if (TRACER.isEnabled()) + { + TRACER.trace("A problem occured while notifying session " + session, t); + } + } + else + { + OM.LOG.warn("A problem occured while notifying session " + session, t); + } + } + + public String authenticateUser(IAuthenticationProtocol protocol) throws SecurityException + { + if (protocol == null) + { + return null; + } + + if (authenticationServer == null || authenticator == null) + { + return null; + } + + try + { + Challenge challenge = authenticationServer.getChallenge(); + Response response = protocol.sendAuthenticationChallenge(challenge); + if (response == null) + { + throw notAuthenticated(); + } + + ByteArrayInputStream bais = new ByteArrayInputStream(authenticationServer.handleResponse(response)); + + ExtendedDataInputStream stream = new ExtendedDataInputStream(bais); + String userID = stream.readString(); + char[] password = stream.readString().toCharArray(); + + authenticator.authenticate(userID, password); + return userID; + } + catch (SecurityException ex) + { + throw ex; + } + catch (Exception ex) + { + Throwable cause = ex.getCause(); + if (cause instanceof SecurityException) + { + throw (SecurityException)cause; + } + + throw new SecurityException(ex); + } + } + + public void changeUserCredentials(IAuthenticationProtocol sessionProtocol, String userID) + { + changeUserCredentials(sessionProtocol, userID, CredentialsUpdateOperation.CHANGE_PASSWORD); + } + + public void resetUserCredentials(IAuthenticationProtocol sessionProtocol, String userID) + { + changeUserCredentials(sessionProtocol, userID, CredentialsUpdateOperation.RESET_PASSWORD); + } + + protected void changeUserCredentials(IAuthenticationProtocol sessionProtocol, String userID, CredentialsUpdateOperation operation) + { + + if (sessionProtocol == null) + { + return; + } + + if (authenticationServer == null || authenticator == null) + { + return; + } + + if (!(authenticator instanceof IAuthenticator2)) + { + throw new SecurityException("Current authenticator does not permit password updates"); //$NON-NLS-1$ + } + + try + { + Challenge challenge = authenticationServer.getChallenge(); + Response response = sessionProtocol.sendCredentialsChallenge(challenge, userID, operation); + if (response == null) + { + throw notAuthenticated(); + } + + ByteArrayInputStream baos = new ByteArrayInputStream(authenticationServer.handleResponse(response)); + ExtendedDataInputStream stream = new ExtendedDataInputStream(baos); + + if (operation == CredentialsUpdateOperation.RESET_PASSWORD) + { + String adminID = stream.readString(); + char[] adminPassword = stream.readString().toCharArray(); + if (!ObjectUtil.equals(userID, stream.readString())) + { + throw new SecurityException("Attempt to reset password of a different user than requested"); //$NON-NLS-1$ + } + char[] newPassword = stream.readString().toCharArray(); + + // this will throw if the current credentials are not authenticated as an administrator + ((IAuthenticator2)authenticator).resetPassword(adminID, adminPassword, userID, newPassword); + } + else + { + userID = stream.readString(); // user can change any password that she can authenticate on the old password + char[] password = stream.readString().toCharArray(); + char[] newPassword = stream.readString().toCharArray(); + + // this will throw if the "old password" provided by the user is not correct + ((IAuthenticator2)authenticator).updatePassword(userID, password, newPassword); + } + } + catch (SecurityException ex) + { + throw ex; + } + catch (Exception ex) + { + Throwable cause = ex.getCause(); + if (cause instanceof SecurityException) + { + throw (SecurityException)cause; + } + + throw new SecurityException(ex); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + initAuthentication(); + } + + protected void initAuthentication() + { + if (authenticator != null) + { + if (authenticationServer == null) + { + authenticationServer = new DiffieHellman.Server(repository.getUUID()); + } + + LifecycleUtil.activate(authenticationServer); + LifecycleUtil.activate(authenticator); + } + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(authenticator); + LifecycleUtil.deactivate(authenticationServer); + + for (InternalSession session : getSessions()) + { + LifecycleUtil.deactivate(session); + } + + super.doDeactivate(); + } + + @SuppressWarnings("deprecation") + private SecurityException notAuthenticated() + { + // Existing clients may expect this deprecated exception type + return new org.eclipse.emf.cdo.common.util.NotAuthenticatedException(); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java new file mode 100644 index 000000000..cbf0d8e6e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2011-2013, 2016 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; + +/** + * Bug 297940, 290032 + * + * @author Caspar De Groot + */ +class TimeStampAuthority +{ + private InternalRepository repository; + + /** + * Holds the begin timestamp that was issued in response to the last call to {@link #startCommit(long, OMMonitor)}. + *

+ * Synchronized on TimeStampAuthority.this. + */ + @ExcludeFromDump + private transient long lastIssuedTimeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + + /** + * Holds the begin timestamp that was last reported finished by a call to {@link #endCommit(long)}. + *

+ * Synchronized on lastFinishedTimeStampLock. + */ + private long lastFinishedTimeStamp; + + private LastCommitTimeStampLock lastFinishedTimeStampLock = 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 runningTransactions = new LinkedList(); + + /** + * A set of timestamps that have been reported finished but have not yet been + */ + private SortedSet finishedTransactions = new TreeSet(); + + TimeStampAuthority(InternalRepository repository) + { + this.repository = repository; + } + + /** + * The purpose of this method is to make sure that no commit can occur at the same time as + * the base of a new branch. Otherwise that commit could change revisions of that branch base. + * See bug 506768 and bug 383602. + */ + synchronized long getMaxBaseTimeForNewBranch() + { + long now = repository.getTimeStamp(); + while (repository.getTimeStamp() == now) + { + ConcurrencyUtil.sleep(1); + } + + return now; + } + + /** + * @deprecated Not used anymore. + */ + @Deprecated + synchronized long[] startCommit(OMMonitor monitor) + { + return startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor); + } + + synchronized long[] startCommit(long timeStampOverride, OMMonitor monitor) + { + monitor.begin(); + lockIfNeeded(); + + 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; + } + + long previousTimeStamp = lastIssuedTimeStamp; + lastIssuedTimeStamp = now; + + runningTransactions.add(now); + return new long[] { now, previousTimeStamp }; + } + 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 (lastFinishedTimeStampLock) + { + long oldValue = lastFinishedTimeStamp; + while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning) + { + finishedTransactions.remove(oldestFinished); + setLastFinishedTimeStampUnsynced(oldestFinished); + } + + // If we actually changed the lastFinishedTimeStamp, we need to notify waiting threads + if (lastFinishedTimeStamp != oldValue) + { + lastFinishedTimeStampLock.notifyAll(); + } + } + + unlockIfNeeded(); + } + + synchronized void failCommit(long timeStamp) + { + // Exclude problems before TransactionCommitContext.setTimeStamp() + if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + unlockIfNeeded(); + } + else + { + endCommit(timeStamp); + } + } + + 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 (lastFinishedTimeStampLock) + { + try + { + lastFinishedTimeStampLock.wait(timeout); + } + catch (Exception ignore) + { + } + + return lastFinishedTimeStamp; + } + } + + void setLastFinishedTimeStamp(long lastCommitTimeStamp) + { + synchronized (lastFinishedTimeStampLock) + { + if (lastCommitTimeStamp > lastFinishedTimeStamp) + { + lastIssuedTimeStamp = lastCommitTimeStamp; + setLastFinishedTimeStampUnsynced(lastCommitTimeStamp); + lastFinishedTimeStampLock.notifyAll(); + } + } + } + + private void setLastFinishedTimeStampUnsynced(long lastCommitTimeStamp) + { + lastFinishedTimeStamp = lastCommitTimeStamp; + repository.getStore().setLastCommitTime(lastFinishedTimeStamp); + } + + private void lockIfNeeded() + { + if (strictOrdering) + { + strictOrderingLock.lock(); + } + } + + private void unlockIfNeeded() + { + if (strictOrdering) + { + strictOrderingLock.unlock(); + } + } + + /** + * 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Transaction.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Transaction.java new file mode 100644 index 000000000..7e9f8536b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/Transaction.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008-2013, 2015, 2016 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.InternalRepository; +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 +{ + private CommitAttempt lastCommitAttempt; + + 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(); + + InternalRepository repository = getRepository(); + return repository.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, UNSPECIFIED_DATE }; + } + }; + } + + public CommitAttempt getLastCommitAttempt() + { + return lastCommitAttempt; + } + + public void setLastCommitAttempt(CommitAttempt lastCommitAttempt) + { + this.lastCommitAttempt = lastCommitAttempt; + } + + @Override + protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + if (timeStamp != UNSPECIFIED_DATE) + { + throw new IllegalArgumentException("Changing the target time is not supported by transactions"); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java new file mode 100644 index 000000000..50b611079 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java @@ -0,0 +1,1818 @@ +/* + * Copyright (c) 2010-2016 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 + * Christian W. Damus (CEA LIST) - bug 399487 + */ +package org.eclipse.emf.cdo.internal.server; + +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.branch.CDOBranchVersion; +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.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +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.security.NoPermissionException; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +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.IRepository; +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.CDOCommitInfoUtil; +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.InternalCDORevisionCache; +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.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalTransaction.CommitAttempt; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; + +import org.eclipse.net4j.util.CheckUtil; +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; +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.net4j.util.om.trace.ContextTracer; + +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.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class TransactionCommitContext implements InternalCommitContext +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, TransactionCommitContext.class); + + private static final InternalCDORevision DETACHED = new StubCDORevision(null); + + private final InternalTransaction transaction; + + private final CDOBranch branch; + + private InternalRepository repository; + + private InternalLockManager lockManager; + + private InternalCDOPackageRegistry repositoryPackageRegistry; + + private boolean packageRegistryLocked; + + private TransactionPackageRegistry packageRegistry; + + private IStoreAccessor accessor; + + private long lastUpdateTime; + + private long lastTreeRestructuringCommit; + + private Boolean treeRestructuring; + + private long timeStamp = CDORevision.UNSPECIFIED_DATE; + + private long previousTimeStamp = CDORevision.UNSPECIFIED_DATE; + + private int commitNumber; + + private String commitComment; + + private CDOBranchPoint commitMergeSource; + + private boolean usingEcore; + + private boolean usingEtypes; + + 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 detachedObjectTypes; + + private CDOBranchVersion[] detachedObjectVersions; + + private InternalCDORevision[] dirtyObjects = new InternalCDORevision[0]; + + private InternalCDORevision[] cachedDetachedRevisions = new InternalCDORevision[0]; + + private Map cachedRevisions; + + private Map oldRevisions = CDOIDUtil.createMap(); + + private Map newRevisions; + + private Set lockedObjects = new HashSet(); + + private List lockedTargets; + + private Map idMappings = CDOIDUtil.createMap(); + + private CDOReferenceAdjuster idMapper = new CDOIDMapper(idMappings); + + private byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN; + + private String rollbackMessage; + + private List xRefs; + + private boolean hasChanges; + + private boolean serializingCommits; + + private boolean ensuringReferentialIntegrity; + + private ExtendedDataInputStream lobs; + + private CDOLockState[] locksOnNewObjects = new CDOLockState[0]; + + private CDOID[] idsToUnlock = new CDOID[0]; + + private Map data; + + private CommitNotificationInfo commitNotificationInfo = new CommitNotificationInfo(); + + private CDOLockChangeInfo lockChangeInfo; + + private final List> postCommitLockStates = new ArrayList>(); + + public TransactionCommitContext(InternalTransaction transaction) + { + this.transaction = transaction; + + branch = transaction.getBranch(); + repository = transaction.getRepository(); + lockManager = repository.getLockingManager(); + serializingCommits = repository.isSerializingCommits(); + ensuringReferentialIntegrity = repository.isEnsuringReferentialIntegrity(); + + repositoryPackageRegistry = repository.getPackageRegistry(false); + } + + public InternalTransaction getTransaction() + { + return transaction; + } + + public CDOBranchPoint getBranchPoint() + { + return branch.getPoint(timeStamp); + } + + public String getUserID() + { + return transaction.getSession().getUserID(); + } + + public int getCommitNumber() + { + return commitNumber; + } + + public String getCommitComment() + { + return commitComment; + } + + public CDOBranchPoint getCommitMergeSource() + { + return commitMergeSource; + } + + public long getLastUpdateTime() + { + return lastUpdateTime; + } + + public byte getRollbackReason() + { + return rollbackReason; + } + + public String getRollbackMessage() + { + return rollbackMessage; + } + + public List getXRefs() + { + return xRefs; + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + if (packageRegistry == null) + { + packageRegistry = new TransactionPackageRegistry(repositoryPackageRegistry); + packageRegistry.activate(); + } + + return packageRegistry; + } + + public boolean isClearResourcePathCache() + { + return commitNotificationInfo.isClearResourcePathCache(); + } + + public byte getSecurityImpact() + { + return commitNotificationInfo.getSecurityImpact(); + } + + public boolean isUsingEcore() + { + return usingEcore; + } + + public boolean isUsingEtypes() + { + return usingEtypes; + } + + public InternalCDOPackageUnit[] getNewPackageUnits() + { + return newPackageUnits; + } + + public InternalCDORevision[] getNewObjects() + { + return newObjects; + } + + public InternalCDORevision[] getDirtyObjects() + { + return dirtyObjects; + } + + public CDOID[] getDetachedObjects() + { + return detachedObjects; + } + + public Map getDetachedObjectTypes() + { + return detachedObjectTypes; + } + + public CDOBranchVersion[] getDetachedObjectVersions() + { + return detachedObjectVersions; + } + + public InternalCDORevision[] getDetachedRevisions() + { + return getDetachedRevisions(true); + } + + public InternalCDORevision[] getDetachedRevisions(boolean check) + { + if (check) + { + // 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 Map getOldRevisions() + { + return oldRevisions; + } + + public Map getNewRevisions() + { + if (newRevisions == null) + { + newRevisions = CDOIDUtil.createMap(); + + for (int i = 0; i < newObjects.length; i++) + { + InternalCDORevision revision = newObjects[i]; + newRevisions.put(revision.getID(), revision); + } + } + + return newRevisions; + } + + public InternalCDORevision 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 (InternalCDORevision)transaction.getRevision(id); + } + + protected Map cacheRevisions() + { + Map cache = CDOIDUtil.createMap(); + 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 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.put(oldID, newID); + if (previousMapping != null && previousMapping != newID) + { + throw new IllegalStateException("previousMapping != null && previousMapping != newID"); //$NON-NLS-1$ + } + } + + public void applyIDMappings(OMMonitor monitor) + { + boolean mapIDs = !idMappings.isEmpty(); + monitor.begin(1 + (mapIDs ? newObjects.length + dirtyObjects.length + dirtyObjectDeltas.length : 0)); + + try + { + if (mapIDs) + { + applyIDMappings(newObjects, monitor.fork(newObjects.length)); + applyIDMappings(dirtyObjects, monitor.fork(dirtyObjects.length)); + for (CDORevisionDelta dirtyObjectDelta : dirtyObjectDeltas) + { + ((InternalCDORevisionDelta)dirtyObjectDelta).adjustReferences(idMapper); + monitor.worked(); + } + } + + // Do not notify handlers before the IDs are fully mapped! + notifyBeforeCommitting(monitor); + } + finally + { + monitor.done(); + } + } + + protected 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(); + } + } + + protected void notifyBeforeCommitting(OMMonitor monitor) + { + repository.notifyWriteAccessHandlers(transaction, this, true, monitor.fork()); + } + + 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 boolean isTreeRestructuring() + { + if (treeRestructuring == null) + { + treeRestructuring = CDORevisionUtil.isTreeRestructuring(dirtyObjectDeltas); + } + + return treeRestructuring; + } + + public void setLastTreeRestructuringCommit(long lastTreeRestructuringCommit) + { + this.lastTreeRestructuringCommit = lastTreeRestructuringCommit; + } + + public void setClearResourcePathCache(boolean clearResourcePathCache) + { + commitNotificationInfo.setClearResourcePathCache(clearResourcePathCache); + } + + public void setSecurityImpact(byte securityImpact, Set impactedRules) + { + commitNotificationInfo.setSecurityImpact(securityImpact); + commitNotificationInfo.setImpactedRules(impactedRules); + } + + public void setUsingEcore(boolean usingEcore) + { + this.usingEcore = usingEcore; + } + + public void setUsingEtypes(boolean usingEtypes) + { + this.usingEtypes = usingEtypes; + } + + 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 detachedObjectTypes) + { + this.detachedObjectTypes = detachedObjectTypes; + } + + public void setDetachedObjectVersions(CDOBranchVersion[] detachedObjectVersions) + { + this.detachedObjectVersions = detachedObjectVersions; + } + + public void setLastUpdateTime(long lastUpdateTime) + { + this.lastUpdateTime = lastUpdateTime; + } + + public void setCommitNumber(int commitNumber) + { + this.commitNumber = commitNumber; + } + + public void setCommitComment(String commitComment) + { + this.commitComment = commitComment; + } + + public void setCommitMergeSource(CDOBranchPoint commitMergeSource) + { + this.commitMergeSource = commitMergeSource; + } + + public ExtendedDataInputStream getLobs() + { + return lobs; + } + + public void setLobs(ExtendedDataInputStream in) + { + lobs = in; + } + + @Deprecated + public boolean isAutoReleaseLocksEnabled() + { + return false; + } + + @Deprecated + public void setAutoReleaseLocksEnabled(boolean on) + { + // Do nothing. + } + + public CDOLockState[] getLocksOnNewObjects() + { + return locksOnNewObjects; + } + + public void setLocksOnNewObjects(CDOLockState[] locksOnNewObjects) + { + this.locksOnNewObjects = locksOnNewObjects; + } + + public CDOID[] getIDsToUnlock() + { + return idsToUnlock; + } + + public void setIDsToUnlock(CDOID[] idsToUnlock) + { + this.idsToUnlock = idsToUnlock; + } + + public T getData(Object key) + { + if (data == null) + { + return null; + } + + @SuppressWarnings("unchecked") + T result = (T)data.get(key); + return result; + } + + public synchronized T setData(Object key, T value) + { + if (data == null) + { + data = new HashMap(); + } + + @SuppressWarnings("unchecked") + T old = (T)data.put(key, value); + return old; + } + + protected InternalCDOPackageUnit[] lockPackageRegistry(InternalCDOPackageUnit[] packageUnits) throws InterruptedException + { + if (!packageRegistryLocked) + { + repository.getPackageRegistryCommitLock().acquire(); + packageRegistryLocked = true; + } + + List noDuplicates = new ArrayList(); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + String id = packageUnit.getID(); + if (!repositoryPackageRegistry.containsKey(id)) + { + noDuplicates.add(packageUnit); + } + } + + int newSize = noDuplicates.size(); + if (packageUnits.length != newSize) + { + return noDuplicates.toArray(new InternalCDOPackageUnit[newSize]); + } + + return packageUnits; + } + + /** + * @since 2.0 + */ + public void write(OMMonitor monitor) + { + try + { + monitor.begin(107); + + hasChanges = newPackageUnits.length != 0 || newObjects.length != 0 || dirtyObjectDeltas.length != 0; + if (!hasChanges) + { + return; + } + + dirtyObjects = new InternalCDORevision[dirtyObjectDeltas.length]; + + if (newPackageUnits.length != 0) + { + newPackageUnits = lockPackageRegistry(newPackageUnits); + } + + lockObjects(); // Can take long and must come before setTimeStamp() + monitor.worked(); + + setTimeStamp(monitor.fork()); + + adjustForCommit(); + monitor.worked(); + + computeDirtyObjects(monitor.fork()); + + checkContainmentCycles(); + checkXRefs(); + checkUnitMoves(); + monitor.worked(); + + detachObjects(monitor.fork()); + writeAccessor(monitor.fork(100)); + } + catch (RollbackException ex) + { + rollbackReason = ex.getRollbackReason(); + rollback(ex.getRollbackMessage()); + } + catch (Throwable t) + { + handleException(t); + } + finally + { + finishMonitor(monitor); + } + } + + public void commit(OMMonitor monitor) + { + try + { + monitor.begin(101); + if (hasChanges) + { + accessor.commit(monitor.fork(100)); + } + else + { + monitor.worked(100); + } + + updateInfraStructure(monitor.fork()); + + if (hasChanges) + { + // Bug 297940 + repository.endCommit(timeStamp); + } + } + catch (Throwable ex) + { + handleException(ex); + } + finally + { + finishMonitor(monitor); + } + } + + public List> getPostCommmitLockStates() + { + return postCommitLockStates; + } + + protected void handleException(Throwable ex) + { + try + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex); + } + + if (ex instanceof IRepository.WriteAccessHandler.TransactionValidationException) + { + rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_VALIDATION_ERROR; + rollback(ex.getLocalizedMessage()); + } + else + { + 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 + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex1); + } + } + catch (Exception ignore) + { + } + } + } + + protected void finishMonitor(OMMonitor monitor) + { + try + { + monitor.done(); + } + catch (Exception ex) + { + try + { + OM.LOG.warn(ex); + } + catch (Exception ignore) + { + } + } + } + + protected void setTimeStamp(OMMonitor monitor) + { + long[] times = createTimeStamp(monitor); // Could throw an exception + timeStamp = times[0]; + previousTimeStamp = times[1]; + CheckUtil.checkState(timeStamp != CDOBranchPoint.UNSPECIFIED_DATE, "Commit timestamp must not be 0"); + + transaction.setLastCommitAttempt(new CommitAttempt(commitNumber, timeStamp, previousTimeStamp)); + } + + protected long[] createTimeStamp(OMMonitor monitor) + { + return repository.createCommitTimeStamp(monitor); + } + + public 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) + { + try + { + if (packageRegistryLocked) + { + repository.getPackageRegistryCommitLock().release(); + } + } + catch (Throwable ex) + { + OM.LOG.warn("A problem occured while releasing the package registry commit lock", ex); + } + + try + { + // Send notifications (in particular FailureCommitInfos) only if timeStamp had been allocated + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + sendCommitNotifications(success); + } + } + catch (Throwable 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; + } + } + } + + protected void sendCommitNotifications(boolean success) + { + commitNotificationInfo.setSender(transaction.getSession()); + commitNotificationInfo.setRevisionProvider(this); + commitNotificationInfo.setLockChangeInfo(lockChangeInfo); + + if (success) + { + commitNotificationInfo.setCommitInfo(createCommitInfo()); + } + else + { + commitNotificationInfo.setCommitInfo(createFailureCommitInfo()); + } + + repository.sendCommitNotification(commitNotificationInfo); + } + + public CDOCommitInfo createCommitInfo() + { + String userID = transaction.getSession().getUserID(); + CDOCommitData commitData = createCommitData(); + + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, commitMergeSource, commitData); + } + + public CDOCommitInfo createFailureCommitInfo() + { + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + return new FailureCommitInfo(commitInfoManager, timeStamp, previousTimeStamp); + } + + protected CDOCommitData createCommitData() + { + List newPackageUnitsCollection = new IndexedList.ArrayBacked() + { + @Override + protected CDOPackageUnit[] getArray() + { + return newPackageUnits; + } + }; + + List newObjectsCollection = new IndexedList.ArrayBacked() + { + @Override + protected CDOIDAndVersion[] getArray() + { + return newObjects; + } + }; + + List changedObjectsCollection = new IndexedList.ArrayBacked() + { + @Override + protected CDORevisionKey[] getArray() + { + return dirtyObjectDeltas; + } + }; + + List detachedObjectsCollection = new IndexedList() + { + @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 CDOCommitInfoUtil.createCommitData(newPackageUnitsCollection, newObjectsCollection, changedObjectsCollection, detachedObjectsCollection); + } + + protected void adjustForCommit() + { + for (InternalCDOPackageUnit newPackageUnit : newPackageUnits) + { + newPackageUnit.setTimeStamp(timeStamp); + } + + for (InternalCDORevision newObject : newObjects) + { + newObject.adjustForCommit(branch, timeStamp); + } + } + + protected void lockObjects() throws InterruptedException + { + lockedObjects.clear(); + lockedTargets = null; + + try + { + CDOFeatureDeltaVisitor deltaTargetLocker = null; + if (ensuringReferentialIntegrity && !serializingCommits) + { + final Set newIDs = new HashSet(); + 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); + } + } + + final boolean supportingBranches = repository.isSupportingBranches(); + 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, branch); + lockedObjects.add(key); + } + + if (deltaTargetLocker != null) + { + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + InternalCDORevisionDelta delta = dirtyObjectDeltas[i]; + delta.accept(deltaTargetLocker); + } + } + + for (int i = 0; i < detachedObjects.length; i++) + { + CDOID id = detachedObjects[i]; + Object key = lockManager.getLockKey(id, branch); + lockedObjects.add(key); + } + + if (!lockedObjects.isEmpty()) + { + try + { + long timeout = repository.getOptimisticLockingTimeout(); + + // First lock all objects (incl. possible ref targets). + // This is a transient operation, it does not check for existance! + lockManager.lock2(LockType.WRITE, transaction, lockedObjects, timeout); + } + catch (Exception ex) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_OPTIMISTIC_LOCKING, ex); + } + + // If all locks could be acquired, check if locked targets do still exist + if (lockedTargets != null) + { + for (CDOID id : lockedTargets) + { + CDORevision revision = transaction.getRevision(id); + if (revision == null || revision instanceof DetachedCDORevision) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY, + "Attempt by " + transaction + " to introduce a stale reference"); + } + } + } + } + } + catch (RuntimeException ex) + { + lockedObjects.clear(); + lockedTargets = null; + throw ex; + } + } + + protected void lockTarget(Object value, Set 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, branch); + lockedObjects.add(key); + + // Let this object be checked for existance after it has been locked + if (lockedTargets == null) + { + lockedTargets = new ArrayList(); + } + + lockedTargets.add(id); + } + } + + protected 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$ + } + + if (!dirtyObjects[i].isWritable()) + { + throw new NoPermissionException(dirtyObjects[i]); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected InternalCDORevision computeDirtyObject(InternalCDORevisionDelta delta) + { + CDOID id = delta.getID(); + + InternalCDORevision oldRevision = null; + String rollbackMessage = null; + byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN; + + try + { + oldRevision = (InternalCDORevision)transaction.getRevision(id); + if (oldRevision != null) + { + if (oldRevision.getBranch() != delta.getBranch() || oldRevision.getVersion() != delta.getVersion()) + { + rollbackMessage = "Attempt by " + transaction + " to modify historical revision: " + delta; + rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_COMMIT_CONFLICT; + } + } + else + { + rollbackMessage = "Revision " + id + " not found by " + transaction; + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + + rollbackMessage = ex.getMessage(); + if (rollbackMessage == null) + { + rollbackMessage = ex.getClass().getName(); + } + } + + if (rollbackMessage != null) + { + // If the object is logically locked (see lockObjects) but has a wrong (newer) version, someone else modified it. + throw new RollbackException(rollbackReason, rollbackMessage); + } + + // Make sure all chunks are loaded + repository.ensureChunks(oldRevision, CDORevision.UNCHUNKED); + + oldRevisions.put(id, oldRevision); + + InternalCDORevision newRevision = oldRevision.copy(); + newRevision.adjustForCommit(branch, timeStamp); + + delta.applyTo(newRevision); + return newRevision; + } + + protected void checkContainmentCycles() + { + if (lastTreeRestructuringCommit == 0) + { + // If this was a tree-restructuring commit then lastTreeRestructuringCommit would be initialized. + return; + } + + if (lastUpdateTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + // Happens during replication (see CommitDelegationRequest). Commits are checked in the master repo. + return; + } + + if (lastTreeRestructuringCommit <= lastUpdateTime) + { + // If this client's original state includes the state of the last tree-restructuring commit there's no danger. + return; + } + + Set objectsThatReachTheRoot = new HashSet(); + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + InternalCDORevisionDelta revisionDelta = dirtyObjectDeltas[i]; + CDOFeatureDelta containerDelta = revisionDelta.getFeatureDelta(CDOContainerFeatureDelta.CONTAINER_FEATURE); + if (containerDelta != null) + { + InternalCDORevision revision = dirtyObjects[i]; + if (!isTheRootReachable(revision, objectsThatReachTheRoot, new HashSet())) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_CONTAINMENT_CYCLE, + "Attempt by " + transaction + " to introduce a containment cycle"); + } + } + } + } + + protected boolean isTheRootReachable(InternalCDORevision revision, Set objectsThatReachTheRoot, Set visited) + { + CDOID id = revision.getID(); + if (!visited.add(id)) + { + // Cycle detected on the way up to the root. + return false; + } + + if (!objectsThatReachTheRoot.add(id)) + { + // Has already been checked before. + return true; + } + + CDOID containerID = (CDOID)revision.getContainerID(); + if (CDOIDUtil.isNull(containerID)) + { + // The tree root has been reached. + return true; + } + + // Use this commit context as CDORevisionProvider for the container revisions. + // This is safe because Repository.commit() serializes all tree-restructuring commits. + InternalCDORevision containerRevision = getRevision(containerID); + + // Recurse Up + return isTheRootReachable(containerRevision, objectsThatReachTheRoot, visited); + } + + protected void checkXRefs() + { + if (ensuringReferentialIntegrity && detachedObjectTypes != null) + { + XRefContext context = new XRefContext(); + xRefs = context.getXRefs(accessor); + if (!xRefs.isEmpty()) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY, + "Attempt by " + transaction + " to introduce a stale reference"); + } + } + } + + protected void checkUnitMoves() + { + if (repository.isSupportingUnits() && isTreeRestructuring()) + { + String checkUnitMoves = repository.getProperties().get(IRepository.Props.CHECK_UNIT_MOVES); + if ("true".equalsIgnoreCase(checkUnitMoves)) + { + InternalUnitManager unitManager = repository.getUnitManager(); + + List unitMoves = unitManager.getUnitMoves(dirtyObjectDeltas, transaction, this); + if (!unitMoves.isEmpty()) + { + StringBuilder builder = new StringBuilder("Attempt by " + transaction + " to move objects between units: "); + CDOIDUtil.write(builder, unitMoves); + + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_UNIT_INTEGRITY, builder.toString()); + } + } + } + } + + public synchronized void rollback(String message) + { + // Check if we already rolled back + if (rollbackMessage == null) + { + rollbackMessage = message; + + 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); + } + } + + releaseImplicitLocks(); + } + } + + public IStoreAccessor getAccessor() + { + return accessor; + } + + protected void updateInfraStructure(OMMonitor monitor) + { + try + { + monitor.begin(9); + addNewPackageUnits(monitor.fork()); + addRevisions(newObjects, monitor.fork()); + addRevisions(dirtyObjects, monitor.fork()); + reviseDetachedObjects(monitor.fork()); + + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + commitInfoManager.setLastCommitOfBranch(branch, timeStamp); + + releaseImplicitLocks(); + monitor.worked(); + + acquireLocksOnNewObjects(); + monitor.worked(); + + autoReleaseExplicitLocks(); + monitor.worked(); + + if (!postCommitLockStates.isEmpty()) + { + lockChangeInfo = createLockChangeInfo(postCommitLockStates); + } + + repository.notifyWriteAccessHandlers(transaction, this, false, monitor.fork()); + } + catch (Throwable t) + { + handleException(t); + } + finally + { + monitor.done(); + } + } + + protected synchronized void releaseImplicitLocks() + { + // Unlock objects locked during commit + if (!lockedObjects.isEmpty()) + { + lockManager.unlock2(LockType.WRITE, transaction, lockedObjects); + lockedObjects.clear(); + } + } + + protected void acquireLocksOnNewObjects() throws InterruptedException + { + final CDOLockOwner owner = CDOLockUtil.createLockOwner(transaction); + final boolean mapIDs = transaction.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; + + for (CDOLockState lockState : locksOnNewObjects) + { + Object target = lockState.getLockedObject(); + + if (mapIDs) + { + CDOIDAndBranch idAndBranch = target instanceof CDOIDAndBranch ? (CDOIDAndBranch)target : null; + CDOID id = idAndBranch != null ? ((CDOIDAndBranch)target).getID() : (CDOID)target; + CDOID newID = idMappings.get(id); + CheckUtil.checkNull(newID, "newID"); + + target = idAndBranch != null ? CDOIDUtil.createIDAndBranch(newID, idAndBranch.getBranch()) : newID; + } + + LockState postCommitLockState = null; + for (LockType type : LockType.values()) + { + if (lockState.isLocked(type, owner, false)) + { + List> lockStates = lockManager.lock2(type, transaction, Collections.singleton(target), 0); + postCommitLockState = lockStates.get(0); + } + } + + if (postCommitLockState != null) + { + postCommitLockStates.add(postCommitLockState); + } + } + } + + protected void autoReleaseExplicitLocks() throws InterruptedException + { + List targets = new ArrayList(); + + // Release locks that have been sent from the client. + for (CDOID id : idsToUnlock) + { + Object target = lockManager.getLockKey(id, branch); + targets.add(target); + } + + // Release durable locks that have been acquired on detached objects. + for (CDOID id : detachedObjects) + { + Object target = lockManager.getLockKey(id, branch); + if (lockManager.hasLock(LockType.WRITE, transaction, target)) + { + // We only need to consider detached objects that have been explicitly locked + targets.add(target); + } + } + + try + { + RWOLockManager.setUnlockAll(true); + + List> lockStates = lockManager.unlock2(true, LockType.WRITE, transaction, targets, false); + if (lockStates != null) + { + postCommitLockStates.addAll(lockStates); + } + } + finally + { + RWOLockManager.setUnlockAll(false); + } + } + + protected CDOLockChangeInfo createLockChangeInfo(List> newLockStates) + { + long timeStamp = getTimeStamp(); + CDOLockState[] newStates = Repository.toCDOLockStates(newLockStates); + + return CDOLockUtil.createLockChangeInfo(timeStamp, transaction, branch, Operation.UNLOCK, null, newStates); + } + + protected void addNewPackageUnits(OMMonitor monitor) + { + InternalCDOPackageRegistry repositoryPackageRegistry = repository.getPackageRegistry(false); + synchronized (repositoryPackageRegistry) + { + try + { + monitor.begin(newPackageUnits.length); + for (int i = 0; i < newPackageUnits.length; i++) + { + InternalCDOPackageUnit packageUnit = newPackageUnits[i]; + packageUnit.setState(CDOPackageUnit.State.LOADED); + packageUnit.setPackageRegistry(repositoryPackageRegistry); + repositoryPackageRegistry.putPackageUnit(packageUnit); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + } + + protected void addRevisions(CDORevision[] revisions, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + + for (CDORevision revision : revisions) + { + if (revision != null) + { + revisionManager.addRevision(revision); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected 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(); + } + } + + protected void detachObjects(OMMonitor monitor) + { + int size = detachedObjects.length; + cachedDetachedRevisions = new InternalCDORevision[size]; + + CDOID[] detachedObjects = getDetachedObjects(); + + try + { + monitor.begin(size); + InternalCDORevisionCache cache = repository.getRevisionManager().getCache(); + + 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)cache.getRevision(id, transaction); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected void writeAccessor(OMMonitor monitor) + { + accessor.write(this, monitor); + } + + @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 values() + { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() + { + return "TransactionPackageRegistry"; + } + } + + /** + * @author Eike Stepper + */ + protected static final class RollbackException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + private final byte rollbackReason; + + private final String rollbackMessage; + + public RollbackException(byte rollbackReason, String rollbackMessage) + { + this.rollbackReason = rollbackReason; + this.rollbackMessage = rollbackMessage; + } + + public RollbackException(byte rollbackReason, Throwable cause) + { + super(cause); + this.rollbackReason = rollbackReason; + rollbackMessage = cause.getMessage(); + } + + public byte getRollbackReason() + { + return rollbackReason; + } + + public String getRollbackMessage() + { + return rollbackMessage; + } + } + + /** + * @author Eike Stepper + */ + private final class XRefContext implements QueryXRefsContext + { + private Map> sourceCandidates = new HashMap>(); + + private Set detachedIDs = new HashSet(); + + private Set dirtyIDs = new HashSet(); + + private List result = new ArrayList(); + + 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 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 instanceof EReference && ((EReference)feature).isContainer())) + { + 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 branch; + } + + public Map getTargetObjects() + { + return detachedObjectTypes; + } + + public EReference[] getSourceReferences() + { + return new EReference[0]; + } + + public Map> 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/UnitManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/UnitManager.java new file mode 100644 index 000000000..d7e3e5815 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/UnitManager.java @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2016 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.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.util.CDOException; +import org.eclipse.emf.cdo.server.IStoreAccessor.UnitSupport; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.server.IView; +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.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.ArrayList; +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; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * @author Eike Stepper + */ +public class UnitManager extends Container implements InternalUnitManager +{ + private final InternalRepository repository; + + private final Map units = CDOIDUtil.createMap(); + + private final Map unitInitializers = CDOIDUtil.createMap(); + + private final Set objectAttachers = new HashSet(); + + private final ReentrantReadWriteLock managerLock = new ReentrantReadWriteLock(); + + public UnitManager(InternalRepository repository) + { + this.repository = repository; + } + + public final InternalRepository getRepository() + { + return repository; + } + + public boolean isUnit(CDOID rootID) + { + checkActive(); + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.containsKey(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + checkActive(); + + WriteLock writeLock = managerLock.writeLock(); + UnitInitializer unitInitializer; + boolean hook = false; + + //////////////////////////////////// + // Phase 1: Register (short, locked) + //////////////////////////////////// + + writeLock.lock(); + + try + { + createUnitHook1(); + + // No need to synchronize on units because all other access holds the manager lock. + if (units.containsKey(rootID)) + { + return null; + } + + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializer = unitInitializers.get(rootID); + if (unitInitializer != null) + { + hook = true; + } + else + { + checkNotNested(rootID, view, units.keySet()); + checkNotNested(rootID, view, unitInitializers.keySet()); + + unitInitializer = createUnitInitializer(rootID, view, revisionHandler); + + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializers.put(rootID, unitInitializer); + + // Synchronize on objectAttachers because objectAttacherFinishedCommit() doesn't acquire the manager lock! + synchronized (objectAttachers) + { + for (ObjectAttacher objectAttacher : objectAttachers) + { + List ids = objectAttacher.removeUnmappedRevisionsFor(unitInitializer); + if (!ids.isEmpty()) + { + unitInitializer.addObjectAttacher(objectAttacher, ids); + } + } + } + } + } + finally + { + writeLock.unlock(); + } + + if (hook) + { + return unitInitializer.hook(rootID, view, revisionHandler, monitor); + } + + IUnit unit = null; + + try + { + ///////////////////////////////////////////////////// + // Phase 2: Initialize (potentially long, not locked) + ///////////////////////////////////////////////////// + + unit = unitInitializer.initialize(monitor); + } + finally + { + /////////////////////////////////// + // Phase 3: Publish (short, locked) + /////////////////////////////////// + + try + { + writeLock.lock(); + + try + { + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializers.remove(rootID); + + if (unit != null) + { + // No need to synchronize on units because all other access holds the manager lock. + units.put(rootID, unit); + } + } + finally + { + writeLock.unlock(); + } + } + finally + { + unitInitializer.notifyHookedInitializers(); + } + } + + fireElementAddedEvent(unit); + return unit; + } + + private void checkNotNested(CDOID rootID, IView view, Set unitIDs) + { + InternalCDORevision rootRevision = (InternalCDORevision)view.getRevision(rootID); + CDOID unitID = getUnitOf(rootRevision, view, unitIDs); + if (unitID != null) + { + throw new CDOException("Attempt to nest the new unit " + rootID + " in the existing unit " + unitID); + } + + Set set = Collections.singleton(rootID); + for (CDOID id : unitIDs) + { + InternalCDORevision revision = (InternalCDORevision)view.getRevision(id); + if (getUnitOf(revision, view, set) != null) + { + throw new CDOException("Attempt to nest the existing unit " + id + " in the new unit " + rootID); + } + } + } + + public IUnit getUnit(CDOID rootID) + { + checkActive(); + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.get(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit[] getUnits() + { + checkActive(); + return getElements(); + } + + public IUnit[] getElements() + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.values().toArray(new IUnit[units.size()]); + } + finally + { + readLock.unlock(); + } + } + + public Map getUnitsOf(Set ids, CDORevisionProvider revisionProvider) + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + Map units = new HashMap(); + + Set rootIDs = getRootIDs(); + if (!rootIDs.isEmpty()) + { + for (CDOID id : ids) + { + InternalCDORevision revision = (InternalCDORevision)revisionProvider.getRevision(id); + CDOID rootID = getUnitOf(revision, revisionProvider, rootIDs); + if (rootID != null) + { + units.put(id, rootID); + } + } + } + + return units; + } + finally + { + readLock.unlock(); + } + } + + public List getUnitMoves(InternalCDORevisionDelta[] deltas, CDORevisionProvider before, CDORevisionProvider after) + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + List unitMoves = new ArrayList(); + + Set rootIDs = getRootIDs(); + if (!rootIDs.isEmpty()) + { + for (InternalCDORevisionDelta delta : deltas) + { + CDOID id = delta.getID(); + + for (CDOFeatureDelta featureDelta : delta.getFeatureDeltas()) + { + EStructuralFeature feature = featureDelta.getFeature(); + if (feature == CDOContainerFeatureDelta.CONTAINER_FEATURE) + { + InternalCDORevision beforeRevision = (InternalCDORevision)before.getRevision(id); + CDOID beforeUnit = getUnitOf(beforeRevision, before, rootIDs); + if (beforeUnit != null) + { + InternalCDORevision afterRevision = (InternalCDORevision)after.getRevision(id); + CDOID afterUnit = getUnitOf(afterRevision, after, rootIDs); + if (afterUnit != beforeUnit) + { + unitMoves.add(delta); + } + } + } + } + } + } + + return unitMoves; + } + finally + { + readLock.unlock(); + } + } + + public InternalObjectAttacher attachObjects(InternalCommitContext commitContext) + { + checkActive(); + + long timeStamp = commitContext.getTimeStamp(); + + ObjectAttacher objectAttacher = null; + Map unitMappings = CDOIDUtil.createMap(); + + /////////////////////////////////////////////// + // Phase 1: Analyze new objects (short, locked) + /////////////////////////////////////////////// + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + attachObjectsHook1(); + + Set rootIDs = getRootIDs(); + boolean checkUnits = !rootIDs.isEmpty(); + + List unmappedRevisions = new ArrayList(); + for (InternalCDORevision revision : commitContext.getNewObjects()) + { + if (checkUnits) + { + CDOID rootID = getUnitOf(revision, commitContext, rootIDs); + if (rootID != null) + { + unitMappings.put(revision.getID(), rootID); + continue; + } + } + + unmappedRevisions.add(revision); + } + + if (!unmappedRevisions.isEmpty()) + { + objectAttacher = createObjectAttacher(commitContext, unmappedRevisions); + + // Read lock holders must synchronize modifications of the private collections. + synchronized (objectAttachers) + { + objectAttachers.add(objectAttacher); + } + } + } + finally + { + readLock.unlock(); + } + + ////////////////////////////////////////////////////////// + // Phase 2: Map objects to existing units (long, unlocked) + ////////////////////////////////////////////////////////// + + if (!unitMappings.isEmpty()) + { + mapAttachedObjectsToUnits(commitContext, timeStamp, unitMappings); + } + + return objectAttacher; + } + + /** + * Does not hold any manager lock when called. + */ + public void objectAttacherFinishedCommit(ObjectAttacher objectAttacher) + { + checkActive(); + + synchronized (objectAttachers) + { + objectAttachers.remove(objectAttacher); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getReader(null); + + try + { + List roots = storeAccessor.readUnitRoots(); + for (CDOID root : roots) + { + IUnit unit = createUnit(root); + + // No need to synchronize on units because all other access call checkActive() + units.put(root, unit); + } + } + finally + { + storeAccessor.release(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + // No need to synchronize on units because all other access call checkActive() + units.clear(); + + super.doDeactivate(); + } + + protected Unit createUnit(CDOID root) + { + return new Unit(root); + } + + protected UnitInitializer createUnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + return new UnitInitializer(rootID, view, revisionHandler); + } + + protected ObjectAttacher createObjectAttacher(InternalCommitContext commitContext, List unmappedRevisions) + { + return new ObjectAttacher(commitContext, unmappedRevisions); + } + + protected void mapAttachedObjectsToUnits(InternalCommitContext commitContext, long timeStamp, Map unitMappings) + { + UnitSupport storeAccessor = (UnitSupport)commitContext.getAccessor(); + storeAccessor.writeUnits(unitMappings, timeStamp); + } + + protected void createUnitHook1() + { + } + + protected void attachObjectsHook1() + { + } + + private Set getRootIDs() + { + Set rootIDs = new HashSet(); + + // No need to synchronize on units because all other modifiers hold the manager write lock. + rootIDs.addAll(units.keySet()); + + // No need to synchronize on unitInitializers because all other modifiers hold the manager write lock. + rootIDs.addAll(unitInitializers.keySet()); + + return rootIDs; + } + + private static CDOID getUnitOf(InternalCDORevision revision, CDORevisionProvider revisionProvider, Set rootIDs) + { + if (rootIDs.isEmpty()) + { + return null; + } + + CDOID id = revision.getID(); + if (rootIDs.contains(id)) + { + return id; + } + + CDORevision parentRevision = CDORevisionUtil.getParentRevision(revision, revisionProvider); + if (parentRevision != null) + { + return getUnitOf((InternalCDORevision)parentRevision, revisionProvider, rootIDs); + } + + return null; + } + + /** + * @author Eike Stepper + */ + protected class Unit implements IUnit + { + private final CDOID rootID; + + private final Set views = new HashSet(); + + public Unit(CDOID rootID) + { + this.rootID = rootID; + } + + public IUnitManager getManager() + { + return UnitManager.this; + } + + public CDOID getRootID() + { + return rootID; + } + + public boolean isOpen() + { + synchronized (views) + { + return !views.isEmpty(); + } + } + + public void open(IView view, final CDORevisionHandler revisionHandler, OMMonitor monitor) + { + synchronized (views) + { + views.add(view); + } + + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getReader(null); + + try + { + storeAccessor.readUnit(view, rootID, revisionHandler, monitor); + } + finally + { + storeAccessor.release(); + } + } + + public void close(IView view) + { + synchronized (views) + { + views.remove(view); + } + + ((InternalView)view).closeUnit(rootID); + } + + @Override + public String toString() + { + return "Unit[" + rootID + "]"; + } + + /** + * Does not hold any manager lock when called. + */ + public void initialize(IView view, long timeStamp, CDORevisionHandler revisionHandler, Map> objectAttachers, OMMonitor monitor) + { + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getWriter(null); + + try + { + Set initializedIDs = new HashSet(); + Object initResult = storeAccessor.initUnit(view, rootID, revisionHandler, initializedIDs, timeStamp, monitor); + + List ids = new ArrayList(); + for (Entry> entry : objectAttachers.entrySet()) + { + ObjectAttacher objectAttacher = entry.getKey(); + if (objectAttacher.awaitFinishedCommit()) + { + for (CDOID id : entry.getValue()) + { + if (!initializedIDs.contains(id)) + { + ids.add(id); + } + } + } + } + + storeAccessor.finishUnit(view, rootID, revisionHandler, timeStamp, initResult, ids); + } + finally + { + storeAccessor.release(); + } + } + } + + /** + * @author Eike Stepper + */ + protected class UnitInitializer implements CDORevisionHandler + { + private final long timeStamp = repository.getTimeStamp(); + + private final Map> concurrentObjectAttachers = new HashMap>(); + + private final CountDownLatch unitInitialized = new CountDownLatch(1); + + private final CDOID rootID; + + private final IView view; + + private final CDORevisionHandler revisionHandler; + + private final List hookedRevisionHandlers = new CopyOnWriteArrayList(); + + private volatile boolean hasHookedRevisionHandlers; + + private Unit unit; + + public UnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + this.rootID = rootID; + this.view = view; + this.revisionHandler = revisionHandler; + } + + public CDOID getRootID() + { + return rootID; + } + + /** + * Does not hold any manager lock when called. + */ + public IUnit initialize(OMMonitor monitor) + { + unit = new Unit(rootID); + unit.initialize(view, timeStamp, revisionHandler, concurrentObjectAttachers, monitor); + return unit; + } + + /** + * Does not hold any manager lock when called. + */ + public IUnit hook(CDOID rootID, IView view, final CDORevisionHandler revisionHandler, OMMonitor monitor) + { + final Set ids = new HashSet(); + hookedRevisionHandlers.add(new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + ids.add(revision.getID()); + return revisionHandler.handleRevision(revision); + } + }); + + // It's okay to do this unsynchronized. The worst thing that could happen is that the hooked revision handler is + // missed a few times during UnitInitializer.handleRevision(), but that's okay because it probably missed many + // revisions already and therefore performs an openUnit() subsequently, anyways. After all, hooked revision + // handlers, + // i.e., concurrent createUnit() calls for the same unit, are extremely rare. + hasHookedRevisionHandlers = true; + + monitor.begin(2); + + try + { + Async async = null; + + try + { + async = monitor.forkAsync(); + + // Now wait for the main revision handler to finish. + while (!unitInitialized.await(100, TimeUnit.MILLISECONDS)) + { + monitor.checkCanceled(); + } + } + catch (InterruptedException ex) + { + return null; + } + finally + { + if (async != null) + { + async.stop(); + } + } + + // Now send the missed revisions. + unit.open(view, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + if (ids.contains(revision.getID())) + { + // This revision has already been sent. Skip to the next one. + return true; + } + + return revisionHandler.handleRevision(revision); + } + }, monitor.fork()); + + return unit; + } + finally + { + monitor.done(); + } + } + + /** + * Does not hold any manager lock when called. + */ + public void notifyHookedInitializers() + { + unitInitialized.countDown(); + } + + public boolean handleRevision(CDORevision revision) + { + if (revisionHandler.handleRevision(revision)) + { + if (hasHookedRevisionHandlers) + { + for (CDORevisionHandler hookedRevisionHandler : hookedRevisionHandlers) + { + hookedRevisionHandler.handleRevision(revision); + } + } + + return true; + } + + return false; + } + + /** + * Holds the manager write lock when called. + */ + public void addObjectAttacher(ObjectAttacher objectAttacher, List ids) + { + concurrentObjectAttachers.put(objectAttacher, ids); + } + } + + /** + * @author Eike Stepper + */ + protected class ObjectAttacher implements InternalObjectAttacher + { + private final InternalCommitContext commitContext; + + private final List unmappedRevisions; + + private final CountDownLatch commitFinished = new CountDownLatch(1); + + private boolean commitSucceeded; + + public ObjectAttacher(InternalCommitContext commitContext, List unmappedRevisions) + { + this.commitContext = commitContext; + this.unmappedRevisions = unmappedRevisions; + } + + /** + * Does not hold any manager lock when called. + */ + public void finishedCommit(boolean success) + { + objectAttacherFinishedCommit(this); + + commitSucceeded = success; + commitFinished.countDown(); + } + + /** + * Holds the manager write lock when called. + */ + public List removeUnmappedRevisionsFor(UnitInitializer unitInitializer) + { + List ids = new ArrayList(); + + Set rootIDs = Collections.singleton(unitInitializer.getRootID()); + for (Iterator it = unmappedRevisions.iterator(); it.hasNext();) + { + InternalCDORevision revision = it.next(); + if (getUnitOf(revision, commitContext, rootIDs) != null) + { + ids.add(revision.getID()); + it.remove(); + } + } + + return ids; + } + + public boolean awaitFinishedCommit() + { + try + { + commitFinished.await(); + } + catch (InterruptedException ex) + { + return false; + } + + return commitSucceeded; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/View.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/View.java new file mode 100644 index 000000000..54baac43e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/View.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 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.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants.UnitOpcode; +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.CDORevisionDelta; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; +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.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.spi.server.InternalView; + +import org.eclipse.net4j.util.AdapterUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.options.IOptionsContainer; +import org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +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 CDOBranchPoint normalizedBranchPoint; + + private String durableLockingID; + + private final InternalRepository repository; + + private final Set changeSubscriptionIDs = new HashSet(); + + private final Set openUnitRoots = new HashSet(); + + private boolean lockNotificationsEnabled; + + private boolean closed; + + private final IRegistry properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + + /** + * @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 getSessionID() + { + return session.getSessionID(); + } + + public int getViewID() + { + return viewID; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public boolean isReadOnly() + { + return true; + } + + public boolean isHistorical() + { + return branchPoint.getTimeStamp() != CDOBranchPoint.UNSPECIFIED_DATE; + } + + public boolean isDurableView() + { + return durableLockingID != null; + } + + public String getDurableLockingID() + { + return durableLockingID; + } + + /** + * @since 2.0 + */ + public InternalRepository getRepository() + { + return repository; + } + + public InternalCDORevision getRevision(CDOID id) + { + CDORevisionManager revisionManager = repository.getRevisionManager(); + return (InternalCDORevision)revisionManager.getRevision(id, normalizedBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + } + + private List getRevisions(List ids) + { + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + return revisionManager.getRevisions(ids, normalizedBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + } + + public void changeTarget(CDOBranchPoint branchPoint, List invalidObjects, List allChangedObjects, List allDetachedObjects) + { + List oldRevisions = getRevisions(invalidObjects); + setBranchPoint(branchPoint); + List newRevisions = getRevisions(invalidObjects); + + Iterator it = newRevisions.iterator(); + for (CDORevision oldRevision : oldRevisions) + { + CDORevision newRevision = it.next(); + if (newRevision == null) + { + allDetachedObjects.add(oldRevision.getID()); + } + else if (newRevision != oldRevision) + { + // Fix for Bug 369646: ensure that revisions are fully loaded + repository.ensureChunks((InternalCDORevision)newRevision, CDORevision.UNCHUNKED); + repository.ensureChunks((InternalCDORevision)oldRevision, CDORevision.UNCHUNKED); + + CDORevisionDelta delta = newRevision.compare(oldRevision); + allChangedObjects.add(delta); + } + } + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + checkOpen(); + validateTimeStamp(branchPoint.getTimeStamp()); + + InternalCDOBranchManager branchManager = getSession().getManager().getRepository().getBranchManager(); + this.branchPoint = CDOBranchUtil.adjustBranchPoint(branchPoint, branchManager); + normalizedBranchPoint = CDOBranchUtil.normalizeBranchPoint(this.branchPoint); + } + + protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + if (timeStamp != UNSPECIFIED_DATE) + { + repository.validateTimeStamp(timeStamp); + } + } + + public void setDurableLockingID(String durableLockingID) + { + this.durableLockingID = durableLockingID; + } + + public boolean openUnit(CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + IUnitManager unitManager = repository.getUnitManager(); + IUnit unit = unitManager.getUnit(rootID); + + switch (opcode) + { + case CREATE: + case CREATE_AND_OPEN: + if (unit != null) + { + return false; + } + + unit = unitManager.createUnit(rootID, this, revisionHandler, monitor); + break; + + case OPEN: + case OPEN_DEMAND_CREATE: + if (unit == null) + { + if (opcode.isCreate()) + { + unit = unitManager.createUnit(rootID, this, revisionHandler, monitor); + break; + } + + return false; + } + + unit.open(this, revisionHandler, monitor); + break; + } + + if (opcode.isOpen()) + { + openUnitRoots.add(rootID); + } + + return true; + } + + public void closeUnit(CDOID rootID) + { + openUnitRoots.remove(rootID); + } + + public boolean isInOpenUnit(CDOID id) + { + if (openUnitRoots.isEmpty()) + { + return false; + } + + if (openUnitRoots.contains(id)) + { + return true; + } + + InternalCDORevision revision = getRevision(id); + if (revision != null) + { + CDOID parentID = revision.getResourceID(); + if (parentID == id) + { + // This must be the root resource; break the recursion. + return false; + } + + if (CDOIDUtil.isNull(parentID)) + { + parentID = (CDOID)revision.getContainerID(); + } + + return isInOpenUnit(parentID); + } + + return false; + } + + /** + * @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) + { + if (isClosed()) + { + return false; + } + + if (changeSubscriptionIDs.contains(id)) + { + return true; + } + + return false; + } + + /** + * @since 2.0 + */ + public synchronized void clearChangeSubscription() + { + checkOpen(); + changeSubscriptionIDs.clear(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) + { + return AdapterUtil.adapt(this, adapter, false); + } + + @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(); + openUnitRoots.clear(); + closed = true; + } + + /** + * @since 2.0 + */ + public boolean isClosed() + { + return closed; + } + + protected void checkOpen() + { + if (isClosed()) + { + throw new IllegalStateException("View closed"); //$NON-NLS-1$ + } + } + + public IOptionsContainer getContainer() + { + return this; + } + + public Options options() + { + return this; + } + + public final IRegistry properties() + { + return properties; + } + + public boolean isLockNotificationEnabled() + { + return lockNotificationsEnabled; + } + + public void setLockNotificationEnabled(boolean enable) + { + lockNotificationsEnabled = enable; + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java new file mode 100644 index 000000000..9ce4e0148 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2008-2012 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 state = new ConcurrentValue(CommitState.STARTING); + + public XATransactionCommitContext(InternalTransaction transaction) + { + super(transaction); + } + + public ConcurrentValue 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java new file mode 100644 index 000000000..6e7b99f55 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2010-2016 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.CDOModelUtil; +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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +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.server.StoreThreadLocal.NoSessionRegisteredException; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision; +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.EcorePackage; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * @author Eike Stepper + */ +public class XRefsQueryHandler implements IQueryHandler +{ + public XRefsQueryHandler() + { + } + + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + try + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + + CDOBranchPoint branchPoint = context; + CDOBranch branch = branchPoint.getBranch(); + + if (branch.isMainBranch()) + { + QueryContext xrefsContext = new QueryContext(info, context); + accessor.queryXRefs(xrefsContext); + } + else + { + QueryContext xrefsContext = new QueryContextBranching(info, context); + accessor.queryXRefs(xrefsContext); + + int maxResults = info.getMaxResults(); + while (!branch.isMainBranch() && (maxResults == CDOQueryInfo.UNLIMITED_RESULTS || context.getResultCount() < maxResults)) + { + branchPoint = branch.getBase(); + branch = branchPoint.getBranch(); + + xrefsContext.setBranchPoint(branchPoint); + accessor.queryXRefs(xrefsContext); + } + } + } + catch (NoSessionRegisteredException ex) + { + // View has been closed - do nothing + } + } + + public static void collectSourceCandidates(IView view, Collection concreteTypes, Map> 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> 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 concreteTypes, Map> 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 concreteTypes, Map> sourceCandidates) + { + if (!eClass.isAbstract() && !eClass.isInterface()) + { + for (EReference eReference : CDOModelUtil.getClassInfo(eClass).getAllPersistentReferences()) + { + collectSourceCandidates(eClass, eReference, concreteTypes, sourceCandidates); + } + } + } + + public static void collectSourceCandidates(EReference eReference, Collection concreteTypes, Map> sourceCandidates, + CDOPackageRegistry packageRegistry) + { + EClass rootClass = eReference.getEContainingClass(); + collectSourceCandidates(rootClass, eReference, concreteTypes, sourceCandidates); + + Collection 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 concreteTypes, + Map> sourceCandidates) + { + if (eClass.isAbstract()) + { + return; + } + + if (eClass.isInterface()) + { + return; + } + + if (eReference.isContainer()) + { + return; + } + + if (eReference.isContainment() && !eReference.isResolveProxies()) + { + return; + } + + if (canReference(eReference.getEReferenceType(), concreteTypes)) + { + List list = sourceCandidates.get(eClass); + if (list == null) + { + list = new ArrayList(); + sourceCandidates.put(eClass, list); + } + + list.add(eReference); + } + } + + private static boolean canReference(EClass declaredType, Collection concreteTypes) + { + for (EClass concreteType : concreteTypes) + { + if (declaredType == EcorePackage.Literals.EOBJECT || declaredType.isSuperTypeOf(concreteType)) + { + return true; + } + } + + return false; + } + + /** + * @author Eike Stepper + * @since 3.0 + */ + private static class QueryContext implements IStoreAccessor.QueryXRefsContext + { + private CDOQueryInfo info; + + private IQueryContext context; + + private CDOBranchPoint branchPoint; + + private Map targetObjects; + + private Map> sourceCandidates; + + private EReference[] sourceReferences; + + public QueryContext(CDOQueryInfo info, IQueryContext context) + { + this.info = info; + this.context = context; + branchPoint = context; + } + + public final void setBranchPoint(CDOBranchPoint branchPoint) + { + this.branchPoint = branchPoint; + } + + public final CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public final long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public final Map getTargetObjects() + { + if (targetObjects == null) + { + IRepository repository = getRepository(); + IStore store = repository.getStore(); + CDOPackageRegistry packageRegistry = repository.getPackageRegistry(); + + targetObjects = CDOIDUtil.createMap(); + StringTokenizer tokenizer = new StringTokenizer(info.getQueryString(), "|"); + while (tokenizer.hasMoreTokens()) + { + String val = tokenizer.nextToken(); + + CDOID id; + if (val.startsWith("e")) + { + id = CDOIDUtil.createExternal(val.substring(1)); + } + else + { + id = store.createObjectID(val.substring(1)); + } + + 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 final EReference[] getSourceReferences() + { + if (sourceReferences == null) + { + sourceReferences = parseSourceReferences(); + } + + return sourceReferences; + } + + public final Map> getSourceCandidates() + { + if (sourceCandidates == null) + { + sourceCandidates = new HashMap>(); + Collection concreteTypes = getTargetObjects().values(); + EReference[] sourceReferences = getSourceReferences(); + + if (sourceReferences.length != 0) + { + InternalRepository repository = (InternalRepository)getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (EReference eReference : sourceReferences) + { + collectSourceCandidates(eReference, concreteTypes, sourceCandidates, packageRegistry); + } + } + else + { + collectSourceCandidates(context.getView(), concreteTypes, sourceCandidates); + } + } + + return sourceCandidates; + } + + public final int getMaxResults() + { + return info.getMaxResults(); + } + + public final IRepository getRepository() + { + return context.getView().getRepository(); + } + + public final boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex) + { + if (CDOIDUtil.isNull(targetID)) + { + return true; + } + + if (isIgnoredObject(sourceID)) + { + return true; + } + + return context.addResult(new CDOIDReference(targetID, sourceID, sourceReference, sourceIndex)); + } + + protected boolean isIgnoredObject(CDOID sourceID) + { + return false; + } + + private EReference[] parseSourceReferences() + { + List result = new ArrayList(); + CDOPackageRegistry packageRegistry = 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()]); + } + } + + /** + * @author Eike Stepper + */ + private static final class QueryContextBranching extends QueryContext + { + private final CDOBranchPoint originalBranchPoint; + + private final Set ignoredObjects = new HashSet(); + + public QueryContextBranching(CDOQueryInfo info, IQueryContext context) + { + super(info, context); + originalBranchPoint = CDOBranchUtil.copyBranchPoint(context); + } + + @Override + protected boolean isIgnoredObject(CDOID sourceID) + { + if (!ignoredObjects.add(sourceID)) + { + return true; + } + + if (isDetachedObject(sourceID)) + { + ignoredObjects.add(sourceID); + return true; + } + + return false; + } + + private boolean isDetachedObject(CDOID sourceID) + { + if (getBranch() == originalBranchPoint.getBranch()) + { + return false; + } + + SyntheticCDORevision[] synthetics = { null }; + + InternalCDORevisionManager revisionManager = (InternalCDORevisionManager)getRepository().getRevisionManager(); + revisionManager.getRevision(sourceID, originalBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true, synthetics); + + return synthetics[0] instanceof DetachedCDORevision; + } + } + + /** + * @author Eike Stepper + */ + public static class Factory extends QueryHandlerFactory + { + public Factory() + { + super(CDOProtocolConstants.QUERY_LANGUAGE_XREFS); + } + + @Override + public XRefsQueryHandler create(String description) throws ProductCreationException + { + return new XRefsQueryHandler(); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java new file mode 100644 index 000000000..3b1495ce9 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2010-2013, 2016 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 + * Christian W. Damus (CEA LIST) - bug 420540 + */ +package org.eclipse.emf.cdo.internal.server.bundle; + +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +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.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.CDOCommand; +import org.eclipse.emf.cdo.spi.server.CDOCommand.CommandException; +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.container.IPluginContainer; +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; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public class CDOCommandProvider implements CommandProvider +{ + private static final String NEW_LINE = "\r\n"; //$NON-NLS-1$ + + private static final CDOCommand list = new CDOCommand("list", "list all active repositories") + { + @Override + public void execute(String[] args) throws Exception + { + IManagedContainer container = CDOServerApplication.getContainer(); + for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP)) + { + if (element instanceof InternalRepository) + { + InternalRepository repository = (InternalRepository)element; + println(repository.getName()); + } + } + } + }; + + private static final CDOCommand start = new CDOCommand("start", "start repositories from a config file", CDOCommand.parameter("config-file")) + { + @Override + public void execute(String[] args) throws Exception + { + String configFile = args[0]; + + IManagedContainer container = CDOServerApplication.getContainer(); + RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container); + IRepository[] repositories = repositoryConfigurator.configure(new File(configFile)); + + println("Repositories started:"); + if (repositories != null) + { + for (IRepository repository : repositories) + { + println(repository.getName()); + } + } + } + }; + + private static final CDOCommand stop = new CDOCommand.WithRepository("stop", "stop a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + LifecycleUtil.deactivate(repository); + println("Repository stopped"); + } + }; + + private static final CDOCommand exportXML = new CDOCommand.WithRepository("export", "export the contents of a repository to an XML file", + CDOCommand.parameter("export-file")) + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + String exportFile = args[0]; + OutputStream out = null; + + try + { + out = new FileOutputStream(exportFile); + + CDOServerExporter.XML exporter = new CDOServerExporter.XML(repository); + exporter.exportRepository(out); + println("Repository exported"); + } + finally + { + IOUtil.close(out); + } + } + }; + + private static final CDOCommand importXML = new CDOCommand.WithRepository("import", "import the contents of a repository from an XML file", + CDOCommand.parameter("import-file")) + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + String importFile = args[0]; + 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); + + println("Repository imported"); + } + finally + { + IOUtil.close(in); + } + } + }; + + private static final CDOCommand branches = new CDOCommand.WithRepository("branches", "dump the branches of a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + branches(repository.getBranchManager().getMainBranch(), ""); + } + + private void branches(InternalCDOBranch branch, String prefix) + { + println(prefix + branch); + prefix += CDOCommand.INDENT; + for (InternalCDOBranch child : branch.getBranches()) + { + branches(child, prefix); + } + } + }; + + private static final CDOCommand packages = new CDOCommand.WithRepository("packages", "dump the packages of a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (InternalCDOPackageUnit packageUnit : packageRegistry.getPackageUnits()) + { + println(packageUnit); + for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos()) + { + println(CDOCommand.INDENT + packageInfo); + } + } + } + }; + + private static final CDOCommand sessions = new CDOCommand.WithRepository("sessions", "dump the sessions of a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + InternalSessionManager sessionManager = repository.getSessionManager(); + for (InternalSession session : sessionManager.getSessions()) + { + println(session); + for (InternalView view : session.getViews()) + { + println(INDENT + view); + } + } + } + }; + + private static final CDOCommand locks = new CDOCommand.WithAccessor("locks", "dump the locks of a repository", CDOCommand.optional("username-prefix")) + { + @Override + public void execute(InternalRepository repository, IStoreAccessor accessor, String[] args) throws Exception + { + String usernamePrefix = args[0]; + + repository.getLockingManager().getLockAreas(usernamePrefix, new IDurableLockingManager.LockArea.Handler() + { + public boolean handleLockArea(IDurableLockingManager.LockArea area) + { + println(area.getDurableLockingID()); + println(CDOCommand.INDENT + "userID = " + area.getUserID()); + println(CDOCommand.INDENT + "branch = " + area.getBranch()); + println(CDOCommand.INDENT + "timeStamp = " + CDOCommonUtil.formatTimeStamp(area.getTimeStamp())); + println(CDOCommand.INDENT + "readOnly = " + area.isReadOnly()); + println(CDOCommand.INDENT + "locks = " + area.getLocks()); + return true; + } + }); + } + }; + + private static final CDOCommand deletelocks = new CDOCommand.WithAccessor("deletelocks", "delete a durable locking area of a repository", + CDOCommand.parameter("area-id")) + { + @Override + public void execute(InternalRepository repository, IStoreAccessor accessor, String[] args) throws Exception + { + String areaID = args[0]; + repository.getLockingManager().deleteLockArea(areaID); + } + }; + + public CDOCommandProvider(BundleContext bundleContext) + { + bundleContext.registerService(CommandProvider.class.getName(), this, null); + } + + public Object _cdo(CommandInterpreter interpreter) + { + try + { + Map commands = getCommands(); + String cmd = interpreter.nextArgument(); + + CDOCommand command = commands.get(cmd); + if (command != null) + { + try + { + command.setInterpreter(interpreter); + command.execute(); + return null; + } + finally + { + command.setInterpreter(null); + } + } + + interpreter.println(getHelp()); + } + catch (CommandException ex) + { + interpreter.println(ex.getMessage()); + } + catch (Exception ex) + { + interpreter.printStackTrace(ex); + } + + return null; + } + + public String getHelp() + { + StringBuilder builder = new StringBuilder(); + + try + { + builder.append("---CDO commands---" + NEW_LINE); + + List commands = new ArrayList(getCommands().values()); + Collections.sort(commands, new Comparator() + { + public int compare(CDOCommand o1, CDOCommand o2) + { + return o1.getName().compareTo(o2.getName()); + } + }); + + for (CDOCommand command : commands) + { + try + { + builder.append(CDOCommand.INDENT); + builder.append(command.getSyntax()); + builder.append(" - "); + builder.append(command.getDescription()); + builder.append(NEW_LINE); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + + return builder.toString(); + } + + public synchronized Map getCommands() + { + Map commands = new HashMap(); + addCommand(commands, list); + addCommand(commands, start); + addCommand(commands, stop); + addCommand(commands, exportXML); + addCommand(commands, importXML); + addCommand(commands, branches); + addCommand(commands, deletelocks); + addCommand(commands, locks); + addCommand(commands, packages); + addCommand(commands, sessions); + + try + { + for (String name : IPluginContainer.INSTANCE.getFactoryTypes(CDOCommand.PRODUCT_GROUP)) + { + try + { + CDOCommand command = createCommand(name); + addCommand(commands, command); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + + return commands; + } + + protected CDOCommand createCommand(String name) + { + return (CDOCommand)IPluginContainer.INSTANCE.getElement(CDOCommand.PRODUCT_GROUP, name, null); + } + + private void addCommand(Map commands, CDOCommand command) + { + commands.put(command.getName(), command); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java new file mode 100644 index 000000000..1ffaf5358 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2007-2012 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 extensions = new ArrayList(); + + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/OM.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/OM.java new file mode 100644 index 000000000..0211a8b5d --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/bundle/OM.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2007, 2008, 2010-2012, 2015 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 Operations & Maintenance 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java new file mode 100644 index 000000000..88e5e8ff0 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2009-2013, 2015 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 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +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 + * @deprecated Not yet supported. + */ +@Deprecated +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(); + } + + /** + * Changing the user's password in an embedded client session is not supported: + * it must be done interactively with the user in a client session. + * + * @since 4.3 + */ + public void changeCredentials() + { + throw new UnsupportedOperationException(); + } + + /** + * Resetting a user's password in an embedded client session is not supported: + * it must be done interactively in a client session. + * + * @since 4.3 + */ + public void resetCredentials(String userID) + { + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java new file mode 100644 index 000000000..771988050 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 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 org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.PlatformObject; + +import java.util.Set; + +/** + * @author Eike Stepper + * @deprecated Not yet supported. + */ +@Deprecated +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 extends PlatformObject implements org.eclipse.emf.cdo.session.CDORepositoryInfo + { + private EmbeddedClientSession session; + + public RepositoryInfo(EmbeddedClientSession session) + { + this.session = session; + } + + public EmbeddedClientSession getSession() + { + return 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 isAuthenticating() + { + return session.getRepository().isAuthenticating(); + } + + public boolean isSupportingAudits() + { + return session.getRepository().isSupportingAudits(); + } + + public boolean isSupportingBranches() + { + return session.getRepository().isSupportingBranches(); + } + + public boolean isSupportingUnits() + { + return session.getRepository().isSupportingUnits(); + } + + @Deprecated + public boolean isSupportingEcore() + { + return session.getRepository().isSupportingEcore(); + } + + public boolean isSerializingCommits() + { + return session.getRepository().isSerializingCommits(); + } + + public boolean isEnsuringReferentialIntegrity() + { + return session.getRepository().isEnsuringReferentialIntegrity(); + } + + public IDGenerationLocation getIDGenerationLocation() + { + return session.getRepository().getIDGenerationLocation(); + } + + public CommitInfoStorage getCommitInfoStorage() + { + return session.getRepository().getCommitInfoStorage(); + } + + public String getStoreType() + { + return session.getRepository().getStoreType(); + } + + public Set getObjectIDTypes() + { + return session.getRepository().getObjectIDTypes(); + } + + public boolean waitWhileInitial(IProgressMonitor monitor) + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java new file mode 100644 index 000000000..4ef5fe8ae --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2009-2016 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 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +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.CDOCommitInfo; +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.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.security.CDOPermission; +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.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext; +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 + * @deprecated Not yet supported. + */ +@Deprecated +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 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(); + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + 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 void openedSession() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public CDOLockState[] getLockStates(int viewID, Collection ids) + { + throw new UnsupportedOperationException(); + } + + public CDOLockState[] getLockStates(int viewID, Collection ids, int depth) + { + 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 CDOBranchPointRange loadObjectLifetime(CDOID id, CDOBranchPoint branchPoint) + { + throw new UnsupportedOperationException(); + } + + public List loadRevisions(List infos, CDOBranchPoint branchPoint, int referenceChunk, int prefetchDepth) + { + try + { + InternalSession session = serverSessionProtocol.getSession(); + StoreThreadLocal.setSession(session); + + List ids = new ArrayList(infos.size()); + for (RevisionInfo info : infos) + { + ids.add(info.getID()); + } + + // @SuppressWarnings("unchecked") + // List revisions = (List)(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> 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 invalidObjects, List allChangedObjects, + List 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 ids = new ArrayList(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 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 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 viewedRevisions, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) + throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.1 + */ + public LockObjectsResult lockObjects2(List keys, int viewID, CDOBranch viewedBranch, LockType type, boolean recursive, long timeout) + throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void unlockObjects(CDOView view, Collection ids, LockType type) + { + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult unlockObjects2(CDOView view, Collection ids, LockType type, boolean recursive) + { + throw new UnsupportedOperationException(); + } + + public LockObjectsResult delegateLockObjects(String lockAreaID, List keys, CDOBranch viewedBranch, LockType type, boolean recursive, + long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection ids, LockType type, boolean recursive) + { + throw new UnsupportedOperationException(); + } + + public String changeLockArea(CDOView view, boolean create) + { + throw new UnsupportedOperationException(); + } + + public List queryLobs(Set 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(); + } + + @Deprecated + public CommitTransactionResult commitTransaction(int transactionID, String comment, boolean releaseLocks, CDOIDProvider idProvider, CDOCommitData commitData, + Collection> lobs, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitTransaction(InternalCDOCommitContext context, OMMonitor monitor) + { + monitor.begin(2); + boolean success = false; + InternalCommitContext serverCommitContext = null; + CommitTransactionResult result = null; + + try + { + InternalCDOTransaction transaction = context.getTransaction(); + CDOCommitData commitData = context.getCommitData(); + + int transactionID = transaction.getViewID(); + InternalTransaction serverTransaction = (InternalTransaction)serverSessionProtocol.getSession().getView(transactionID); + serverCommitContext = serverTransaction.createCommitContext(); + serverCommitContext.preWrite(); + serverCommitContext.setAutoReleaseLocksEnabled(transaction.options().isAutoReleaseLocksEnabled()); + + List npu = commitData.getNewPackageUnits(); + serverCommitContext.setNewPackageUnits(npu.toArray(new InternalCDOPackageUnit[npu.size()])); + + List 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 rd = commitData.getChangedObjects(); + serverCommitContext.setDirtyObjectDeltas(rd.toArray(new InternalCDORevisionDelta[rd.size()])); + + List 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 entry : serverCommitContext.getIDMappings().entrySet()) + // { + // result.addIDMapping(entry.getKey(), entry.getValue()); + // } + } + finally + { + if (serverCommitContext != null) + { + serverCommitContext.postCommit(success); + } + + monitor.done(); + } + + return result; + } + + @Deprecated + public CommitTransactionResult commitDelegation(CDOBranch branch, String userID, String comment, CDOCommitData commitData, + Map detachedObjectTypes, Collection> lobs, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitDelegation(InternalCDOCommitContext context, 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 CDOCommitInfo resetTransaction(int transactionID, int commitNumber) + { + throw new UnsupportedOperationException(); + } + + public List getRemoteSessions(InternalCDORemoteSessionManager manager, boolean subscribe) + { + throw new UnsupportedOperationException(); + } + + public Set sendRemoteMessage(CDORemoteSessionMessage message, List 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 loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, CDORevisionAvailabilityInfo targetBaseInfo, + CDORevisionAvailabilityInfo sourceBaseInfo) + { + throw new UnsupportedOperationException(); + } + + public MergeDataResult loadMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo) + { + throw new UnsupportedOperationException(); + } + + public Map loadPermissions(InternalCDORevision[] revisions) + { + 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; + } + + /** + * Change of credentials may not be requested by embedded client sessions, only by + * interactive user client sessions. + */ + public void requestChangeCredentials() + { + throw new UnsupportedOperationException(); + } + + /** + * Reset of credentials may not be requested by embedded client sessions, only by + * interactive client sessions. + */ + public void requestResetCredentials(String userID) + { + throw new UnsupportedOperationException(); + } + + public boolean requestUnit(int viewID, CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java new file mode 100644 index 000000000..25aac1dba --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2009-2013, 2016 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 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +package org.eclipse.emf.cdo.internal.server.embedded; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.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; +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; +import org.eclipse.net4j.util.security.DiffieHellman.Client.Response; +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; + +/** + * @author Eike Stepper + * @deprecated Not yet supported. + */ +@Deprecated +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; + } + + @Deprecated + public org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult sendAuthenticationChallenge(byte[] randomToken) throws Exception + { + return clientSessionProtocol.handleAuthenticationChallenge(randomToken); + } + + public Response sendAuthenticationChallenge(Challenge challenge) throws Exception + { + throw new UnsupportedOperationException(); + } + + public Response sendCredentialsChallenge(Challenge challenge, String userID, CredentialsUpdateOperation operation) throws Exception + { + throw new UnsupportedOperationException(); + } + + 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) + { + throw new UnsupportedOperationException(); + } + + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleCommitNotification(commitInfo); + } + + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo info) throws Exception + { + throw new UnsupportedOperationException(); + } + + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java new file mode 100644 index 000000000..4d21fb8fc --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java @@ -0,0 +1,1442 @@ +/* + * Copyright (c) 2009-2016 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.common.util.CDOTimeProvider; +import org.eclipse.emf.cdo.internal.common.revision.CDORevisionKeyImpl; +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.BranchLoader3; +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.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.emf.internal.cdo.transaction.CDOTransactionImpl; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.Predicate; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.collection.AbstractFilteredIterator; +import org.eclipse.net4j.util.collection.BidirectionalIterator; +import org.eclipse.net4j.util.collection.LimitedIterator; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.collection.PredicateIterator; +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.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader3, DurableLocking2 +{ + public static final String TYPE = "mem"; //$NON-NLS-1$ + + private long creationTime; + + private Map properties = new HashMap(); + + private Map branchInfos = new HashMap(); + + private int lastBranchID; + + private int lastLocalBranchID; + + private Map> revisions = new HashMap>(); + + private List commitInfos = new ArrayList(); + + private Map objectTypes = CDOIDUtil.createMap(); + + private Map lockAreas = new HashMap(); + + private Map lobs = new HashMap(); + + 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 + @Deprecated + public boolean isLocal(CDOID id) + { + throw new UnsupportedOperationException(); + } + + @Override + public void ensureLastObjectID(CDOID id) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + return; + } + + super.ensureLastObjectID(id); + } + + public synchronized Map getPersistentProperties(Set names) + { + if (names == null || names.isEmpty()) + { + return new HashMap(properties); + } + + Map result = new HashMap(); + for (String name : names) + { + String value = properties.get(name); + if (value != null) + { + result.put(name, value); + } + } + + return result; + } + + public synchronized void setPersistentProperties(Map properties) + { + this.properties.putAll(properties); + } + + public synchronized void removePersistentProperties(Set names) + { + for (String name : names) + { + properties.remove(name); + } + } + + public synchronized Pair createBranch(int branchID, BranchInfo branchInfo) + { + if (branchID == NEW_BRANCH) + { + branchID = ++lastBranchID; + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = --lastLocalBranchID; + } + + branchInfos.put(branchID, branchInfo); + return Pair.create(branchID, branchInfo.getBaseTimeStamp()); + } + + public synchronized BranchInfo loadBranch(int branchID) + { + return branchInfos.get(branchID); + } + + public synchronized SubBranchInfo[] loadSubBranches(int branchID) + { + List result = new ArrayList(); + for (Entry 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 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; + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + BranchInfo branchInfo = branchInfos.get(branchID); + if (branchInfo != null) + { + branchInfo.setName(newName); + } + } + + public synchronized void loadCommitInfos(final CDOBranch branch, long startTime, final long endTime, CDOCommitInfoHandler handler) + { + InternalCDOCommitInfoManager manager = getRepository().getCommitInfoManager(); + + // Optimize the getCommitInfo(timeStamp) case. + if (startTime == endTime && startTime > CDOBranchPoint.UNSPECIFIED_DATE) + { + int index = Collections.binarySearch(commitInfos, new CommitInfoKey(startTime)); + if (index >= 0) + { + // Found. + CommitInfo commitInfo = commitInfos.get(index); + commitInfo.handle(manager, handler); + } + + return; + } + + boolean counting = endTime < CDOBranchPoint.UNSPECIFIED_DATE; + int count = CDOCommitInfoUtil.decodeCount(endTime); + boolean forward = counting ? count > 0 : endTime > startTime; + + int startIndex; + if (startTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + startIndex = commitInfos.size(); + } + else + { + startIndex = Collections.binarySearch(commitInfos, new CommitInfoKey(startTime)); + if (startIndex >= 0) + { + // Found. + if (!forward) + { + ++startIndex; + } + } + else + { + // Not found. + startIndex = -(startIndex + 1); // Insertion point. + if (forward) + { + --startIndex; + } + } + } + + ListIterator listIterator = commitInfos.listIterator(startIndex); + Iterator iterator = new BidirectionalIterator(listIterator, !forward); + + if (branch != null) + { + iterator = new AbstractFilteredIterator(iterator) + { + @Override + protected boolean isValid(CommitInfo element) + { + return element.getBranch() == branch; + } + }; + } + + if (counting) + { + iterator = new LimitedIterator(iterator, Math.abs(count)); + } + else if (startTime != CDOBranchPoint.UNSPECIFIED_DATE || endTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + Predicate predicate = forward ? new UpTo(endTime) : new DownTo(endTime); + iterator = new PredicateIterator(iterator, predicate); + } + + while (iterator.hasNext()) + { + CommitInfo commitInfo = iterator.next(); + commitInfo.handle(manager, handler); + } + } + + public synchronized Set readChangeSet(CDOChangeSetSegment[] segments) + { + Set ids = new HashSet(); + for (CDOChangeSetSegment segment : segments) + { + for (List list : revisions.values()) + { + readChangeSet(segment, list, ids); + } + } + + return ids; + } + + private void readChangeSet(CDOChangeSetSegment segment, List list, Set 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 (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 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 && 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 list : revisions.values()) + { + enforceListLimit(list); + } + } + + this.listLimit = listLimit; + } + + /** + * @since 2.0 + */ + public synchronized List getCurrentRevisions() + { + ArrayList simpleRevisions = new ArrayList(); + Iterator> itr = revisions.values().iterator(); + while (itr.hasNext()) + { + List 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 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 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 list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return getRevision(list, branchPoint); + } + + public synchronized void addRevision(InternalCDORevision revision, boolean raw) + { + InternalCDOBranch branch = revision.getBranch(); + if (branch.getBranchManager().getRepository() != getRepository()) + { + throw new IllegalArgumentException("Branch does not belong to this repository: " + branch); + } + + Object listKey = getListKey(revision.getID(), branch); + List list = revisions.get(listKey); + if (list == null) + { + list = new ArrayList(); + 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, CDOBranchPoint mergeSource) + { + 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, mergeSource); + 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 list = revisions.get(listKey); + if (list == null) + { + return false; + } + + for (Iterator 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 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(); + 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 (Map.Entry> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (branch != context.getBranch()) + { + continue; + } + + List 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); + if (CDOTransactionImpl.isResourceMatch(revisionName, name, exactMatch)) + { + if (!context.addResource(revision.getID())) + { + // No more results allowed + break; + } + } + } + } + + public synchronized void queryXRefs(QueryXRefsContext context) + { + Set targetIDs = context.getTargetObjects().keySet(); + Map> sourceCandidates = context.getSourceCandidates(); + + for (Entry> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (branch != context.getBranch()) + { + continue; + } + + List 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 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 ids = (List)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 targetIDs, CDOID targetID, CDOID sourceID, EReference sourceReference, int index) + { + for (CDOID id : targetIDs) + { + if (id == 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 list = revisions.get(listKey); + if (list != null) + { + for (Iterator 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 locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public synchronized LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 objectsToLock) + { + LockArea area = getLockArea(durableLockingID); + Map locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockingManager(); + 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 objectsToUnlock) + { + LockArea area = getLockArea(durableLockingID); + Map locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockingManager(); + 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 locks = area.getLocks(); + locks.clear(); + } + + public synchronized void queryLobs(List ids) + { + for (Iterator 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 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> getAllRevisions() + { + Map> result = new HashMap>(); + InternalCDOBranchManager branchManager = getRepository().getBranchManager(); + result.put(branchManager.getMainBranch(), new ArrayList()); + + for (Integer branchID : branchInfos.keySet()) + { + InternalCDOBranch branch = branchManager.getBranch(branchID); + result.put(branch, new ArrayList()); + } + + for (List list : revisions.values()) + { + for (InternalCDORevision revision : list) + { + CDOBranch branch = revision.getBranch(); + List 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 list) + { + int version = CDOBranchVersion.UNSPECIFIED_VERSION; + for (InternalCDORevision revision : list) + { + if (revision.getVersion() > version) + { + version = revision.getVersion(); + } + } + + return version; + } + + private InternalCDORevision getRevisionByVersion(List list, int version) + { + for (InternalCDORevision revision : list) + { + if (revision.getVersion() == version) + { + return revision; + } + } + + return null; + } + + private InternalCDORevision getRevision(List 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 list, InternalCDORevision revision, boolean raw) + { + boolean resource = !(revision instanceof SyntheticCDORevision) && revision.isResourceNode(); + 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) + { + throw new IllegalStateException( + "Concurrent modification of " + rev.getEClass().getName() + "@" + new CDORevisionKeyImpl(rev.getID(), rev.getBranch(), version)); + } + + // 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 list) + { + while (list.size() > listLimit) + { + list.remove(0); + } + } + + /** + * @author Eike Stepper + */ + private static final class UpTo implements Predicate + { + private final long timeStamp; + + public UpTo(long timeStamp) + { + this.timeStamp = timeStamp; + } + + public boolean apply(CDOTimeProvider commitInfo) + { + return commitInfo.getTimeStamp() <= timeStamp; + } + } + + /** + * @author Eike Stepper + */ + private static final class DownTo implements Predicate + { + private final long timeStamp; + + public DownTo(long timeStamp) + { + this.timeStamp = timeStamp; + } + + public boolean apply(CDOTimeProvider commitInfo) + { + return commitInfo.getTimeStamp() >= timeStamp; + } + } + + /** + * @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 id == that.getID() && branch == that.getBranch(); + } + + return false; + } + + @Override + public String toString() + { + return MessageFormat.format("{0}:{1}", id, branch.getID()); + } + } + + /** + * @author Eike Stepper + */ + private static class CommitInfoKey implements CDOTimeProvider, Comparable + { + private long timeStamp; + + public CommitInfoKey(long timeStamp) + { + this.timeStamp = timeStamp; + } + + public long getTimeStamp() + { + return timeStamp; + } + + public int compareTo(CommitInfoKey o) + { + return CDOCommonUtil.compareTimeStamps(timeStamp, o.timeStamp); + } + } + + /** + * @author Eike Stepper + */ + private static final class CommitInfo extends CommitInfoKey + { + private CDOBranch branch; + + private long previousTimeStamp; + + private String userID; + + private String comment; + + private CDOBranchPoint mergeSource; + + public CommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource) + { + super(timeStamp); + this.branch = branch; + this.previousTimeStamp = previousTimeStamp; + this.userID = userID; + this.comment = comment; + this.mergeSource = mergeSource; + } + + public CDOBranch getBranch() + { + return branch; + } + + public void handle(InternalCDOCommitInfoManager manager, CDOCommitInfoHandler handler) + { + CDOCommitInfo commitInfo = manager.createCommitInfo(branch, getTimeStamp(), previousTimeStamp, userID, comment, mergeSource, null); + handler.handleCommitInfo(commitInfo); + } + + @Override + public String toString() + { + return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}, {5}]", branch, getTimeStamp(), previousTimeStamp, userID, comment, mergeSource); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java new file mode 100644 index 000000000..bcd4b1aac --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2009-2016 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.Raw2; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader3; +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 Raw2, DurableLocking2, BranchLoader3 +{ + private final MEMStore store; + + private final IQueryHandler testQueryHandler = new IQueryHandler() + { + public void executeQuery(CDOQueryInfo info, IQueryContext queryContext) + { + List filters = new ArrayList(); + Object context = info.getParameters().get("context"); //$NON-NLS-1$ + Long sleep = (Long)info.getParameters().get("sleep"); //$NON-NLS-1$ + Integer integers = (Integer)info.getParameters().get("integers"); //$NON-NLS-1$ + Integer error = (Integer)info.getParameters().get("error"); //$NON-NLS-1$ + + if (integers != null) + { + executeQuery(integers, error, queryContext); + return; + } + + 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); + } + }); + } + } + + int i = 0; + for (InternalCDORevision revision : store.getCurrentRevisions()) + { + if (sleep != null) + { + try + { + Thread.sleep(sleep); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + if (isValid(revision, filters)) + { + throwExceptionAt(error, ++i); + + if (!queryContext.addResult(revision)) + { + // No more results allowed + break; + } + } + } + } + + private void throwExceptionAt(Integer error, int i) + { + if (error != null && i == error) + { + throw new RuntimeException("Simulated problem in query execution at result " + i); + } + } + + private void executeQuery(int integers, Integer error, IQueryContext queryContext) + { + for (int i = 1; i <= integers; ++i) + { + throwExceptionAt(error, i); + + if (!queryContext.addResult(i)) + { + // No more results allowed + break; + } + } + } + + private boolean isValid(InternalCDORevision revision, List filters) + { + for (Object filter : filters) + { + if (!filter.equals(revision)) + { + return false; + } + } + + return true; + } + }; + + private List newRevisions; + + public MEMStoreAccessor(MEMStore store, ISession session) + { + super(store, session); + this.store = store; + } + + /** + * @since 2.0 + */ + public MEMStoreAccessor(MEMStore store, ITransaction transaction) + { + super(store, transaction); + this.store = store; + } + + @Override + public MEMStore getStore() + { + return store; + } + + /** + * @since 2.0 + */ + public MEMStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + return new MEMStoreChunkReader(this, revision, feature); + } + + public Collection readPackageUnits() + { + return Collections.emptySet(); + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + throw new UnsupportedOperationException(); + } + + public Pair createBranch(int branchID, BranchInfo branchInfo) + { + return store.createBranch(branchID, branchInfo); + } + + public BranchInfo loadBranch(int branchID) + { + return store.loadBranch(branchID); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + return store.loadSubBranches(branchID); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + return store.loadBranches(startID, endID, branchHandler); + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + store.renameBranch(branchID, oldName, newName); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + store.loadCommitInfos(branch, startTime, endTime, handler); + } + + public Set readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + return store.readChangeSet(segments); + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, CDORevisionCacheAdder cache) + { + return store.getRevision(id, branchPoint); + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, CDORevisionCacheAdder cache) + { + return store.getRevisionByVersion(id, branchVersion); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, CDORevisionHandler handler) + { + store.handleRevisions(eClass, branch, timeStamp, exactTime, handler); + } + + /** + * @since 2.0 + */ + @Override + protected void doCommit(OMMonitor monitor) + { + newRevisions = null; + } + + @Override + public void doWrite(InternalCommitContext context, OMMonitor monitor) + { + synchronized (store) + { + super.doWrite(context, monitor); + } + } + + @Deprecated + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor) + { + store.addCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource); + } + + @Override + protected void doRollback(CommitContext context) + { + if (newRevisions != null) + { + 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) + { + if (newRevisions == null) + { + newRevisions = new ArrayList(); + } + + newRevisions.add(revision); + store.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(); + InternalCDORevision revision = store.getRevisionByVersion(id, revisionDelta); + if (revision == null) + { + 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.applyTo(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) + { + store.detachObject(id, branch, timeStamp); + } + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context) + { + store.queryResources(context); + } + + public void queryXRefs(QueryXRefsContext context) + { + store.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 + { + store.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, OMMonitor monitor) throws IOException + { + store.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + store.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, null, monitor); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource, monitor); + } + + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + store.rawDelete(id, version, branch); + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + // Do nothing + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return store.createLockArea(userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return store.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + } + + public void updateLockArea(LockArea lockArea) + { + store.updateLockArea(lockArea); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + return store.getLockArea(durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + store.getLockAreas(userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + store.deleteLockArea(durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection objectsToLock) + { + store.lock(durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection objectsToUnlock) + { + store.unlock(durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + store.unlock(durableLockingID); + } + + public void queryLobs(List ids) + { + store.queryLobs(ids); + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + store.handleLobs(fromTime, toTime, handler); + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + store.loadLob(id, out); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + store.writeBlob(id, size, inputStream); + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + store.writeClob(id, size, reader); + } + + @Override + protected void doActivate() throws Exception + { + // Do nothing + } + + @Override + protected void doDeactivate() throws Exception + { + if (newRevisions != null) + { + newRevisions.clear(); + newRevisions = null; + } + } + + @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java new file mode 100644 index 000000000..068394295 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2009-2012 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 executeRead() + { + CDOID id = getRevision().getID(); + CDOBranchVersion branchVersion = getRevision(); + + MEMStore store = getAccessor().getStore(); + List 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java new file mode 100644 index 000000000..9e444a77d --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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 repositoryProperties, Element storeConfig) + { + return new MEMStore(); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/messages/Messages.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/messages/Messages.java new file mode 100644 index 000000000..cc57d1c6c --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/messages/Messages.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/messages/messages.properties b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/messages/messages.properties new file mode 100644 index 000000000..14b8a7077 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/messages/messages.properties @@ -0,0 +1,16 @@ +# Copyright (c) 2009, 2012 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java new file mode 100644 index 000000000..b3378d4d1 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2010-2012, 2016 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() + { + } + + 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(); + setState(ONLINE); + } + else + { + // Bug 312879 + setReplicationCountersToLatest(); + + // Switch on synchronizer + doStartSynchronization(); + } + } + + super.changingType(oldType, newType); + } + + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java new file mode 100644 index 000000000..51d690f0b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2012, 2015 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.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.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * @author Eike Stepper + */ +public class OfflineClone extends SynchronizableRepository +{ + public OfflineClone() + { + } + + @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, //$NON-NLS-1$ + baseTimeStamp); + } + finally + { + StoreThreadLocal.release(); + } + } + + /** + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java new file mode 100644 index 000000000..3cc874214 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2010-2013, 2016 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.begin(); + + try + { + notifyBeforeCommitting(monitor); + } + finally + { + monitor.done(); + } + } + + @Override + protected void lockObjects() throws InterruptedException + { + // Do nothing + } + + @Override + protected void checkXRefs() + { + // Do nothing + } + + @Override + protected void checkContainmentCycles() + { + // Do nothing + } + + private static InternalCDOPackageUnit[] getNewPackageUnits(CDOCommitInfo commitInfo, InternalCDOPackageRegistry packageRegistry) + { + List 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 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 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 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java new file mode 100644 index 000000000..1e857cfd5 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2010-2015 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.CDOCommonRepository.State; +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.CDOBranchChangedEvent; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.CDOSession; +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.branch.CDOBranchAdjustable; +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.PriorityQueueRunnable; +import org.eclipse.net4j.util.concurrent.PriorityQueueRunner; +import org.eclipse.net4j.util.container.IContainerDelta; +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.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; + +/** + * @author Eike Stepper + * @since 3.0 + */ +public class RepositorySynchronizer extends PriorityQueueRunner implements InternalRepositorySynchronizer +{ + 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 = true; + + 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; + } + + public boolean isEmpty() + { + return remoteSession == null; + } + + public CDOSession[] getElements() + { + if (remoteSession == null) + { + return new CDOSession[0]; + } + + return new CDOSession[] { remoteSession }; + } + + @Override + protected String getThreadName() + { + return "CDORepositorySynchronizer"; //$NON-NLS-1$ + } + + @Override + protected void noWork(WorkContext context) + { + if (!localRepository.isActive()) + { + context.terminate(); + } + } + + @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) + { + closeRemoteSession(); + } + + super.doDeactivate(); + } + + protected void handleConnect() + { + scheduleReplicate(); + + remoteSession.addListener(remoteSessionListener); + remoteSession.getBranchManager().addListener(remoteSessionListener); + + fireEvent(new SingleDeltaContainerEvent(this, remoteSession, IContainerDelta.Kind.ADDED)); + } + + protected void handleDisconnect() + { + if (TRACER.isEnabled()) + { + TRACER.trace("Disconnected from master."); //$NON-NLS-1$ + } + + if (localRepository.hasBeenReplicated()) + { + localRepository.setState(CDOCommonRepository.State.OFFLINE); + } + else + { + localRepository.setState(CDOCommonRepository.State.INITIAL); + } + + if (remoteSession != null) + { + CDOSession element = remoteSession; + closeRemoteSession(); + fireEvent(new SingleDeltaContainerEvent(this, element, IContainerDelta.Kind.REMOVED)); + } + + reconnect(); + } + + private void closeRemoteSession() + { + remoteSession.removeListener(remoteSessionListener); + remoteSession.getBranchManager().removeListener(remoteSessionListener); + remoteSession.close(); + remoteSession = null; + } + + private void reconnect() + { + clearQueue(); + if (isActive()) + { + scheduleConnect(); + } + } + + private void scheduleConnect() + { + synchronized (connectLock) + { + State state = localRepository.getState(); + if (state.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 CDOBranchChangedEvent) + { + CDOBranchChangedEvent e = (CDOBranchChangedEvent)event; + if (e.getChangeKind() == ChangeKind.CREATED) + { + addWork(new BranchRunnable(e.getBranch())); + } + else + { + throw new UnsupportedOperationException("Branch renaming not supported: " + RepositorySynchronizer.this); + } + } + 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 final class ConnectRunnable extends PriorityQueueRunnable + { + 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(); + } + catch (Exception ex) + { + remoteSession = null; + + if (isActive()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Connection attempt failed. Retrying in {0} seconds...", retryInterval); //$NON-NLS-1$ + } + + fireThrowable(ex); + sleepRetryInterval(); + reconnect(); + } + + return; + } + + if (TRACER.isEnabled()) + { + TRACER.trace("Connected to master."); //$NON-NLS-1$ + } + + handleConnect(); + } + } + + @Override + protected Integer getPriority() + { + return CONNECT_PRIORITY; + } + + private void ensureNOOPRevisionCache() + { + // Ensure that incoming revisions are not cached! + InternalCDORevisionCache cache = remoteSession.getRevisionManager().getCache(); + if (!(cache instanceof NOOPRevisionCache)) + { + String message = "Master session does not use a NOOPRevisionCache: " + cache.getClass().getName(); + OM.LOG.error(message); + throw new Error(message); + } + } + } + + /** + * @author Eike Stepper + */ + private final class ReplicateRunnable extends PriorityQueueRunnable + { + public ReplicateRunnable() + { + } + + public void run() + { + try + { + checkActive(); + if (TRACER.isEnabled()) + { + TRACER.trace("Synchronizing with master..."); //$NON-NLS-1$ + } + + boolean firstSyncing = !localRepository.hasBeenReplicated(); + if (!firstSyncing) + { + 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); + } + + if (firstSyncing) + { + CDOID id = remoteSession.getRepositoryInfo().getRootResourceID(); + localRepository.setRootResourceID(id); + } + + localRepository.setState(CDOCommonRepository.State.ONLINE); + if (TRACER.isEnabled()) + { + TRACER.trace("Synchronized with master."); //$NON-NLS-1$ + } + } + catch (RuntimeException ex) + { + if (isActive()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Replication attempt failed. Retrying in {0} seconds...", ex, retryInterval); //$NON-NLS-1$ + } + + fireThrowable(ex); + sleepRetryInterval(); + handleDisconnect(); + } + } + } + + @Override + protected Integer getPriority() + { + return REPLICATE_PRIORITY; + } + } + + /** + * @author Eike Stepper + */ + private final class BranchRunnable extends PriorityQueueRunnable + { + private CDOBranch branch; + + public BranchRunnable(CDOBranch branch) + { + this.branch = branch; + } + + public void run() + { + try + { + localRepository.handleBranch(branch); + } + catch (Exception ex) + { + fireThrowable(ex); + } + } + + @Override + public int compareTo(PriorityQueueRunnable o) + { + int result = super.compareTo(o); + if (result == 0) + { + result = branch.compareTo(((BranchRunnable)o).branch); + } + + return result; + } + + @Override + protected Integer getPriority() + { + return BRANCH_PRIORITY; + } + } + + /** + * @author Eike Stepper + */ + private abstract class RetryingRunnable extends PriorityQueueRunnable + { + private List failedRuns; + + public RetryingRunnable() + { + } + + public void run() + { + try + { + doRun(); + } + catch (Exception ex) + { + fireThrowable(ex); + if (failedRuns == null) + { + failedRuns = new ArrayList(); + } + + failedRuns.add(ex); + if (failedRuns.size() <= maxRecommits) + { + if (TRACER.isEnabled()) + { + String simpleName = RetryingRunnable.this.getClass().getSimpleName(); + TRACER.format(simpleName + " failed. Trying again in {0} seconds...", recommitInterval); //$NON-NLS-1$ + } + + if (recommitTimer == null) + { + recommitTimer = new Timer("RetryTimer-" + RepositorySynchronizer.this); + } + + recommitTimer.schedule(new TimerTask() + { + @Override + public void run() + { + try + { + addWork(RetryingRunnable.this); + } + catch (Exception ex) + { + if (TRACER.isEnabled()) + { + TRACER.format("{0} failed. Exiting.", RetryingRunnable.this.getClass().getSimpleName()); //$NON-NLS-1$ + } + + fireThrowable(ex); + } + } + }, recommitInterval * 1000L); + } + else + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex); + } + + fireThrowable(ex); + } + } + } + + protected abstract void doRun(); + + protected abstract String getErrorMessage(); + } + + /** + * @author Eike Stepper + */ + 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(PriorityQueueRunnable 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 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()); + + if (lockChangeInfo instanceof CDOBranchAdjustable) + { + ((CDOBranchAdjustable)lockChangeInfo).adjustBranches(localRepository.getBranchManager()); + } + + localRepository.handleLockChangeInfo(lockChangeInfo); + } + finally + { + StoreThreadLocal.release(); + } + } + + @Override + protected String getErrorMessage() + { + return "Replication of master lock changes failed:" + lockChangeInfo; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java new file mode 100644 index 000000000..8057dfdc5 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java @@ -0,0 +1,1165 @@ +/* + * Copyright (c) 2010-2016 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.CDOObject; +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.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.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.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.CDOLockUtil; +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.protocol.CDOProtocol.CommitNotificationInfo; +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.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOException; +import org.eclipse.emf.cdo.internal.common.commit.DelegatingCommitInfo; +import org.eclipse.emf.cdo.internal.common.revision.AbstractCDORevisionCache; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +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.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchAdjustable; +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.commit.CDOCommitInfoUtil; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +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.InternalCDORevisionCache; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +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.collection.IndexedList; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; +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 org.eclipse.emf.spi.cdo.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext; + +import java.io.IOException; +import java.util.ArrayList; +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: + *
    + *
  • Handle new package units that had been committed during offline (testDisconnectAndCommitAndMergeWithNewPackages). + *
  • Make CDOIDs of new objects temporary when merging out of temp branch. + *
  • Provide custom branching strategies. + *
  • Consider non-auditing masters. + *
  • Test out-of-order commits. + *
  • Don't create branches table if branching not supported. + *
  • Implement raw replication for NUMERIC and DECIMAL. + *
  • Notify new branches during raw replication. + *
+ * + * @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 list = new ArrayList(Arrays.asList(super.getElements())); + list.add(synchronizer); + return list.toArray(); + } + + public boolean hasBeenReplicated() + { + return getLastReplicatedCommitTime() != CDOBranchPoint.UNSPECIFIED_DATE; + } + + 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; + } + } + + @Override + public void setLastCommitTimeStamp(long lastCommitTimeStamp) + { + super.setLastCommitTimeStamp(lastCommitTimeStamp); + + if (getType() == MASTER) + { + // This MASTER might become a BACKUP, so don't replicate this commit in the future + setLastReplicatedCommitTime(lastCommitTimeStamp); + } + } + + public String[] getLockAreaIDs() + { + try + { + StoreThreadLocal.setSession(replicatorSession); + final List areaIDs = new LinkedList(); + getLockingManager().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(final CDOCommitInfo commitInfo) + { + CDOBranch branch = commitInfo.getBranch(); + if (branch.isLocal()) + { + return; + } + + // Convert branches from remoteSession to localRepository + InternalCDOBranchManager newBranchManager = getBranchManager(); + + for (CDOIDAndVersion key : commitInfo.getNewObjects()) + { + if (key instanceof CDOBranchAdjustable) + { + CDOBranchAdjustable branchAdjustable = (CDOBranchAdjustable)key; + branchAdjustable.adjustBranches(newBranchManager); + } + } + + for (CDORevisionKey key : commitInfo.getChangedObjects()) + { + if (key instanceof CDOBranchAdjustable) + { + CDOBranchAdjustable branchAdjustable = (CDOBranchAdjustable)key; + branchAdjustable.adjustBranches(newBranchManager); + } + } + + for (CDOIDAndVersion key : commitInfo.getDetachedObjects()) + { + if (key instanceof CDOBranchAdjustable) + { + CDOBranchAdjustable branchAdjustable = (CDOBranchAdjustable)key; + branchAdjustable.adjustBranches(newBranchManager); + } + } + + final InternalCDOBranch newBranch = newBranchManager.getBranch(branch.getID()); + CDOCommitInfo newCommitInfo = new DelegatingCommitInfo() + { + @Override + protected CDOCommitInfo getDelegate() + { + return commitInfo; + } + + @Override + public CDOBranch getBranch() + { + return newBranch; + } + }; + + long timeStamp = newCommitInfo.getTimeStamp(); + CDOBranchPoint head = newBranch.getHead(); + + InternalTransaction transaction = replicatorSession.openTransaction(++lastTransactionID, head); + ReplicatorCommitContext commitContext = new ReplicatorCommitContext(transaction, newCommitInfo); + 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(); + if (owner == null) + { + return; + } + + String durableLockingID = owner.getDurableLockingID(); + CDOBranch viewedBranch = lockChangeInfo.getBranch(); + InternalLockManager lockManager = getLockingManager(); + LockType lockType = lockChangeInfo.getLockType(); + + InternalView view = null; + + try + { + view = SyncingUtil.openViewWithLockArea(replicatorSession, lockManager, viewedBranch, durableLockingID); + List lockables = new LinkedList(); + + 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, false, timeout); + } + else if (lockChangeInfo.getOperation() == Operation.UNLOCK) + { + super.doUnlock(view, lockType, lockables, false); + } + else + { + throw new IllegalStateException("Unexpected: " + lockChangeInfo.getOperation()); + } + } + finally + { + LifecycleUtil.deactivate(view); + } + } + + public boolean handleLockArea(LockArea area) + { + try + { + StoreThreadLocal.setSession(replicatorSession); + getLockingManager().updateLockArea(area); + + getSessionManager().sendLockNotification(null, CDOLockUtil.createLockChangeInfo()); + return true; + } + finally + { + StoreThreadLocal.release(); + } + } + + /** + * Called by ReplicateRepositoryRawRequest.confirming(). + */ + public void replicateRaw(CDODataInput in, OMMonitor monitor) throws IOException + { + try + { + long previousCommitTime = getLastCommitTimeStamp(); + + int fromBranchID = lastReplicatedBranchID + 1; + int toBranchID = in.readXInt(); + long fromCommitTime = lastReplicatedCommitTime + 1L; + long toCommitTime = in.readXLong(); + + StoreThreadLocal.setSession(replicatorSession); + IStoreAccessor.Raw accessor = (IStoreAccessor.Raw)StoreThreadLocal.getAccessor(); + accessor.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + + replicateRawReviseRevisions(); + replicateRawReloadLocks(); + replicateRawNotifyClients(lastReplicatedCommitTime, toCommitTime, previousCommitTime); + + 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 replicateRawReloadLocks() + { + getLockingManager().reloadLocks(); + } + + private void replicateRawNotifyClients(long fromCommitTime, long toCommitTime, long previousCommitTime) + { + InternalCDOCommitInfoManager manager = getCommitInfoManager(); + InternalSessionManager sessionManager = getSessionManager(); + + Map branches = replicateRawGetBranches(fromCommitTime, toCommitTime); + for (Entry 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 newPackages = Collections.emptyList(); // TODO Notify about new packages + List newObjects = changeSet.getNewObjects(); + List changedObjects = changeSet.getChangedObjects(); + List detachedObjects = changeSet.getDetachedObjects(); + + CDOCommitData data = CDOCommitInfoUtil.createCommitData(newPackages, newObjects, changedObjects, detachedObjects); + + String comment = ""; //$NON-NLS-1$ + final CDOCommitInfo commitInfo = manager.createCommitInfo( // + branch, toCommitTime, previousCommitTime, SYSTEM_USER_ID, comment, null, data); + + CommitNotificationInfo info = new CommitNotificationInfo(); + info.setSender(replicatorSession); + info.setCommitInfo(commitInfo); + info.setClearResourcePathCache(true); + + sessionManager.sendCommitNotification(info); + } + + CDOLockChangeInfo lockChangeInfo = CDOLockUtil.createLockChangeInfo(); + sessionManager.sendLockNotification(replicatorSession, lockChangeInfo); + } + + private Map replicateRawGetBranches(long fromCommitTime, long toCommitTime) + { + final Map branches = new HashMap(); + 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 void notifyWriteAccessHandlers(ITransaction transaction, CommitContext commitContext, boolean beforeCommit, OMMonitor monitor) + { + if (beforeCommit && commitContext.getNewPackageUnits().length != 0) + { + throw new IllegalStateException( + "Synchronizable repositories don't support dynamic addition of new packages. Use IRepository.setInitialPackages() instead."); + } + + super.notifyWriteAccessHandlers(transaction, commitContext, beforeCommit, monitor); + } + + @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(); + + InternalCDORevisionCache cache = getRevisionManager().getCache(); + if (cache instanceof AbstractCDORevisionCache) + { + // Enable branch checks to ensure that no branches from the replicator session are used + ((AbstractCDORevisionCache)cache).setBranchManager(getBranchManager()); + } + + InternalStore store = getStore(); + if (!store.isFirstStart()) + { + Map map = store.getPersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN)); + if (!map.containsKey(PROP_GRACEFULLY_SHUT_DOWN)) + { + setReplicationCountersToLatest(); + } + else + { + Set names = new HashSet(); + 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)); + + Type type = getType(); + if (type == MASTER) + { + setState(ONLINE); + } + else + { + if (hasBeenReplicated()) + { + setState(OFFLINE); + } + else if (type == BACKUP) + { + if (getLastReplicatedCommitTime() == CDOBranchPoint.UNSPECIFIED_DATE) + { + boolean usedToBeMaster = getRootResourceID() != null; + if (usedToBeMaster) + { + setLastReplicatedCommitTime(getLastCommitTimeStamp()); + } + } + } + + startSynchronization(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + stopSynchronization(); + + Map map = new HashMap(); + 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(); + } + } + + @Override + protected void setPostActivateState() + { + // Do nothing (keep INITIAL) + } + + protected void setReplicationCountersToLatest() + { + setLastReplicatedBranchID(getStore().getLastBranchID()); + setLastReplicatedCommitTime(getStore().getLastNonLocalCommitTime()); + } + + @Override + protected void initRootResource() + { + // Non-MASTER repositories must wait for the first replication to receive their root resource ID + if (getType() == MASTER) + { + super.initRootResource(); + } + } + + @Override + public LockObjectsResult lock(InternalView view, LockType lockType, List revisionKeys, boolean recursive, long timeout) + { + if (view.getBranch().isLocal()) + { + return super.lock(view, lockType, revisionKeys, recursive, 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, false, timeout); + } + + private LockObjectsResult lockOnMaster(InternalView view, LockType type, List revKeys, boolean recursive, 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 for view " + view); + } + + LockObjectsResult masterLockingResult = sessionProtocol.delegateLockObjects(areaID, revKeys, view.getBranch(), type, recursive, 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(); + if (!remoteSession.waitForUpdate(requiredTimestamp, 10000)) + { + throw new TimeoutRuntimeException(); + } + } + + return masterLockingResult; + } + + private LockObjectsResult lockThrough(InternalView view, LockType type, List keys, boolean recursive, long timeout) + { + try + { + LockObjectsResult masterLockingResult = lockOnMaster(view, type, keys, recursive, timeout); + if (!masterLockingResult.isSuccessful()) + { + return masterLockingResult; + } + + LockObjectsResult localLockingResult = super.lock(view, type, keys, recursive, timeout); + return localLockingResult; + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + @Override + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List objectIDs, boolean recursive) + { + if (view.getBranch().isLocal()) + { + super.unlock(view, lockType, objectIDs, recursive); + } + + 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, recursive); + } + + private void unlockOnMaster(InternalView view, LockType lockType, List objectIDs, boolean recursive) + { + InternalCDOSession remoteSession = getSynchronizer().getRemoteSession(); + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + + String lockAreaID = view.getDurableLockingID(); + if (lockAreaID == null) + { + throw new IllegalStateException("Durable locking is not enabled for view " + view); + } + + sessionProtocol.delegateUnlockObjects(lockAreaID, objectIDs, lockType, recursive); + } + + private UnlockObjectsResult unlockThrough(InternalView view, LockType lockType, List objectIDs, boolean recursive) + { + unlockOnMaster(view, lockType, objectIDs, recursive); + return super.unlock(view, lockType, objectIDs, recursive); + } + + /** + * @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 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 getNewPackageUnits() + { + final InternalCDOPackageUnit[] newPackageUnits = commitContext.getNewPackageUnits(); + return new IndexedList() + { + @Override + public CDOPackageUnit get(int index) + { + return newPackageUnits[index]; + } + + @Override + public int size() + { + return newPackageUnits.length; + } + }; + } + + public List getNewObjects() + { + final InternalCDORevision[] newObjects = commitContext.getNewObjects(); + return new IndexedList() + { + @Override + public CDOIDAndVersion get(int index) + { + return newObjects[index]; + } + + @Override + public int size() + { + return newObjects.length; + } + }; + } + + public List getChangedObjects() + { + final InternalCDORevisionDelta[] changedObjects = commitContext.getDirtyObjectDeltas(); + return new IndexedList() + { + @Override + public CDORevisionKey get(int index) + { + return changedObjects[index]; + } + + @Override + public int size() + { + return changedObjects.length; + } + }; + } + + public List getDetachedObjects() + { + final CDOID[] detachedObjects = commitContext.getDetachedObjects(); + return new IndexedList() + { + @Override + public CDOIDAndVersion get(int index) + { + return CDOIDUtil.createIDAndVersion(detachedObjects[index], CDOBranchVersion.UNSPECIFIED_VERSION); + } + + @Override + public int size() + { + return detachedObjects.length; + } + }; + } + + public synchronized Map 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 WriteThroughCommitContext extends TransactionCommitContext + { + private static final int ARTIFICIAL_VIEW_ID = 0; + + private CommitTransactionResult result; + + 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) + { + // Prepare commit to the master + final CDOCommitData commitData = new CommitContextData(this); + + InternalCDOCommitContext ctx = new InternalCDOCommitContext() + { + public boolean isPartialCommit() + { + return false; + } + + public Map getRevisionDeltas() + { + throw new UnsupportedOperationException(); + } + + public List getNewPackageUnits() + { + return commitData.getNewPackageUnits(); + } + + public Map getNewObjects() + { + throw new UnsupportedOperationException(); + } + + public Collection> getLobs() + { + return Collections.emptySet(); // TODO (CD) Did we forget to support this earlier? + } + + public Map getDirtyObjects() + { + throw new UnsupportedOperationException(); + } + + public Map getDetachedObjects() + { + throw new UnsupportedOperationException(); + } + + public void preCommit() + { + throw new UnsupportedOperationException(); + } + + public void postCommit(CommitTransactionResult result) + { + throw new UnsupportedOperationException(); + } + + public InternalCDOTransaction getTransaction() + { + return null; + } + + public CDOCommitData getCommitData() + { + return commitData; + } + + public int getViewID() + { + return ARTIFICIAL_VIEW_ID; + } + + public String getUserID() + { + return WriteThroughCommitContext.this.getUserID(); + } + + @Deprecated + public boolean isAutoReleaseLocks() + { + return WriteThroughCommitContext.this.isAutoReleaseLocksEnabled(); + } + + public Collection getLocksOnNewObjects() + { + CDOLockState[] locksOnNewObjectsArray = WriteThroughCommitContext.this.getLocksOnNewObjects(); + return Arrays.asList(locksOnNewObjectsArray); + } + + public Collection getIDsToUnlock() + { + CDOID[] idsToUnlockArray = WriteThroughCommitContext.this.getIDsToUnlock(); + return Arrays.asList(idsToUnlockArray); + } + + public String getCommitComment() + { + return WriteThroughCommitContext.this.getCommitComment(); + } + + public CDOBranchPoint getCommitMergeSource() + { + return WriteThroughCommitContext.this.getCommitMergeSource(); + } + + public CDOBranch getBranch() + { + return WriteThroughCommitContext.this.getTransaction().getBranch(); + } + }; + + // Delegate commit to the master + CDOSessionProtocol sessionProtocol = getSynchronizer().getRemoteSession().getSessionProtocol(); + result = sessionProtocol.commitDelegation(ctx, 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(); // result is set to null later! + 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) + { + long timeStamp = result.getTimeStamp(); + long previousTimeStamp = result.getPreviousTimeStamp(); + result = null; + + InternalRepository repository = getTransaction().getSession().getManager().getRepository(); + repository.forceCommitTimeStamp(timeStamp, monitor); + + return new long[] { timeStamp, previousTimeStamp }; + } + + @Override + protected void lockObjects() throws InterruptedException + { + // Do nothing + } + + private void addIDMappings(Map idMappings) + { + for (Map.Entry idMapping : idMappings.entrySet()) + { + CDOID oldID = idMapping.getKey(); + CDOID newID = idMapping.getValue(); + addIDMapping(oldID, newID); + } + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerBrowser.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerBrowser.java new file mode 100644 index 000000000..8d42fa048 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerBrowser.java @@ -0,0 +1,1448 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016 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.CDOCommonRepository.CommitInfoStorage; +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.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.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade; +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.InternalLockManager; +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.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.IManagedContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.factory.ProductCreationException; +import org.eclipse.net4j.util.io.IOUtil; + +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.Map.Entry; +import java.util.Set; + +/** + * A simple HTTP server that web browsers can connect to in order to render internal server data for debugging purposes. + *

+ * Actual content is contributed through pluggable {@link CDOServerBrowser.Page pages}. + *

+ * Note: Don't use this server in production, it's unsecure and does not perform or scale! + * + * @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> params = new InheritableThreadLocal>() + { + @Override + protected Map initialValue() + { + return new HashMap(); + } + }; + + private int port = 7777; + + private ServerSocket serverSocket; + + private Map repositories; + + private List pages = new ArrayList(); + + public CDOServerBrowser(Map repositories) + { + this.repositories = repositories; + setDaemon(true); + } + + public Map 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 map = this.params.get(); + for (String param : params.split("&")) + { + if (param.length() != 0) + { + String[] keyValue = param.split("="); + if (keyValue.length == 1) + { + map.put(keyValue[0], "true"); + } + else + { + map.put(keyValue[0], keyValue[1]); + } + } + } + } + + protected void clearParams() + { + Map map = params.get(); + map.clear(); + } + + public void removeParam(String key) + { + Map map = params.get(); + map.remove(key); + } + + public String getParam(String key) + { + Map map = params.get(); + return map.get(key); + } + + /** + * @since 4.5 + */ + public boolean isParam(String key) + { + Map map = params.get(); + return "true".equalsIgnoreCase(map.get(key)); + } + + public String href(String label, String resource, String... params) + { + Map map = new HashMap(this.params.get()); + for (int i = 0; i < params.length;) + { + map.put(params[i++], params[i++]); + } + + List list = new ArrayList(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 "" + escape(label) + ""; + } + + public String escape(String raw) + { + if (raw == null) + { + return "null"; + } + + return raw.replace("<", "<"); + } + + 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("

CDO Server Browser 4.0


\r\n"); + + for (Page page : pages) + { + pout.println("

" + href(page.getLabel(), page.getName()) + "

"); + } + } + + protected void showPage(PrintStream pout, Page page) + { + String repo = getParam("repo"); + + List repoNames = new ArrayList(getRepositoryNames()); + Collections.sort(repoNames); + + pout.print("

" + page.getLabel() + ":  "); + for (String repoName : repoNames) + { + InternalRepository repository = getRepository(repoName); + if (!page.canDisplay(repository)) + { + continue; + } + + if (repo == null) + { + repo = repoName; + } + + if (repoName.equals(repo)) + { + pout.print("" + escape(repoName) + "  "); + } + else + { + pout.print(href(repoName, page.getName(), "repo", repoName) + "  "); + } + } + + pout.print("

"); + + InternalRepository repository = getRepository(repo); + if (repository != null) + { + pout.print("

\r\n"); + + InternalSession session = repository.getSessionManager().openSession(null); + StoreThreadLocal.setSession(session); + + try + { + page.display(this, repository, pout); + } + finally + { + StoreThreadLocal.release(); + session.close(); + } + } + } + + protected Set getRepositoryNames() + { + return repositories.keySet(); + } + + protected InternalRepository getRepository(String name) + { + return repositories.get(name); + } + + @Override + protected String getThreadName() + { + return "CDOServerBrowser"; + } + + protected void initPages(List pages) + { + pages.add(new PackagesPage()); + pages.add(new LocksPage()); + pages.add(new RevisionsPage.FromCache()); + pages.add(new RevisionsPage.FromStore()); + pages.add(new LobsPage()); + pages.add(new HistoryPage()); + + IManagedContainer container = getPagesContainer(); + for (String factoryType : container.getFactoryTypes(Page.PRODUCT_GROUP)) + { + try + { + Page page = (Page)container.getElement(Page.PRODUCT_GROUP, factoryType, null); + pages.add(page); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + + /** + * @since 4.1 + */ + protected IManagedContainer getPagesContainer() + { + return IPluginContainer.INSTANCE; + } + + @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(); + } + + /** + * @since 4.5 + */ + public static String formatTimeStamp(long timeStamp) + { + String str = CDOCommonUtil.formatTimeStamp(timeStamp); + if (!CDOCommonUtil.UNSPECIFIED_DATE_STRING.equals(str)) + { + str += " - " + timeStamp; + str = str.replaceAll(" ", " "); + } + + return str; + } + + /** + * A {@link CDOServerBrowser server browser} for the repositories in a {@link IManagedContainer managed container}. + * + * @author Eike Stepper + */ + public static class ContainerBased extends CDOServerBrowser + { + private IContainer container; + + private IListener containerListener = new ContainerEventAdapter() + { + @Override + protected void onAdded(IContainer container, Object element) + { + addElement(element); + } + + @Override + protected void onRemoved(IContainer container, Object element) + { + removeElement(element); + } + }; + + public ContainerBased(IContainer container) + { + super(new HashMap()); + this.container = container; + } + + public ContainerBased() + { + this(IPluginContainer.INSTANCE); + } + + public IContainer getContainer() + { + return container; + } + + @Override + protected IManagedContainer getPagesContainer() + { + if (container instanceof IManagedContainer) + { + return (IManagedContainer)container; + } + + return IPluginContainer.INSTANCE; + } + + @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()); + } + } + + /** + * Creates {@link CDOServerBrowser server browsers} for the repositories in a {@link IManagedContainer managed + * container}. + * + * @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 + { + int port = 0; + + if (!StringUtil.isEmpty(description)) + { + int digits = 0; + for (int i = 0; i < description.length(); i++) + { + if (Character.isDigit(description.charAt(i))) + { + ++digits; + } + else + { + break; + } + } + + if (digits != 0) + { + port = Integer.parseInt(description.substring(0, digits)); + } + } + + if (port == 0) + { + port = IOUtil.getFreePort(); + } + + browser.setPort(port); + } + catch (Exception ex) + { + OM.LOG.warn(ex); + } + + return browser; + } + } + } + + /** + * Represents pluggable content for a {@link CDOServerBrowser server 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); + } + + /** + * An abstract base implementation of a {@link Page server browser page}. + * + * @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; + } + } + + /** + * A {@link Page server browser page} that renders the package registry contents of a repository. + * + * @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, "  "); + } + } + + protected String showPackage(InternalCDOPackageInfo info, InternalCDOPackageRegistry packageRegistry, CDOServerBrowser browser, String param, + PrintStream out, String prefix) + { + EPackage ePackage = info.getEPackage(); + out.println("

" + prefix + ePackage.getName() + "  [" + ePackage.getNsURI() + "]

"); + + 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 + "  " + label); + + if (classifier instanceof EEnum) + { + EEnum eenum = (EEnum)classifier; + out.print("  " + eenum.getELiterals()); + } + else if (classifier instanceof EDataType) + { + EDataType eDataType = (EDataType)classifier; + out.print("  " + eDataType.getInstanceClassName()); + } + + out.println("
"); + } + + for (EPackage sub : ePackage.getESubpackages()) + { + InternalCDOPackageInfo subInfo = packageRegistry.getPackageInfo(sub); + param = showPackage(subInfo, packageRegistry, browser, param, out, prefix + "  "); + } + + return param; + } + } + + /** + * A {@link Page server browser page} that renders the locking manager contents of a repository. + * + * @author Eike Stepper + * @since 4.2 + */ + public static class LocksPage extends AbstractPage + { + public static final String NAME = "locks"; + + public LocksPage() + { + super(NAME, "Locks"); + } + + public boolean canDisplay(InternalRepository repository) + { + return true; + } + + public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out) + { + InternalLockManager lockingManager = repository.getLockingManager(); + for (InternalSession session : repository.getSessionManager().getSessions()) + { + boolean sessionRendered = false; + for (InternalView view : session.getViews()) + { + Map locks = lockingManager.getLocks(view); + if (locks != null && !locks.isEmpty()) + { + if (!sessionRendered) + { + int sessionID = session.getSessionID(); + String userID = session.getUserID(); + out.println("

Session " + sessionID + "  [" + userID + "]

"); + out.println("
    "); + sessionRendered = true; + } + + out.println("
  • " + view + "
  • "); + out.println("
      "); + + for (Entry entry : locks.entrySet()) + { + out.println("
    • " + entry.getKey() + " = " + entry.getValue() + "
    • "); + } + + out.println("
    "); + } + + if (sessionRendered) + { + out.println("
"); + } + } + } + } + } + + /** + * A {@link Page server browser page} that renders {@link CDORevision revisions}. + * + * @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> allRevisions = getAllRevisions(repository); + Map> ids = getAllIDs(allRevisions); + + out.print("\r\n"); + out.print("\r\n"); + + out.print("\r\n"); + out.print("\r\n"); + + if (revision[0] != null) + { + out.print("\r\n"); + } + + out.print("\r\n"); + out.print("
\r\n"); + out.print(""); + out.println(""); + + out.println(""); + out.println(""); + + lastRevision = null; + versionsBuilder = null; + } + } + }.dump(); + + out.print("
\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 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(" "); + } + + 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("" + version + ""); + } + else + { + versionsBuilder.append(browser.href(version, getName(), "revision", key)); + } + + lastRevision = rev; + } + + protected void dumpLastRevision() + { + if (versionsBuilder != null) + { + PrintStream out = out(); + + out.println("
    "); + out.println(getCDOIDLabel(lastRevision)); + out.println("    "); + out.println(versionsBuilder.toString()); + out.println("
   \r\n"); + showRevision(out, browser, allRevisions, ids, revision[0], repository); + out.print("
\r\n"); + } + + /** + * @since 4.0 + */ + protected void showRevision(PrintStream pout, CDOServerBrowser browser, Map> allRevisions, Map> 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> ids, InternalCDORevision revision) + { + String className = revision.getEClass().toString(); + className = className.substring(className.indexOf(' ')); + className = StringUtil.replace(className, new String[] { "(", ")", "," }, new String[] { "
", "", "
" }); + className = className.substring("
".length() + 1); + + String created = formatTimeStamp(revision.getTimeStamp()); + String commitInfo = browser.href(created, HistoryPage.NAME, "time", String.valueOf(revision.getTimeStamp())); + + pout.print("\r\n"); + showKeyValue(pout, true, "type", "" + revision.getClass().getSimpleName() + ""); + 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", formatTimeStamp(revision.getRevised())); + if (revision instanceof SyntheticCDORevision) + { + if (revision instanceof PointerCDORevision) + { + PointerCDORevision pointer = (PointerCDORevision)revision; + CDOBranchVersion target = pointer.getTarget(); + CDOBranch branch = target.getBranch(); + int version = target.getVersion(); + + String label = getVersionLabel("v", version, branch); + CDORevisionKey targetKey = CDORevisionUtil.createRevisionKey(pointer.getID(), branch, version); + String value = CDORevisionUtil.formatRevisionKey(targetKey); + showKeyValue(pout, true, "target", browser.href(label, getName(), "revision", value)); + } + } + else + { + 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("
\r\n"); + } + + /** + * @since 4.0 + */ + protected Object getRevisionValue(Object value, CDOServerBrowser browser, Map> ids, InternalCDORevision context) + { + if (value instanceof CDOID) + { + List revisions = ids.get(value); + if (revisions != null) + { + StringBuilder builder = new StringBuilder(); + builder.append(getCDOIDLabel(revisions.get(0))); + + if (browser != null) + { + builder.append("  "); + for (CDORevision revision : revisions) + { + String versionPrefix = getVersionPrefix(revision); + int version = revision.getVersion(); + CDOBranch branch = revision.getBranch(); + String label = getVersionLabel(versionPrefix, version, branch); + + 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 ? "" : "
"); + builder.append(getRevisionValue(element, browser, ids, context)); + } + + return builder.toString(); + } + + return value; + } + + private String getVersionLabel(String versionPrefix, int version, CDOBranch branch) + { + String label = versionPrefix + version; + String branchName = branch.getName(); + if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName)) + { + label += "[" + branchName + "]"; + } + return label; + } + + 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("\r\n"); + pout.print("" + key + "\r\n"); + pout.print(""); + pout.print(value); + pout.print("\r\n"); + pout.print("\r\n"); + } + + protected abstract Map> getAllRevisions(InternalRepository repository); + + private Map> getAllIDs(Map> allRevisions) + { + Map> ids = CDOIDUtil.createMap(); + for (List list : allRevisions.values()) + { + for (CDORevision revision : list) + { + CDOID id = revision.getID(); + List revisions = ids.get(id); + if (revisions == null) + { + revisions = new ArrayList(); + ids.put(id, revisions); + } + + revisions.add(revision); + } + } + + return ids; + } + + protected String getCDOIDLabel(CDORevision revision) + { + String label = revision.toString(); + return label.substring(0, label.indexOf(':')); + } + + /** + * A {@link Page server browser page} that renders the {@link CDORevision revisions} in a revision cache. + * + * @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> getAllRevisions(InternalRepository repository) + { + return repository.getRevisionManager().getCache().getAllRevisions(); + } + } + + /** + * A {@link Page server browser page} that renders the {@link CDORevision revisions} in a {@link IStore store}. + * + * @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> getAllRevisions(InternalRepository repository) + { + return ((CDOAllRevisionsProvider)repository.getStore()).getAllRevisions(); + } + } + } + + /** + * A {@link Page server browser page} that renders {@link CDOLobInfo large object infos}. + * + * @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("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + + if (details[0] != null) + { + out.print("\r\n"); + out.print("\r\n"); + } + + out.print("\r\n"); + out.print("
\r\n"); + + 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); + } + + out.print("   \r\n"); + if (details[0] instanceof ByteArrayOutputStream) + { + ByteArrayOutputStream baos = (ByteArrayOutputStream)details[0]; + String hex = HexUtil.bytesToHex(baos.toByteArray()); + + out.println("

Blob " + details[1] + " (" + details[2] + ")

"); + out.println("
\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("
\r\n"); + } + else + { + CharArrayWriter caw = (CharArrayWriter)details[0]; + out.println("

Clob " + details[1] + " (" + details[2] + ")

"); + out.println("
" + caw + "
"); + } + + out.print("
\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; + } + } + + /** + * A {@link Page server browser page} that renders {@link CDOCommitInfo commit infos}. + * + * @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("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("
\r\n"); + + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + final String param = browser.getParam("time"); + + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + + if (repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE) + { + out.print("\r\n"); + } + + out.print("\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("
TimeBranchUserCommentMerge
\r\n"); + out.print("
   \r\n"); + + if (auditing) + { + CDOCommitInfo commitInfo = details[0]; + if (commitInfo != null) + { + out.print("

Commit Info " + commitInfo.getTimeStamp() + "

\r\n"); + showCommitData(out, commitInfo, browser); + } + } + else + { + out.print("

No audit data available in this repository.

\r\n"); + } + + out.print("
\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 = formatTimeStamp(commitInfo.getTimeStamp()); + String label = formatted; + if (!selected && auditing) + { + label = browser.href(formatted, getName(), "time", timeStamp); + } + + out.print("\r\n"); + out.print("\r\n"); + out.print(label); + out.print("\r\n"); + + CDOBranch branch = commitInfo.getBranch(); + out.print("\r\n"); + out.print(branch.getName() + "[" + branch.getID() + "]"); + out.print("\r\n"); + + String userID = commitInfo.getUserID(); + out.print("\r\n"); + out.print(StringUtil.isEmpty(userID) ? " " : browser.escape(userID)); + out.print("\r\n"); + + String comment = commitInfo.getComment(); + out.print("\r\n"); + out.print(StringUtil.isEmpty(comment) ? " " : browser.escape(comment)); + out.print("\r\n"); + + CDOCommonRepository repository = commitInfo.getCommitInfoManager().getRepository(); + if (repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE) + { + out.print("\r\n"); + + CDOBranchPoint mergeSource = commitInfo.getMergeSource(); + if (mergeSource == null) + { + out.print(" "); + } + else + { + String mergeSourceLabel = browser.escape(mergeSource.getBranch().getPathName()) + " - " + formatTimeStamp(mergeSource.getTimeStamp()); + out.print(browser.href(mergeSourceLabel, getName(), "time", String.valueOf(mergeSource.getTimeStamp()))); + } + + out.print("\r\n"); + } + + out.print("\r\n"); + return selected; + } + + protected void showCommitData(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser) + { + out.print("

New Objects:

\r\n"); + out.print("
    \r\n"); + for (CDOIDAndVersion key : commitInfo.getNewObjects()) + { + CDORevision newObject = (CDORevision)key; + out.print( + "
  • " + browser.href(newObject.toString(), RevisionsPage.FromStore.NAME, "revision", CDORevisionUtil.formatRevisionKey(newObject)) + "
    \r\n"); + } + + out.print("
\r\n"); + out.print("

Changed Objects:

\r\n"); + out.print("
    \r\n"); + for (CDORevisionKey key : commitInfo.getChangedObjects()) + { + CDORevisionDelta changedObject = (CDORevisionDelta)key; + out.print("
  • " + changedObject.toString() + "
    \r\n"); + } + + out.print("
\r\n"); + out.print("

Detached Objects:

\r\n"); + out.print("
    \r\n"); + for (CDOIDAndVersion key : commitInfo.getDetachedObjects()) + { + out.print("
  • " + key.toString() + "
    \r\n"); + } + + out.print("
\r\n"); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerExporter.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerExporter.java new file mode 100644 index 000000000..db592ee4c --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerExporter.java @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2010-2016 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.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; + +/** + * Exports the complete contents of a {@link IRepository repository} in a format suitable for {@link CDOServerImporter + * imports} into new repositories. + *

+ * Subtypes specifiy the actual exchange format. + * + * @author Eike Stepper + * @since 4.0 + */ +public abstract class CDOServerExporter +{ + 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(true); + 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; + + /** + * XML constants being used by both {@link CDOServerExporter exporters} and {@link CDOServerImporter importers}. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @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"; + + /** + * @since 4.6 + */ + public static final String MERGE_SOURCE_BRANCH = "mergeSourceBranch"; + + /** + * @since 4.6 + */ + public static final String MERGE_SOURCE_TIME = "mergeSourceTime"; + } + + /** + * An {@link CDOServerExporter exporter} that creates XML output suitable to be interpreted by an + * {@link CDOServerImporter.XML XML importer}. + * + * @author Eike Stepper + */ + public static class XML extends CDOServerExporter 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(); + + InternalRepository repository = (InternalRepository)getRepository(); + repository.ensureChunks(rev, CDORevision.UNCHUNKED); + + CDOClassInfo classInfo = rev.getClassInfo(); + for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) + { + if (feature.isMany()) + { + @SuppressWarnings("unchecked") + List list = (List)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()); + + CDOBranchPoint mergeSource = commitInfo.getMergeSource(); + if (mergeSource != null) + { + out.attribute(MERGE_SOURCE_BRANCH, mergeSource.getBranch().getID()); + out.attribute(MERGE_SOURCE_TIME, mergeSource.getTimeStamp()); + } + } + + 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(); + } + + if (value instanceof BigDecimal) + { + return BigDecimal.class.getSimpleName(); + } + + if (value instanceof BigInteger) + { + return BigInteger.class.getSimpleName(); + } + + throw new IllegalArgumentException("Invalid type: " + value.getClass().getName()); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerImporter.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerImporter.java new file mode 100644 index 000000000..395b895c5 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerImporter.java @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2010-2012, 2014, 2016 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.server.IStoreAccessor.Raw2; +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.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Imports the complete contents of a {@link IRepository repository} from the output created by a + * {@link CDOServerExporter exporter} into a new repository. + *

+ * Subtypes specifiy the actual exchange format. + * + * @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; + + /** + * Persists the data that has been read by a {@link CDOServerImporter importer} into a new {@link IRepository + * repository}. + * + * @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(); + } + + /** + * Persists the data that has been read by a {@link CDOServerImporter importer} into a new {@link IRepository + * repository}. + * + * @author Eike Stepper + * @since 4.6 + */ + public static interface Handler2 extends Handler + { + public void handleCommitInfo(long time, long previous, int branch, String user, String comment, int mergeSourceBranchID, long mergeSourceTime); + } + + /** + * @author Eike Stepper + */ + private final class FlushHandler implements Handler2 + { + private OMMonitor monitor = new Monitor(); + + private IStoreAccessor.Raw accessor; + + private Map models = new HashMap(); + + private LinkedList packageUnits = new LinkedList(); + + private List 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(true); + 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(); + 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) + { + handleCommitInfo(time, previous, branchID, user, comment, 0, CDOBranchPoint.UNSPECIFIED_DATE); + } + + public void handleCommitInfo(long time, long previous, int branchID, String user, String comment, int mergeSourceBranchID, long mergeSourceTime) + { + CDOBranch branch = repository.getBranchManager().getBranch(branchID); + if (mergeSourceBranchID != 0 && accessor instanceof Raw2) + { + CDOBranch mergeSourceBranch = repository.getBranchManager().getBranch(mergeSourceBranchID); + CDOBranchPoint mergeSource = mergeSourceBranch.getPoint(mergeSourceTime); + + Raw2 raw2 = (Raw2)accessor; + raw2.rawStore(branch, time, previous, user, comment, mergeSource, monitor); + } + else + { + 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; + } + } + } + + /** + * An {@link CDOServerImporter importer} that reads and interprets XML output created by an + * {@link CDOServerExporter.XML XML exporter}. + * + * @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); + + if (handler instanceof Handler2) + { + Handler2 handler2 = (Handler2)handler; + + value = attributes.getValue(MERGE_SOURCE_BRANCH); + if (value != null) + { + int mergeSourceBranch = Integer.parseInt(value); + long mergeSourceTime = Long.parseLong(attributes.getValue(MERGE_SOURCE_TIME)); + + handler2.handleCommitInfo(time, previous, branch, user, comment, mergeSourceBranch, mergeSourceTime); + return; + } + } + + 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)); + } + + if (BigDecimal.class.getSimpleName().equals(type)) + { + return new BigDecimal(str); + } + + if (BigInteger.class.getSimpleName().equals(type)) + { + return new BigInteger(str); + } + + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerUtil.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerUtil.java new file mode 100644 index 000000000..0e6b5770f --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/CDOServerUtil.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 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.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.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.InternalRepository; +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.container.IPluginContainer; +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; + +/** + * Various static methods that may help with CDO {@link IRepository repositories} and server-side {@link CDOView views}. + * + * @author Eike Stepper + * @apiviz.exclude + */ +public final class CDOServerUtil +{ + private CDOServerUtil() + { + } + + // /** + // * @since 4.2 + // */ + // public static CDOView openView(ISession session, CDOBranchPoint branchPoint, CDORevisionProvider revisionProvider) + // { + // return new ServerCDOView((InternalSession)session, branchPoint, revisionProvider); + // } + // + // /** + // * @since 4.2 + // */ + // public static CDOView openView(ISession session, CDOBranchPoint branchPoint) + // { + // CDORevisionManager revisionManager = session.getManager().getRepository().getRevisionManager(); + // CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branchPoint); + // return new ServerCDOView((InternalSession)session, branchPoint, revisionProvider); + // } + // + // /** + // * @since 4.2 + // */ + // public static CDOView openView(IView view) + // { + // ISession session = view.getSession(); + // CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); + // return openView(session, branchPoint, view); + // } + // + // /** + // * @since 4.2 + // */ + // public static CDOView openView(IStoreAccessor.CommitContext commitContext) + // { + // ISession session = commitContext.getTransaction().getSession(); + // CDOBranchPoint branchPoint = commitContext.getBranchPoint(); + // return openView(session, branchPoint, commitContext); + // } + + /** + * @since 4.2 + */ + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, CDORevisionProvider revisionProvider) + { + return new ServerCDOView((InternalSession)session, branchPoint, revisionProvider); + } + + /** + * @since 4.2 + */ + public static CDOView openView(ISession session, CDOBranchPoint branchPoint) + { + CDORevisionManager revisionManager = session.getManager().getRepository().getRevisionManager(); + CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branchPoint); + return openView(session, branchPoint, revisionProvider); + } + + /** + * @since 4.2 + */ + public static CDOView openView(IView view) + { + ISession session = view.getSession(); + CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); + return openView(session, branchPoint, view); + } + + /** + * @since 4.2 + */ + public static CDOView openView(IStoreAccessor.CommitContext commitContext) + { + ISession session = commitContext.getTransaction().getSession(); + CDOBranchPoint branchPoint = commitContext.getBranchPoint(); + return openView(session, branchPoint, commitContext); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(ISession, CDOBranchPoint, CDORevisionProvider)}. + */ + @Deprecated + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled, CDORevisionProvider revisionProvider) + { + return openView(session, branchPoint, revisionProvider); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(ISession, CDOBranchPoint)}. + */ + @Deprecated + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled) + { + return openView(session, branchPoint); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(IView)}. + */ + @Deprecated + public static CDOView openView(IView view, boolean legacyModeEnabled) + { + return openView(view); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(IStoreAccessor.CommitContext)}. + */ + @Deprecated + public static CDOView openView(IStoreAccessor.CommitContext commitContext, boolean legacyModeEnabled) + { + return openView(commitContext); + } + + /** + * @since 3.0 + * @deprecated Not yet supported. + */ + @Deprecated + public static org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration createSessionConfiguration() + { + return new org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration(); + } + + /** + * @since 3.0 + */ + public static ISessionManager createSessionManager() + { + return new SessionManager(); + } + + public static IRepository createRepository(String name, IStore store, Map 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 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 props, IRepositorySynchronizer synchronizer, + boolean master, boolean allowBackupCommits) + { + FailoverParticipant repository = new FailoverParticipant(); + initRepository(repository, name, store, props); + + if (synchronizer != null) + { + 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 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 props, IRepositorySynchronizer synchronizer) + { + return createFailoverParticipant(name, store, props, synchronizer, false); + } + + /** + * @since 4.0 + */ + public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, Map props) + { + return createFailoverParticipant(name, store, props, null); + } + + private static void initRepository(Repository repository, String name, IStore store, Map props) + { + repository.setName(name); + repository.setStore((InternalStore)store); + repository.setProperties(props); + } + + public static void addRepository(IManagedContainer container, IRepository repository) + { + InternalRepository internal = (InternalRepository)repository; + if (internal.getContainer() == null && container != IPluginContainer.INSTANCE) + { + internal.setContainer(container); + } + + String productGroup = RepositoryFactory.PRODUCT_GROUP; + String type = RepositoryFactory.TYPE; + String name = repository.getName(); + + container.putElement(productGroup, type, name, 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$ + } + + /** + * An abstract {@link IRepository.ReadAccessHandler read-access handler} that grants or denies access to single + * {@link CDORevision revisions}. + * + * @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 additionalRevisions) throws RuntimeException + { + List violations = new ArrayList(); + for (CDORevision revision : revisions) + { + String violation = validate(session, revision); + if (violation != null) + { + violations.add(violation); + } + } + + if (!violations.isEmpty()) + { + throwException(session, violations); + } + + for (Iterator 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 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java new file mode 100644 index 000000000..642743cae --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2013 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.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.ContainmentCycleException; + +/** + * An unchecked exception that can thrown from a commit operation that is based on stale information + * about the tree structure of the model and would introduce a containment cycle. + *

+ * This situation results from a network race condition and can not be prevented by write locks on + * the changed objects. The committing client must {@link CDOTransaction#rollback() rollback} the transaction + * , replay the original changes and try to {@link CDOTransaction#commit() commit} again. + * + * @author Eike Stepper + * @since 4.0 + * @deprecated As of 4.2 no longer used in the server; replaced by {@link ContainmentCycleException} in the client. + */ +@Deprecated +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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ILockingManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ILockingManager.java new file mode 100644 index 000000000..2a22692d4 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ILockingManager.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011, 2012, 2015 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.server; + +import org.eclipse.emf.cdo.common.CDOCommonSession; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; + +/** + * Manages all persistent aspects of durable CDO views and provides for vetoable + * {@link #addDurableViewHandler(ILockingManager.DurableViewHandler) interception} of the durable view resumption + * process. + * + * @author Caspar De Groot + * @since 4.1 + */ +public interface ILockingManager extends IDurableLockingManager +{ + public void addDurableViewHandler(DurableViewHandler handler); + + public void removeDurableViewHandler(DurableViewHandler handler); + + public DurableViewHandler[] getDurableViewHandlers(); + + /** + * A call-back interface primarily intended to allow implementers to prevent the view from being opened by throwing an + * exception. See {@link ILockingManager#addDurableViewHandler(DurableViewHandler)}. + * + * @author Caspar De Groot + * @since 4.1 + */ + public interface DurableViewHandler + { + /** + * A call-back method primarily intended to allow implementers to prevent the view from being opened by throwing an + * exception. See {@link ILockingManager#addDurableViewHandler(DurableViewHandler)}. + */ + public void openingView(CDOCommonSession session, int viewID, boolean readOnly, LockArea area) throws Exception; + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IMEMStore.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IMEMStore.java new file mode 100644 index 000000000..cdc2dcd27 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IMEMStore.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, 2010-2012, 2015 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IPermissionManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IPermissionManager.java new file mode 100644 index 000000000..f8bf88db8 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IPermissionManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012, 2013 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; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.security.CDOPermission; + +import java.util.Set; + +/** + * Provides the protection level of {@link CDORevision revisions} in the context of a specific user. + * + * @author Eike Stepper + * @since 4.1 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IPermissionManager +{ + /** + * @deprecated As of 4.2 call {@link #getPermission(CDORevision, CDOBranchPoint, ISession)}. + */ + @Deprecated + public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, String userID); + + /** + * @since 4.2 + */ + public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, ISession session); + + /** + * @since 4.3 + */ + public boolean hasAnyRule(ISession session, Set rules); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryContext.java new file mode 100644 index 000000000..f02729296 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryContext.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-2012, 2015 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; +import org.eclipse.emf.cdo.view.CDOQuery; + +/** + * Represents the execution state of a {@link CDOQuery query} in the server towards a {@link IQueryHandler query + * handler}. + * + * @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 true to indicate that more results can be passed subsequently, false otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResult(Object object); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryHandler.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryHandler.java new file mode 100644 index 000000000..c1704def3 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008-2012, 2015 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; +import org.eclipse.emf.cdo.view.CDOQuery; + +/** + * A query language handler that is capable of executing a {@link CDOQuery query}. + * + * @author Eike Stepper + * @since 2.0 + */ +public interface IQueryHandler +{ + /** + * Executes the {@link CDOQuery query} represented by the specified {@link CDOQueryInfo query info} by + * {@link IQueryContext#addResult(Object) passing} the query results to the query execution engine represented by the + * specified {@link IQueryContext execution context}. + * + * @since 3.0 + */ + public void executeQuery(CDOQueryInfo info, IQueryContext context); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java new file mode 100644 index 000000000..62a3ab79e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2008-2012, 2015 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; +import org.eclipse.emf.cdo.view.CDOQuery; + +/** + * Provides the consumer with {@link IQueryHandler query handlers} that are capable of executing {@link CDOQuery + * queries} represented by specific {@link CDOQueryInfo query infos}. + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepository.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepository.java new file mode 100644 index 000000000..e1ae3e374 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepository.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 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 + * Christian W. Damus (CEA LIST) - bug 399487 + */ +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.commit.CDOCommitInfoManager; +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.emf.cdo.common.util.CDOCommonUtil; + +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.lifecycle.ILifecycle; +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; +import java.util.Set; + +/** + * A CDO repository. + * + * @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, ILifecycle +{ + /** + * @since 3.0 + */ + public static final String SYSTEM_USER_ID = CDOCommonUtil.SYSTEM_USER_ID; + + public IStore getStore(); + + public Map 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(); + + /** + * @since 4.2 + */ + public CDOCommitInfoManager getCommitInfoManager(); + + public ISessionManager getSessionManager(); + + /** + * @since 4.5 + */ + public IUnitManager getUnitManager(); + + /** + * @since 4.0 + * @deprecated As of 4.1 use {@link #getLockingManager()}. + */ + @Deprecated + public IDurableLockingManager getLockManager(); + + /** + * @since 4.1 + */ + public ILockingManager getLockingManager(); + + /** + * @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.1 + * @deprecated As of 4.2 call {@link CDOCommitInfoManager#getCommitInfoHandlers()} + */ + @Deprecated + public CDOCommitInfoHandler[] getCommitInfoHandlers(); + + /** + * @since 4.0 + * @deprecated As of 4.2 call {@link CDOCommitInfoManager#addCommitInfoHandler(CDOCommitInfoHandler)} + */ + @Deprecated + public void addCommitInfoHandler(CDOCommitInfoHandler handler); + + /** + * @since 4.0 + * @deprecated As of 4.2 call {@link CDOCommitInfoManager#removeCommitInfoHandler(CDOCommitInfoHandler)} + */ + @Deprecated + public void removeCommitInfoHandler(CDOCommitInfoHandler handler); + + /** + * @since 4.1 + */ + public Set getHandlers(); + + /** + * @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 additionalRevisions) throws RuntimeException; + } + + /** + * Provides a way to handle commits that are received from a client. + * + * @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. Note that you must not alter the + * internal state of the commit context in any way! + * @param monitor + * A monitor that should be used by the implementor to avoid timeouts. + * @throws TransactionValidationException + * to indicate that the commit operation must not be executed against the backend store because some + * semantic validation checks failed. The message should describe the validation failure and will be + * passed through to the client + * @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. Note that you must not alter the internal + * state of the commit context in any way! + * @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); + + /** + * An exception that a {@link WriteAccessHandler} may throw to indicate that a + * {@linkplain WriteAccessHandler#handleTransactionBeforeCommitting(ITransaction, org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext, OMMonitor) transaction commit} + * was rejected because one or more semantic validation checks reported errors. + * + * @author Christian W. Damus (CEA LIST) + * + * @since 4.3 + */ + public static final class TransactionValidationException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + public TransactionValidationException() + { + } + + public TransactionValidationException(String message, Throwable cause) + { + super(message, cause); + } + + public TransactionValidationException(String message) + { + super(message); + } + + public TransactionValidationException(Throwable cause) + { + super(cause); + } + + } + } + + /** + * Contains symbolic constants that specifiy valid keys of {@link IRepository#getProperties() repository properties}. + * + * @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.5 + */ + public static final String SUPPORTING_UNITS = "supportingUnits"; //$NON-NLS-1$ + + /** + * @since 4.5 + */ + public static final String CHECK_UNIT_MOVES = "checkUnitMoves"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String SERIALIZE_COMMITS = "serializeCommits"; //$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$ + + /** + * Possible values: NO | YES | WITH_MERGE_SOURCE. + * + * @since 4.6 + */ + public static final String COMMIT_INFO_STORAGE = "commitInfoStorage"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String OPTIMISTIC_LOCKING_TIMEOUT = "optimisticLockingTimeout"; //$NON-NLS-1$ + + /** + * @since 4.0 + * @deprecated As of 4.2 instances of Ecore are always supported (on demand). + */ + @Deprecated + public static final String SUPPORTING_ECORE = "supportingEcore"; //$NON-NLS-1$ + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositoryFactory.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositoryFactory.java new file mode 100644 index 000000000..481aaf4c2 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositoryFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2007, 2009, 2011, 2012 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; + +/** + * Creates CDO {@link IRepository repositories}. + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositoryProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositoryProvider.java new file mode 100644 index 000000000..831f4112e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositoryProvider.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2007, 2011, 2012, 2015 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; + +/** + * Provides the consumer with CDO {@link IRepository repositories} specified by their name. + * + * @author Eike Stepper + * @apiviz.uses {@link IRepository} - - provides + */ +public interface IRepositoryProvider +{ + public IRepository getRepository(String name); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java new file mode 100644 index 000000000..da0bd3e19 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2012, 2015 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.container.IContainer; + +/** + * Synchronizes a {@link ISynchronizableRepository synchronizable repository} with a master 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 org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory} oneway - - remote + */ +public interface IRepositorySynchronizer extends IContainer +{ + /** + * @since 4.4 + */ + public static final int DEFAULT_RETRY_INTERVAL = 3; + + /** + * @since 4.4 + */ + public static final int DEFAULT_MAX_RECOMMITS = 10; + + /** + * @since 4.4 + */ + public static final int DEFAULT_RECOMMIT_INTERVAL = 1; + + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISession.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISession.java new file mode 100644 index 000000000..bdacdd055 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISession.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007-2013 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.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.ISessionProtocol; + +import org.eclipse.net4j.util.container.IContainer; + +/** + * The server-side representation of a client {@link CDOSession session}. + * + * @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 +{ + /** + * @since 3.0 + */ + public ISessionManager getManager(); + + /** + * @since 3.0 + */ + public ISessionProtocol getProtocol(); + + /** + * @since 4.0 + * @deprecated The return value of this method can not be relied upon to be strictly ordered! + */ + @Deprecated + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISessionManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISessionManager.java new file mode 100644 index 000000000..22327d48e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISessionManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012 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; +import org.eclipse.net4j.util.security.IAuthenticator; + +/** + * Manages the user {@link ISession sessions} of a {@link IRepository repository}. + * + * @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 +{ + /** + * @since 2.0 + */ + public IRepository getRepository(); + + public ISession[] getSessions(); + + /** + * @since 2.0 + */ + public ISession getSession(int sessionID); + + /** + * @since 4.2 + */ + public IAuthenticator getAuthenticator(); + + /** + * @since 4.2 + */ + public void setAuthenticator(IAuthenticator authenticator); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStore.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStore.java new file mode 100644 index 000000000..1f4b91c5b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStore.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2007-2012, 2015 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.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; + +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Represents the physical data storage back-end of a CDO {@link IRepository repository}, such as a database or a file + * system folder. + * + * @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 getObjectIDTypes(); + + /** + * @since 4.0 + */ + public CDOID createObjectID(String val); + + /** + * @since 2.0 + */ + public Set getSupportedChangeFormats(); + + /** + * @since 2.0 + */ + public Set getSupportedRevisionTemporalities(); + + /** + * @since 2.0 + */ + public Set getSupportedRevisionParallelisms(); + + /** + * @since 2.0 + */ + public RevisionTemporality getRevisionTemporality(); + + /** + * @since 2.0 + */ + public RevisionParallelism getRevisionParallelism(); + + /** + * Returns trueif this store was activated for the first time, false 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 names if names is not + * null and not {@link Collection#isEmpty() empty}, all existing property entries otherwise. + * + * @since 4.0 + */ + public Map getPersistentProperties(Set names); + + /** + * @since 4.0 + */ + public void setPersistentProperties(Map properties); + + /** + * @since 4.0 + */ + public void removePersistentProperties(Set 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 null. 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 null, 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 + * null. + * @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 null. + * @since 2.0 + */ + public IStoreAccessor getWriter(ITransaction transaction); + + /** + * @since 2.0 + */ + public ProgressDistributor getIndicatingCommitDistributor(); + + /** + * Enumerates the possible data formats a {@link IStore store} can accept for commit operations. + * + * @author Eike Stepper + * @since 2.0 + */ + public enum ChangeFormat + { + /** + * An indication that the store accepts full {@link CDORevision revisions} for dirty objects. + */ + REVISION, + + /** + * An indication that the store accepts incremental {@link CDORevisionDelta revision deltas} for dirty objects. + */ + DELTA + } + + /** + * Enumerates the possible history recording options a {@link IStore store} can accept. + * + * @author Eike Stepper + * @since 2.0 + */ + public enum RevisionTemporality + { + /** + * An indication that the store can work without auditing. + */ + NONE, + + /** + * An indication that the store can work with auditing. + */ + AUDITING + } + + /** + * Enumerates the possible branching options a {@link IStore store} can accept. + * + * @author Eike Stepper + * @since 2.0 + */ + public enum RevisionParallelism + { + /** + * An indication that the store can work without branching. + */ + NONE, + + /** + * An indication that the store can work with branching. + */ + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreAccessor.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreAccessor.java new file mode 100644 index 000000000..3363f19bc --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreAccessor.java @@ -0,0 +1,879 @@ +/* + * Copyright (c) 2007-2013, 2016 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.CDOLockState; +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; + +/** + * Represents a connection to a physical data storage back-end. + * + * @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 false, + * null otherwise. + * + * @since 2.0 + */ + public ITransaction getTransaction(); + + /** + * Returns true if this accessor has been configured for read-only access to the back-end, + * false otherwise. + * + * @since 2.0 + */ + public boolean isReader(); + + /** + * @since 2.0 + */ + public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature); + + /** + * @since 2.0 + */ + public Collection readPackageUnits(); + + /** + * Demand loads a given package proxy that has been created on startup of the repository. + *

+ * This method must only load the given package, not 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 all of the following + * conditions are met: + *

    + *
  • The eClass parameter is null or equal to revision.getEClass(). + *
  • The branch parameter is null or equal to revision.getBranch(). + *
  • One of the following conditions is met: + *
      + *
    • The timeStamp parameter is {@link CDOBranchPoint#INVALID_DATE INVALID}. + *
    • The exactTime parameter is true and the timeStamp parameter is + * {@link CDOBranchPoint#UNSPECIFIED_DATE UNSPECIFIED} or equal to revision.getTimeStamp(). + *
    • The exactTime parameter is false and the timeStamp parameter is between + * revision.getTimeStamp() and revision.getRevised(). + *
    + *
+ * + * @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 readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments); + + /** + * Returns the CDOID of the resource node with the given folderID and name if a resource with this + * folderID and name exists in the store, null 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. + *

+ * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object. + *

+ * Usage context: 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 ids); + + /** + * Serializes the content of the large object identified by the given {@link CDOLob#getID() ID} to the given + * stream. + *

+ * 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 stream. + * @throws IOException + * if the stream 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)}. + *

+ * Note: {@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. + *

+ * Note: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and + * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads. + *

+ * Note: Implementors should detect if dirty write occurred. In this case it should throw an exception. + * + *

+   * if (revision.getVersion() != revisionDelta.getOriginVersion())
+   * {
+   *   throw new ConcurrentModificationException("Trying to update object " + revisionDelta.getID()
+   *       + " that was already modified");
+   * }
+   * 
+ * + * @since 2.0 + */ + public void commit(OMMonitor monitor); + + /** + * Note: {@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 CommitContext 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. + * @noextend This interface is not intended to be extended by clients. + * @apiviz.exclude + */ + public interface CommitContext extends CDORevisionProvider + { + /** + * Returns the transactional view (ITransaction) which is the scope of the commit operation represented + * by this CommitContext. + * + * @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 4.6 + */ + public CDOBranchPoint getCommitMergeSource(); + + /** + * @since 4.2 + */ + public long getLastUpdateTime(); + + /** + * Returns the temporary, transactional package manager associated with the commit operation represented by this + * CommitContext. 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(); + + /** + * @since 4.2 + */ + public boolean isClearResourcePathCache(); + + /** + * @since 4.3 + */ + public byte getSecurityImpact(); + + /** + * @since 4.2 + */ + public boolean isUsingEcore(); + + /** + * @since 4.2 + */ + public boolean isUsingEtypes(); + + /** + * Returns an array of the new package units that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDOPackageUnit[] getNewPackageUnits(); + + /** + * Returns an array of the new objects that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDORevision[] getNewObjects(); + + /** + * Returns an array of the dirty objects that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDORevision[] getDirtyObjects(); + + /** + * Returns an array of the dirty object deltas that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDORevisionDelta[] getDirtyObjectDeltas(); + + /** + * Returns an array of the removed object that are part of the commit operation represented by this + * CommitContext. + * + * @since 2.0 + */ + public CDOID[] getDetachedObjects(); + + /** + * Returns a map with an {@link EClass} value per {@link CDOID} type. + * + * @since 4.0 + */ + public Map getDetachedObjectTypes(); + + /** + * @since 4.2 + */ + public CDOBranchVersion[] getDetachedObjectVersions(); + + /** + * @since 4.6 + */ + public Map getOldRevisions(); + + /** + * @since 4.6 + */ + public Map getNewRevisions(); + + /** + * Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the + * stream is: + *

+ *

    + *
  1. {@link ExtendedDataInputStream#readInt() int}: the number of lobs to be read from the stream. + *
  2. The following data can be read from the stream in a loop with one iteration per lob in the stream: + *
      + *
    1. {@link ExtendedDataInputStream#readByteArray() int + byte[]}: the id of the lob (prepended by the size of the + * id). + *
    2. {@link ExtendedDataInputStream#readLong() long}: the size of the lob. The following interpretation applies: + *
        + *
      • 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}. + *
      • 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}. + *
      + *
    + *
+ * + * @since 4.0 + */ + public ExtendedDataInputStream getLobs(); + + /** + * + * @since 3.0 + * @deprecated As of 4.5 no longer supported. See {@link #getIDsToUnlock()}. + */ + @Deprecated + public boolean isAutoReleaseLocksEnabled(); + + /** + * Returns an array of the locks on the new objects that are part of the commit operation represented by this + * CommitContext. + * + * @since 4.1 + */ + public CDOLockState[] getLocksOnNewObjects(); + + /** + * @since 4.6 + */ + public CDOID[] getIDsToUnlock(); + + /** + * Returns an unmodifiable map from all temporary IDs to their persistent counter parts. + */ + public Map getIDMappings(); + + /** + * @since 4.0 + */ + public CDOCommitInfo createCommitInfo(); + + /** + * @see CDOProtocolConstants + * @since 4.2 + */ + public byte getRollbackReason(); + + /** + * @since 3.0 + */ + public String getRollbackMessage(); + + /** + * @since 4.0 + */ + public List getXRefs(); + + /** + * @since 4.1 + */ + public List> getPostCommmitLockStates(); + + /** + * @since 4.3 + */ + public T getData(Object key); + + /** + * @since 4.3 + */ + public T setData(Object key, T data); + } + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources + * query}. + * + * @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 true to indicate that more results can be passed subsequently, false otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResource(CDOID resourceID); + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources + * query} that is supposed to deliver one exact resource, or null. + * + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ + public interface ExactMatch extends QueryResourcesContext + { + public CDOID getResourceID(); + } + } + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryXRefs(QueryXRefsContext) XRefs query}. + * + * @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 getTargetObjects(); + + public EReference[] getSourceReferences(); + + /** + * @since 4.0 + */ + public Map> 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 true to indicate that more results can be passed subsequently, false 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); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support raw data access as needed by + * {@link IRepositorySynchronizer repository synchronizers} or {@link CDOServerImporter server importers}. + * + * @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}. + *

+ * Implementation note: 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}. + *

+ * Usage context: 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 stream 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 stream 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}. + *

+ * Implementation note: 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}. + *

+ * Usage context: 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 stream 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 stream 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. + *

+ * Implementation note: 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 may be used to report proper progress of this operation to the caller and + * may be used to react to cancelation requests of the caller and must be 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. + *

+ * Implementation note: 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 may be used to report proper progress of this operation to the caller and + * may be used to react to cancelation requests of the caller and must be 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}. + *

+ * Implementation note: 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}. + *

+ * Implementation note: 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}. + *

+ * Implementation note: 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}. + *

+ * Implementation note: 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) + */ + 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 may be used to report proper progress of this operation to the caller and + * may be used to react to cancelation requests of the caller and must be 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); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support raw data access as needed by + * {@link IRepositorySynchronizer repository synchronizers} or {@link CDOServerImporter server importers}. + * + * @author Eike Stepper + * @since 4.6 + * @apiviz.exclude + */ + public interface Raw2 extends Raw + { + /** + * 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}. + *

+ * Implementation note: 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}. + * @param mergeSource + * the {@link CDOCommitInfo#getMergeSource() merge source} of the commit info to be stored in the backend + * represented by this {@link IStoreAccessor.Raw2 raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support durable locking. + * + * @see DurableLocking2 + * @author Eike Stepper + * @since 4.0 + * @apiviz.exclude + */ + public interface DurableLocking extends IDurableLockingManager + { + public void lock(String durableLockingID, LockType type, Collection objectsToLock); + + public void unlock(String durableLockingID, LockType type, Collection objectsToUnlock); + + public void unlock(String durableLockingID); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support durable locking. + * + * @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 locks); + + public void updateLockArea(LockArea lockArea); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support units. + * + * @author Eike Stepper + * @since 4.5 + * @apiviz.exclude + */ + public interface UnitSupport extends IStoreAccessor + { + public List readUnitRoots(); + + public void readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, OMMonitor monitor); + + public Object initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, Set initializedIDs, long timeStamp, OMMonitor monitor); + + public void finishUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, long timeStamp, Object initResult, List ids); + + public void writeUnits(Map unitMappings, long timeStamp); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreChunkReader.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreChunkReader.java new file mode 100644 index 000000000..875c4df28 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreChunkReader.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012, 2015 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.cdo.session.CDOCollectionLoadingPolicy; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.List; + +/** + * Reads {@link Chunk chunks} of + * {@link org.eclipse.emf.cdo.session.CDOSession.Options#setCollectionLoadingPolicy(CDOCollectionLoadingPolicy) + * partially loaded} lists from a physical data storage backend. + * + * @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 executeRead(); + + /** + * Represents a {@link List#subList(int, int) sublist} of consecutive elements that are subject to partial + * collection loading. + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreFactory.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreFactory.java new file mode 100644 index 000000000..2ea046d6d --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IStoreFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007, 2011, 2012, 2015 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; + +/** + * Creates {@link IStore stores}. + * + * @author Eike Stepper + * @apiviz.uses {@link IStore} - - creates + */ +public interface IStoreFactory +{ + public String getStoreType(); + + /** + * @since 4.0 + */ + public IStore createStore(String repositoryName, Map repositoryProperties, Element storeConfig); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISynchronizableRepository.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISynchronizableRepository.java new file mode 100644 index 000000000..fc94ac53b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ISynchronizableRepository.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2012 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.2 + */ + public boolean hasBeenReplicated(); + + /** + * @since 4.1 + */ + public void goOnline(); + + /** + * @since 4.1 + */ + public void goOffline(); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ITransaction.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ITransaction.java new file mode 100644 index 000000000..0b27ad376 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/ITransaction.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2007-2012, 2015 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; +import org.eclipse.emf.cdo.transaction.CDOTransaction; + +/** + * The server-side representation of a client {@link CDOTransaction transaction}. + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IUnit.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IUnit.java new file mode 100644 index 000000000..a506be563 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IUnit.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 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.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Eike Stepper + * @since 4.5 + */ +public interface IUnit +{ + public IUnitManager getManager(); + + public CDOID getRootID(); + + public boolean isOpen(); + + public void open(IView view, CDORevisionHandler revisionHandler, OMMonitor monitor); + + public void close(IView view); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IUnitManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IUnitManager.java new file mode 100644 index 000000000..603bfc066 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IUnitManager.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016 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.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; + +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.Map; +import java.util.Set; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Eike Stepper + * @since 4.5 + */ +public interface IUnitManager extends IContainer +{ + public IRepository getRepository(); + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler, OMMonitor monitor); + + public boolean isUnit(CDOID rootID); + + public IUnit getUnit(CDOID rootID); + + public IUnit[] getUnits(); + + public Map getUnitsOf(Set ids, CDORevisionProvider revisionProvider); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IView.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IView.java new file mode 100644 index 000000000..05959807a --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/IView.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012, 2015 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; +import org.eclipse.emf.cdo.view.CDOView; + +/** + * The server-side representation of a client {@link CDOView view}. + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java new file mode 100644 index 000000000..bdafb5144 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2007-2012, 2015 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; +import org.eclipse.emf.cdo.session.CDOSession; + +/** + * An unchecked exception being thrown when opening a {@link CDOSession session} to a named {@link IRepository + * repository} that cannot be found. + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/StoreThreadLocal.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/StoreThreadLocal.java new file mode 100644 index 000000000..9f053a199 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/StoreThreadLocal.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2008-2013, 2016 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.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +/** + * Provides server-side consumers with the {@link IStoreAccessor store accessor} that is valid in the context of a + * specific {@link ISession session} during read operations or a specific {@link CommitContext commit context} during + * write operations. + * + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ +public final class StoreThreadLocal +{ + private static final ThreadLocal SESSION = new InheritableThreadLocal(); + + private static final ThreadLocal ACCESSOR = new InheritableThreadLocal(); + + private static final ThreadLocal COMMIT_CONTEXT = new InheritableThreadLocal(); + + private StoreThreadLocal() + { + } + + /** + * @since 3.0 + */ + public static void setSession(InternalSession session) + { + if (session == null) + { + SESSION.remove(); + } + else + { + SESSION.set(session); + } + + ACCESSOR.remove(); + COMMIT_CONTEXT.remove(); + } + + /** + * Returns the session associated with the current thread. + * + * @return Never null. + * @throws IllegalStateException + * if no session is associated with the current thread. + * @since 3.0 + */ + public static InternalSession getSession() throws NoSessionRegisteredException + { + InternalSession session = SESSION.get(); + if (session == null) + { + throw new NoSessionRegisteredException(); + } + + return session; + } + + /** + * @since 4.2 + */ + public static boolean hasSession() + { + return SESSION.get() != null; + } + + public static void setAccessor(IStoreAccessor accessor) + { + if (accessor == null) + { + ACCESSOR.remove(); + SESSION.remove(); + } + else + { + ACCESSOR.set(accessor); + SESSION.set(accessor.getSession()); + } + } + + public static IStoreAccessor getAccessor() throws NoSessionRegisteredException + { + 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) + { + if (commitContext == null) + { + COMMIT_CONTEXT.remove(); + } + else + { + COMMIT_CONTEXT.set(commitContext); + } + } + + public static IStoreAccessor.CommitContext getCommitContext() + { + return COMMIT_CONTEXT.get(); + } + + /** + * @since 4.2 + */ + public static void release() + { + try + { + IStoreAccessor accessor = ACCESSOR.get(); + if (accessor != null) + { + if (LifecycleUtil.isActive(accessor)) + { + accessor.release(); + } + } + } + finally + { + remove(); + } + } + + /** + * @since 4.5 + */ + public static void remove() + { + ACCESSOR.remove(); + SESSION.remove(); + COMMIT_CONTEXT.remove(); + } + + /** + * An {@link IllegalStateException} that can be thrown from {@link StoreThreadLocal#getSession()}. + * + * @author Eike Stepper + * @since 4.2 + */ + public static final class NoSessionRegisteredException extends IllegalStateException + { + private static final long serialVersionUID = 1L; + + public NoSessionRegisteredException() + { + super("session == null"); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/CDOSession.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/CDOSession.java new file mode 100644 index 000000000..40f952173 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/CDOSession.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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; + +/** + * Deprecated, not yet supported. + * + * @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. + * @deprecated Not yet supported. + */ +@Deprecated +public interface CDOSession extends org.eclipse.emf.cdo.session.CDOSession +{ +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java new file mode 100644 index 000000000..cf2fadd6a --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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; + +/** + * Deprecated, not yet supported. + * + * @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. + * @deprecated Not yet supported. + */ +@Deprecated +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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/package-info.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/package-info.java new file mode 100644 index 000000000..d10a2cd46 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/embedded/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/IMEMStore.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/IMEMStore.java new file mode 100644 index 000000000..9a8d5bc5e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/IMEMStore.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011, 2012, 2015 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 {@link IStore 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. + *

+ * 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. + *

+ * 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java new file mode 100644 index 000000000..de44dbf33 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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; + +/** + * Creates {@link IMEMStore} instances. + * + * @author Eike Stepper + * @since 2.0 + */ +public final class MEMStoreUtil +{ + private MEMStoreUtil() + { + } + + /** + * Creates a {@link IMEMStore} instance. + * + * @since 4.0 + */ + public static IMEMStore createMEMStore() + { + return new MEMStore(); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/package-info.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/package-info.java new file mode 100644 index 000000000..40ec9bb90 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/mem/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/package-info.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/package-info.java new file mode 100644 index 000000000..ab74980eb --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/server/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011, 2012, 2015 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/AuthenticationUtil.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/AuthenticationUtil.java new file mode 100644 index 000000000..d274d4f96 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/AuthenticationUtil.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013-2016 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: + * Christian W. Damus (CEA LIST) - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import java.util.concurrent.Callable; + +/** + * Static utility methods for binding {@link IAuthenticationProtocol authentication protocols} to the current thread. + * + * @author Christian W. Damus (CEA LIST) + * @since 4.3 + */ +public final class AuthenticationUtil +{ + private static final ThreadLocal AUTHENTICATION_PROTOCOL = new ThreadLocal(); + + // Not instantiable by clients. + private AuthenticationUtil() + { + } + + /** + * Obtains the authentication protocol, if any, on which the current thread should + * authenticate administrative operations in handling incoming signals. + * + * @return the authentication protocol to use, or {@code null} if authentication is not required + */ + public static IAuthenticationProtocol getAuthenticationProtocol() + { + return AUTHENTICATION_PROTOCOL.get(); + } + + /** + * Wrap an {@code operation} to make an authentication protocol {@linkplain #getAuthenticationProtocol() available} + * to the thread that invokes it, for the duration of the {@code operation}'s execution. + */ + public static Callable authenticatingOperation(IAuthenticationProtocol authenticationProtocol, final Callable operation) + { + return new AuthenticatingOperation(authenticationProtocol) + { + @Override + protected V doCall() throws Exception + { + return operation.call(); + } + }; + } + + /** + * Encapsulation of an administrative operation requiring (potentially) client + * authentication to authorize the operation. + * + * @author Christian W. Damus (CEA LIST) + * + * @since 4.3 + */ + public static abstract class AuthenticatingOperation implements Callable + { + private final IAuthenticationProtocol authenticationProtocol; + + public AuthenticatingOperation(IAuthenticationProtocol authenticationProtocol) + { + this.authenticationProtocol = authenticationProtocol; + } + + public final V call() throws Exception + { + V result; + + try + { + AuthenticationUtil.AUTHENTICATION_PROTOCOL.set(authenticationProtocol); + result = doCall(); + } + finally + { + AuthenticationUtil.AUTHENTICATION_PROTOCOL.remove(); + } + + return result; + } + + protected abstract V doCall() throws Exception; + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/CDOCommand.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/CDOCommand.java new file mode 100644 index 000000000..d004b99f7 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/CDOCommand.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2013, 2015, 2016 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.CDOServerApplication; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.StoreThreadLocal; + +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.factory.ProductCreationException; + +import org.eclipse.osgi.framework.console.CommandInterpreter; + +import org.osgi.framework.Bundle; + +import java.util.Dictionary; + +/** + * @author Eike Stepper + * @since 4.3 + */ +public abstract class CDOCommand extends org.eclipse.net4j.util.factory.Factory +{ + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.commands"; + + public static final String INDENT = " "; //$NON-NLS-1$ + + private static final CommandParameter[] NO_PARAMETERS = new CommandParameter[0]; + + private final String description; + + private final CommandParameter[] parameters; + + private CommandInterpreter interpreter; + + public CDOCommand(String name, String description, CommandParameter... parameters) + { + super(PRODUCT_GROUP, name); + this.description = description; + this.parameters = parameters == null ? NO_PARAMETERS : parameters; + } + + public CDOCommand(String name, String description) + { + this(name, description, NO_PARAMETERS); + } + + public final CDOCommand create(String description) throws ProductCreationException + { + return this; + } + + public final CommandInterpreter getInterpreter() + { + return interpreter; + } + + public final void setInterpreter(CommandInterpreter interpreter) + { + this.interpreter = interpreter; + } + + public final String getName() + { + return getType(); + } + + public final String getDescription() + { + return description; + } + + public final CommandParameter[] getParameters() + { + return parameters; + } + + public final String getSyntax() + { + StringBuilder builder = new StringBuilder(); + builder.append("cdo "); + builder.append(getName()); + + for (CommandParameter parameter : parameters) + { + builder.append(" "); + if (parameter.isOptional()) + { + builder.append("["); + } + + builder.append("<"); + builder.append(parameter.getName()); + builder.append(">"); + + if (parameter.isOptional()) + { + builder.append("]"); + } + } + + return builder.toString(); + } + + public final Object executeCommand(String cmd) + { + return interpreter.execute(cmd); + } + + public final void print(Object o) + { + interpreter.print(o); + } + + public final void println() + { + interpreter.println(); + } + + public final void println(Object o) + { + interpreter.println(o); + } + + public final void printStackTrace(Throwable t) + { + interpreter.printStackTrace(t); + } + + public final void printDictionary(Dictionary dic, String title) + { + interpreter.printDictionary(dic, title); + } + + public final void printBundleResource(Bundle bundle, String resource) + { + interpreter.printBundleResource(bundle, resource); + } + + public final void execute() throws Exception + { + int length = parameters.length; + String[] args = new String[length]; + for (int i = 0; i < parameters.length; i++) + { + String arg; + + CommandParameter parameter = parameters[i]; + if (parameter.isOptional()) + { + arg = nextArgumentOptional(); + } + else + { + arg = nextArgument(); + } + + args[i] = arg; + } + + execute(args); + } + + public abstract void execute(String[] args) throws Exception; + + private String nextArgument() + { + String argument = interpreter.nextArgument(); + if (argument == null && parameters != null) + { + throw new CommandException("Syntax: " + getSyntax()); + } + + return argument; + } + + private String nextArgumentOptional() + { + return interpreter.nextArgument(); + } + + public static CommandParameter[] parameters(CommandParameter parameter, CommandParameter[] parameters) + { + if (parameters == null || parameters.length == 0) + { + return new CommandParameter[] { parameter }; + } + + CommandParameter[] result = new CommandParameter[1 + parameters.length]; + result[0] = parameter; + System.arraycopy(parameters, 0, result, 1, parameters.length); + return result; + } + + public static CommandParameter parameter(String name, boolean optional) + { + return new CommandParameter(name, optional); + } + + public static CommandParameter parameter(String name) + { + return parameter(name, false); + } + + public static CommandParameter optional(String name) + { + return parameter(name, true); + } + + protected static String[] trimFirstArgument(String[] args) + { + int length = args.length - 1; + String[] result = new String[length]; + System.arraycopy(args, 1, result, 0, length); + return result; + } + + /** + * @author Eike Stepper + */ + public static abstract class WithRepository extends CDOCommand + { + public WithRepository(String name, String description, CommandParameter... parameters) + { + super(name, description, parameters(parameter("repository-name"), parameters)); + } + + public WithRepository(String name, String description) + { + this(name, description, NO_PARAMETERS); + } + + @Override + public final void execute(String[] args) throws Exception + { + String repositoryName = args[0]; + InternalRepository repository = getRepository(repositoryName); + if (repository == null) + { + throw new CommandException("Repository not found: " + repositoryName); + } + + execute(repository, trimFirstArgument(args)); + } + + public abstract void execute(InternalRepository repository, String[] args) throws Exception; + + 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 + */ + public static abstract class WithAccessor extends CDOCommand.WithRepository + { + public WithAccessor(String name, String description, CommandParameter... parameters) + { + super(name, description, parameters); + } + + public WithAccessor(String name, String description) + { + this(name, description, NO_PARAMETERS); + } + + @Override + public final void execute(InternalRepository repository, String[] args) throws Exception + { + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + try + { + execute(repository, accessor, args); + } + finally + { + StoreThreadLocal.release(); + } + } + + public abstract void execute(InternalRepository repository, IStoreAccessor accessor, String[] args) throws Exception; + } + + /** + * @author Eike Stepper + */ + public static final class CommandParameter + { + private final String name; + + private final boolean optional; + + public CommandParameter(String name, boolean optional) + { + this.name = name; + this.optional = optional; + } + + public String getName() + { + return name; + } + + public boolean isOptional() + { + return optional; + } + } + + /** + * @author Eike Stepper + */ + public static final class CommandException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + public CommandException(String message) + { + super(message); + } + } + + // /** + // * @author Eike Stepper + // */ + // public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory + // { + // public Factory(String type) + // { + // super(CDOCommand.PRODUCT_GROUP, type); + // } + // + // public abstract CDOCommand create(String description) throws ProductCreationException; + // } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java new file mode 100644 index 000000000..f758a39f3 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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; + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @since 3.0 + */ + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + return (IQueryHandler)container.getElement(QueryHandlerFactory.PRODUCT_GROUP, info.getQueryLanguage(), null); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java new file mode 100644 index 000000000..3f409138b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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.IRepository; +import org.eclipse.emf.cdo.server.IRepositoryProvider; + +import org.eclipse.net4j.util.container.IManagedContainer; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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) + { + return null; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/DelegatingQueryResourcesContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/DelegatingQueryResourcesContext.java new file mode 100644 index 000000000..d58803c0d --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/DelegatingQueryResourcesContext.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 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.server.IStoreAccessor.QueryResourcesContext; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ +public abstract class DelegatingQueryResourcesContext implements QueryResourcesContext +{ + public CDOBranch getBranch() + { + return getDelegate().getBranch(); + } + + public long getTimeStamp() + { + return getDelegate().getTimeStamp(); + } + + public CDOID getFolderID() + { + return getDelegate().getFolderID(); + } + + public String getName() + { + return getDelegate().getName(); + } + + public boolean exactMatch() + { + return getDelegate().exactMatch(); + } + + public int getMaxResults() + { + return getDelegate().getMaxResults(); + } + + public boolean addResource(CDOID resourceID) + { + return getDelegate().addResource(resourceID); + } + + protected abstract QueryResourcesContext getDelegate(); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/DurableLockArea.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/DurableLockArea.java new file mode 100644 index 000000000..4c6426ce5 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/DurableLockArea.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011, 2012, 2016 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 locks; + + public DurableLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java new file mode 100644 index 000000000..0c7922cd7 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public class FactoriesQueryHandlerProvider implements IQueryHandlerProvider +{ + private IRegistry registry; + + public FactoriesQueryHandlerProvider() + { + } + + public FactoriesQueryHandlerProvider(IRegistry registry) + { + setRegistry(registry); + } + + public FactoriesQueryHandlerProvider(IFactory factory) + { + addFactory(factory); + } + + public IRegistry getRegistry() + { + if (registry == null) + { + registry = new HashMapRegistry(); + } + + return registry; + } + + public void setRegistry(IRegistry 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAppExtension.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAppExtension.java new file mode 100644 index 000000000..3b32a94b9 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAppExtension.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2009, 2011, 2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAppExtension2.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAppExtension2.java new file mode 100644 index 000000000..a6047f343 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAppExtension2.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013, 2015 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: + * Christian W. Damus - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import java.io.Reader; + +/** + * An optional extension of the {@link IAppExtension} interface for app extensions that support invocation + * on the XML configurations of dynamically-managed repositories. These may be instantiated multiple + * times, will only be given repository configurations (not Net4j acceptors etc.) and are stopped if and + * when their associated repositories are deleted. + * + * @author Christian W. Damus (CEA LIST) + * @since 4.3 + */ +public interface IAppExtension2 extends IAppExtension +{ + public void startDynamic(Reader xmlConfigReader) throws Exception; +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAuthenticationProtocol.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAuthenticationProtocol.java new file mode 100644 index 000000000..fd0abbd45 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/IAuthenticationProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, 2016 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 + * Christian W. Damus (CEA LIST) - bug 418454: factored out authentication from ISessionProtocol + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; +import org.eclipse.net4j.util.security.DiffieHellman.Client.Response; +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; + +/** + * @author Eike Stepper + * + * @since 4.3 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IAuthenticationProtocol +{ + /** + * Sends a challenge to the client to authenticate the user attempting to + * or already connected. + * + * @since 4.2 + */ + public Response sendAuthenticationChallenge(Challenge challenge) throws Exception; + + /** + * Sends a challenge to the client to change the authenticated user's credentials. + * This is an optional operation; implementators may simply throw + * {@link UnsupportedOperationException}. + * + * @since 4.3 + * + * @throws UnsupportedOperationException if credentials change is not supported + */ + public Response sendCredentialsChallenge(Challenge challenge, String userID, CredentialsUpdateOperation operation) throws Exception; +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java new file mode 100644 index 000000000..721e8268f --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009-2013, 2016 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 + * Christian W. Damus (CEA LIST) - bug 399306 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.branch.InternalCDOBranch; + +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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, IAuthenticationProtocol +{ + /** + * @since 4.0 + * @deprecated As of 4.2 {@link #sendAuthenticationChallenge(Challenge)} is called. + */ + @Deprecated + public org.eclipse.emf.cdo.spi.common.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; + + /** + * @deprecated As of 4.3 use {@link #sendBranchNotification(InternalCDOBranch, ChangeKind)}. + */ + @Deprecated + public void sendBranchNotification(InternalCDOBranch branch) throws Exception; + + /** + * @since 4.3 + */ + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception; + + /** + * @deprecated As of 4.2 use {@link #sendCommitNotification(CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception; + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception; + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info) 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java new file mode 100644 index 000000000..dbb9078da --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 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.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.util.CDOTimeProvider; +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; +import java.util.Set; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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, CDOTimeProvider +{ + @SuppressWarnings("unchecked") + public static final ProgressDistributable[] OPS = ProgressDistributor.array( // + new ProgressDistributable.Default() + { + public void runLoop(int index, InternalCommitContext commitContext, OMMonitor monitor) throws Exception + { + commitContext.write(monitor.fork()); + } + }, // + + new ProgressDistributable.Default() + { + 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(); + + /** + * @since 4.5 + */ + public IStoreAccessor getAccessor(); + + /** + * @since 4.2 + */ + public long getTimeStamp(); + + /** + * @since 4.5 + */ + public boolean isTreeRestructuring(); + + /** + * @since 4.2 + */ + public void setLastTreeRestructuringCommit(long lastTreeRestructuringCommit); + + 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(); + + /** + * @since 4.6 + */ + public InternalCDORevision[] getDetachedRevisions(boolean check); + + /** + * @since 4.2 + */ + public void setClearResourcePathCache(boolean clearResourcePathCache); + + /** + * @since 4.2 + */ + public void setUsingEcore(boolean usingEcore); + + /** + * @since 4.2 + */ + public void setUsingEtypes(boolean usingEtypes); + + 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 detachedObjectTypes); + + /** + * @since 4.2 + */ + public void setDetachedObjectVersions(CDOBranchVersion[] detachedObjectVersions); + + /** + * @since 4.2 + */ + public void setLastUpdateTime(long lastUpdateTime); + + /** + * @deprecated As of 4.5 no longer supported. See {@link #setIDsToUnlock(CDOID[])}. + */ + @Deprecated + public void setAutoReleaseLocksEnabled(boolean on); + + /** + * @since 4.1 + */ + public void setLocksOnNewObjects(CDOLockState[] locksOnNewObjects); + + /** + * @since 4.6 + */ + public void setIDsToUnlock(CDOID[] idsToUnlock); + + /** + * @since 4.5 + */ + public void setCommitNumber(int commitNumber); + + public void setCommitComment(String comment); + + /** + * @since 4.6 + */ + public void setCommitMergeSource(CDOBranchPoint mergeSource); + + /** + * @since 4.0 + */ + public void setLobs(ExtendedDataInputStream in); + + public void addIDMapping(CDOID oldID, CDOID newID); + + public void applyIDMappings(OMMonitor monitor); + + /** + * @since 4.3 + */ + public void setSecurityImpact(byte securityImpact, Set impactedRules); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java new file mode 100644 index 000000000..18d3c9f2c --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 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.protocol.CDODataInput; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.concurrent.ExecutionException; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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. + * + * @deprecated As of 4.5 use {@link #preCommit(InternalCommitContext, CDODataInput, OMMonitor)}. + */ + @Deprecated + public void preCommit(InternalCommitContext commitContext, OMMonitor monitor); + + /** + * Create a future to execute commitContext in a different thread. + * @since 4.5 + */ + public void preCommit(InternalCommitContext commitContext, CDODataInput in, 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java new file mode 100644 index 000000000..350f46e3f --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalLockManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalLockManager.java new file mode 100644 index 000000000..f9e68e38f --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalLockManager.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009-2012, 2014-2016 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.revision.CDOIDAndBranch; +import org.eclipse.emf.cdo.server.ILockingManager; +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, ILockingManager +{ + 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 getLocks(IView view); + + /** + * @since 4.0 + */ + @Deprecated + public void lock(boolean explicit, LockType type, IView context, Collection objects, long timeout) throws InterruptedException; + + /** + * @since 4.1 + */ + public List> lock2(boolean explicit, LockType type, IView context, Collection objects, boolean recursive, + 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 objects); + + /** + * @since 4.1 + */ + public List> unlock2(boolean explicit, LockType type, IView context, Collection objects, boolean recursive); + + /** + * 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> 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 getLockState(Object key); + + /** + * @since 4.4 + */ + public List> getLockStates(); + + /** + * @since 4.1 + */ + public void setLockState(Object key, LockState lockState); + + /** + * @since 4.1 + */ + public void reloadLocks(); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java new file mode 100644 index 000000000..75f82efae --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java new file mode 100644 index 000000000..4a4f9741e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 +{ + public int getQueryID(); + + public CDOQueryInfo getQueryInfo(); + + public InternalView getView(); + + public CDOQueryQueue getQueue(); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalRepository.java new file mode 100644 index 000000000..406201d69 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalRepository.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2009-2016 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.protocol.CDOProtocol.CommitNotificationInfo; +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.util.CDOTimeProvider; +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.BranchLoader3; +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.CDORevisionUnchunker; +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.RevisionLoader2; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.event.IEvent; +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.MergeDataResult; +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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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, BranchLoader3, RevisionLoader2, CommitInfoLoader, CDORevisionUnchunker +{ + 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 properties); + + public InternalCDOBranchManager getBranchManager(); + + public void setBranchManager(InternalCDOBranchManager branchManager); + + /** + * @since 4.6 + */ + public CDOTimeProvider getTimeProvider(); + + /** + * @since 4.6 + */ + public void setTimeProvider(CDOTimeProvider timeProvider); + + /** + * @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); + + /** + * @deprecated As of 4.1 use {@link #getLockingManager()}. + */ + @Deprecated + public InternalLockManager getLockManager(); + + /** + * @since 4.1 + */ + public InternalLockManager getLockingManager(); + + /** + * @since 4.5 + */ + public InternalUnitManager getUnitManager(); + + /** + * @since 4.5 + */ + public void setUnitManager(InternalUnitManager unitManager); + + public InternalQueryManager getQueryManager(); + + public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider); + + /** + * @since 4.3 + */ + public IManagedContainer getContainer(); + + /** + * @since 4.3 + */ + public void setContainer(IManagedContainer container); + + 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 + * long 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.5 + */ + public void executeOutsideStartCommit(Runnable runnable); + + /** + * @since 4.2 + */ + public void commit(InternalCommitContext commitContext, OMMonitor monitor); + + /** + * @since 4.0 + * @deprecated As of 4.2 use {@link #sendCommitNotification(InternalSession, CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(ISessionProtocol.CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache); + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info); + + public void setRootResourceID(CDOID rootResourceID); + + /** + * @since 4.0 + */ + public void setLastCommitTimeStamp(long commitTimeStamp); + + /** + * @since 4.1 + */ + public void ensureChunks(InternalCDORevision revision); + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, int chunkEnd); + + public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions, List 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 + * @deprecated as of 4.6 use {@link #getMergeData2(CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, OMMonitor)}. + */ + @Deprecated + public Set getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, CDORevisionAvailabilityInfo targetBaseInfo, + CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor); + + /** + * @since 4.6 + */ + public MergeDataResult getMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor); + + /** + * @since 4.0 + */ + public void queryLobs(List 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 + * @deprecated As of 4.3 use {@link #initSystemPackages()}. + */ + @Deprecated + public void initSystemPackages(); + + /** + * @since 4.3 + */ + public void initSystemPackages(boolean firstStart); + + /** + * @since 4.0 + */ + public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp); + + /** + * @since 4.1 + */ + public LockObjectsResult lock(InternalView view, LockType type, List keys, boolean recursive, long timeout); + + /** + * @since 4.1 + */ + public UnlockObjectsResult unlock(InternalView view, LockType type, List ids, boolean recursive); + + /** + * @since 4.2 + */ + public long getOptimisticLockingTimeout(); + + /** + * @since 4.3 + */ + public void setOptimisticLockingTimeout(long optimisticLockingTimeout); + + /** + * @author Eike Stepper + * @since 4.6 + */ + public interface PackagesInitializedEvent extends IEvent + { + public InternalRepository getSource(); + + public boolean isFirstStart(); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java new file mode 100644 index 000000000..581374dce --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSession.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSession.java new file mode 100644 index 000000000..899ffff0d --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSession.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 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.CDOBranchChangedEvent.ChangeKind; +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.protocol.CDOProtocol.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.security.CDOPermissionProvider; +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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.3 + * @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, CDOPermissionProvider, CDOCommonSession.Options +{ + public static final int TEMP_VIEW_ID = 0; + + public InternalSessionManager getManager(); + + /** + * @since 4.2 + */ + public void setUserID(String userID); + + /** + * @since 4.5 + */ + public long getFirstUpdateTime(); + + /** + * @since 4.5 + */ + public void setFirstUpdateTime(long firstUpdateTime); + + /** + * @since 4.5 + */ + public boolean isOpenOnClientSide(); + + /** + * @since 4.5 + */ + public void setOpenOnClientSide(); + + 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 revisions, + List 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; + + /** + * @deprecated As of 4.3 use {@link #sendBranchNotification(InternalCDOBranch, ChangeKind)}. + */ + @Deprecated + public void sendBranchNotification(InternalCDOBranch branch) throws Exception; + + /** + * @since 4.3 + */ + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception; + + /** + * @deprecated As of 4.2 use {@link #sendCommitNotification(CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception; + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception; + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info) 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java new file mode 100644 index 000000000..f90b17678 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 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 + * Christian W. Damus (CEA LIST) - bug 399306 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.CommitNotificationInfo; +import org.eclipse.emf.cdo.server.IPermissionManager; +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.DiffieHellman; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IUserManager; + +import java.util.List; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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); + + /** + * @since 4.1 + * @deprecated As of 4.2 use {@link #getAuthenticator()} + */ + @Deprecated + public IUserManager getUserManager(); + + /** + * @deprecated As of 4.2 use {@link #setAuthenticator(IAuthenticator)} + */ + @Deprecated + public void setUserManager(IUserManager userManager); + + /** + * @since 4.2 + */ + public DiffieHellman.Server getAuthenticationServer(); + + /** + * @since 4.2 + */ + public void setAuthenticationServer(DiffieHellman.Server authenticationServer); + + /** + * Initiates the change-credentials protocol with the client and processes the + * client response to update the user's credentials. + * + * @since 4.3 + */ + public void changeUserCredentials(IAuthenticationProtocol sessionProtocol, String userID); + + /** + * Initiates the administrative reset-credentials protocol with the client and + * processes the client response to reset the specified {@code userID}'s credentials. + * + * @since 4.3 + */ + public void resetUserCredentials(IAuthenticationProtocol sessionProtocol, String userID); + + /** + * Challenges the connected user to authenticate the connection. + * + * @param sessionProtocol the authenticatable session protocol + * @return the user ID with which the user authenticated herself, or {@code null} + * if the server does not require authentication for this connection + * + * @throws SecurityException on failure to authenticate + * + * @since 4.3 + */ + public String authenticateUser(IAuthenticationProtocol sessionProtocol) throws SecurityException; + + /** + * @since 4.1 + */ + public IPermissionManager getPermissionManager(); + + /** + * @since 4.1 + */ + public void setPermissionManager(IPermissionManager permissionManager); + + public InternalSession[] getSessions(); + + public InternalSession getSession(int sessionID); + + /** + * @return Never null + */ + public InternalSession openSession(ISessionProtocol sessionProtocol); + + public void sessionClosed(InternalSession session); + + /** + * @since 4.5 + */ + public void openedOnClientSide(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); + + /** + * @deprecated As of 4.3 use {@link #sendBranchNotification(InternalSession, InternalCDOBranch, ChangeKind)}. + */ + @Deprecated + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch); + + /** + * @since 4.3 + */ + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch, ChangeKind changeKind); + + /** + * @deprecated As of 4.2 use {@link #sendCommitNotification(InternalSession, CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(ISessionProtocol.CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache); + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info); + + /** + * @since 4.1 + */ + public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo); + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode); + + public List sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, int[] recipients); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalStore.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalStore.java new file mode 100644 index 000000000..89e0611fe --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalStore.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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); + + /** + * @deprecated Not used anymore. + */ + @Deprecated + 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); + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + * @deprecated As of 4.6 use IRepositoryConfig.CAPABILITY_EXTERNAL_REFS. + */ + @Deprecated + public interface NoExternalReferences + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoQueryXRefs + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoLargeObjects + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoFeatureMaps + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoHandleRevisions + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoRawAccess + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public interface NoChangeSets + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public interface NoCommitInfos + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public interface NoDurableLocking + { + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java new file mode 100644 index 000000000..798ae39f1 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalTransaction.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalTransaction.java new file mode 100644 index 000000000..eb2fd38e9 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalTransaction.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2016 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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(); + + /** + * @since 4.5 + */ + public CommitAttempt getLastCommitAttempt(); + + /** + * @since 4.5 + */ + public void setLastCommitAttempt(CommitAttempt lastCommitAttempt); + + /** + * @author Eike Stepper + * @since 4.5 + */ + public static final class CommitAttempt + { + private final int commitNumber; + + private final long timeStamp; + + private final long previousTimeStamp; + + public CommitAttempt(int commitNumber, long timeStamp, long previousTimeStamp) + { + this.commitNumber = commitNumber; + this.timeStamp = timeStamp; + this.previousTimeStamp = previousTimeStamp; + } + + public int getCommitNumber() + { + return commitNumber; + } + + public long getTimeStamp() + { + return timeStamp; + } + + public long getPreviousTimeStamp() + { + return previousTimeStamp; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java new file mode 100644 index 000000000..cf8f1e60c --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 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.CDORevisionProvider; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import java.util.List; + +/** + * @author Eike Stepper + * @since 4.5 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. +*/ +public interface InternalUnitManager extends IUnitManager +{ + public InternalRepository getRepository(); + + public List getUnitMoves(InternalCDORevisionDelta[] deltas, CDORevisionProvider before, CDORevisionProvider after); + + public InternalObjectAttacher attachObjects(InternalCommitContext commitContext); + + /** + * @author Eike Stepper + */ + public interface InternalObjectAttacher + { + public void finishedCommit(boolean success); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalView.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalView.java new file mode 100644 index 000000000..b18824358 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/InternalView.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2012, 2016 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.protocol.CDOProtocolConstants.UnitOpcode; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.lifecycle.ILifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.List; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 invalidObjects, List allChangedObjects, List allDetachedObjects); + + public void subscribe(CDOID id); + + public void unsubscribe(CDOID id); + + public boolean hasSubscription(CDOID id); + + public void clearChangeSubscription(); + + public void doClose(); + + /** + * @since 4.5 + */ + public boolean openUnit(CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor); + + /** + * @since 4.5 + */ + public void closeUnit(CDOID rootID); + + /** + * @since 4.5 + */ + public boolean isInOpenUnit(CDOID id); +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/LongIDStore.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/LongIDStore.java new file mode 100644 index 000000000..d46bae15a --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/LongIDStore.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2009-2012, 2016 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class LongIDStore extends Store +{ + /** + * @since 3.0 + */ + public static final Set 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 supportedChangeFormats, Set supportedRevisionTemporalities, + Set 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()); + } + + @Deprecated + public boolean isLocal(CDOID id) + { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.0 + */ + public void ensureLastObjectID(CDOID id) + { + long addedID = CDOIDUtil.getLong(id); + if (addedID > getLastObjectID()) + { + setLastObjectID(addedID); + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java new file mode 100644 index 000000000..85643519e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009-2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java new file mode 100644 index 000000000..c58017983 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010-2013, 2016 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ +public class ObjectWriteAccessHandler implements WriteAccessHandler +{ + private IStoreAccessor.CommitContext commitContext; + + private CDOView view; + + private EObject[] newObjects; + + private EObject[] dirtyObjects; + + public ObjectWriteAccessHandler() + { + } + + /** + * @deprecated As of 4.2 the legacy mode is always enabled. + */ + @Deprecated + public ObjectWriteAccessHandler(boolean legacyModeEnabled) + { + } + + /** + * @deprecated As of 4.2 the legacy mode is always enabled. + */ + @Deprecated + public final boolean isLegacyModeEnabled() + { + return true; + } + + 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); + } + + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java new file mode 100644 index 000000000..c52f61660 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2009, 2011, 2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java new file mode 100644 index 000000000..849d87fa4 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009, 2011, 2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java new file mode 100644 index 000000000..0b371254b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2011-2013, 2015 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 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +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.OMPlatform; +import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.net4j.util.security.AuthenticatorFactory; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.UserManagerFactory; + +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.InputSource; +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.io.Reader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 repositoryFactories = new HashMap(); + + private Map storeFactories = new HashMap(); + + public RepositoryConfigurator() + { + this(null); + } + + public RepositoryConfigurator(IManagedContainer container) + { + this.container = container; + } + + public IManagedContainer getContainer() + { + return container; + } + + public Map getRepositoryFactories() + { + return repositoryFactories; + } + + public Map 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$ + } + + return configure(getDocument(configFile)); + } + + /** + * @since 4.3 + */ + public IRepository[] configure(Reader configReader) throws ParserConfigurationException, SAXException, IOException, CoreException + { + if (TRACER.isEnabled()) + { + TRACER.trace("Configuring CDO server from dynamic configuration"); //$NON-NLS-1$ + } + + return configure(getDocument(configReader)); + } + + /** + * @since 4.3 + */ + protected IRepository[] configure(Document document) throws ParserConfigurationException, SAXException, IOException, CoreException + { + List repositories = new ArrayList(); + 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); + } + + /** + * @since 4.3 + */ + protected Document getDocument(Reader configReader) throws ParserConfigurationException, SAXException, IOException + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(new InputSource(configReader)); + } + + 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 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); + + setUserManager(repository, repositoryConfig); + setAuthenticator(repository, repositoryConfig); + + 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(UserManagerFactory.PRODUCT_GROUP, type, description); + if (userManager == null) + { + throw new IllegalStateException("UserManager factory not found: " + type); //$NON-NLS-1$ + } + + return userManager; + } + + /** + * @since 4.2 + */ + @SuppressWarnings("deprecation") + protected void setUserManager(InternalRepository repository, Element repositoryConfig) throws CoreException + { + 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); + } + } + } + + /** + * @since 4.2 + */ + protected Element getAuthenticatorConfig(Element repositoryConfig) + { + NodeList authenticatorConfig = repositoryConfig.getElementsByTagName("authenticator"); //$NON-NLS-1$ + if (authenticatorConfig.getLength() > 1) + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + throw new IllegalStateException("At most one authenticator must be configured for repository " + repositoryName); //$NON-NLS-1$ + } + + return (Element)(authenticatorConfig.getLength() > 0 ? authenticatorConfig.item(0) : null); + } + + /** + * @since 4.2 + */ + protected IAuthenticator getAuthenticator(Element authenticatorConfig) throws CoreException + { + String type = authenticatorConfig.getAttribute("type"); //$NON-NLS-1$ + String description = authenticatorConfig.getAttribute("description"); //$NON-NLS-1$ + return getAuthenticator(type, description); + } + + /** + * @since 4.2 + */ + protected IAuthenticator getAuthenticator(String type, String description) throws CoreException + { + IAuthenticator authenticator = (IAuthenticator)container.getElement(AuthenticatorFactory.PRODUCT_GROUP, type, description); + if (authenticator == null) + { + throw new IllegalStateException("Authenticator factory not found: " + type); //$NON-NLS-1$ + } + + return authenticator; + } + + /** + * @since 4.2 + */ + protected void setAuthenticator(InternalRepository repository, Element repositoryConfig) throws CoreException + { + Element authenticatorConfig = getAuthenticatorConfig(repositoryConfig); + if (authenticatorConfig != null) + { + IAuthenticator authenticator = getAuthenticator(authenticatorConfig); + if (authenticator != null) + { + InternalSessionManager sessionManager = repository.getSessionManager(); + if (sessionManager == null) + { + sessionManager = new SessionManager(); + repository.setSessionManager(sessionManager); + } + + sessionManager.setAuthenticator(authenticator); + } + } + } + + protected EPackage[] getInitialPackages(Element repositoryConfig) + { + List result = new ArrayList(); + + 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 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 getProperties(Element element, int levels) + { + Map properties = new HashMap(); + collectProperties(element, "", properties, levels); //$NON-NLS-1$ + return properties; + } + + private static void collectProperties(Element element, String prefix, Map 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 createExecutableExtension(String extPointName, String elementName, String attributeName, String type) throws CoreException + { + if (OMPlatform.INSTANCE.isExtensionRegistryAvailable()) + { + 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java new file mode 100644 index 000000000..4ebbb2db9 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009, 2011, 2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java new file mode 100644 index 000000000..dc567c54b --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2011, 2012, 2016 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.AuthenticatorFactory; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.SecurityUtil; +import org.eclipse.net4j.util.security.UserManagerFactory; + +import java.util.Arrays; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ +public abstract class RepositoryUserManager extends Lifecycle implements IUserManager, IAuthenticator +{ + 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 + { + try + { + char[] password = getPassword(userID); + return SecurityUtil.encrypt(data, password, algorithmName, salt, count); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new SecurityException(ex); + } + } + + /** + * @since 4.2 + */ + public void authenticate(String userID, char[] password) throws SecurityException + { + char[] userPassword = getPassword(userID); + if (!Arrays.equals(password, userPassword)) + { + throw new SecurityException("Access denied"); //$NON-NLS-1$ + } + } + + protected IRepository getRepository(IManagedContainer container, String repositoryName) + { + return CDOServerUtil.getRepository(container, repositoryName); + } + + /** + * @since 4.2 + */ + protected char[] getPassword(String userID) + { + 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$ + } + return password; + } + + protected abstract char[] getPassword(IRepository repository, String userID); + + public static void prepareContainer(IManagedContainer container, RepositoryUserManagerFactory factory) + { + container.registerFactory(factory); + container.addPostProcessor(new RepositoryInjector()); + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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; + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public static abstract class RepositoryAuthenticatorFactory extends AuthenticatorFactory + { + protected RepositoryAuthenticatorFactory(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; + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/Store.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/Store.java new file mode 100644 index 000000000..cab2a557e --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/Store.java @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2009-2012, 2016 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 objectIDTypes; + + private Set supportedChangeFormats; + + private Set supportedRevisionTemporalities; + + private Set 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 objectIDTypes, Set supportedChangeFormats, + Set supportedRevisionTemporalities, Set 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; + } + + /** + * @since 4.2 + */ + public Store(String type) + { + this.type = type; + } + + public final String getType() + { + return type; + } + + /** + * @since 3.0 + */ + public Set getObjectIDTypes() + { + return objectIDTypes; + } + + /** + * @since 4.0 + */ + protected void setObjectIDTypes(Set objectIDTypes) + { + this.objectIDTypes = objectIDTypes; + } + + public Set getSupportedChangeFormats() + { + return supportedChangeFormats; + } + + public Set getSupportedRevisionTemporalities() + { + return supportedRevisionTemporalities; + } + + public final Set 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); + + if (id != null) + { + 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 null 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. + *

+ * 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). Note: Closing a session implies 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 null. + * @param forReleasing + * Enables lazy pool creation. The implementor is not supposed to create a new pool if false is + * passed. If true 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 null 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. + *

+ * 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). Note: Closing a session implies 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 null. + * @param forReleasing + * Enables lazy pool creation. The implementor is not supposed to create a new pool if false is + * passed. If true 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 new {@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 new {@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 Set set(T... elements) + { + return Collections.unmodifiableSet(new HashSet(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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessor.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessor.java new file mode 100644 index 000000000..f4a4da3fc --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessor.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 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.branch.CDOBranchPoint; +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 org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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(); + CDOBranchPoint mergeSource = context.getCommitMergeSource(); + + Store store = getStore(); + boolean deltas = store.getSupportedChangeFormats().contains(IStore.ChangeFormat.DELTA); + + InternalCDOPackageUnit[] newPackageUnits = context.getNewPackageUnits(); + InternalCDORevision[] newObjects = context.getNewObjects(); + CDOID[] detachedObjects = context.getDetachedObjects(); + InternalCDORevisionDelta[] dirtyObjectDeltas = context.getDirtyObjectDeltas(); + InternalCDORevision[] dirtyObjects = context.getDirtyObjects(); + + int dirtyCount = deltas ? dirtyObjectDeltas.length : dirtyObjects.length; + int postProcessCount = needsRevisionPostProcessing() ? newObjects.length + detachedObjects.length + dirtyCount : 0; + + try + { + monitor.begin(1 + newPackageUnits.length + 2 + newObjects.length + detachedObjects.length + dirtyCount + postProcessCount + 1); + + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, mergeSource, monitor.fork()); + writePackageUnits(newPackageUnits, monitor.fork(newPackageUnits.length)); + + IDGenerationLocation idGenerationLocation = store.getRepository().getIDGenerationLocation(); + if (idGenerationLocation == 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) + { + writeNewObjectRevisions(context, newObjects, branch, monitor.fork(newObjects.length)); + } + + if (dirtyCount != 0) + { + if (deltas) + { + writeRevisionDeltas(dirtyObjectDeltas, branch, timeStamp, monitor.fork(dirtyCount)); + } + else + { + writeDirtyObjectRevisions(context, dirtyObjects, branch, monitor.fork(dirtyCount)); + } + } + + if (needsRevisionPostProcessing()) + { + postProcessRevisions(context, monitor.fork(postProcessCount)); + } + + ExtendedDataInputStream in = context.getLobs(); + if (in != null) + { + Async async = monitor.forkAsync(); + + 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 + { + async.stop(); + } + } + else + { + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + /** + * @since 4.6 + */ + protected boolean needsRevisionPostProcessing() + { + return false; + } + + /** + * @since 4.6 + */ + protected void postProcessRevisions(InternalCommitContext context, OMMonitor monitor) + { + } + + /** + * @since 3.0 + */ + protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor) + { + context.applyIDMappings(monitor.fork()); + } + + /** + * @since 4.0 + * @deprecated As of 4.6 override {@link #writeCommitInfo(CDOBranch, long, long, String, String, CDOBranchPoint, OMMonitor)}. + */ + @Deprecated + protected abstract void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, OMMonitor monitor); + + /** + * @since 4.6 + */ + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + /** + * @since 4.5 + */ + protected void writeNewObjectRevisions(InternalCommitContext context, InternalCDORevision[] newObjects, CDOBranch branch, OMMonitor monitor) + { + writeRevisions(newObjects, branch, monitor); + } + + /** + * @since 4.5 + */ + protected void writeDirtyObjectRevisions(InternalCommitContext context, InternalCDORevision[] dirtyObjects, CDOBranch branch, OMMonitor monitor) + { + writeRevisions(dirtyObjects, branch, 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java new file mode 100644 index 000000000..c5f372413 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2011-2013, 2015, 2016 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.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.common.revision.CDOIDAndVersionImpl; +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.commit.CDOCommitInfoUtil; +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.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.util.ArrayList; +import java.util.List; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 commitContexts = new ArrayList(); + + 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; + } + } + } + + store.setLastCommitTime(latest); + store.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 + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 3.0 + */ + public static class CommitDataRevisionHandler implements CDORevisionHandler + { + private IStoreAccessor storeAccessor; + + private long timeStamp; + + private InternalCDORevisionManager revisionManager; + + private List newPackageUnits = new ArrayList(); + + private List newObjects = new ArrayList(); + + private List changedObjects = new ArrayList(); + + private List detachedObjects = new ArrayList(); + + 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, this); + return CDOCommitInfoUtil.createCommitData(newPackageUnits, newObjects, changedObjects, detachedObjects); + } + + /** + * @since 4.0 + */ + public boolean handleRevision(CDORevision rev) + { + if (rev instanceof DetachedCDORevision) + { + CDOIDAndVersion copy = new CDOIDAndVersionImpl(rev.getID(), rev.getVersion()); + detachedObjects.add(copy); + return true; + } + + if (rev.getTimeStamp() != timeStamp) + { + throw new IllegalArgumentException("Invalid revision time stamp: " + CDOCommonUtil.formatTimeStamp(rev.getTimeStamp())); + } + + 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); + } + 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; + } + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java new file mode 100644 index 000000000..25e29ab13 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2009-2012 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.LinkedList; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public class StoreAccessorPool +{ + /** + * @since 4.2 + */ + public static final int DEFAULT_CAPACITY = 15; + + /** + * 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 null if + * this pool is not contextual. + */ + private Object context; + + private int capacity = DEFAULT_CAPACITY; + + private LinkedList accessors = new LinkedList(); + + public StoreAccessorPool(IStore store, Object context) + { + this.store = store; + this.context = context; + } + + public IStore getStore() + { + return store; + } + + public Object getContext() + { + return context; + } + + /** + * @since 4.2 + */ + public int getCapacity() + { + return capacity; + } + + /** + * @since 4.2 + */ + public void setCapacity(int capacity) + { + this.capacity = capacity; + retainStoreAccessors(capacity); + } + + /** + * Passivates the given {@link StoreAccessor store accessor} and adds it to this pool if the pool size is smaller than the {@link #getCapacity() capacity}, + * or disposes of the store accessor otherwise. + * + * @since 4.0 + */ + public void addStoreAccessor(StoreAccessorBase storeAccessor) + { + try + { + storeAccessor.doPassivate(); + + boolean full = false; + synchronized (accessors) + { + if (accessors.size() >= capacity) + { + full = true; + } + else + { + accessors.addFirst(storeAccessor); + } + } + + if (full) + { + disposeStoreAccessor(storeAccessor); + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + + /** + * Returns a {@link StoreAccessor store accessor} from this pool if one is available, or null 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; + synchronized (accessors) + { + 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() + { + retainStoreAccessors(0); + + context = null; + store = null; + } + + /** + * @since 4.2 + */ + protected void retainStoreAccessors(int targetSize) + { + synchronized (accessors) + { + while (accessors.size() > targetSize) + { + StoreAccessorBase accessor = accessors.removeLast(); + disposeStoreAccessor(accessor); + } + } + } + + /** + * @since 4.2 + */ + protected void disposeStoreAccessor(StoreAccessorBase accessor) + { + LifecycleUtil.deactivate(accessor, OMLogger.Level.WARN); + } +} diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java new file mode 100644 index 000000000..715d8ad18 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2009, 2011, 2012 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class StoreChunkReader implements IStoreChunkReader +{ + private IStoreAccessor accessor; + + private CDORevision revision; + + private EStructuralFeature feature; + + private List chunks = new ArrayList(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 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/SyncingUtil.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/SyncingUtil.java new file mode 100644 index 000000000..e7645da34 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/SyncingUtil.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2016 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/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/package-info.java b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/package-info.java new file mode 100644 index 000000000..0cba6b352 --- /dev/null +++ b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server.source/org/eclipse/emf/cdo/spi/server/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2011, 2012, 2015 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; diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server/org.eclipse.emf.cdo.server-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.server/org.eclipse.emf.cdo.server-4.5.0.jar deleted file mode 100644 index bb7f770d2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.server/org.eclipse.emf.cdo.server-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.source/org.eclipse.emf.cdo.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.source/org.eclipse.emf.cdo.source-4.5.0.jar deleted file mode 100644 index 08113fd81..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.source/org.eclipse.emf.cdo.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.all.source/org.eclipse.emf.cdo.tests.all.source-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.all.source/org.eclipse.emf.cdo.tests.all.source-4.1.300.jar deleted file mode 100644 index 623441898..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.all.source/org.eclipse.emf.cdo.tests.all.source-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.all/org.eclipse.emf.cdo.tests.all-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.all/org.eclipse.emf.cdo.tests.all-4.1.300.jar deleted file mode 100644 index 5d5979ddc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.all/org.eclipse.emf.cdo.tests.all-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db.source/org.eclipse.emf.cdo.tests.db.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db.source/org.eclipse.emf.cdo.tests.db.source-4.0.500.jar deleted file mode 100644 index 459fb7338..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db.source/org.eclipse.emf.cdo.tests.db.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db/org.eclipse.emf.cdo.tests.db-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db/org.eclipse.emf.cdo.tests.db-4.0.500.jar deleted file mode 100644 index 5558b8a6d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db/org.eclipse.emf.cdo.tests.db-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db4o.source/org.eclipse.emf.cdo.tests.db4o.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db4o.source/org.eclipse.emf.cdo.tests.db4o.source-4.0.500.jar deleted file mode 100644 index 9d671e24c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db4o.source/org.eclipse.emf.cdo.tests.db4o.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db4o/org.eclipse.emf.cdo.tests.db4o-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db4o/org.eclipse.emf.cdo.tests.db4o-4.0.500.jar deleted file mode 100644 index 4ea6659ca..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.db4o/org.eclipse.emf.cdo.tests.db4o-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.hibernate.source/org.eclipse.emf.cdo.tests.hibernate.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.hibernate.source/org.eclipse.emf.cdo.tests.hibernate.source-4.1.400.jar deleted file mode 100644 index 516fdbfd0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.hibernate.source/org.eclipse.emf.cdo.tests.hibernate.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.hibernate/org.eclipse.emf.cdo.tests.hibernate-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.hibernate/org.eclipse.emf.cdo.tests.hibernate-4.1.400.jar deleted file mode 100644 index 413600163..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.hibernate/org.eclipse.emf.cdo.tests.hibernate-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.lissome.source/org.eclipse.emf.cdo.tests.lissome.source-4.2.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.lissome.source/org.eclipse.emf.cdo.tests.lissome.source-4.2.100.jar deleted file mode 100644 index 90037d114..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.lissome.source/org.eclipse.emf.cdo.tests.lissome.source-4.2.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.lissome/org.eclipse.emf.cdo.tests.lissome-4.2.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.lissome/org.eclipse.emf.cdo.tests.lissome-4.2.100.jar deleted file mode 100644 index 3e9699121..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.lissome/org.eclipse.emf.cdo.tests.lissome-4.2.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mango.source/org.eclipse.emf.cdo.tests.mango.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mango.source/org.eclipse.emf.cdo.tests.mango.source-3.0.500.jar deleted file mode 100644 index f340be98b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mango.source/org.eclipse.emf.cdo.tests.mango.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mango/org.eclipse.emf.cdo.tests.mango-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mango/org.eclipse.emf.cdo.tests.mango-3.0.500.jar deleted file mode 100644 index 82ded1a78..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mango/org.eclipse.emf.cdo.tests.mango-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model1.source/org.eclipse.emf.cdo.tests.model1.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model1.source/org.eclipse.emf.cdo.tests.model1.source-3.0.500.jar deleted file mode 100644 index 497fc0e15..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model1.source/org.eclipse.emf.cdo.tests.model1.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model1/org.eclipse.emf.cdo.tests.model1-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model1/org.eclipse.emf.cdo.tests.model1-3.0.500.jar deleted file mode 100644 index db20c815e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model1/org.eclipse.emf.cdo.tests.model1-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model2.source/org.eclipse.emf.cdo.tests.model2.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model2.source/org.eclipse.emf.cdo.tests.model2.source-3.0.500.jar deleted file mode 100644 index 103341e5d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model2.source/org.eclipse.emf.cdo.tests.model2.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model2/org.eclipse.emf.cdo.tests.model2-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model2/org.eclipse.emf.cdo.tests.model2-3.0.500.jar deleted file mode 100644 index 97484ea6e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model2/org.eclipse.emf.cdo.tests.model2-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model3.source/org.eclipse.emf.cdo.tests.model3.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model3.source/org.eclipse.emf.cdo.tests.model3.source-3.0.500.jar deleted file mode 100644 index cb551ab4e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model3.source/org.eclipse.emf.cdo.tests.model3.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model3/org.eclipse.emf.cdo.tests.model3-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model3/org.eclipse.emf.cdo.tests.model3-3.0.500.jar deleted file mode 100644 index fb0dd3c42..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model3/org.eclipse.emf.cdo.tests.model3-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4.source/org.eclipse.emf.cdo.tests.model4.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4.source/org.eclipse.emf.cdo.tests.model4.source-3.0.500.jar deleted file mode 100644 index 3c9469b9c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4.source/org.eclipse.emf.cdo.tests.model4.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4/org.eclipse.emf.cdo.tests.model4-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4/org.eclipse.emf.cdo.tests.model4-3.0.500.jar deleted file mode 100644 index c480944d1..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4/org.eclipse.emf.cdo.tests.model4-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4interfaces.source/org.eclipse.emf.cdo.tests.model4interfaces.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4interfaces.source/org.eclipse.emf.cdo.tests.model4interfaces.source-3.0.500.jar deleted file mode 100644 index 37d5c6c17..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4interfaces.source/org.eclipse.emf.cdo.tests.model4interfaces.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4interfaces/org.eclipse.emf.cdo.tests.model4interfaces-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4interfaces/org.eclipse.emf.cdo.tests.model4interfaces-3.0.500.jar deleted file mode 100644 index 35091a515..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model4interfaces/org.eclipse.emf.cdo.tests.model4interfaces-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model5.source/org.eclipse.emf.cdo.tests.model5.source-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model5.source/org.eclipse.emf.cdo.tests.model5.source-3.0.500.jar deleted file mode 100644 index 180c32b42..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model5.source/org.eclipse.emf.cdo.tests.model5.source-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model5/org.eclipse.emf.cdo.tests.model5-3.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model5/org.eclipse.emf.cdo.tests.model5-3.0.500.jar deleted file mode 100644 index 03c6d03e4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model5/org.eclipse.emf.cdo.tests.model5-3.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model6.source/org.eclipse.emf.cdo.tests.model6.source-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model6.source/org.eclipse.emf.cdo.tests.model6.source-4.0.300.jar deleted file mode 100644 index 17a9ad983..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model6.source/org.eclipse.emf.cdo.tests.model6.source-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model6/org.eclipse.emf.cdo.tests.model6-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model6/org.eclipse.emf.cdo.tests.model6-4.0.300.jar deleted file mode 100644 index 29bb97078..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.model6/org.eclipse.emf.cdo.tests.model6-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mongodb.source/org.eclipse.emf.cdo.tests.mongodb.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mongodb.source/org.eclipse.emf.cdo.tests.mongodb.source-4.0.400.jar deleted file mode 100644 index 2e31f8f33..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mongodb.source/org.eclipse.emf.cdo.tests.mongodb.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mongodb/org.eclipse.emf.cdo.tests.mongodb-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mongodb/org.eclipse.emf.cdo.tests.mongodb-4.0.400.jar deleted file mode 100644 index 16cb1a1a3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.mongodb/org.eclipse.emf.cdo.tests.mongodb-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.objectivity.source/org.eclipse.emf.cdo.tests.objectivity.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.objectivity.source/org.eclipse.emf.cdo.tests.objectivity.source-4.0.400.jar deleted file mode 100644 index e69ffd0f2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.objectivity.source/org.eclipse.emf.cdo.tests.objectivity.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.objectivity/org.eclipse.emf.cdo.tests.objectivity-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.objectivity/org.eclipse.emf.cdo.tests.objectivity-4.0.400.jar deleted file mode 100644 index bef3a3831..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.objectivity/org.eclipse.emf.cdo.tests.objectivity-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.source/org.eclipse.emf.cdo.tests.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.source/org.eclipse.emf.cdo.tests.source-4.0.500.jar deleted file mode 100644 index 22787f782..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.source/org.eclipse.emf.cdo.tests.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.ui.source/org.eclipse.emf.cdo.tests.ui.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.ui.source/org.eclipse.emf.cdo.tests.ui.source-4.0.400.jar deleted file mode 100644 index a71207fd3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.ui.source/org.eclipse.emf.cdo.tests.ui.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.ui/org.eclipse.emf.cdo.tests.ui-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.ui/org.eclipse.emf.cdo.tests.ui-4.0.400.jar deleted file mode 100644 index 1d2bd57ea..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.ui/org.eclipse.emf.cdo.tests.ui-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.uml.source/org.eclipse.emf.cdo.tests.uml.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.uml.source/org.eclipse.emf.cdo.tests.uml.source-4.2.300.jar deleted file mode 100644 index fe2e818d5..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.uml.source/org.eclipse.emf.cdo.tests.uml.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.uml/org.eclipse.emf.cdo.tests.uml-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.uml/org.eclipse.emf.cdo.tests.uml-4.2.300.jar deleted file mode 100644 index 11c8a0ef1..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests.uml/org.eclipse.emf.cdo.tests.uml-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests/org.eclipse.emf.cdo.tests-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests/org.eclipse.emf.cdo.tests-4.0.500.jar deleted file mode 100644 index 8987bbfc9..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.tests/org.eclipse.emf.cdo.tests-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.repository.source/org.eclipse.emf.cdo.transfer.repository.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.repository.source/org.eclipse.emf.cdo.transfer.repository.source-4.2.300.jar deleted file mode 100644 index 66157fbff..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.repository.source/org.eclipse.emf.cdo.transfer.repository.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.repository/org.eclipse.emf.cdo.transfer.repository-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.repository/org.eclipse.emf.cdo.transfer.repository-4.2.300.jar deleted file mode 100644 index 97f061afe..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.repository/org.eclipse.emf.cdo.transfer.repository-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.source/org.eclipse.emf.cdo.transfer.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.source/org.eclipse.emf.cdo.transfer.source-4.2.300.jar deleted file mode 100644 index 649fb3ed8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.source/org.eclipse.emf.cdo.transfer.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.ui.source/org.eclipse.emf.cdo.transfer.ui.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.ui.source/org.eclipse.emf.cdo.transfer.ui.source-4.2.300.jar deleted file mode 100644 index 23ba21207..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.ui.source/org.eclipse.emf.cdo.transfer.ui.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.ui/org.eclipse.emf.cdo.transfer.ui-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.ui/org.eclipse.emf.cdo.transfer.ui-4.2.300.jar deleted file mode 100644 index 19aabcadd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.ui/org.eclipse.emf.cdo.transfer.ui-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.source/org.eclipse.emf.cdo.transfer.workspace.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.source/org.eclipse.emf.cdo.transfer.workspace.source-4.2.300.jar deleted file mode 100644 index cc1a710b1..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.source/org.eclipse.emf.cdo.transfer.workspace.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.ui.source/org.eclipse.emf.cdo.transfer.workspace.ui.source-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.ui.source/org.eclipse.emf.cdo.transfer.workspace.ui.source-4.2.200.jar deleted file mode 100644 index 063807c1d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.ui.source/org.eclipse.emf.cdo.transfer.workspace.ui.source-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.ui/org.eclipse.emf.cdo.transfer.workspace.ui-4.2.200.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.ui/org.eclipse.emf.cdo.transfer.workspace.ui-4.2.200.jar deleted file mode 100644 index 7723b034e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace.ui/org.eclipse.emf.cdo.transfer.workspace.ui-4.2.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace/org.eclipse.emf.cdo.transfer.workspace-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace/org.eclipse.emf.cdo.transfer.workspace-4.2.300.jar deleted file mode 100644 index 479b6515b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer.workspace/org.eclipse.emf.cdo.transfer.workspace-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer/org.eclipse.emf.cdo.transfer-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer/org.eclipse.emf.cdo.transfer-4.2.300.jar deleted file mode 100644 index 3f4f93989..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.transfer/org.eclipse.emf.cdo.transfer-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.admin.source/org.eclipse.emf.cdo.ui.admin.source-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.admin.source/org.eclipse.emf.cdo.ui.admin.source-4.1.300.jar deleted file mode 100644 index a8f8cd23c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.admin.source/org.eclipse.emf.cdo.ui.admin.source-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.admin/org.eclipse.emf.cdo.ui.admin-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.admin/org.eclipse.emf.cdo.ui.admin-4.1.300.jar deleted file mode 100644 index 0131c93ed..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.admin/org.eclipse.emf.cdo.ui.admin-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.compare.source/org.eclipse.emf.cdo.ui.compare.source-4.4.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.compare.source/org.eclipse.emf.cdo.ui.compare.source-4.4.0.jar deleted file mode 100644 index eba19214b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.compare.source/org.eclipse.emf.cdo.ui.compare.source-4.4.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.compare/org.eclipse.emf.cdo.ui.compare-4.4.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.compare/org.eclipse.emf.cdo.ui.compare-4.4.0.jar deleted file mode 100644 index c578e7e0b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.compare/org.eclipse.emf.cdo.ui.compare-4.4.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.defs.source/org.eclipse.emf.cdo.ui.defs.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.defs.source/org.eclipse.emf.cdo.ui.defs.source-4.0.400.jar deleted file mode 100644 index 8904a4e44..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.defs.source/org.eclipse.emf.cdo.ui.defs.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.defs/org.eclipse.emf.cdo.ui.defs-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.defs/org.eclipse.emf.cdo.ui.defs-4.0.400.jar deleted file mode 100644 index bd62ccdd4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.defs/org.eclipse.emf.cdo.ui.defs-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.shared.source/org.eclipse.emf.cdo.ui.shared.source-4.3.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.shared.source/org.eclipse.emf.cdo.ui.shared.source-4.3.0.jar deleted file mode 100644 index f6ae08450..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.shared.source/org.eclipse.emf.cdo.ui.shared.source-4.3.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.shared/org.eclipse.emf.cdo.ui.shared-4.3.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.shared/org.eclipse.emf.cdo.ui.shared-4.3.0.jar deleted file mode 100644 index ad2b515b3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.shared/org.eclipse.emf.cdo.ui.shared-4.3.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.source/org.eclipse.emf.cdo.ui.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.source/org.eclipse.emf.cdo.ui.source-4.5.0.jar deleted file mode 100644 index a41f86aba..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.source/org.eclipse.emf.cdo.ui.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.team.source/org.eclipse.emf.cdo.ui.team.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.team.source/org.eclipse.emf.cdo.ui.team.source-4.2.300.jar deleted file mode 100644 index 21acb6e45..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.team.source/org.eclipse.emf.cdo.ui.team.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.team/org.eclipse.emf.cdo.ui.team-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.team/org.eclipse.emf.cdo.ui.team-4.2.300.jar deleted file mode 100644 index 2f50bbca7..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui.team/org.eclipse.emf.cdo.ui.team-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui/org.eclipse.emf.cdo.ui-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui/org.eclipse.emf.cdo.ui-4.5.0.jar deleted file mode 100644 index 751b6264b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.ui/org.eclipse.emf.cdo.ui-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.workspace.source/org.eclipse.emf.cdo.workspace.source-4.2.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.workspace.source/org.eclipse.emf.cdo.workspace.source-4.2.100.jar deleted file mode 100644 index 70e674e6b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.workspace.source/org.eclipse.emf.cdo.workspace.source-4.2.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo.workspace/org.eclipse.emf.cdo.workspace-4.2.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo.workspace/org.eclipse.emf.cdo.workspace-4.2.100.jar deleted file mode 100644 index a157eb606..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo.workspace/org.eclipse.emf.cdo.workspace-4.2.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo/org.eclipse.emf.cdo-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo/org.eclipse.emf.cdo-4.5.0.jar deleted file mode 100644 index a91b1f860..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.cdo/org.eclipse.emf.cdo-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.cdo/org.eclipse.emf.cdo-4.6.100.jar b/bundles/cnf/localrepo/org.eclipse.emf.cdo/org.eclipse.emf.cdo-4.6.100.jar new file mode 100644 index 000000000..4e7df9d05 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.cdo/org.eclipse.emf.cdo-4.6.100.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.codegen.ecore.ui/org.eclipse.emf.codegen.ecore.ui-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.codegen.ecore.ui/org.eclipse.emf.codegen.ecore.ui-2.12.0.jar deleted file mode 100644 index 00c65e7f1..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.codegen.ecore.ui/org.eclipse.emf.codegen.ecore.ui-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.codegen.ecore/org.eclipse.emf.codegen.ecore-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.codegen.ecore/org.eclipse.emf.codegen.ecore-2.12.0.jar deleted file mode 100644 index 5bb0092c1..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.codegen.ecore/org.eclipse.emf.codegen.ecore-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.codegen.ui/org.eclipse.emf.codegen.ui-2.6.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.codegen.ui/org.eclipse.emf.codegen.ui-2.6.0.jar deleted file mode 100644 index 44bcc4c02..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.codegen.ui/org.eclipse.emf.codegen.ui-2.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.codegen/org.eclipse.emf.codegen-2.11.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.codegen/org.eclipse.emf.codegen-2.11.0.jar deleted file mode 100644 index c6ae9f218..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.codegen/org.eclipse.emf.codegen-2.11.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.common.ui/org.eclipse.emf.common.ui-2.11.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.common.ui/org.eclipse.emf.common.ui-2.11.0.jar deleted file mode 100644 index 24fc192fc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.common.ui/org.eclipse.emf.common.ui-2.11.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.common/org.eclipse.emf.common-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.common/org.eclipse.emf.common-2.12.0.jar deleted file mode 100644 index 08910161d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.common/org.eclipse.emf.common-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.common/org.eclipse.emf.common-2.14.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.common/org.eclipse.emf.common-2.14.0.jar new file mode 100644 index 000000000..c4498485f Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.common/org.eclipse.emf.common-2.14.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.converter/org.eclipse.emf.converter-2.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.converter/org.eclipse.emf.converter-2.7.0.jar deleted file mode 100644 index a9db34023..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.converter/org.eclipse.emf.converter-2.7.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.databinding.edit/org.eclipse.emf.databinding.edit-1.3.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.databinding.edit/org.eclipse.emf.databinding.edit-1.3.0.jar deleted file mode 100644 index 80e1bdbd0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.databinding.edit/org.eclipse.emf.databinding.edit-1.3.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.databinding/org.eclipse.emf.databinding-1.3.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.databinding/org.eclipse.emf.databinding-1.3.0.jar deleted file mode 100644 index a200badf0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.databinding/org.eclipse.emf.databinding-1.3.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.change.edit/org.eclipse.emf.ecore.change.edit-2.6.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.change.edit/org.eclipse.emf.ecore.change.edit-2.6.0.jar deleted file mode 100644 index 63a8e6297..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ecore.change.edit/org.eclipse.emf.ecore.change.edit-2.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.change/org.eclipse.emf.ecore.change-2.11.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.change/org.eclipse.emf.ecore.change-2.11.0.jar deleted file mode 100644 index ba9d392e6..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ecore.change/org.eclipse.emf.ecore.change-2.11.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.change/org.eclipse.emf.ecore.change-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.change/org.eclipse.emf.ecore.change-2.12.0.jar new file mode 100644 index 000000000..51b0fd32c Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.ecore.change/org.eclipse.emf.ecore.change-2.12.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.edit/org.eclipse.emf.ecore.edit-2.9.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.edit/org.eclipse.emf.ecore.edit-2.9.0.jar deleted file mode 100644 index 285270d4e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ecore.edit/org.eclipse.emf.ecore.edit-2.9.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.editor/org.eclipse.emf.ecore.editor-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.editor/org.eclipse.emf.ecore.editor-2.12.0.jar deleted file mode 100644 index 6f78ee176..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ecore.editor/org.eclipse.emf.ecore.editor-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.xmi/org.eclipse.emf.ecore.xmi-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.xmi/org.eclipse.emf.ecore.xmi-2.12.0.jar deleted file mode 100644 index fd278fee8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ecore.xmi/org.eclipse.emf.ecore.xmi-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore.xmi/org.eclipse.emf.ecore.xmi-2.14.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore.xmi/org.eclipse.emf.ecore.xmi-2.14.0.jar new file mode 100644 index 000000000..d4d100fad Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.ecore.xmi/org.eclipse.emf.ecore.xmi-2.14.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore/org.eclipse.emf.ecore-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore/org.eclipse.emf.ecore-2.12.0.jar deleted file mode 100644 index 7a6abb76a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.ecore/org.eclipse.emf.ecore-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.ecore/org.eclipse.emf.ecore-2.14.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.ecore/org.eclipse.emf.ecore-2.14.0.jar new file mode 100644 index 000000000..29e46580f Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf.ecore/org.eclipse.emf.ecore-2.14.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.edit.ui/org.eclipse.emf.edit.ui-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.edit.ui/org.eclipse.emf.edit.ui-2.12.0.jar deleted file mode 100644 index 431ad1698..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.edit.ui/org.eclipse.emf.edit.ui-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.edit/org.eclipse.emf.edit-2.12.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.edit/org.eclipse.emf.edit-2.12.0.jar deleted file mode 100644 index 90857a960..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.edit/org.eclipse.emf.edit-2.12.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.exporter/org.eclipse.emf.exporter-2.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.exporter/org.eclipse.emf.exporter-2.7.0.jar deleted file mode 100644 index 02bbb8b4b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.exporter/org.eclipse.emf.exporter-2.7.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.importer.ecore/org.eclipse.emf.importer.ecore-2.8.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.importer.ecore/org.eclipse.emf.importer.ecore-2.8.0.jar deleted file mode 100644 index e70822c1d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.importer.ecore/org.eclipse.emf.importer.ecore-2.8.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.importer.java/org.eclipse.emf.importer.java-2.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.importer.java/org.eclipse.emf.importer.java-2.7.0.jar deleted file mode 100644 index 01cb20f6a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.importer.java/org.eclipse.emf.importer.java-2.7.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.importer.rose/org.eclipse.emf.importer.rose-2.8.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.importer.rose/org.eclipse.emf.importer.rose-2.8.0.jar deleted file mode 100644 index bdad423ec..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.importer.rose/org.eclipse.emf.importer.rose-2.8.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.importer/org.eclipse.emf.importer-2.9.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.importer/org.eclipse.emf.importer-2.9.0.jar deleted file mode 100644 index 4fe3fd615..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.importer/org.eclipse.emf.importer-2.9.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore.editor/org.eclipse.emf.mapping.ecore.editor-2.6.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore.editor/org.eclipse.emf.mapping.ecore.editor-2.6.0.jar deleted file mode 100644 index 908c1f773..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore.editor/org.eclipse.emf.mapping.ecore.editor-2.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore/org.eclipse.emf.mapping.ecore-2.6.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore/org.eclipse.emf.mapping.ecore-2.6.0.jar deleted file mode 100644 index 32546a5db..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore/org.eclipse.emf.mapping.ecore-2.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2ecore.editor/org.eclipse.emf.mapping.ecore2ecore.editor-2.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2ecore.editor/org.eclipse.emf.mapping.ecore2ecore.editor-2.7.0.jar deleted file mode 100644 index 94771e26e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2ecore.editor/org.eclipse.emf.mapping.ecore2ecore.editor-2.7.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2ecore/org.eclipse.emf.mapping.ecore2ecore-2.9.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2ecore/org.eclipse.emf.mapping.ecore2ecore-2.9.0.jar deleted file mode 100644 index d350bf2f3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2ecore/org.eclipse.emf.mapping.ecore2ecore-2.9.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2xml.ui/org.eclipse.emf.mapping.ecore2xml.ui-2.8.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2xml.ui/org.eclipse.emf.mapping.ecore2xml.ui-2.8.0.jar deleted file mode 100644 index b78246d5c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2xml.ui/org.eclipse.emf.mapping.ecore2xml.ui-2.8.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2xml/org.eclipse.emf.mapping.ecore2xml-2.9.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2xml/org.eclipse.emf.mapping.ecore2xml-2.9.0.jar deleted file mode 100644 index 41ab6802b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ecore2xml/org.eclipse.emf.mapping.ecore2xml-2.9.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ui/org.eclipse.emf.mapping.ui-2.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping.ui/org.eclipse.emf.mapping.ui-2.7.0.jar deleted file mode 100644 index 2e89a1094..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping.ui/org.eclipse.emf.mapping.ui-2.7.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf.mapping/org.eclipse.emf.mapping-2.9.0.jar b/bundles/cnf/localrepo/org.eclipse.emf.mapping/org.eclipse.emf.mapping-2.9.0.jar deleted file mode 100644 index 54a5bf179..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf.mapping/org.eclipse.emf.mapping-2.9.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf/org.eclipse.emf-2.6.0.jar b/bundles/cnf/localrepo/org.eclipse.emf/org.eclipse.emf-2.6.0.jar deleted file mode 100644 index 855bed640..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.emf/org.eclipse.emf-2.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.emf/org.eclipse.emf-2.7.0.jar b/bundles/cnf/localrepo/org.eclipse.emf/org.eclipse.emf-2.7.0.jar new file mode 100644 index 000000000..f5f373176 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.emf/org.eclipse.emf-2.7.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.source/org.eclipse.net4j.buddies.chat.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.source/org.eclipse.net4j.buddies.chat.source-4.0.500.jar deleted file mode 100644 index 0b7812d08..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.source/org.eclipse.net4j.buddies.chat.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.ui.source/org.eclipse.net4j.buddies.chat.ui.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.ui.source/org.eclipse.net4j.buddies.chat.ui.source-4.0.400.jar deleted file mode 100644 index 7d1c8402e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.ui.source/org.eclipse.net4j.buddies.chat.ui.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.ui/org.eclipse.net4j.buddies.chat.ui-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.ui/org.eclipse.net4j.buddies.chat.ui-4.0.400.jar deleted file mode 100644 index bd0408cca..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat.ui/org.eclipse.net4j.buddies.chat.ui-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat/org.eclipse.net4j.buddies.chat-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat/org.eclipse.net4j.buddies.chat-4.0.500.jar deleted file mode 100644 index 4e77449c2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.chat/org.eclipse.net4j.buddies.chat-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.common.source/org.eclipse.net4j.buddies.common.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.common.source/org.eclipse.net4j.buddies.common.source-4.0.400.jar deleted file mode 100644 index fade35380..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.common.source/org.eclipse.net4j.buddies.common.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.common/org.eclipse.net4j.buddies.common-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.common/org.eclipse.net4j.buddies.common-4.0.400.jar deleted file mode 100644 index dfc5c4834..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.common/org.eclipse.net4j.buddies.common-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.server.source/org.eclipse.net4j.buddies.server.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.server.source/org.eclipse.net4j.buddies.server.source-4.0.500.jar deleted file mode 100644 index 867afb061..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.server.source/org.eclipse.net4j.buddies.server.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.server/org.eclipse.net4j.buddies.server-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.server/org.eclipse.net4j.buddies.server-4.0.500.jar deleted file mode 100644 index 912791c98..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.server/org.eclipse.net4j.buddies.server-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.source/org.eclipse.net4j.buddies.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.source/org.eclipse.net4j.buddies.source-4.0.500.jar deleted file mode 100644 index d5d886974..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.source/org.eclipse.net4j.buddies.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.ui.source/org.eclipse.net4j.buddies.ui.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.ui.source/org.eclipse.net4j.buddies.ui.source-4.0.400.jar deleted file mode 100644 index 4e96d0d61..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.ui.source/org.eclipse.net4j.buddies.ui.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.ui/org.eclipse.net4j.buddies.ui-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies.ui/org.eclipse.net4j.buddies.ui-4.0.400.jar deleted file mode 100644 index f23d5096d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies.ui/org.eclipse.net4j.buddies.ui-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.buddies/org.eclipse.net4j.buddies-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.buddies/org.eclipse.net4j.buddies-4.0.500.jar deleted file mode 100644 index 134553340..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.buddies/org.eclipse.net4j.buddies-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.derby.source/org.eclipse.net4j.db.derby.source-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.derby.source/org.eclipse.net4j.db.derby.source-4.1.300.jar deleted file mode 100644 index 918d78884..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.derby.source/org.eclipse.net4j.db.derby.source-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.derby/org.eclipse.net4j.db.derby-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.derby/org.eclipse.net4j.db.derby-4.1.300.jar deleted file mode 100644 index 7f063157d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.derby/org.eclipse.net4j.db.derby-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.doc.source/org.eclipse.net4j.db.doc.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.doc.source/org.eclipse.net4j.db.doc.source-4.1.400.jar deleted file mode 100644 index bb8b88131..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.doc.source/org.eclipse.net4j.db.doc.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.doc/org.eclipse.net4j.db.doc-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.doc/org.eclipse.net4j.db.doc-4.1.400.jar deleted file mode 100644 index e2f0fba94..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.doc/org.eclipse.net4j.db.doc-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.h2.source/org.eclipse.net4j.db.h2.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.h2.source/org.eclipse.net4j.db.h2.source-4.2.300.jar deleted file mode 100644 index 065e6bc3a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.h2.source/org.eclipse.net4j.db.h2.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.h2/org.eclipse.net4j.db.h2-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.h2/org.eclipse.net4j.db.h2-4.2.300.jar deleted file mode 100644 index b386052a4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.h2/org.eclipse.net4j.db.h2-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.h2/org.eclipse.net4j.db.h2-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.h2/org.eclipse.net4j.db.h2-4.3.100.jar new file mode 100644 index 000000000..36eef3126 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.net4j.db.h2/org.eclipse.net4j.db.h2-4.3.100.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.hsqldb.source/org.eclipse.net4j.db.hsqldb.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.hsqldb.source/org.eclipse.net4j.db.hsqldb.source-4.2.300.jar deleted file mode 100644 index ff0a3d071..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.hsqldb.source/org.eclipse.net4j.db.hsqldb.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.hsqldb/org.eclipse.net4j.db.hsqldb-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.hsqldb/org.eclipse.net4j.db.hsqldb-4.2.300.jar deleted file mode 100644 index 92acc978f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.hsqldb/org.eclipse.net4j.db.hsqldb-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc.source/org.eclipse.net4j.db.jdbc.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc.source/org.eclipse.net4j.db.jdbc.source-4.3.100.jar deleted file mode 100644 index 45fbe4d5e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc.source/org.eclipse.net4j.db.jdbc.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc/org.eclipse.net4j.db.jdbc-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc/org.eclipse.net4j.db.jdbc-4.3.100.jar deleted file mode 100644 index 6602aa1b0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc/org.eclipse.net4j.db.jdbc-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc/org.eclipse.net4j.db.jdbc-4.3.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc/org.eclipse.net4j.db.jdbc-4.3.300.jar new file mode 100644 index 000000000..5ccd70165 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.net4j.db.jdbc/org.eclipse.net4j.db.jdbc-4.3.300.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.mysql.source/org.eclipse.net4j.db.mysql.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.mysql.source/org.eclipse.net4j.db.mysql.source-4.2.300.jar deleted file mode 100644 index d44bc513f..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.mysql.source/org.eclipse.net4j.db.mysql.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.mysql/org.eclipse.net4j.db.mysql-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.mysql/org.eclipse.net4j.db.mysql-4.2.300.jar deleted file mode 100644 index d311d3a48..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.mysql/org.eclipse.net4j.db.mysql-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.oracle.source/org.eclipse.net4j.db.oracle.source-1.1.200.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.oracle.source/org.eclipse.net4j.db.oracle.source-1.1.200.jar deleted file mode 100644 index 92235a6dc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.oracle.source/org.eclipse.net4j.db.oracle.source-1.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.oracle/org.eclipse.net4j.db.oracle-1.1.200.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.oracle/org.eclipse.net4j.db.oracle-1.1.200.jar deleted file mode 100644 index 2da271301..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.oracle/org.eclipse.net4j.db.oracle-1.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.postgresql.source/org.eclipse.net4j.db.postgresql.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.postgresql.source/org.eclipse.net4j.db.postgresql.source-4.2.300.jar deleted file mode 100644 index 38955e50a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.postgresql.source/org.eclipse.net4j.db.postgresql.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.postgresql/org.eclipse.net4j.db.postgresql-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.postgresql/org.eclipse.net4j.db.postgresql-4.2.300.jar deleted file mode 100644 index dd78d5217..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.postgresql/org.eclipse.net4j.db.postgresql-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.source/org.eclipse.net4j.db.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.source/org.eclipse.net4j.db.source-4.5.0.jar deleted file mode 100644 index 86edb8b1b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.source/org.eclipse.net4j.db.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.tests.source/org.eclipse.net4j.db.tests.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.tests.source/org.eclipse.net4j.db.tests.source-4.0.400.jar deleted file mode 100644 index 410b92132..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.tests.source/org.eclipse.net4j.db.tests.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db.tests/org.eclipse.net4j.db.tests-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db.tests/org.eclipse.net4j.db.tests-4.0.400.jar deleted file mode 100644 index c00ddd9f8..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db.tests/org.eclipse.net4j.db.tests-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.db/org.eclipse.net4j.db-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.db/org.eclipse.net4j.db-4.5.0.jar deleted file mode 100644 index b0a433d57..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.db/org.eclipse.net4j.db-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.debug.source/org.eclipse.net4j.debug.source-3.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.debug.source/org.eclipse.net4j.debug.source-3.0.400.jar deleted file mode 100644 index 8f9306f83..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.debug.source/org.eclipse.net4j.debug.source-3.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.debug/org.eclipse.net4j.debug-3.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.debug/org.eclipse.net4j.debug-3.0.400.jar deleted file mode 100644 index 94a901b39..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.debug/org.eclipse.net4j.debug-3.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.defs.source/org.eclipse.net4j.defs.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.defs.source/org.eclipse.net4j.defs.source-4.0.400.jar deleted file mode 100644 index b730963da..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.defs.source/org.eclipse.net4j.defs.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.defs/org.eclipse.net4j.defs-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.defs/org.eclipse.net4j.defs-4.0.400.jar deleted file mode 100644 index c4107c902..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.defs/org.eclipse.net4j.defs-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.doc.source/org.eclipse.net4j.doc.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.doc.source/org.eclipse.net4j.doc.source-4.1.400.jar deleted file mode 100644 index 874e477fa..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.doc.source/org.eclipse.net4j.doc.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.doc/org.eclipse.net4j.doc-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.doc/org.eclipse.net4j.doc-4.1.400.jar deleted file mode 100644 index cec7cdc4c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.doc/org.eclipse.net4j.doc-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.examples.installer.source/org.eclipse.net4j.examples.installer.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.examples.installer.source/org.eclipse.net4j.examples.installer.source-4.1.400.jar deleted file mode 100644 index 2671f3fdc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.examples.installer.source/org.eclipse.net4j.examples.installer.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.examples.installer/org.eclipse.net4j.examples.installer-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.examples.installer/org.eclipse.net4j.examples.installer-4.1.400.jar deleted file mode 100644 index 559bcd828..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.examples.installer/org.eclipse.net4j.examples.installer-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.examples.source/org.eclipse.net4j.examples.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.examples.source/org.eclipse.net4j.examples.source-4.0.500.jar deleted file mode 100644 index dd13b6af4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.examples.source/org.eclipse.net4j.examples.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.examples/org.eclipse.net4j.examples-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.examples/org.eclipse.net4j.examples-4.0.500.jar deleted file mode 100644 index b7122e64c..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.examples/org.eclipse.net4j.examples-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.common.source/org.eclipse.net4j.http.common.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.common.source/org.eclipse.net4j.http.common.source-4.0.400.jar deleted file mode 100644 index dd1ee6c76..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.common.source/org.eclipse.net4j.http.common.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.common/org.eclipse.net4j.http.common-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.common/org.eclipse.net4j.http.common-4.0.400.jar deleted file mode 100644 index 8f66649c3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.common/org.eclipse.net4j.http.common-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.server.source/org.eclipse.net4j.http.server.source-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.server.source/org.eclipse.net4j.http.server.source-4.0.500.jar deleted file mode 100644 index 9d5b90e88..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.server.source/org.eclipse.net4j.http.server.source-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.server/org.eclipse.net4j.http.server-4.0.500.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.server/org.eclipse.net4j.http.server-4.0.500.jar deleted file mode 100644 index 205643e7e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.server/org.eclipse.net4j.http.server-4.0.500.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.source/org.eclipse.net4j.http.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.source/org.eclipse.net4j.http.source-4.0.400.jar deleted file mode 100644 index 02af54c6b..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.source/org.eclipse.net4j.http.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.tests.source/org.eclipse.net4j.http.tests.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.tests.source/org.eclipse.net4j.http.tests.source-4.0.400.jar deleted file mode 100644 index 9b85a89cd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.tests.source/org.eclipse.net4j.http.tests.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http.tests/org.eclipse.net4j.http.tests-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http.tests/org.eclipse.net4j.http.tests-4.0.400.jar deleted file mode 100644 index 79bc011ff..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http.tests/org.eclipse.net4j.http.tests-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.http/org.eclipse.net4j.http-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.http/org.eclipse.net4j.http-4.0.400.jar deleted file mode 100644 index 0db6136b4..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.http/org.eclipse.net4j.http-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.admin.source/org.eclipse.net4j.jms.admin.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.admin.source/org.eclipse.net4j.jms.admin.source-4.0.400.jar deleted file mode 100644 index 282d9f2ce..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.admin.source/org.eclipse.net4j.jms.admin.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.admin/org.eclipse.net4j.jms.admin-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.admin/org.eclipse.net4j.jms.admin-4.0.400.jar deleted file mode 100644 index 3b9371bec..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.admin/org.eclipse.net4j.jms.admin-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.api.source/org.eclipse.net4j.jms.api.source-3.1.200.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.api.source/org.eclipse.net4j.jms.api.source-3.1.200.jar deleted file mode 100644 index c060d3797..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.api.source/org.eclipse.net4j.jms.api.source-3.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.api/org.eclipse.net4j.jms.api-3.1.200.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.api/org.eclipse.net4j.jms.api-3.1.200.jar deleted file mode 100644 index 2c0f67646..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.api/org.eclipse.net4j.jms.api-3.1.200.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.jdbc.source/org.eclipse.net4j.jms.server.jdbc.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.jdbc.source/org.eclipse.net4j.jms.server.jdbc.source-4.0.400.jar deleted file mode 100644 index 1703487c0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.jdbc.source/org.eclipse.net4j.jms.server.jdbc.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.jdbc/org.eclipse.net4j.jms.server.jdbc-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.jdbc/org.eclipse.net4j.jms.server.jdbc-4.0.400.jar deleted file mode 100644 index b7f7cce03..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.jdbc/org.eclipse.net4j.jms.server.jdbc-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.source/org.eclipse.net4j.jms.server.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.source/org.eclipse.net4j.jms.server.source-4.0.400.jar deleted file mode 100644 index b88b06976..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server.source/org.eclipse.net4j.jms.server.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server/org.eclipse.net4j.jms.server-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.server/org.eclipse.net4j.jms.server-4.0.400.jar deleted file mode 100644 index 45d840935..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.server/org.eclipse.net4j.jms.server-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.source/org.eclipse.net4j.jms.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.source/org.eclipse.net4j.jms.source-4.0.400.jar deleted file mode 100644 index f3f3f48cd..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.source/org.eclipse.net4j.jms.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.tests.source/org.eclipse.net4j.jms.tests.source-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.tests.source/org.eclipse.net4j.jms.tests.source-4.0.300.jar deleted file mode 100644 index c62d7bbe0..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.tests.source/org.eclipse.net4j.jms.tests.source-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms.tests/org.eclipse.net4j.jms.tests-4.0.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms.tests/org.eclipse.net4j.jms.tests-4.0.300.jar deleted file mode 100644 index 8803c4660..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms.tests/org.eclipse.net4j.jms.tests-4.0.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jms/org.eclipse.net4j.jms-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jms/org.eclipse.net4j.jms-4.0.400.jar deleted file mode 100644 index 5401b771a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jms/org.eclipse.net4j.jms-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jvm.source/org.eclipse.net4j.jvm.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jvm.source/org.eclipse.net4j.jvm.source-4.1.400.jar deleted file mode 100644 index 5127b52ba..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jvm.source/org.eclipse.net4j.jvm.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.jvm/org.eclipse.net4j.jvm-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.jvm/org.eclipse.net4j.jvm-4.1.400.jar deleted file mode 100644 index 72cc7cc7a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.jvm/org.eclipse.net4j.jvm-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.sdk.source/org.eclipse.net4j.sdk.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.sdk.source/org.eclipse.net4j.sdk.source-4.1.400.jar deleted file mode 100644 index 944b53a66..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.sdk.source/org.eclipse.net4j.sdk.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.sdk/org.eclipse.net4j.sdk-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.sdk/org.eclipse.net4j.sdk-4.1.400.jar deleted file mode 100644 index 7717f78de..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.sdk/org.eclipse.net4j.sdk-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.source/org.eclipse.net4j.source-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.source/org.eclipse.net4j.source-4.5.0.jar deleted file mode 100644 index 940963572..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.source/org.eclipse.net4j.source-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.tcp.source/org.eclipse.net4j.tcp.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.tcp.source/org.eclipse.net4j.tcp.source-4.1.400.jar deleted file mode 100644 index 81f9175f6..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.tcp.source/org.eclipse.net4j.tcp.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.tcp/org.eclipse.net4j.tcp-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.tcp/org.eclipse.net4j.tcp-4.1.400.jar deleted file mode 100644 index 170a4316d..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.tcp/org.eclipse.net4j.tcp-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.tcp/org.eclipse.net4j.tcp-4.1.600.jar b/bundles/cnf/localrepo/org.eclipse.net4j.tcp/org.eclipse.net4j.tcp-4.1.600.jar new file mode 100644 index 000000000..391e4256b Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.net4j.tcp/org.eclipse.net4j.tcp-4.1.600.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.tests.source/org.eclipse.net4j.tests.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.tests.source/org.eclipse.net4j.tests.source-4.1.400.jar deleted file mode 100644 index dcaa930fc..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.tests.source/org.eclipse.net4j.tests.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.tests/org.eclipse.net4j.tests-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.tests/org.eclipse.net4j.tests-4.1.400.jar deleted file mode 100644 index 5ddc72210..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.tests/org.eclipse.net4j.tests-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.ui.defs.source/org.eclipse.net4j.ui.defs.source-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.ui.defs.source/org.eclipse.net4j.ui.defs.source-4.0.400.jar deleted file mode 100644 index ba8432d84..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.ui.defs.source/org.eclipse.net4j.ui.defs.source-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.ui.defs/org.eclipse.net4j.ui.defs-4.0.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.ui.defs/org.eclipse.net4j.ui.defs-4.0.400.jar deleted file mode 100644 index 59dbdf3ba..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.ui.defs/org.eclipse.net4j.ui.defs-4.0.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.ui.shared.source/org.eclipse.net4j.ui.shared.source-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.net4j.ui.shared.source/org.eclipse.net4j.ui.shared.source-4.3.100.jar deleted file mode 100644 index fe57b35e5..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.ui.shared.source/org.eclipse.net4j.ui.shared.source-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.ui.shared/org.eclipse.net4j.ui.shared-4.3.100.jar b/bundles/cnf/localrepo/org.eclipse.net4j.ui.shared/org.eclipse.net4j.ui.shared-4.3.100.jar deleted file mode 100644 index 35848b724..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.ui.shared/org.eclipse.net4j.ui.shared-4.3.100.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.ui.source/org.eclipse.net4j.ui.source-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.ui.source/org.eclipse.net4j.ui.source-4.2.300.jar deleted file mode 100644 index 511ebf8cf..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.ui.source/org.eclipse.net4j.ui.source-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.ui/org.eclipse.net4j.ui-4.2.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.ui/org.eclipse.net4j.ui-4.2.300.jar deleted file mode 100644 index fb604ce64..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.ui/org.eclipse.net4j.ui-4.2.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.defs.source/org.eclipse.net4j.util.defs.source-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.defs.source/org.eclipse.net4j.util.defs.source-4.1.300.jar deleted file mode 100644 index cb8c3c587..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.defs.source/org.eclipse.net4j.util.defs.source-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.defs/org.eclipse.net4j.util.defs-4.1.300.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.defs/org.eclipse.net4j.util.defs-4.1.300.jar deleted file mode 100644 index 5853754d2..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.defs/org.eclipse.net4j.util.defs-4.1.300.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.doc.source/org.eclipse.net4j.util.doc.source-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.doc.source/org.eclipse.net4j.util.doc.source-4.1.400.jar deleted file mode 100644 index 8077ef97a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.doc.source/org.eclipse.net4j.util.doc.source-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.doc/org.eclipse.net4j.util.doc-4.1.400.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.doc/org.eclipse.net4j.util.doc-4.1.400.jar deleted file mode 100644 index d8a8bf0a9..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.doc/org.eclipse.net4j.util.doc-4.1.400.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.source/org.eclipse.net4j.util.source-3.6.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.source/org.eclipse.net4j.util.source-3.6.0.jar deleted file mode 100644 index 650570ba3..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.source/org.eclipse.net4j.util.source-3.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.ui.source/org.eclipse.net4j.util.ui.source-3.6.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.ui.source/org.eclipse.net4j.util.ui.source-3.6.0.jar deleted file mode 100644 index 3748e3b78..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.ui.source/org.eclipse.net4j.util.ui.source-3.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util.ui/org.eclipse.net4j.util.ui-3.6.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util.ui/org.eclipse.net4j.util.ui-3.6.0.jar deleted file mode 100644 index 416ea894a..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util.ui/org.eclipse.net4j.util.ui-3.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util/org.eclipse.net4j.util-3.6.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util/org.eclipse.net4j.util-3.6.0.jar deleted file mode 100644 index c8d71031e..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j.util/org.eclipse.net4j.util-3.6.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j.util/org.eclipse.net4j.util-3.8.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j.util/org.eclipse.net4j.util-3.8.0.jar new file mode 100644 index 000000000..b6d1cad6a Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.net4j.util/org.eclipse.net4j.util-3.8.0.jar differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j/org.eclipse.net4j-4.5.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j/org.eclipse.net4j-4.5.0.jar deleted file mode 100644 index 56ad9bbee..000000000 Binary files a/bundles/cnf/localrepo/org.eclipse.net4j/org.eclipse.net4j-4.5.0.jar and /dev/null differ diff --git a/bundles/cnf/localrepo/org.eclipse.net4j/org.eclipse.net4j-4.7.0.jar b/bundles/cnf/localrepo/org.eclipse.net4j/org.eclipse.net4j-4.7.0.jar new file mode 100644 index 000000000..b7bd33893 Binary files /dev/null and b/bundles/cnf/localrepo/org.eclipse.net4j/org.eclipse.net4j-4.7.0.jar differ diff --git a/bundles/cnf/releaserepo/index.xml b/bundles/cnf/releaserepo/index.xml new file mode 100644 index 000000000..b6d063b39 --- /dev/null +++ b/bundles/cnf/releaserepo/index.xml @@ -0,0 +1,2 @@ + + diff --git a/bundles/cnf/releaserepo/index.xml.sha b/bundles/cnf/releaserepo/index.xml.sha index 07ba11363..5d575f069 100644 --- a/bundles/cnf/releaserepo/index.xml.sha +++ b/bundles/cnf/releaserepo/index.xml.sha @@ -1 +1 @@ -0400d6060920df51e4069b81cd760256a0e891a20f37e9dc0a06c4d05a97367b +30e127cf4ce3e74b05cc161129b9f1566c6408f1f1297d664c76b5543a9d07b8 \ No newline at end of file diff --git a/bundles/org.eclipse.emf.cdo.net4j/.classpath b/bundles/org.eclipse.emf.cdo.net4j/.classpath new file mode 100644 index 000000000..b263de401 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.eclipse.emf.cdo.net4j/.gitignore b/bundles/org.eclipse.emf.cdo.net4j/.gitignore new file mode 100644 index 000000000..57b341172 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/generated/ diff --git a/bundles/org.eclipse.emf.cdo.net4j/.project b/bundles/org.eclipse.emf.cdo.net4j/.project new file mode 100644 index 000000000..d4608928a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/.project @@ -0,0 +1,23 @@ + + + org.eclipse.emf.cdo.net4j + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/bundles/org.eclipse.emf.cdo.net4j/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.emf.cdo.net4j/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..2c715ef3b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/bundles/org.eclipse.emf.cdo.net4j/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.emf.cdo.net4j/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..bb35fa0a8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.eclipse.emf.cdo.net4j/bnd.bnd b/bundles/org.eclipse.emf.cdo.net4j/bnd.bnd new file mode 100644 index 000000000..b79dea897 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/bnd.bnd @@ -0,0 +1,17 @@ +-buildpath: \ + org.eclipse.emf.cdo,\ + org.eclipse.equinox.common,\ + org.eclipse.emf.cdo.common,\ + org.eclipse.net4j.util,\ + org.eclipse.emf.ecore,\ + osgi.core,\ + org.eclipse.emf.common,\ + org.eclipse.net4j +Bundle-Version: 4.1.500 +Export-Package: \ + org.eclipse.emf.cdo.internal.net4j,\ + org.eclipse.emf.cdo.internal.net4j.bundle,\ + org.eclipse.emf.cdo.internal.net4j.messages,\ + org.eclipse.emf.cdo.internal.net4j.protocol,\ + org.eclipse.emf.cdo.internal.net4j.testrecorder,\ + org.eclipse.emf.cdo.net4j \ No newline at end of file diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDONet4jSessionConfigurationImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDONet4jSessionConfigurationImpl.java new file mode 100644 index 000000000..2ebca0d24 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDONet4jSessionConfigurationImpl.java @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.net4j.testrecorder.TestRecorderSession; +import org.eclipse.emf.cdo.net4j.CDONet4jSession; +import org.eclipse.emf.cdo.net4j.CDOSession; +import org.eclipse.emf.cdo.session.CDORepositoryInfo; + +import org.eclipse.emf.internal.cdo.session.CDOSessionConfigurationImpl; + +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.signal.SignalProtocol; +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.io.IStreamWrapper; +import org.eclipse.net4j.util.om.OMPlatform; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.OpenSessionResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RepositoryTimeResult; +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.PlatformObject; + +import java.util.Set; + +/** + * @author Eike Stepper + */ +@SuppressWarnings("deprecation") +public class CDONet4jSessionConfigurationImpl extends CDOSessionConfigurationImpl implements org.eclipse.emf.cdo.net4j.CDOSessionConfiguration +{ + private static final boolean TEST_RECORDER = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.test.recorder.enabled"); + + private String repositoryName; + + private IConnector connector; + + private IStreamWrapper streamWrapper; + + private long signalTimeout = SignalProtocol.DEFAULT_TIMEOUT; + + public CDONet4jSessionConfigurationImpl() + { + } + + public String getRepositoryName() + { + return repositoryName; + } + + public void setRepositoryName(String repositoryName) + { + this.repositoryName = repositoryName; + } + + public IConnector getConnector() + { + return connector; + } + + public void setConnector(IConnector connector) + { + checkNotOpen(); + uncheckedSetConnector(connector); + } + + protected void uncheckedSetConnector(IConnector connector) + { + this.connector = connector; + } + + public IStreamWrapper getStreamWrapper() + { + return streamWrapper; + } + + public void setStreamWrapper(IStreamWrapper streamWrapper) + { + checkNotOpen(); + this.streamWrapper = streamWrapper; + } + + public long getSignalTimeout() + { + return signalTimeout; + } + + public void setSignalTimeout(long signalTimeout) + { + this.signalTimeout = signalTimeout; + } + + public CDONet4jSession openNet4jSession() + { + return (CDONet4jSession)super.openSession(); + } + + @Override + public CDOSession openSession() + { + return (CDOSession)openNet4jSession(); + } + + public InternalCDOSession createSession() + { + if (isActivateOnOpen()) + { + CheckUtil.checkState(connector, "connector"); //$NON-NLS-1$ + } + + if (TEST_RECORDER) + { + return new TestRecorderSession(); + } + + return new CDONet4jSessionImpl(); + } + + @Override + protected void configureSession(InternalCDOSession session) + { + super.configureSession(session); + + CDONet4jSessionImpl sessionImpl = (CDONet4jSessionImpl)session; + sessionImpl.setStreamWrapper(streamWrapper); + sessionImpl.setConnector(connector); + sessionImpl.setRepositoryName(repositoryName); + sessionImpl.setUserID(getUserID()); + sessionImpl.setSignalTimeout(signalTimeout); + } + + /** + * @author Eike Stepper + */ + public static class RepositoryInfo extends PlatformObject implements CDORepositoryInfo + { + private String uuid; + + private String name; + + private Type type; + + private State state; + + private String storeType; + + private Set objectIDTypes; + + private long creationTime; + + private RepositoryTimeResult timeResult; + + private CDOID rootResourceID; + + private boolean authenticating; + + private boolean supportingAudits; + + private boolean supportingBranches; + + private boolean supportingUnits; + + private boolean serializingCommits; + + private boolean ensuringReferentialIntegrity; + + private IDGenerationLocation idGenerationLocation; + + private CommitInfoStorage commitInfoStorage; + + private InternalCDOSession session; + + public RepositoryInfo(InternalCDOSession session, OpenSessionResult result) + { + this.session = session; + uuid = result.getUUID(); + name = result.getName(); + type = result.getType(); + state = result.getState(); + storeType = result.getStoreType(); + objectIDTypes = result.getObjectIDTypes(); + creationTime = result.getCreationTime(); + timeResult = result.getRepositoryTimeResult(); + rootResourceID = result.getRootResourceID(); + authenticating = result.isAuthenticating(); + supportingAudits = result.isSupportingAudits(); + supportingBranches = result.isSupportingBranches(); + supportingUnits = result.isSupportingUnits(); + serializingCommits = result.isEnsuringReferentialIntegrity(); + ensuringReferentialIntegrity = result.isEnsuringReferentialIntegrity(); + idGenerationLocation = result.getIDGenerationLocation(); + commitInfoStorage = result.getCommitInfoStorage(); + } + + public InternalCDOSession getSession() + { + return session; + } + + public String getName() + { + return name; + } + + /** + * Must be callable before session activation has finished! + */ + public String getUUID() + { + return uuid; + } + + public Type getType() + { + return type; + } + + public void setType(Type type) + { + this.type = type; + } + + public State getState() + { + return state; + } + + public void setState(State state) + { + this.state = state; + } + + public String getStoreType() + { + return storeType; + } + + public Set getObjectIDTypes() + { + return objectIDTypes; + } + + public long getCreationTime() + { + return creationTime; + } + + public long getTimeStamp() + { + return getTimeStamp(false); + } + + public long getTimeStamp(boolean forceRefresh) + { + if (timeResult == null || forceRefresh) + { + timeResult = refreshTime(); + } + + return timeResult.getAproximateRepositoryTime(); + } + + public CDOID getRootResourceID() + { + return rootResourceID; + } + + public void setRootResourceID(CDOID rootResourceID) + { + // The rootResourceID may only be set if it is currently null + if (this.rootResourceID == null || this.rootResourceID.isNull()) + { + this.rootResourceID = rootResourceID; + } + else if (this.rootResourceID != null && this.rootResourceID.equals(rootResourceID)) + { + // Do nothing; it is the same. + } + else + { + throw new IllegalStateException("rootResourceID must not be changed unless it is null"); + } + } + + public boolean isAuthenticating() + { + return authenticating; + } + + public boolean isSupportingAudits() + { + return supportingAudits; + } + + public boolean isSupportingBranches() + { + return supportingBranches; + } + + public boolean isSupportingUnits() + { + return supportingUnits; + } + + @Deprecated + public boolean isSupportingEcore() + { + return true; + } + + public boolean isSerializingCommits() + { + return serializingCommits; + } + + public boolean isEnsuringReferentialIntegrity() + { + return ensuringReferentialIntegrity; + } + + public IDGenerationLocation getIDGenerationLocation() + { + return idGenerationLocation; + } + + public CommitInfoStorage getCommitInfoStorage() + { + return commitInfoStorage; + } + + public boolean waitWhileInitial(IProgressMonitor monitor) + { + return CDOCommonUtil.waitWhileInitial(this, session, monitor); + } + + private RepositoryTimeResult refreshTime() + { + return session.getSessionProtocol().getRepositoryTime(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDONet4jSessionImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDONet4jSessionImpl.java new file mode 100644 index 000000000..3cce65b35 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDONet4jSessionImpl.java @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2009-2016 Eike Stepper (Loehne, 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 226778 + * Simon McDuff - bug 230832 + * Simon McDuff - bug 233490 + * Simon McDuff - bug 213402 + * Victor Roldan Betancort - maintenance + * Andre Dietisheim - bug 256649 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +package org.eclipse.emf.cdo.internal.net4j; + +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.util.NotAuthenticatedException; +import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionConfigurationImpl.RepositoryInfo; +import org.eclipse.emf.cdo.internal.net4j.protocol.CDOClientProtocol; +import org.eclipse.emf.cdo.internal.net4j.protocol.CommitTransactionRequest; +import org.eclipse.emf.cdo.net4j.CDONet4jSession; +import org.eclipse.emf.cdo.session.CDORepositoryInfo; +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.CDOCommitInfoUtil; +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.InternalCDORevisionManager; + +import org.eclipse.emf.internal.cdo.session.CDOSessionImpl; +import org.eclipse.emf.internal.cdo.session.DelegatingSessionProtocol; + +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.signal.ISignalProtocol; +import org.eclipse.net4j.signal.RemoteException; +import org.eclipse.net4j.signal.SignalProtocol; +import org.eclipse.net4j.util.io.IStreamWrapper; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.OpenSessionResult; + +/** + * @author Eike Stepper + */ +@SuppressWarnings("deprecation") +public class CDONet4jSessionImpl extends CDOSessionImpl implements org.eclipse.emf.cdo.net4j.CDOSession +{ + private IStreamWrapper streamWrapper; + + private IConnector connector; + + private String repositoryName; + + private long signalTimeout = SignalProtocol.DEFAULT_TIMEOUT; + + public CDONet4jSessionImpl() + { + } + + public IStreamWrapper getStreamWrapper() + { + return streamWrapper; + } + + public void setStreamWrapper(IStreamWrapper streamWrapper) + { + this.streamWrapper = streamWrapper; + } + + public IConnector getConnector() + { + return connector; + } + + public void setConnector(IConnector connector) + { + this.connector = connector; + } + + public String getRepositoryName() + { + return repositoryName; + } + + public void setRepositoryName(String repositoryName) + { + this.repositoryName = repositoryName; + } + + public long getSignalTimeout() + { + return signalTimeout; + } + + public void setSignalTimeout(long signalTimeout) + { + this.signalTimeout = signalTimeout; + + // Deal with the possibility that the sessionProtocol has already been created. + CDOClientProtocol clientProtocol = getClientProtocol(); + if (clientProtocol != null) + { + clientProtocol.setTimeout(this.signalTimeout); + } + } + + public void changeCredentials() + { + // Send a request to the server to initiate (from the server) the password change protocol + CDOSessionProtocol sessionProtocol = getSessionProtocol(); + sessionProtocol.requestChangeCredentials(); + } + + public void resetCredentials(String userID) + { + // Send a request to the server to initiate (from the server) the password reset protocol + CDOSessionProtocol sessionProtocol = getSessionProtocol(); + sessionProtocol.requestResetCredentials(userID); + } + + @Override + public OptionsImpl options() + { + return (OptionsImpl)super.options(); + } + + @Override + protected OptionsImpl createOptions() + { + return new OptionsImpl(); + } + + protected InternalCDOBranchManager createBranchManager() + { + return CDOBranchUtil.createBranchManager(); + } + + protected OpenSessionResult openSession() + { + CDOClientProtocol protocol = createProtocol(); + setSessionProtocol(protocol); + hookSessionProtocol(); + + try + { + String userID = getUserID(); + boolean passiveUpdateEnabled = options().isPassiveUpdateEnabled(); + PassiveUpdateMode passiveUpdateMode = options().getPassiveUpdateMode(); + LockNotificationMode lockNotificationMode = options().getLockNotificationMode(); + + // TODO (CD) The next call is on the CDOClientProtocol; shouldn't it be on the DelegatingSessionProtocol instead? + OpenSessionResult result = protocol.openSession(repositoryName, userID, passiveUpdateEnabled, passiveUpdateMode, lockNotificationMode); + + if (result == null) + { + // Skip to response because the user has canceled the authentication + return null; + } + + setSessionID(result.getSessionID()); + setUserID(result.getUserID()); + setLastUpdateTime(result.getLastUpdateTime()); + setRepositoryInfo(new RepositoryInfo(this, result)); + return result; + } + catch (RemoteException ex) + { + if (ex.getCause() instanceof SecurityException) + { + throw (SecurityException)ex.getCause(); + } + + throw ex; + } + } + + @Override + protected void doActivate() throws Exception + { + // Package registry must be available when CDOPackageUnits are received in the open session response! + InternalCDOPackageRegistry packageRegistry = getPackageRegistry(); + if (packageRegistry == null) + { + packageRegistry = new CDOPackageRegistryImpl(); + setPackageRegistry(packageRegistry); + } + + packageRegistry.setPackageProcessor(this); + packageRegistry.setPackageLoader(this); + packageRegistry.activate(); + + OpenSessionResult result = openSession(); + if (result == null) + { + throw new NotAuthenticatedException(); + } + + super.doActivate(); + CDORepositoryInfo repository = getRepositoryInfo(); + CDOSessionProtocol sessionProtocol = getSessionProtocol(); + + InternalCDORevisionManager revisionManager = getRevisionManager(); + if (revisionManager == null) + { + revisionManager = (InternalCDORevisionManager)CDORevisionUtil.createRevisionManager(); + setRevisionManager(revisionManager); + } + + if (!revisionManager.isActive()) + { + revisionManager.setSupportingAudits(repository.isSupportingAudits()); + revisionManager.setSupportingBranches(repository.isSupportingBranches()); + revisionManager.setRevisionLoader(sessionProtocol); + revisionManager.setRevisionLocker(this); + revisionManager.activate(); + } + + InternalCDOBranchManager branchManager = getBranchManager(); + if (branchManager == null) + { + branchManager = createBranchManager(); + setBranchManager(branchManager); + } + + if (!branchManager.isActive()) + { + branchManager.setRepository(repository); + branchManager.setBranchLoader(sessionProtocol); + branchManager.initMainBranch(isMainBranchLocal(), repository.getCreationTime()); + branchManager.activate(); + } + + InternalCDOCommitInfoManager commitInfoManager = getCommitInfoManager(); + if (commitInfoManager == null) + { + commitInfoManager = CDOCommitInfoUtil.createCommitInfoManager(true); + setCommitInfoManager(commitInfoManager); + } + + if (!commitInfoManager.isActive()) + { + commitInfoManager.setRepository(repository); + commitInfoManager.setCommitInfoLoader(sessionProtocol); + commitInfoManager.setLastCommitOfBranch(null, getLastUpdateTime()); + commitInfoManager.activate(); + } + + for (InternalCDOPackageUnit packageUnit : result.getPackageUnits()) + { + getPackageRegistry().putPackageUnit(packageUnit); + } + + repository.getTimeStamp(true); + sessionProtocol.openedSession(); + } + + @Override + protected void doDeactivate() throws Exception + { + CDOSessionProtocol sessionProtocol = getSessionProtocol(); + super.doDeactivate(); + + InternalCDOCommitInfoManager commitInfoManager = getCommitInfoManager(); + if (commitInfoManager.getCommitInfoLoader() == sessionProtocol) + { + commitInfoManager.deactivate(); + } + + InternalCDORevisionManager revisionManager = getRevisionManager(); + if (revisionManager.getRevisionLoader() == sessionProtocol) + { + revisionManager.deactivate(); + } + + InternalCDOBranchManager branchManager = getBranchManager(); + if (branchManager.getBranchLoader() == sessionProtocol) + { + branchManager.deactivate(); + } + + getPackageRegistry().deactivate(); + } + + private CDOClientProtocol createProtocol() + { + CDOClientProtocol protocol = new CDOClientProtocol(); + protocol.setInfraStructure(this); + if (streamWrapper != null) + { + protocol.setStreamWrapper(streamWrapper); + } + + protocol.open(connector); + protocol.setTimeout(signalTimeout); + return protocol; + } + + /** + * Gets the CDOClientProtocol instance, which may be wrapped inside a DelegatingSessionProtocol + */ + private CDOClientProtocol getClientProtocol() + { + CDOSessionProtocol sessionProtocol = getSessionProtocol(); + CDOClientProtocol clientProtocol; + if (sessionProtocol instanceof DelegatingSessionProtocol) + { + clientProtocol = (CDOClientProtocol)((DelegatingSessionProtocol)sessionProtocol).getDelegate(); + } + else + { + clientProtocol = (CDOClientProtocol)sessionProtocol; + } + + return clientProtocol; + } + + /** + * @author Eike Stepper + */ + protected class OptionsImpl extends org.eclipse.emf.internal.cdo.session.CDOSessionImpl.OptionsImpl implements org.eclipse.emf.cdo.net4j.CDOSession.Options + { + private int commitTimeout = CommitTransactionRequest.DEFAULT_MONITOR_TIMEOUT_SECONDS; + + private int progressInterval = CommitTransactionRequest.DEFAULT_MONITOR_PROGRESS_SECONDS; + + public OptionsImpl() + { + } + + @Override + public CDONet4jSession getContainer() + { + return (CDONet4jSession)super.getContainer(); + } + + public ISignalProtocol getNet4jProtocol() + { + CDOSessionProtocol protocol = getSessionProtocol(); + if (protocol instanceof DelegatingSessionProtocol) + { + protocol = ((DelegatingSessionProtocol)protocol).getDelegate(); + } + + @SuppressWarnings("unchecked") + ISignalProtocol signalProtocol = (ISignalProtocol)protocol; + return signalProtocol; + } + + public ISignalProtocol getProtocol() + { + @SuppressWarnings("unchecked") + ISignalProtocol net4jProtocol = (ISignalProtocol)(ISignalProtocol)getNet4jProtocol(); + return net4jProtocol; + } + + public int getCommitTimeout() + { + return commitTimeout; + } + + public synchronized void setCommitTimeout(int commitTimeout) + { + this.commitTimeout = commitTimeout; + } + + public int getProgressInterval() + { + return progressInterval; + } + + public synchronized void setProgressInterval(int progressInterval) + { + this.progressInterval = progressInterval; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDOSessionRecoveryEventImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDOSessionRecoveryEventImpl.java new file mode 100644 index 000000000..fe7772573 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/CDOSessionRecoveryEventImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010-2012, 2014 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.net4j.CDOSessionRecoveryEvent; +import org.eclipse.emf.cdo.session.CDOSession; + +import org.eclipse.net4j.util.event.Event; + +/** + * @author Caspar De Groot + */ +public class CDOSessionRecoveryEventImpl extends Event implements CDOSessionRecoveryEvent +{ + private static final long serialVersionUID = 1L; + + private Type type; + + public CDOSessionRecoveryEventImpl(CDOSession source, Type type) + { + super(source); + this.type = type; + } + + @Override + public CDOSession getSource() + { + return (CDOSession)super.getSource(); + } + + public Type getType() + { + return type; + } + + @Override + protected String formatAdditionalParameters() + { + return "type=" + type; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/FailoverCDOSessionConfigurationImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/FailoverCDOSessionConfigurationImpl.java new file mode 100644 index 000000000..df6651737 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/FailoverCDOSessionConfigurationImpl.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.net4j.FailoverCDOSessionConfiguration; +import org.eclipse.emf.cdo.session.CDOSession.ExceptionHandler; + +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.util.container.IManagedContainer; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class FailoverCDOSessionConfigurationImpl extends RecoveringCDOSessionConfigurationImpl implements FailoverCDOSessionConfiguration +{ + private String monitorConnectorDescription; + + private String repositoryGroup; + + public FailoverCDOSessionConfigurationImpl(String monitorConnectorDescription, String repositoryGroup, IManagedContainer container) + { + super(container); + + this.monitorConnectorDescription = monitorConnectorDescription; + this.repositoryGroup = repositoryGroup; + } + + public String getMonitorConnectorDescription() + { + return monitorConnectorDescription; + } + + public String getRepositoryGroup() + { + return repositoryGroup; + } + + @Override + public void setRepositoryName(String repositoryName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setConnector(IConnector connector) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setExceptionHandler(ExceptionHandler exceptionHandler) + { + throw new UnsupportedOperationException(); + } + + @Override + public InternalCDOSession createSession() + { + return new FailoverCDOSessionImpl(); + } + + @Override + protected void configureSession(InternalCDOSession session) + { + super.configureSession(session); + + FailoverCDOSessionImpl sessionImpl = (FailoverCDOSessionImpl)session; + sessionImpl.setMonitorConnectionDescription(monitorConnectorDescription); + sessionImpl.setRepositoryGroup(repositoryGroup); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/FailoverCDOSessionImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/FailoverCDOSessionImpl.java new file mode 100644 index 000000000..e2a5bd7bc --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/FailoverCDOSessionImpl.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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 + * Caspar De Groot - maintenance + */ +package org.eclipse.emf.cdo.internal.net4j; + +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.signal.RequestWithConfirmation; +import org.eclipse.net4j.signal.SignalProtocol; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; + +/** + * @author Eike Stepper + */ +public class FailoverCDOSessionImpl extends RecoveringCDOSessionImpl +{ + private String monitorConnectorDescription; + + private String repositoryGroup; + + public FailoverCDOSessionImpl() + { + } + + public void setMonitorConnectionDescription(String monitorConnectorDescription) + { + this.monitorConnectorDescription = monitorConnectorDescription; + } + + public void setRepositoryGroup(String repositoryGroup) + { + this.repositoryGroup = repositoryGroup; + } + + @Override + protected void updateConnectorAndRepositoryName() + { + queryRepositoryInfoFromMonitor(); + IConnector connector = createTCPConnector(getUseHeartBeat()); + setConnector(connector); + } + + protected void queryRepositoryInfoFromMonitor() + { + IConnector connector = getTCPConnector(monitorConnectorDescription); + SignalProtocol protocol = new SignalProtocol("failover-client"); + protocol.open(connector); + + try + { + String oldRepositoryConnectorDescription = getRepositoryConnectorDescription(); + String oldRepositoryName = getRepositoryName(); + + while (ObjectUtil.equals(getRepositoryConnectorDescription(), oldRepositoryConnectorDescription) + && ObjectUtil.equals(getRepositoryName(), oldRepositoryName)) + { + new RequestWithConfirmation(protocol, (short)1, "QueryRepositoryInfo") + { + @Override + protected void requesting(ExtendedDataOutputStream out) throws Exception + { + out.writeString(repositoryGroup); + } + + @Override + protected Boolean confirming(ExtendedDataInputStream in) throws Exception + { + setRepositoryConnectorDescription(in.readString()); + setRepositoryName(in.readString()); + return true; + } + }.send(); + } + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + finally + { + protocol.close(); + if (connector.getChannels().isEmpty()) + { + connector.close(); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/Net4jConnectorInjector.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/Net4jConnectorInjector.java new file mode 100644 index 000000000..c1296d9e0 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/Net4jConnectorInjector.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010-2013, 2016 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.net4j.Net4jUtil; +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.container.IElementProcessor; +import org.eclipse.net4j.util.container.IManagedContainer; + +/** + * @author Eike Stepper + */ +public class Net4jConnectorInjector implements IElementProcessor +{ + private static final String SCHEME_SEPARATOR = "://"; //$NON-NLS-1$ + + public Net4jConnectorInjector() + { + } + + public Object process(IManagedContainer container, String productGroup, String factoryType, String description, Object element) + { + if (element instanceof CDONet4jSessionImpl) + { + CDONet4jSessionImpl session = (CDONet4jSessionImpl)element; + if (session.getConnector() == null) + { + IConnector connector = getConnector(container, description); + session.setConnector(connector); + } + } + + return element; + } + + protected IConnector getConnector(IManagedContainer container, String description) + { + int pos = description.indexOf(SCHEME_SEPARATOR); + if (pos == -1) + { + throw new IllegalArgumentException("Malformed URI, could not find scheme separator :// in " + description); + } + + String factoryType = description.substring(0, pos); + if (StringUtil.isEmpty(factoryType)) + { + throw new IllegalArgumentException("Factory type not defined in " + description); + } + + String connectorDescription = description.substring(pos + SCHEME_SEPARATOR.length()); + if (StringUtil.isEmpty(connectorDescription)) + { + throw new IllegalArgumentException("Connector description not defined in " + description); + } + + pos = connectorDescription.indexOf('?'); + if (pos != -1) + { + connectorDescription = connectorDescription.substring(0, pos); + } + + return Net4jUtil.getConnector(container, factoryType, connectorDescription); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/Net4jSessionFactory.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/Net4jSessionFactory.java new file mode 100644 index 000000000..83b66fcfc --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/Net4jSessionFactory.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010-2013 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA) - bug 399641: container-aware factories + */ +package org.eclipse.emf.cdo.internal.net4j; + +import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; +import org.eclipse.emf.cdo.net4j.CDONet4jUtil; +import org.eclipse.emf.cdo.session.CDOSession; + +import org.eclipse.emf.internal.cdo.session.CDOSessionFactory; + +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.container.IManagedContainerFactory; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.security.CredentialsProviderFactory; +import org.eclipse.net4j.util.security.IPasswordCredentialsProvider; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.net.URI; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public class Net4jSessionFactory extends CDOSessionFactory implements IManagedContainerFactory +{ + public static final String TYPE = "cdo"; //$NON-NLS-1$ + + private IManagedContainer managedContainer = IPluginContainer.INSTANCE; + + public Net4jSessionFactory() + { + super(TYPE); + } + + @Override + protected InternalCDOSession createSession(URI uri, Map parameters) + { + String userID = parameters.get("userID"); //$NON-NLS-1$ + String repositoryName = parameters.get("repositoryName"); //$NON-NLS-1$ + if (repositoryName == null) + { + throw new IllegalArgumentException("Repository name is missing: " + uri); + } + + CDONet4jSessionConfiguration configuration = CDONet4jUtil.createNet4jSessionConfiguration(); + configuration.setRepositoryName(repositoryName); + configuration.setUserID(userID); + configuration.setCredentialsProvider(getCredentialsProvider()); + + // The session will be activated by the container + configuration.setActivateOnOpen(false); + return (InternalCDOSession)configuration.openNet4jSession(); + } + + protected IPasswordCredentialsProvider getCredentialsProvider() + { + try + { + IManagedContainer container = getManagedContainer(); + String type = getCredentialsProviderType(); + return (IPasswordCredentialsProvider)container.getElement(CredentialsProviderFactory.PRODUCT_GROUP, type, null); + } + catch (Exception ex) + { + return null; + } + } + + public IManagedContainer getManagedContainer() + { + return managedContainer; + } + + public void setManagedContainer(IManagedContainer managedContainer) + { + this.managedContainer = managedContainer; + } + + protected String getCredentialsProviderType() + { + return "interactive"; + } + + public static CDOSession get(IManagedContainer container, String description) + { + return (CDOSession)container.getElement(PRODUCT_GROUP, TYPE, description); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/ReconnectingCDOSessionConfigurationImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/ReconnectingCDOSessionConfigurationImpl.java new file mode 100644 index 000000000..81b914288 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/ReconnectingCDOSessionConfigurationImpl.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.net4j.ReconnectingCDOSessionConfiguration; +import org.eclipse.emf.cdo.session.CDOSession.ExceptionHandler; + +import org.eclipse.net4j.util.container.IManagedContainer; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +/** + * @author Caspar De Groot + */ +public class ReconnectingCDOSessionConfigurationImpl extends RecoveringCDOSessionConfigurationImpl implements ReconnectingCDOSessionConfiguration +{ + private String hostAndPort; + + private long reconnectInterval = 0; + + private int maxReconnectAttempts = Integer.MAX_VALUE; + + public ReconnectingCDOSessionConfigurationImpl(String hostAndPort, String repositoryName, IManagedContainer container) + { + super(container); + + this.hostAndPort = hostAndPort; + setRepositoryName(repositoryName); + } + + public long getReconnectInterval() + { + return reconnectInterval; + } + + public void setReconnectInterval(long reconnectInterval) + { + this.reconnectInterval = reconnectInterval; + } + + public int getMaxReconnectAttempts() + { + return maxReconnectAttempts; + } + + public void setMaxReconnectAttempts(int maxReconnectAttempts) + { + this.maxReconnectAttempts = maxReconnectAttempts; + } + + @Override + public void setExceptionHandler(ExceptionHandler handler) + { + throw new UnsupportedOperationException(); + } + + @Override + public InternalCDOSession createSession() + { + ReconnectingCDOSessionImpl session = new ReconnectingCDOSessionImpl(); + + // A ReconnectingCDOSessionImpl has its own exceptionHandler; but the configuration mechanism + // expects the configuration object (i.e. *this*) to hold a reference to the desired handler. + // We therefore fetch the handler from the session and plug it into *this*, so that the + // config mechanism can proceed normally. (It will "set" the same handler again.) + // + super.setExceptionHandler(session.getExceptionHandler()); + return session; + } + + @Override + protected void configureSession(InternalCDOSession session) + { + super.configureSession(session); + + ReconnectingCDOSessionImpl sessionImpl = (ReconnectingCDOSessionImpl)session; + sessionImpl.setRepositoryConnectorDescription(hostAndPort); + sessionImpl.setReconnectInterval(reconnectInterval); + sessionImpl.setMaxReconnectAttempts(maxReconnectAttempts); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/ReconnectingCDOSessionImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/ReconnectingCDOSessionImpl.java new file mode 100644 index 000000000..6aeefd4a5 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/ReconnectingCDOSessionImpl.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2010-2012, 2014 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.net4j.connector.ConnectorException; +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.util.lifecycle.LifecycleException; + +/** + * @author Caspar De Groot + */ +public class ReconnectingCDOSessionImpl extends RecoveringCDOSessionImpl +{ + private long reconnectInterval = 0; + + private int maxReconnectAttempts = Integer.MAX_VALUE; + + public ReconnectingCDOSessionImpl() + { + } + + public long getReconnectInterval() + { + return reconnectInterval; + } + + public void setReconnectInterval(long reconnectInterval) + { + this.reconnectInterval = reconnectInterval; + } + + public int getMaxReconnectAttempts() + { + return maxReconnectAttempts; + } + + public void setMaxReconnectAttempts(int maxReconnectAttempts) + { + this.maxReconnectAttempts = maxReconnectAttempts; + } + + @Override + public void setConnector(IConnector connector) + { + // Do nothing (ignore an externally configured connector) + // Note: we cannot throw UnsupportedOperationException because the + // SessionConfig object will call this. + } + + @Override + public void setRepositoryConnectorDescription(String description) + { + if (getRepositoryConnectorDescription() != null) + { + throw new IllegalStateException("Don't call setRepositoryConnectorDescription more than once"); + } + + super.setRepositoryConnectorDescription(description); + } + + @Override + protected void updateConnectorAndRepositoryName() + { + removeTCPConnector(); + + IConnector newConnector = null; + int failedAttempts = 0; + long startOfLastAttempt = 0; + + while (newConnector == null && failedAttempts < maxReconnectAttempts) + { + try + { + if (startOfLastAttempt > 0) + { + delayAsNeeded(startOfLastAttempt); + } + + startOfLastAttempt = System.currentTimeMillis(); + newConnector = createTCPConnector(getUseHeartBeat()); + } + catch (ConnectorException ex) + { + failedAttempts++; + } + catch (LifecycleException ex) + { + failedAttempts++; + } + } + + if (newConnector == null) + { + throw new RuntimeException("Recovery failed"); // TODO (CD) Create custom exception type? + } + + super.setConnector(newConnector); + } + + private void delayAsNeeded(long startOfLastAttempt) + { + long timeToWait = requiredDelay(startOfLastAttempt); + while (timeToWait > 0) + { + try + { + Thread.sleep(timeToWait); + timeToWait = 0; + } + catch (InterruptedException ex) + { + timeToWait = requiredDelay(startOfLastAttempt); + } + } + } + + private long requiredDelay(long startOfLastAttempt) + { + long now = System.currentTimeMillis(); + long timeSinceLastAttempt = now - startOfLastAttempt; + long timeToWait = reconnectInterval - timeSinceLastAttempt; + return timeToWait; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/RecoveringCDOSessionConfigurationImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/RecoveringCDOSessionConfigurationImpl.java new file mode 100644 index 000000000..81c959d44 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/RecoveringCDOSessionConfigurationImpl.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.net4j.RecoveringCDOSessionConfiguration; + +import org.eclipse.net4j.util.container.IManagedContainer; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +/** + * @author Caspar De Groot + */ +public abstract class RecoveringCDOSessionConfigurationImpl extends CDONet4jSessionConfigurationImpl implements RecoveringCDOSessionConfiguration +{ + private IManagedContainer container; + + private boolean heartBeatEnabled = false; + + private long heartBeatPeriod = 1000L; + + private long heartBeatTimeout = 5000L; + + private long connectorTimeout = 10000L; + + public RecoveringCDOSessionConfigurationImpl(IManagedContainer container) + { + this.container = container; + } + + protected IManagedContainer getContainer() + { + return container; + } + + public long getConnectorTimeout() + { + return connectorTimeout; + } + + public void setConnectorTimeout(long timeout) + { + connectorTimeout = timeout; + } + + public boolean isHeartBeatEnabled() + { + return heartBeatEnabled; + } + + public void setHeartBeatEnabled(boolean enabled) + { + heartBeatEnabled = enabled; + } + + public long getHeartBeatTimeout() + { + return heartBeatTimeout; + } + + public void setHeartBeatTimeout(long timeout) + { + heartBeatTimeout = timeout; + } + + public long getHeartBeatPeriod() + { + return heartBeatPeriod; + } + + public void setHeartBeatPeriod(long period) + { + heartBeatPeriod = period; + } + + @Override + protected void configureSession(InternalCDOSession session) + { + super.configureSession(session); + + if (heartBeatEnabled && (heartBeatPeriod == 0 || heartBeatTimeout == 0)) + { + throw new IllegalStateException("Cannot use a heartbeat with zero value set for period or timeout."); + } + + RecoveringCDOSessionImpl sessionImpl = (RecoveringCDOSessionImpl)session; + sessionImpl.setContainer(getContainer()); + sessionImpl.setUseHeartBeat(heartBeatEnabled); + sessionImpl.setHeartBeatPeriod(heartBeatPeriod); + sessionImpl.setHeartBeatTimeout(heartBeatTimeout); + sessionImpl.setConnectorTimeout(connectorTimeout); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/RecoveringCDOSessionImpl.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/RecoveringCDOSessionImpl.java new file mode 100644 index 000000000..334a8d340 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/RecoveringCDOSessionImpl.java @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2010-2012, 2014, 2015, 2018 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.util.TransportException; +import org.eclipse.emf.cdo.net4j.CDOSessionRecoveryEvent; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.session.CDOSessionEvent; +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.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.transaction.CDOTransaction; + +import org.eclipse.net4j.Net4jUtil; +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.signal.heartbeat.HeartBeatProtocol; +import org.eclipse.net4j.util.container.IContainerDelta; +import org.eclipse.net4j.util.container.IContainerEvent; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.InternalCDOView; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Caspar De Groot + */ +public abstract class RecoveringCDOSessionImpl extends CDONet4jSessionImpl +{ + private IManagedContainer container; + + private String repositoryConnectorDescription; + + private boolean useHeartBeat; + + private long heartBeatPeriod = 1000L; + + private long heartBeatTimeout = 5000L; + + private long connectorTimeout = 10000L; + + public RecoveringCDOSessionImpl() + { + setExceptionHandler(new RecoveringExceptionHandler()); + } + + public long getConnectorTimeout() + { + return connectorTimeout; + } + + public void setConnectorTimeout(long connectorTimeout) + { + this.connectorTimeout = connectorTimeout; + } + + public void setContainer(IManagedContainer container) + { + this.container = container; + } + + public IManagedContainer getContainer() + { + return container; + } + + public void setUseHeartBeat(boolean useHeartBeat) + { + this.useHeartBeat = useHeartBeat; + } + + public boolean getUseHeartBeat() + { + return useHeartBeat; + } + + public void setHeartBeatTimeout(long timeout) + { + heartBeatTimeout = timeout; + } + + public long getHeartBeatTimeout() + { + return heartBeatTimeout; + } + + public void setHeartBeatPeriod(long period) + { + heartBeatPeriod = period; + } + + public long getHeartBeatPeriod() + { + return heartBeatPeriod; + } + + @Override + protected void sessionProtocolDeactivated() + { + recover(); + } + + protected void recover() + { + fireEvent(createRecoveryStartedEvent()); + + CDOSessionProtocol oldSessionProtocol = getSessionProtocol(); + unhookSessionProtocol(); + List runnables = recoverSession(); + + // Check if the sessionProtocol was replaced. (This may not be the case + // if the protocol is wrapped inside a DelegatingSessionProtocol.) + CDOSessionProtocol newSessionProtocol = getSessionProtocol(); + if (newSessionProtocol != oldSessionProtocol) + { + handleProtocolChange(oldSessionProtocol, newSessionProtocol); + } + + for (AfterRecoveryRunnable runnable : runnables) + { + runnable.run(newSessionProtocol); + } + + boolean passiveUpdateEnabled = options().isPassiveUpdateEnabled(); + refresh(passiveUpdateEnabled); + + CDOSessionEvent event = createRecoveryFinishedEvent(); + fireEvent(event); + } + + protected void handleProtocolChange(CDOSessionProtocol oldProtocol, CDOSessionProtocol newProtocol) + { + // The revisionManager, branchManager, and commitInfoManager, hold their own + // references to the sessionProtocol. We need to update those: + + InternalCDORevisionManager revisionManager = getRevisionManager(); + revisionManager.deactivate(); + revisionManager.setRevisionLoader(newProtocol); + revisionManager.activate(); + + InternalCDOBranchManager branchManager = getBranchManager(); + branchManager.deactivate(); + branchManager.setBranchLoader(newProtocol); + branchManager.activate(); + + InternalCDOCommitInfoManager commitInfoManager = getCommitInfoManager(); + commitInfoManager.deactivate(); + commitInfoManager.setCommitInfoLoader(newProtocol); + commitInfoManager.activate(); + } + + protected CDOSessionEvent createRecoveryStartedEvent() + { + return new CDOSessionRecoveryEventImpl(this, CDOSessionRecoveryEvent.Type.STARTED); + } + + protected CDOSessionEvent createRecoveryFinishedEvent() + { + return new CDOSessionRecoveryEventImpl(this, CDOSessionRecoveryEvent.Type.FINISHED); + } + + protected IConnector createTCPConnector(boolean heartBeat) + { + IConnector connector = getTCPConnector(repositoryConnectorDescription); + if (heartBeat) + { + new HeartBeatProtocol(connector, container).start(heartBeatPeriod, heartBeatTimeout); + } + + connector.addListener(new AutoCloser()); + return connector; + } + + protected IConnector getTCPConnector(String description) + { + IManagedContainer container = getContainer(); + return Net4jUtil.getConnector(container, "tcp", description, connectorTimeout); + } + + protected List recoverSession() + { + try + { + List runnables = new ArrayList(); + for (InternalCDOView view : getViews()) + { + runnables.add(new OpenViewRunnable(view)); + } + + updateConnectorAndRepositoryName(); + openSession(); + + CDOSessionProtocol sessionProtocol = getSessionProtocol(); + sessionProtocol.openedSession(); + + return runnables; + } + catch (RuntimeException ex) + { + deactivate(); + throw ex; + } + catch (Error ex) + { + deactivate(); + throw ex; + } + } + + @Override + public void setSessionProtocol(CDOSessionProtocol sessionProtocol) + { + super.setSessionProtocol(sessionProtocol); + + // Bug 534014: The DelegatingSessionProtocol of this session is deactivated by CDOSessionImpl.sessionProtocolListener + // when the delegate protocol becomes inactive. The super.setSessionProtocol() method just replaces the delegate + // protocol but doesn't reactivate the DelegatingSessionProtocol. Reactivate it now. + LifecycleUtil.activate(getSessionProtocol()); + } + + protected IConnector removeTCPConnector() + { + return (IConnector)container.removeElement("org.eclipse.net4j.connectors", "tcp", repositoryConnectorDescription); + } + + protected void setRepositoryConnectorDescription(String description) + { + repositoryConnectorDescription = description; + } + + protected String getRepositoryConnectorDescription() + { + return repositoryConnectorDescription; + } + + @Override + protected void doActivate() throws Exception + { + updateConnectorAndRepositoryName(); + super.doActivate(); + } + + protected abstract void updateConnectorAndRepositoryName(); + + /** + * @author Eike Stepper + */ + public static interface AfterRecoveryRunnable + { + public void run(CDOSessionProtocol sessionProtocol); + } + + /** + * @author Eike Stepper + */ + private class RecoveringExceptionHandler implements ExceptionHandler + { + public void handleException(CDOSession session, int attempt, Exception exception) throws Exception + { + if (exception instanceof TransportException) + { + recover(); + } + else + { + throw exception; + } + } + } + + /** + * @author Eike Stepper + */ + private static final class OpenViewRunnable implements AfterRecoveryRunnable + { + private int viewID; + + private CDOBranchPoint branchPoint; + + private boolean transaction; + + public OpenViewRunnable(InternalCDOView view) + { + viewID = view.getViewID(); + branchPoint = CDOBranchUtil.copyBranchPoint(view); + transaction = view instanceof CDOTransaction; + } + + public void run(CDOSessionProtocol sessionProtocol) + { + sessionProtocol.openView(viewID, !transaction, branchPoint); + } + } + + /** + * @author Eike Stepper + */ + private static final class AutoCloser implements IListener + { + public void notifyEvent(IEvent event) + { + if (event instanceof IContainerEvent) + { + IContainerEvent containerEvent = (IContainerEvent)event; + if (containerEvent.getDelta().getKind() == IContainerDelta.Kind.REMOVED) + { + IConnector connector = (IConnector)event.getSource(); + if (connector.getChannels().size() == 0) + { + LifecycleUtil.deactivate(connector); + } + } + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/bundle/OM.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/bundle/OM.java new file mode 100644 index 000000000..f827d0a83 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/bundle/OM.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.bundle; + +import org.eclipse.emf.cdo.internal.net4j.protocol.CommitTransactionRequest; + +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.pref.OMPreference; +import org.eclipse.net4j.util.om.pref.OMPreferences; +import org.eclipse.net4j.util.om.trace.OMTracer; + +/** + * The Operations & Maintenance class of this bundle. + * + * @author Eike Stepper + */ +public abstract class OM +{ + public static final String BUNDLE_ID = "org.eclipse.emf.cdo.net4j"; //$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 PERF = BUNDLE.tracer("perf"); //$NON-NLS-1$ + + public static final OMTracer PERF_REVISION = PERF.tracer("revision"); //$NON-NLS-1$ + + public static final OMTracer PERF_REVISION_LOADING = PERF_REVISION.tracer("loading"); //$NON-NLS-1$ + + public static final OMLogger LOG = BUNDLE.logger(); + + public static final OMPreferences PREFS = BUNDLE.preferences(); + + public static final OMPreference PREF_COMMIT_MONITOR_PROGRESS_SECONDS = // + PREFS.init("PREF_COMMIT_MONITOR_PROGRESS_SECONDS", CommitTransactionRequest.DEFAULT_MONITOR_PROGRESS_SECONDS); //$NON-NLS-1$ + + public static final OMPreference PREF_COMMIT_MONITOR_TIMEOUT_SECONDS = // + PREFS.init("PREF_COMMIT_MONITOR_TIMEOUT_SECONDS", CommitTransactionRequest.DEFAULT_MONITOR_TIMEOUT_SECONDS); //$NON-NLS-1$ + + /** + * @author Eike Stepper + */ + public static final class Activator extends OSGiActivator + { + public Activator() + { + super(BUNDLE); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/messages/Messages.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/messages/Messages.java new file mode 100644 index 000000000..1c8456927 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/messages/Messages.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2011, 2012 Eike Stepper (Loehne, 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.net4j.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.net4j.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/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/messages/messages.properties b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/messages/messages.properties new file mode 100644 index 000000000..13f37398f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/messages/messages.properties @@ -0,0 +1,14 @@ +# Copyright (c) 2009, 2012, 2013 Eike Stepper (Loehne, 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 + +CommitTransactionPhase2Request.1=Missing informations. {0} is not involved in the commit +CommitTransactionPhase2Request.2=Missing informations. {0} is not mapped in the commit +OpenSessionRequest.0=Repository {0} not found +OpenSessionRequest.3=Failed to open session for repository {0} +SyncRevisionsRequest.2=Did not expect to receive object with id "{0}" diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/BranchNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/BranchNotificationIndication.java new file mode 100644 index 000000000..703e76e83 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/BranchNotificationIndication.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2013, 2015 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class BranchNotificationIndication extends CDOClientIndication +{ + public BranchNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_BRANCH_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + CDOBranch branch = in.readCDOBranch(); + ChangeKind changeKind = in.readEnum(ChangeKind.class); + + if (changeKind == ChangeKind.RENAMED) + { + String name = in.readString(); + ((InternalCDOBranch)branch).basicSetName(name); + } + else + { + InternalCDOBranchManager branchManager = getSession().getBranchManager(); + branchManager.handleBranchChanged((InternalCDOBranch)branch, changeKind); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientIndication.java new file mode 100644 index 000000000..25ba3c394 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientIndication.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2009-2013, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoManager; +import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDOListFactory; +import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; +import org.eclipse.emf.cdo.internal.common.revision.CDOListWithElementProxiesImpl; +import org.eclipse.emf.cdo.spi.common.protocol.CDODataInputImpl; + +import org.eclipse.net4j.signal.Indication; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.StringIO; +import org.eclipse.net4j.util.lifecycle.LifecycleException; +import org.eclipse.net4j.util.lifecycle.LifecycleState; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public abstract class CDOClientIndication extends Indication +{ + public CDOClientIndication(CDOClientProtocol protocol, short signalID) + { + super(protocol, signalID); + } + + @Override + public CDOClientProtocol getProtocol() + { + return (CDOClientProtocol)super.getProtocol(); + } + + protected InternalCDOSession getSession() + { + return (InternalCDOSession)getProtocol().getSession(); + } + + @Override + protected final void indicating(ExtendedDataInputStream in) throws Exception + { + final InternalCDOSession session = getSession(); + if (session.getLifecycleState() == LifecycleState.ACTIVATING) + { + LifecycleUtil.waitForActive(session, 10000L); + } + + try + { + LifecycleUtil.checkActive(session); + } + catch (LifecycleException ex) + { + throw new LifecycleException(session + " is inactive in " + getClass().getName(), ex); + } + + indicating(new CDODataInputImpl(in) + { + public CDOPackageRegistry getPackageRegistry() + { + return session.getPackageRegistry(); + } + + @Override + protected boolean isXCompression() + { + return CDOProtocolConstants.X_COMPRESSION; + } + + @Override + protected StringIO getPackageURICompressor() + { + return getProtocol().getPackageURICompressor(); + } + + @Override + protected CDOListFactory getListFactory() + { + return CDOListWithElementProxiesImpl.FACTORY; + } + + @Override + protected CDOBranchManager getBranchManager() + { + return session.getBranchManager(); + } + + @Override + protected CDOCommitInfoManager getCommitInfoManager() + { + return session.getCommitInfoManager(); + } + + @Override + protected CDORevisionFactory getRevisionFactory() + { + return session.getRevisionManager().getFactory(); + } + + @Override + protected CDOLobStore getLobStore() + { + return session.getLobStore(); + } + }); + } + + protected abstract void indicating(CDODataInput in) throws IOException; +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java new file mode 100644 index 000000000..8a317e8f6 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2009-2016, 2018 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +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.CDOCommitInfo; +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.revision.CDOIDAndVersion; +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.security.CDOPermission; +import org.eclipse.emf.cdo.common.util.TransportException; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.session.remote.CDORemoteSession; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +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.RevisionInfo; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.emf.internal.cdo.session.CDOSessionImpl; + +import org.eclipse.net4j.signal.RemoteException; +import org.eclipse.net4j.signal.Request; +import org.eclipse.net4j.signal.RequestWithConfirmation; +import org.eclipse.net4j.signal.RequestWithMonitoring; +import org.eclipse.net4j.signal.SignalReactor; +import org.eclipse.net4j.signal.security.AuthenticatingSignalProtocol; +import org.eclipse.net4j.signal.security.AuthenticationIndication; +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.io.StringCompressor; +import org.eclipse.net4j.util.io.StringIO; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.PerfTracer; +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; + +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.InternalCDOTransaction.InternalCDOCommitContext; +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class CDOClientProtocol extends AuthenticatingSignalProtocol implements CDOSessionProtocol +{ + private static final PerfTracer REVISION_LOADING = new PerfTracer(OM.PERF_REVISION_LOADING, CDOClientProtocol.class); + + private static final boolean COMPRESS_PACKAGE_URIS = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.protocol.compressPackageURIs", + !StringCompressor.BYPASS); + + private StringIO packageURICompressor = COMPRESS_PACKAGE_URIS ? new StringCompressor(true) : StringIO.DIRECT; + + public CDOClientProtocol() + { + super(PROTOCOL_NAME); + } + + @Override + public int getVersion() + { + return PROTOCOL_VERSION; + } + + public CDOSession getSession() + { + return getInfraStructure(); + } + + public StringIO getPackageURICompressor() + { + return packageURICompressor; + } + + public OpenSessionResult openSession(String repositoryName, String userID, boolean passiveUpdateEnabled, PassiveUpdateMode passiveUpdateMode, + LockNotificationMode lockNotificationMode) + { + return send(new OpenSessionRequest(this, repositoryName, userID, passiveUpdateEnabled, passiveUpdateMode, lockNotificationMode), new Monitor()); + } + + public void disablePassiveUpdate() + { + send(new DisablePassiveUpdateRequest(this)); + } + + public void setPassiveUpdateMode(PassiveUpdateMode mode) + { + send(new SetPassiveUpdateModeRequest(this, mode)); + } + + public RepositoryTimeResult getRepositoryTime() + { + return send(new RepositoryTimeRequest(this)); + } + + public void openedSession() + { + send(new OpenedSessionRequest(this)); + } + + public EPackage[] loadPackages(CDOPackageUnit packageUnit) + { + return send(new LoadPackagesRequest(this, (InternalCDOPackageUnit)packageUnit)); + } + + public Pair createBranch(int branchID, BranchInfo branchInfo) + { + return send(new CreateBranchRequest(this, branchID, branchInfo)); + } + + public BranchInfo loadBranch(int branchID) + { + return send(new LoadBranchRequest(this, branchID)); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + return send(new LoadSubBranchesRequest(this, branchID)); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler handler) + { + return send(new LoadBranchesRequest(this, startID, endID, handler)); + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + send(new RenameBranchRequest(this, branchID, oldName, newName)); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + send(new LoadCommitInfosRequest(this, branch, startTime, endTime, handler)); + } + + public CDOCommitData loadCommitData(long timeStamp) + { + return send(new LoadCommitDataRequest(this, timeStamp)); + } + + public Object loadChunk(InternalCDORevision revision, EStructuralFeature feature, int accessIndex, int fetchIndex, int fromIndex, int toIndex) + { + return send(new LoadChunkRequest(this, revision, feature, accessIndex, fetchIndex, fromIndex, toIndex)); + } + + public List loadRevisions(List infos, CDOBranchPoint branchPoint, int referenceChunk, int prefetchDepth) + { + return send(new LoadRevisionsRequest(this, infos, branchPoint, referenceChunk, prefetchDepth)); + } + + public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk) + { + return send(new LoadRevisionByVersionRequest(this, id, branchVersion, referenceChunk)); + } + + public CDOBranchPointRange loadObjectLifetime(CDOID id, CDOBranchPoint branchPoint) + { + return send(new LoadObjectLifetimeRequest(this, id, branchPoint)); + } + + public RefreshSessionResult refresh(long lastUpdateTime, Map> viewedRevisions, int initialChunkSize, + boolean enablePassiveUpdates) + { + return send(new RefreshSessionRequest(this, lastUpdateTime, viewedRevisions, initialChunkSize, enablePassiveUpdates)); + } + + public void openView(int viewID, boolean readOnly, CDOBranchPoint branchPoint) + { + send(new OpenViewRequest(this, viewID, readOnly, branchPoint)); + } + + public CDOBranchPoint openView(int viewID, boolean readOnly, String durableLockingID) + { + return send(new OpenViewRequest(this, viewID, readOnly, durableLockingID)); + } + + public void switchTarget(int viewID, CDOBranchPoint branchPoint, List invalidObjects, List allChangedObjects, + List allDetachedObjects, OMMonitor monitor) + { + send(new SwitchTargetRequest(this, viewID, branchPoint, invalidObjects, allChangedObjects, allDetachedObjects), monitor); + } + + public void closeView(int viewID) + { + send(new CloseViewRequest(this, viewID)); + } + + public void changeSubscription(int viewID, List ids, boolean subscribeMode, boolean clear) + { + send(new ChangeSubscriptionRequest(this, viewID, ids, subscribeMode, clear)); + } + + public void query(CDOView view, AbstractQueryIterator queryResult) + { + send(new QueryRequest(this, view, queryResult)); + } + + public boolean cancelQuery(int queryId) + { + try + { + return new QueryCancelRequest(this, queryId).send(); + } + catch (Exception ignore) + { + return false; + } + } + + @Deprecated + public LockObjectsResult lockObjects(List revisions, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) + throws InterruptedException + { + // List revisionKeys = new LinkedList(); + // for (InternalCDORevision rev : revisions) + // { + // revisionKeys.add(rev); + // } + // + // return lockObjects2(revisionKeys, viewID, viewedBranch, lockType, false, timeout); + + throw new UnsupportedOperationException(); + } + + public LockObjectsResult lockObjects2(List revisionKeys, int viewID, CDOBranch viewedBranch, LockType lockType, boolean recursive, + long timeout) throws InterruptedException + { + InterruptedException interruptedException = null; + RuntimeException runtimeException = null; + + try + { + return new LockObjectsRequest(this, revisionKeys, viewID, lockType, recursive, timeout).send(); + } + catch (RemoteException ex) + { + if (ex.getCause() instanceof RuntimeException) + { + runtimeException = (RuntimeException)ex.getCause(); + } + else if (ex.getCause() instanceof InterruptedException) + { + interruptedException = (InterruptedException)ex.getCause(); + } + else + { + runtimeException = WrappedException.wrap(ex); + } + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + + if (interruptedException != null) + { + throw interruptedException; + } + + throw runtimeException; + } + + public LockObjectsResult delegateLockObjects(String lockAreaID, List revisionKeys, CDOBranch viewedBranch, LockType lockType, + boolean recursive, long timeout) throws InterruptedException + { + InterruptedException interruptedException = null; + RuntimeException runtimeException = null; + + try + { + return new LockDelegationRequest(this, lockAreaID, revisionKeys, viewedBranch, lockType, recursive, timeout).send(); + } + catch (RemoteException ex) + { + if (ex.getCause() instanceof RuntimeException) + { + runtimeException = (RuntimeException)ex.getCause(); + } + else if (ex.getCause() instanceof InterruptedException) + { + interruptedException = (InterruptedException)ex.getCause(); + } + else + { + runtimeException = WrappedException.wrap(ex); + } + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + + if (interruptedException != null) + { + throw interruptedException; + } + + throw runtimeException; + } + + @Deprecated + public void unlockObjects(CDOView view, Collection objectIDs, LockType lockType) + { + // send(new UnlockObjectsRequest(this, view.getViewID(), objectIDs, lockType, false)); + + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult unlockObjects2(CDOView view, Collection objectIDs, LockType lockType, boolean recursive) + { + return send(new UnlockObjectsRequest(this, view.getViewID(), objectIDs, lockType, recursive)); + } + + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection objectIDs, LockType lockType, boolean recursive) + { + return send(new UnlockDelegationRequest(this, lockAreaID, objectIDs, lockType, recursive)); + } + + public boolean isObjectLocked(CDOView view, CDOObject object, LockType lockType, boolean byOthers) + { + return send(new ObjectLockedRequest(this, view, object, lockType, byOthers)); + } + + public String changeLockArea(CDOView view, boolean create) + { + return send(new LockAreaRequest(this, view, create)); + } + + public List queryLobs(Set ids) + { + return send(new QueryLobsRequest(this, ids)); + } + + public void loadLob(CDOLobInfo info, Object outputStreamOrWriter) throws IOException + { + try + { + new LoadLobRequest(this, info, outputStreamOrWriter).send(); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (IOException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new TransportException(ex); + } + } + + public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime, CDORevisionHandler handler) + { + send(new HandleRevisionsRequest(this, eClass, branch, exactBranch, timeStamp, exactTime, handler)); + } + + @Deprecated + public CommitTransactionResult commitTransaction(int transactionID, String comment, boolean releaseLocks, CDOIDProvider idProvider, CDOCommitData commitData, + Collection> lobs, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitTransaction(InternalCDOCommitContext context, OMMonitor monitor) + { + return send(new CommitTransactionRequest(this, context), monitor); + } + + @Deprecated + public CommitTransactionResult commitDelegation(CDOBranch branch, String userID, String comment, CDOCommitData commitData, + Map detachedObjectTypes, Collection> lobs, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitDelegation(InternalCDOCommitContext context, OMMonitor monitor) + { + return send(new CommitDelegationRequest(this, context), monitor); + } + + public CommitTransactionResult commitXATransactionPhase1(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + return send(new CommitXATransactionPhase1Request(this, xaContext), monitor); + } + + public CommitTransactionResult commitXATransactionPhase2(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + return send(new CommitXATransactionPhase2Request(this, xaContext), monitor); + } + + public CommitTransactionResult commitXATransactionPhase3(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + return send(new CommitXATransactionPhase3Request(this, xaContext), monitor); + } + + public CommitTransactionResult commitXATransactionCancel(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + return send(new CommitXATransactionCancelRequest(this, xaContext), monitor); + } + + public CDOCommitInfo resetTransaction(int transactionID, int commitNumber) + { + return send(new ResetTransactionRequest(this, transactionID, commitNumber)); + } + + public List getRemoteSessions(InternalCDORemoteSessionManager manager, boolean subscribe) + { + return send(new GetRemoteSessionsRequest(this, subscribe)); + } + + public Set sendRemoteMessage(CDORemoteSessionMessage message, List recipients) + { + return send(new RemoteMessageRequest(this, message, recipients)); + } + + public boolean unsubscribeRemoteSessions() + { + return send(new UnsubscribeRemoteSessionsRequest(this)); + } + + public void replicateRepository(CDOReplicationContext context, OMMonitor monitor) + { + send(new ReplicateRepositoryRequest(this, context, monitor)); + } + + public void replicateRepositoryRaw(CDORawReplicationContext context, OMMonitor monitor) + { + send(new ReplicateRepositoryRawRequest(this, context), monitor); + } + + public CDOChangeSetData[] loadChangeSets(CDOBranchPointRange... ranges) + { + return send(new LoadChangeSetsRequest(this, ranges)); + } + + @Deprecated + public Set loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, CDORevisionAvailabilityInfo targetBaseInfo, + CDORevisionAvailabilityInfo sourceBaseInfo) + { + throw new UnsupportedOperationException(); + } + + public MergeDataResult loadMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo) + { + return send(new LoadMergeDataRequest(this, targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo)); + } + + @Deprecated + public CDOLockState[] getLockStates(int viewID, Collection ids) + { + return getLockStates(viewID, ids, CDOLockState.DEPTH_NONE); + } + + public CDOLockState[] getLockStates(int viewID, Collection ids, int depth) + { + return send(new LockStateRequest(this, viewID, ids, depth)); + } + + public void enableLockNotifications(int viewID, boolean on) + { + send(new EnableLockNotificationRequest(this, viewID, on)); + } + + public void setLockNotificationMode(LockNotificationMode mode) + { + send(new SetLockNotificationModeRequest(this, mode)); + } + + public Map loadPermissions(InternalCDORevision[] revisions) + { + return send(new LoadPermissionsRequest(this, revisions)); + } + + public void requestChangeCredentials() + { + send(new ChangeCredentialsRequest(this, CredentialsUpdateOperation.CHANGE_PASSWORD, null), new Monitor()); + } + + public void requestResetCredentials(String userID) + { + send(new ChangeCredentialsRequest(this, CredentialsUpdateOperation.RESET_PASSWORD, userID), new Monitor()); + } + + public boolean requestUnit(int viewID, CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + return send(new UnitRequest(this, viewID, rootID, opcode, revisionHandler), monitor); + } + + @Override + protected StringCompressor getStringCompressor() + { + if (COMPRESS_PACKAGE_URIS) + { + return (StringCompressor)packageURICompressor; + } + + return super.getStringCompressor(); + } + + @Override + protected SignalReactor createSignalReactor(short signalID) + { + switch (signalID) + { + case SIGNAL_AUTHENTICATION: + return new AuthenticationIndication(this, SIGNAL_AUTHENTICATION); + + case SIGNAL_BRANCH_NOTIFICATION: + return new BranchNotificationIndication(this); + + case SIGNAL_REPOSITORY_TYPE_NOTIFICATION: + return new RepositoryTypeNotificationIndication(this); + + case SIGNAL_REPOSITORY_STATE_NOTIFICATION: + return new RepositoryStateNotificationIndication(this); + + case SIGNAL_COMMIT_NOTIFICATION: + return new CommitNotificationIndication(this); + + case SIGNAL_REMOTE_SESSION_NOTIFICATION: + return new RemoteSessionNotificationIndication(this); + + case SIGNAL_REMOTE_MESSAGE_NOTIFICATION: + return new RemoteMessageNotificationIndication(this); + + case SIGNAL_LOCK_NOTIFICATION: + return new LockNotificationIndication(this); + + case SIGNAL_CREDENTIALS_CHALLENGE: + return new CredentialsChallengeIndication(this); + + default: + return super.createSignalReactor(signalID); + } + } + + private void send(Request request) + { + try + { + request.sendAsync(); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new TransportException(ex); + } + } + + private RESULT send(RequestWithConfirmation request) + { + try + { + return request.send(); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new TransportException(ex); + } + } + + private RESULT send(RequestWithMonitoring request, OMMonitor monitor) + { + try + { + return request.send(monitor); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new TransportException(ex); + } + } + + private List send(LoadRevisionsRequest request) + { + try + { + REVISION_LOADING.start(request); + return send((RequestWithConfirmation>)request); + } + finally + { + REVISION_LOADING.stop(request); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocolFactory.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocolFactory.java new file mode 100644 index 000000000..c06c9458d --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocolFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.net4j.util.container.IManagedContainer; + +import org.eclipse.spi.net4j.ClientProtocolFactory; + +/** + * @author Eike Stepper + */ +public final class CDOClientProtocolFactory extends ClientProtocolFactory +{ + public static final String TYPE = CDOProtocolConstants.PROTOCOL_NAME; + + public CDOClientProtocolFactory() + { + super(TYPE); + } + + public CDOClientProtocol create(String description) + { + return new CDOClientProtocol(); + } + + public static CDOClientProtocol get(IManagedContainer container, String description) + { + return (CDOClientProtocol)container.getElement(PRODUCT_GROUP, TYPE, description); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientRequest.java new file mode 100644 index 000000000..ccd0e90aa --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientRequest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2009-2013, 2017 Eike Stepper (Loehne, 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 - maintenance + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoManager; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.lob.CDOLobStore; +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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDOListFactory; +import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; +import org.eclipse.emf.cdo.internal.common.revision.CDOListWithElementProxiesImpl; +import org.eclipse.emf.cdo.spi.common.protocol.CDODataInputImpl; +import org.eclipse.emf.cdo.spi.common.protocol.CDODataOutputImpl; +import org.eclipse.emf.cdo.spi.common.revision.CDORevisionUnchunker; + +import org.eclipse.net4j.signal.RequestWithConfirmation; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; +import org.eclipse.net4j.util.io.StringIO; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public abstract class CDOClientRequest extends RequestWithConfirmation +{ + public CDOClientRequest(CDOClientProtocol protocol, short signalID) + { + super(protocol, signalID); + } + + @Override + public CDOClientProtocol getProtocol() + { + return (CDOClientProtocol)super.getProtocol(); + } + + protected InternalCDOSession getSession() + { + return (InternalCDOSession)getProtocol().getSession(); + } + + protected CDOIDProvider getIDProvider() + { + throw new UnsupportedOperationException(); + } + + @Override + protected void requesting(ExtendedDataOutputStream out) throws Exception + { + requesting(new CDODataOutputImpl(out) + { + @Override + public CDOPackageRegistry getPackageRegistry() + { + return getSession().getPackageRegistry(); + } + + @Override + public CDORevisionUnchunker getRevisionUnchunker() + { + return getSession(); + } + + @Override + public CDOIDProvider getIDProvider() + { + return CDOClientRequest.this.getIDProvider(); + } + + @Override + protected boolean isXCompression() + { + return CDOProtocolConstants.X_COMPRESSION; + } + + @Override + protected StringIO getPackageURICompressor() + { + return getProtocol().getPackageURICompressor(); + } + }); + } + + @Override + protected RESULT confirming(ExtendedDataInputStream in) throws Exception + { + return confirming(new CDODataInputImpl(in) + { + public CDOPackageRegistry getPackageRegistry() + { + return getSession().getPackageRegistry(); + } + + @Override + protected boolean isXCompression() + { + return CDOProtocolConstants.X_COMPRESSION; + } + + @Override + protected StringIO getPackageURICompressor() + { + return getProtocol().getPackageURICompressor(); + } + + @Override + protected CDOBranchManager getBranchManager() + { + return getSession().getBranchManager(); + } + + @Override + protected CDOCommitInfoManager getCommitInfoManager() + { + return getSession().getCommitInfoManager(); + } + + @Override + protected CDORevisionFactory getRevisionFactory() + { + return getSession().getRevisionManager().getFactory(); + } + + @Override + protected CDOLobStore getLobStore() + { + return getSession().getLobStore(); + } + + @Override + protected CDOListFactory getListFactory() + { + return CDOListWithElementProxiesImpl.FACTORY; + } + }); + } + + protected abstract void requesting(CDODataOutput out) throws IOException; + + protected abstract RESULT confirming(CDODataInput in) throws IOException; +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientRequestWithMonitoring.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientRequestWithMonitoring.java new file mode 100644 index 000000000..ede30b45c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientRequestWithMonitoring.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010-2013, 2017 Eike Stepper (Loehne, 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 215688 + * Simon McDuff - bug 213402 + * Andre Dietisheim - bug 256649 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoManager; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.lob.CDOLobStore; +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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDOListFactory; +import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; +import org.eclipse.emf.cdo.internal.common.revision.CDOListWithElementProxiesImpl; +import org.eclipse.emf.cdo.spi.common.protocol.CDODataInputImpl; +import org.eclipse.emf.cdo.spi.common.protocol.CDODataOutputImpl; +import org.eclipse.emf.cdo.spi.common.revision.CDORevisionUnchunker; + +import org.eclipse.net4j.signal.RequestWithMonitoring; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; +import org.eclipse.net4j.util.io.StringIO; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public abstract class CDOClientRequestWithMonitoring extends RequestWithMonitoring +{ + private ExtendedDataOutputStream requestStream; + + private ExtendedDataInputStream confirmationStream; + + public CDOClientRequestWithMonitoring(CDOClientProtocol protocol, short signalID) + { + super(protocol, signalID); + } + + @Override + public CDOClientProtocol getProtocol() + { + return (CDOClientProtocol)super.getProtocol(); + } + + protected ExtendedDataOutputStream getRequestStream() + { + return requestStream; + } + + protected ExtendedDataInputStream getConfirmationStream() + { + return confirmationStream; + } + + protected InternalCDOSession getSession() + { + return (InternalCDOSession)getProtocol().getSession(); + } + + protected CDOIDProvider getIDProvider() + { + return null; + } + + @Override + protected int getMonitorProgressSeconds() + { + org.eclipse.emf.cdo.net4j.CDONet4jSession session = (org.eclipse.emf.cdo.net4j.CDONet4jSession)getSession(); + return session.options().getProgressInterval(); + } + + @Override + protected final void requesting(ExtendedDataOutputStream out, OMMonitor monitor) throws Exception + { + requestStream = out; + requesting(new CDODataOutputImpl(out) + { + @Override + public CDOPackageRegistry getPackageRegistry() + { + return getSession().getPackageRegistry(); + } + + @Override + public CDOIDProvider getIDProvider() + { + return CDOClientRequestWithMonitoring.this.getIDProvider(); + } + + @Override + public CDORevisionUnchunker getRevisionUnchunker() + { + return getSession(); + } + + @Override + protected boolean isXCompression() + { + return CDOProtocolConstants.X_COMPRESSION; + } + + @Override + protected StringIO getPackageURICompressor() + { + return getProtocol().getPackageURICompressor(); + } + }, monitor); + } + + @Override + protected final RESULT confirming(ExtendedDataInputStream in, OMMonitor monitor) throws Exception + { + confirmationStream = in; + return confirming(new CDODataInputImpl(in) + { + public CDOPackageRegistry getPackageRegistry() + { + return getSession().getPackageRegistry(); + } + + @Override + protected boolean isXCompression() + { + return CDOProtocolConstants.X_COMPRESSION; + } + + @Override + protected StringIO getPackageURICompressor() + { + return getProtocol().getPackageURICompressor(); + } + + @Override + protected CDOBranchManager getBranchManager() + { + return getSession().getBranchManager(); + } + + @Override + protected CDOCommitInfoManager getCommitInfoManager() + { + return getSession().getCommitInfoManager(); + } + + @Override + protected CDORevisionFactory getRevisionFactory() + { + return getSession().getRevisionManager().getFactory(); + } + + @Override + protected CDOLobStore getLobStore() + { + return getSession().getLobStore(); + } + + @Override + protected CDOListFactory getListFactory() + { + return CDOListWithElementProxiesImpl.FACTORY; + } + }, monitor); + } + + protected abstract void requesting(CDODataOutput out, OMMonitor monitor) throws IOException; + + protected abstract RESULT confirming(CDODataInput in, OMMonitor monitor) throws IOException; +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOTimeRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOTimeRequest.java new file mode 100644 index 000000000..6b51a2af7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOTimeRequest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RepositoryTimeResult; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public abstract class CDOTimeRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, CDOTimeRequest.class); + + private RepositoryTimeResult repositoryTimeResult = new RepositoryTimeResult(); + + public CDOTimeRequest(CDOClientProtocol protocol, short signalID) + { + super(protocol, signalID); + } + + public RepositoryTimeResult getRepositoryTimeResult() + { + return repositoryTimeResult; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + repositoryTimeResult.setRequested(System.currentTimeMillis()); + if (TRACER.isEnabled()) + { + TRACER.format("Requested: {0}", CDOCommonUtil.formatTimeStamp(repositoryTimeResult.getRequested())); //$NON-NLS-1$ + } + } + + @Override + protected RESULT confirming(CDODataInput in) throws IOException + { + repositoryTimeResult.setConfirmed(System.currentTimeMillis()); + if (TRACER.isEnabled()) + { + TRACER.format("Confirmed: {0}", CDOCommonUtil.formatTimeStamp(repositoryTimeResult.getConfirmed())); //$NON-NLS-1$ + } + + repositoryTimeResult.setIndicated(in.readXLong()); + if (TRACER.isEnabled()) + { + TRACER.format("Read indicated: {0}", CDOCommonUtil.formatTimeStamp(repositoryTimeResult.getIndicated())); //$NON-NLS-1$ + } + + repositoryTimeResult.setResponded(in.readXLong()); + if (TRACER.isEnabled()) + { + TRACER.format("Read responded: {0}", CDOCommonUtil.formatTimeStamp(repositoryTimeResult.getResponded())); //$NON-NLS-1$ + } + + return null; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ChangeCredentialsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ChangeCredentialsRequest.java new file mode 100644 index 000000000..2e7dd550c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ChangeCredentialsRequest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, 2014 Eike Stepper (Loehne, 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: + * Christian W. Damus (CEA LIST) - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +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.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; + +import java.io.IOException; + +/** + * Request from the client to the server to initiate (from the server) the change-credentials protocol. + * + * @author Christian W. Damus (CEA LIST) + */ +public class ChangeCredentialsRequest extends CDOClientRequestWithMonitoring +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, ChangeCredentialsRequest.class); + + private final CredentialsUpdateOperation operation; + + private final String userID; + + public ChangeCredentialsRequest(CDOClientProtocol protocol, CredentialsUpdateOperation operation, String userID) + { + super(protocol, CDOProtocolConstants.SIGNAL_CHANGE_CREDENTIALS); + + this.operation = operation; + this.userID = userID; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Requesting %s of user credentials", operation); //$NON-NLS-1$ + } + + out.writeEnum(operation); + out.writeString(userID); + } + + @Override + protected Boolean confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ChangeSubscriptionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ChangeSubscriptionRequest.java new file mode 100644 index 000000000..3926405d9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ChangeSubscriptionRequest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009-2012, 2015-2017 Eike Stepper (Loehne, 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 230832 + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.util.List; + +/** + * @author Simon McDuff + */ +public class ChangeSubscriptionRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, ChangeSubscriptionRequest.class); + + private int viewID; + + private List ids; + + /** + * true - it will subscribe id's.
+ * false - it will unsubscribe id's. + */ + private boolean subscribeMode; + + private boolean clear; + + public ChangeSubscriptionRequest(CDOClientProtocol protocol, int viewID, List ids, boolean subscribeMode, boolean clear) + { + super(protocol, CDOProtocolConstants.SIGNAL_CHANGE_SUBSCRIPTION); + this.viewID = viewID; + this.ids = ids; + this.subscribeMode = subscribeMode; + this.clear = clear; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.trace("View " + viewID + " subscribing to " + ids.size()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + out.writeXInt(viewID); + out.writeBoolean(clear); + out.writeXInt(subscribeMode ? ids.size() : -ids.size()); + for (CDOID id : ids) + { + out.writeCDOID(id); + } + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CloseViewRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CloseViewRequest.java new file mode 100644 index 000000000..0f9ab1c96 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CloseViewRequest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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 java.io.IOException; + +/** + * @author Eike Stepper + */ +public class CloseViewRequest extends CDOClientRequest +{ + private int viewID; + + public CloseViewRequest(CDOClientProtocol protocol, int viewID) + { + super(protocol, CDOProtocolConstants.SIGNAL_CLOSE_VIEW); + this.viewID = viewID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(viewID); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitDelegationRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitDelegationRequest.java new file mode 100644 index 000000000..9ad454d14 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitDelegationRequest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2013 Eike Stepper (Loehne, 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.net4j.protocol; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Map; + +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.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext; + +/** + * @author Eike Stepper + */ +public class CommitDelegationRequest extends CommitTransactionRequest { + private static final DelegationIDProvider delegationIDProvider = new DelegationIDProvider(); + + private CDOBranch branch; + + private String userID; + + /** BEGIN SPECMATE PATCH */ + private InternalCDOCommitContext context; + + private Map detachedTypes; + + /** BEGIN SPECMATE PATCH */ + + public CommitDelegationRequest(CDOClientProtocol protocol, InternalCDOCommitContext context) { + super(protocol, CDOProtocolConstants.SIGNAL_COMMIT_DELEGATION, context); + branch = context.getBranch(); + userID = context.getUserID(); + + this.context = context; + + try { + CDOCommitData commitData = this.context.getCommitData(); + Method m = commitData.getClass().getDeclaredMethod("getDetachedTypes"); // NoSuchFieldException + m.setAccessible(true); + this.detachedTypes = (Map) m.invoke(commitData, null); + } catch (Exception e) { + ; + } + } + + @Override + protected void requestingTransactionInfo(CDODataOutput out) throws IOException { + out.writeCDOBranch(branch); + out.writeString(userID); + } + + @Override + protected long getLastUpdateTime() { + return CDOBranchPoint.UNSPECIFIED_DATE; + } + + @Override + protected CDOBranch getBranch() { + return branch; + } + + @Override + protected EClass getObjectType(CDOID id) { + /** BEGIN SPECMATE PATCH */ + if (this.detachedTypes != null) { + return this.detachedTypes.get(id); + } else { + throw new UnsupportedOperationException(); + } + /** END SPECMATE PATCH */ + // The types of detached objects are delivered through the wire and don't need to be queried locally. + /*throw new UnsupportedOperationException();*/ + } + + @Override + protected CDOIDProvider getIDProvider() { + return delegationIDProvider; + } + + /** + * @author Eike Stepper + */ + private static class DelegationIDProvider implements CDOIDProvider { + public CDOID provideCDOID(Object idOrObject) { + return (CDOID) idOrObject; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java new file mode 100644 index 000000000..21a9a1890 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009-2013 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class CommitNotificationIndication extends CDOClientIndication +{ + public CommitNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_COMMIT_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + CommitNotificationInfo info = new CDOProtocol.CommitNotificationInfo(in); + + InternalCDOSession session = getSession(); + session.handleCommitNotification(info); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java new file mode 100644 index 000000000..3e46a50bd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2009-2013, 2016, 2017 Eike Stepper (Loehne, 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 215688 + * Simon McDuff - bug 213402 + * Andre Dietisheim - bug 256649 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOObjectReference; +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.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.id.CDOIDTemp; +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.CDOLob; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +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.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.security.CDOPermission; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.session.CDORepositoryInfo; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; + +import org.eclipse.emf.internal.cdo.object.CDOObjectReferenceImpl; +import org.eclipse.emf.internal.cdo.view.AbstractCDOView; + +import org.eclipse.net4j.util.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.InternalCDOSession.CommitToken; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class CommitTransactionRequest extends CDOClientRequestWithMonitoring { + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, CommitTransactionRequest.class); + + private static long sleepMillisForTesting = 0L; + + private final int commitNumber; + + private final String commitComment; + + private CDOBranchPoint commitMergeSource; + + private final CDOCommitData commitData; + + private final Collection> lobs; + + private final Collection locksOnNewObjects; + + private final Collection idsToUnlock; + + private final int viewID; + + /** + * Is null in {@link CommitDelegationRequest}. + */ + private final InternalCDOTransaction transaction; + + private boolean clearResourcePathCache; + + public CommitTransactionRequest(CDOClientProtocol protocol, InternalCDOCommitContext context) { + this(protocol, CDOProtocolConstants.SIGNAL_COMMIT_TRANSACTION, context); + } + + public CommitTransactionRequest(CDOClientProtocol protocol, short signalID, InternalCDOCommitContext context) { + super(protocol, signalID); + transaction = context.getTransaction(); + /** BEGIN SPECMATE PATCH */ + if (transaction != null) { + /** END SPECMATE PATCH */ + CommitToken commitToken = transaction.getCommitToken(); + if (commitToken != null) { + commitNumber = commitToken.getCommitNumber(); + } else { + commitNumber = 0; + } + /** BEGIN SPECMATE PATCH */ + } else { + commitNumber = 0; + } + /** END SPECMATE PATCH */ + + commitComment = context.getCommitComment(); + commitMergeSource = context.getCommitMergeSource(); + commitData = context.getCommitData(); + lobs = context.getLobs(); + locksOnNewObjects = context.getLocksOnNewObjects(); + idsToUnlock = context.getIDsToUnlock(); + viewID = context.getViewID(); + } + + @Override + protected int getMonitorTimeoutSeconds() { + org.eclipse.emf.cdo.net4j.CDONet4jSession session = (org.eclipse.emf.cdo.net4j.CDONet4jSession) getSession(); + return session.options().getCommitTimeout(); + } + + @Override + protected CDOIDProvider getIDProvider() { + return transaction; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException { + requestingTransactionInfo(out); + requestingCommit(out); + } + + protected void requestingTransactionInfo(CDODataOutput out) throws IOException { + out.writeXInt(viewID); + } + + protected void requestingCommit(CDODataOutput out) throws IOException { + List newPackageUnits = commitData.getNewPackageUnits(); + List newObjects = commitData.getNewObjects(); + List changedObjects = commitData.getChangedObjects(); + List detachedObjects = commitData.getDetachedObjects(); + + out.writeXLong(getLastUpdateTime()); + out.writeXInt(commitNumber); + out.writeString(commitComment); + CDOBranchUtil.writeBranchPointOrNull(out, commitMergeSource); + + out.writeXInt(locksOnNewObjects.size()); + out.writeXInt(idsToUnlock.size()); + out.writeXInt(newPackageUnits.size()); + out.writeXInt(newObjects.size()); + out.writeXInt(changedObjects.size()); + out.writeXInt(detachedObjects.size()); + + if (TRACER.isEnabled()) { + TRACER.format("Writing {0} locks on new objects", locksOnNewObjects.size()); //$NON-NLS-1$ + } + + for (CDOLockState lockState : locksOnNewObjects) { + out.writeCDOLockState(lockState); + } + + if (TRACER.isEnabled()) { + TRACER.format("Writing {0} unlocks on changed objects", idsToUnlock.size()); //$NON-NLS-1$ + } + + for (CDOID id : idsToUnlock) { + out.writeCDOID(id); + } + + if (TRACER.isEnabled()) { + TRACER.format("Writing {0} new package units", newPackageUnits.size()); //$NON-NLS-1$ + } + + for (CDOPackageUnit newPackageUnit : newPackageUnits) { + out.writeCDOPackageUnit(newPackageUnit, true); + } + + if (TRACER.isEnabled()) { + TRACER.format("Writing {0} new objects", newObjects.size()); //$NON-NLS-1$ + } + + for (CDOIDAndVersion newObject : newObjects) { + out.writeCDORevision((CDORevision) newObject, CDORevision.UNCHUNKED); + + if (sleepMillisForTesting != 0L) { + ConcurrencyUtil.sleep(sleepMillisForTesting); + } + } + + if (TRACER.isEnabled()) { + TRACER.format("Writing {0} dirty objects", changedObjects.size()); //$NON-NLS-1$ + } + + CDORepositoryInfo repositoryInfo = getSession().getRepositoryInfo(); + CDOID rootResourceID = repositoryInfo.getRootResourceID(); + for (CDORevisionKey changedObject : changedObjects) { + CDORevisionDelta delta = (CDORevisionDelta) changedObject; + if (!clearResourcePathCache && AbstractCDOView.canHaveResourcePathImpact(delta, rootResourceID)) { + clearResourcePathCache = true; + } + + out.writeCDORevisionDelta(delta); + } + + out.writeBoolean(clearResourcePathCache); + + if (TRACER.isEnabled()) { + TRACER.format("Writing {0} detached objects", detachedObjects.size()); //$NON-NLS-1$ + } + + boolean auditing = repositoryInfo.isSupportingAudits(); + boolean branching = repositoryInfo.isSupportingBranches(); + boolean ensuringReferentialIntegrity = repositoryInfo.isEnsuringReferentialIntegrity(); + CDOBranch transactionBranch = getBranch(); + + for (CDOIDAndVersion detachedObject : detachedObjects) { + CDOID id = detachedObject.getID(); + out.writeCDOID(id); + if (auditing || ensuringReferentialIntegrity) { + EClass eClass = getObjectType(id); + out.writeCDOClassifierRef(eClass); + } + + if (auditing) { + int version = detachedObject.getVersion(); + if (branching && detachedObject instanceof CDORevisionKey) { + CDOBranch branch = ((CDORevisionKey) detachedObject).getBranch(); + if (branch != transactionBranch) { + out.writeXInt(-version); + out.writeCDOBranch(branch); + continue; + } + } + + out.writeXInt(version); + } + } + + requestingLobs(); + } + + protected void requestingLobs() throws IOException { + ExtendedDataOutputStream out = getRequestStream(); + out.writeInt(lobs.size()); + for (CDOLob lob : lobs) { + Closeable closeable = null; + + try { + out.writeByteArray(lob.getID()); + long size = lob.getSize(); + + if (lob instanceof CDOBlob) { + CDOBlob blob = (CDOBlob) lob; + out.writeLong(size); + + InputStream contents = blob.getContents(); + closeable = contents; + IOUtil.copyBinary(contents, out, size); + } else { + CDOClob clob = (CDOClob) lob; + out.writeLong(-size); + + Reader contents = clob.getContents(); + closeable = contents; + IOUtil.copyCharacter(contents, new OutputStreamWriter(out), size); + } + } finally { + IOUtil.close(closeable); + } + } + } + + protected long getLastUpdateTime() { + return transaction.getLastUpdateTime(); + } + + protected CDOBranch getBranch() { + return transaction.getBranch(); + } + + protected EClass getObjectType(CDOID id) { + CDOObject object = transaction.getObject(id); + return object.eClass(); + } + + @Override + protected CommitTransactionResult confirming(CDODataInput in, OMMonitor monitor) throws IOException { + CommitTransactionResult result = confirmingCheckError(in); + if (result != null) { + return result; + } + + result = confirmingResult(in); + confirmingMappingNewObjects(in, result); + confirmingNewLockStates(in, result); + confirmingNewPermissions(in, result); + return result; + } + + protected CommitTransactionResult confirmingCheckError(CDODataInput in) throws IOException { + boolean success = in.readBoolean(); + if (success) { + return null; + } + + CommitTransactionResult result = new CommitTransactionResult(); + result.setIDProvider(transaction); + result.setClearResourcePathCache(clearResourcePathCache); + result.setRollbackReason(in.readByte()); + result.setRollbackMessage(in.readString()); + result.setBranchPoint(in.readCDOBranchPoint()); + result.setPreviousTimeStamp(in.readXLong()); + + int size = in.readXInt(); + if (size != 0) { + List xRefs = new ArrayList(size); + result.setXRefs(xRefs); + + for (int i = 0; i < size; i++) { + CDOIDReference idReference = in.readCDOIDReference(); + xRefs.add(new CDOObjectReferenceImpl(transaction, idReference)); + } + } + + return result; + } + + protected CommitTransactionResult confirmingResult(CDODataInput in) throws IOException { + CommitTransactionResult result = new CommitTransactionResult(); + result.setIDProvider(transaction); + result.setClearResourcePathCache(clearResourcePathCache); + result.setBranchPoint(in.readCDOBranchPoint()); + result.setPreviousTimeStamp(in.readXLong()); + result.setSecurityImpact(in.readByte()); + return result; + } + + protected void confirmingMappingNewObjects(CDODataInput in, CommitTransactionResult result) throws IOException { + for (;;) { + CDOID id = in.readCDOID(); + if (CDOIDUtil.isNull(id)) { + break; + } + + if (id instanceof CDOIDTemp) { + CDOIDTemp oldID = (CDOIDTemp) id; + CDOID newID = in.readCDOID(); + result.addIDMapping(oldID, newID); + } else { + throw new ClassCastException("Not a temporary ID: " + id); + } + } + } + + protected void confirmingNewLockStates(CDODataInput in, CommitTransactionResult result) throws IOException { + int n = in.readXInt(); + CDOLockState[] newLockStates = new CDOLockState[n]; + + for (int i = 0; i < n; i++) { + newLockStates[i] = in.readCDOLockState(); + } + + result.setNewLockStates(newLockStates); + } + + protected void confirmingNewPermissions(CDODataInput in, CommitTransactionResult result) throws IOException { + if (in.readBoolean()) { + int n = in.readXInt(); + for (int i = 0; i < n; i++) { + CDOID id = in.readCDOID(); + CDOPermission permission = in.readEnum(CDOPermission.class); + + result.addNewPermission(id, permission); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionCancelRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionCancelRequest.java new file mode 100644 index 000000000..cea3e30cd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionCancelRequest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +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.transaction.CDOTransaction; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +import java.io.IOException; + +/** + * Determine at which moment the server side can complete the transaction. + *

+ * At this stage, everything on the database was done except to flush on the disk. + *

+ * It is useful to assure that all {@link CDOTransaction} involve in that commit are synchronize. + * + * @author Simon McDuff + */ +public class CommitXATransactionCancelRequest extends CommitXATransactionRequest +{ + public CommitXATransactionCancelRequest(CDOClientProtocol protocol, InternalCDOXACommitContext xaContext) + { + super(protocol, CDOProtocolConstants.SIGNAL_XA_COMMIT_TRANSACTION_CANCEL, xaContext); + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + requestingTransactionInfo(out); + } + + @Override + protected CommitTransactionResult confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + return confirmingCheckError(in); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase1Request.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase1Request.java new file mode 100644 index 000000000..d650917f7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase1Request.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.transaction.CDOTransaction; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +import java.io.IOException; + +/** + * Phase 1 will send all the modifications to the server. + *

+ * It needs to fill id mappings for objects immediately to be use by other {@link CDOTransaction} involve in that + * commit. + * + * @author Simon McDuff + */ +public class CommitXATransactionPhase1Request extends CommitXATransactionRequest +{ + public CommitXATransactionPhase1Request(CDOClientProtocol protocol, InternalCDOXACommitContext xaContext) + { + super(protocol, CDOProtocolConstants.SIGNAL_XA_COMMIT_TRANSACTION_PHASE1, xaContext); + } + + @Override + protected CDOIDProvider getIDProvider() + { + return getCommitContext(); + } + + @Override + protected CommitTransactionResult confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + CommitTransactionResult result = confirmingCheckError(in); + if (result != null) + { + return result; + } + + result = confirmingResult(in); + confirmingMappingNewObjects(in, result); + return result; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase2Request.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase2Request.java new file mode 100644 index 000000000..f0301afcd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase2Request.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2010-2013, 2015-2017 Eike Stepper (Loehne, 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 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.common.id.CDOIDTempObjectExternalImpl; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.internal.net4j.messages.Messages; +import org.eclipse.emf.cdo.util.CDOURIUtil; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Map; +import java.util.Map.Entry; + +/** + *

+ * Phase 2 consist of sending the mapping of temporary/persistent CDOID from other CDOTransaction. + *

+ * It will return confirmation only when the commit is ready to flush to disk. + * + * @author Simon McDuff + */ +public class CommitXATransactionPhase2Request extends CommitXATransactionRequest +{ + private static final ContextTracer PROTOCOL = new ContextTracer(OM.DEBUG_PROTOCOL, CommitXATransactionPhase1Request.class); + + public CommitXATransactionPhase2Request(CDOClientProtocol protocol, InternalCDOXACommitContext xaContext) + { + super(protocol, CDOProtocolConstants.SIGNAL_XA_COMMIT_TRANSACTION_PHASE2, xaContext); + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + requestingTransactionInfo(out); + requestingIDMapping(out); + } + + /** + * Write IDs that are needed. only If it needs to + */ + protected void requestingIDMapping(CDODataOutput out) throws IOException + { + InternalCDOXACommitContext context = getCommitContext(); + Map requestedIDs = context.getRequestedIDs(); + int size = requestedIDs.size(); + out.writeXInt(size); + if (PROTOCOL.isEnabled()) + { + PROTOCOL.format("Number of ids requested: {0}", size); //$NON-NLS-1$ + } + + for (Entry entry : requestedIDs.entrySet()) + { + CDOIDTempObjectExternalImpl tempID = entry.getKey(); + URI oldURIExternal = URI.createURI(tempID.toURIFragment()); + CDOID oldCDOID = CDOIDUtil.read(oldURIExternal.fragment()); + + InternalCDOXACommitContext commitContext = context.getTransactionManager().getCommitContext(entry.getValue()); + if (commitContext == null) + { + throw new IllegalStateException(MessageFormat.format(Messages.getString("CommitTransactionPhase2Request.1"), entry //$NON-NLS-1$ + .getValue())); + } + + CDOID newID = commitContext.getResult().getIDMappings().get(oldCDOID); + if (newID == null) + { + throw new IllegalStateException(MessageFormat.format(Messages.getString("CommitTransactionPhase2Request.2"), oldCDOID //$NON-NLS-1$ + .toURIFragment())); + } + + CDOID newIDExternal = CDOURIUtil.convertExternalCDOID(oldURIExternal, newID); + if (PROTOCOL.isEnabled()) + { + PROTOCOL.format("ID mapping: {0} --> {1}", tempID.toURIFragment(), newIDExternal.toURIFragment()); //$NON-NLS-1$ + } + + out.writeCDOID(tempID); + out.writeCDOID(newIDExternal); + + context.getResult().addIDMapping(tempID, newIDExternal); + } + } + + @Override + protected CommitTransactionResult confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + return confirmingCheckError(in); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase3Request.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase3Request.java new file mode 100644 index 000000000..2b6a62dcd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionPhase3Request.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +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.transaction.CDOTransaction; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +import java.io.IOException; + +/** + * Determine at which moment the server side can complete the transaction. + *

+ * At this stage, everything on the database was done except to flush on the disk. + *

+ * It is useful to assure that all {@link CDOTransaction} involve in that commit are synchronize. + * + * @author Simon McDuff + */ +public class CommitXATransactionPhase3Request extends CommitXATransactionRequest +{ + public CommitXATransactionPhase3Request(CDOClientProtocol protocol, InternalCDOXACommitContext xaContext) + { + super(protocol, CDOProtocolConstants.SIGNAL_XA_COMMIT_TRANSACTION_PHASE3, xaContext); + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + requestingTransactionInfo(out); + } + + @Override + protected CommitTransactionResult confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + CommitTransactionResult result = confirmingCheckError(in); + if (result != null) + { + return result; + } + + InternalCDOXACommitContext context = getCommitContext(); + return context.getResult(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionRequest.java new file mode 100644 index 000000000..5f09ac37b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitXATransactionRequest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +/** + * @author Eike Stepper + */ +public class CommitXATransactionRequest extends CommitTransactionRequest +{ + private InternalCDOXACommitContext xaContext; + + public CommitXATransactionRequest(CDOClientProtocol protocol, short signalID, InternalCDOXACommitContext xaContext) + { + super(protocol, signalID, xaContext); + this.xaContext = xaContext; + } + + protected InternalCDOXACommitContext getCommitContext() + { + return xaContext; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CreateBranchRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CreateBranchRequest.java new file mode 100644 index 000000000..e45f1e7c5 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CreateBranchRequest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2013, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.spi.common.branch.InternalCDOBranchManager.BranchLoader.BranchInfo; + +import org.eclipse.net4j.util.collection.Pair; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class CreateBranchRequest extends CDOClientRequest> +{ + private int branchID; + + private BranchInfo branchInfo; + + public CreateBranchRequest(CDOClientProtocol protocol, int branchID, BranchInfo branchInfo) + { + super(protocol, CDOProtocolConstants.SIGNAL_CREATE_BRANCH); + this.branchID = branchID; + this.branchInfo = branchInfo; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(branchID); + branchInfo.write(out); + } + + @Override + protected Pair confirming(CDODataInput in) throws IOException + { + branchID = in.readXInt(); + long baseTimeStamp = in.readXLong(); + return Pair.create(branchID, baseTimeStamp); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CredentialsChallengeIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CredentialsChallengeIndication.java new file mode 100644 index 000000000..527cffd85 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CredentialsChallengeIndication.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - Adapted from AuthenticationIndication for 399306 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; + +import org.eclipse.net4j.signal.IndicationWithMonitoring; +import org.eclipse.net4j.signal.SignalProtocol; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; +import org.eclipse.net4j.util.security.DiffieHellman; +import org.eclipse.net4j.util.security.DiffieHellman.Client.Response; +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; +import org.eclipse.net4j.util.security.IPasswordCredentialsProvider; +import org.eclipse.net4j.util.security.IPasswordCredentialsUpdate; +import org.eclipse.net4j.util.security.IPasswordCredentialsUpdateProvider; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.ByteArrayOutputStream; + +/** + * Implementation of the CDO client handler for the server-initiated change-credentials protocol. + * + * @author Christian W. Damus (CEA LIST) + */ +public class CredentialsChallengeIndication extends IndicationWithMonitoring +{ + private Challenge challenge; + + private CredentialsUpdateOperation operation; + + private String userID; + + public CredentialsChallengeIndication(SignalProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_CREDENTIALS_CHALLENGE); + } + + @Override + public CDOClientProtocol getProtocol() + { + return (CDOClientProtocol)super.getProtocol(); + } + + protected InternalCDOSession getSession() + { + return (InternalCDOSession)getProtocol().getSession(); + } + + @Override + protected int getIndicatingWorkPercent() + { + return 1; + } + + @Override + protected void indicating(ExtendedDataInputStream in, OMMonitor monitor) throws Exception + { + operation = in.readEnum(CredentialsUpdateOperation.class); + userID = in.readString(); // May be null if operation is not reset + challenge = new Challenge(in); + } + + @Override + protected void responding(ExtendedDataOutputStream out, OMMonitor monitor) throws Exception + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + IPasswordCredentialsProvider credentialsProvider = getSession().getCredentialsProvider(); + if (!(credentialsProvider instanceof IPasswordCredentialsUpdateProvider)) + { + throw new IllegalStateException("No credentials update provider configured"); //$NON-NLS-1$ + } + + IPasswordCredentialsUpdate credentials = ((IPasswordCredentialsUpdateProvider)credentialsProvider).getCredentialsUpdate(userID, operation); + if (credentials == null) + { + // User canceled. Fine + out.writeBoolean(false); + return; + } + + String authUserID = credentials.getUserID(); + String authPassword = new String(credentials.getPassword()); + String newPassword = new String(credentials.getNewPassword()); + if (StringUtil.isEmpty(newPassword)) + { + throw new IllegalStateException("No new password provided"); //$NON-NLS-1$ + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ExtendedDataOutputStream stream = new ExtendedDataOutputStream(baos); + + switch (operation) + { + case CHANGE_PASSWORD: + stream.writeString(authUserID); + stream.writeString(authPassword); + stream.writeString(newPassword); + break; + case RESET_PASSWORD: + stream.writeString(authUserID); + stream.writeString(authPassword); + stream.writeString(userID); + stream.writeString(newPassword); + break; + } + + stream.close(); + byte[] clearText = baos.toByteArray(); + + DiffieHellman.Client client = new DiffieHellman.Client(); + Response response = client.handleChallenge(challenge, clearText); + out.writeBoolean(true); + response.write(out); + } + catch (Throwable ex) + { + out.writeBoolean(false); + OM.LOG.error(ex); + } + finally + { + async.stop(); + monitor.done(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/DisablePassiveUpdateRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/DisablePassiveUpdateRequest.java new file mode 100644 index 000000000..2ce5a2d2f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/DisablePassiveUpdateRequest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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.internal.net4j.protocol; + +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 java.io.IOException; + +/** + * @author Eike Stepper + */ +public class DisablePassiveUpdateRequest extends CDOClientRequest +{ + public DisablePassiveUpdateRequest(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_DISABLE_PASSIVE_UPDATE); + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/EnableLockNotificationRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/EnableLockNotificationRequest.java new file mode 100644 index 000000000..f6f382f7e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/EnableLockNotificationRequest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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 java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class EnableLockNotificationRequest extends CDOClientRequest +{ + private int viewID; + + private boolean on; + + public EnableLockNotificationRequest(CDOClientProtocol protocol, int viewID, boolean on) + { + super(protocol, CDOProtocolConstants.SIGNAL_ENABLE_LOCK_NOTIFICATION); + this.viewID = viewID; + this.on = on; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(viewID); + out.writeBoolean(on); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/GetRemoteSessionsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/GetRemoteSessionsRequest.java new file mode 100644 index 000000000..caa36c417 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/GetRemoteSessionsRequest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.session.remote.CDORemoteSession; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class GetRemoteSessionsRequest extends CDOClientRequest> +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, GetRemoteSessionsRequest.class); + + private boolean subscribe; + + public GetRemoteSessionsRequest(CDOClientProtocol protocol, boolean subscribe) + { + super(protocol, CDOProtocolConstants.SIGNAL_GET_REMOTE_SESSIONS); + this.subscribe = subscribe; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing subscribe: {0}", subscribe); //$NON-NLS-1$ + } + + out.writeBoolean(subscribe); + } + + @Override + protected List confirming(CDODataInput in) throws IOException + { + List result = new ArrayList(); + + for (;;) + { + int sessionID = in.readXInt(); + if (sessionID == CDOProtocolConstants.NO_MORE_REMOTE_SESSIONS) + { + break; + } + + String userID = in.readString(); + boolean subscribed = in.readBoolean(); + InternalCDORemoteSessionManager manager = getSession().getRemoteSessionManager(); + CDORemoteSession remoteSession = manager.createRemoteSession(sessionID, userID, subscribed); + result.add(remoteSession); + } + + return result; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/HandleRevisionsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/HandleRevisionsRequest.java new file mode 100644 index 000000000..d1b0ff5c6 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/HandleRevisionsRequest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +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.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class HandleRevisionsRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, HandleRevisionsRequest.class); + + private EClass eClass; + + private CDOBranch branch; + + private boolean exactBranch; + + private long timeStamp; + + private boolean exactTime; + + private CDORevisionHandler handler; + + public HandleRevisionsRequest(CDOClientProtocol protocol, EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + super(protocol, CDOProtocolConstants.SIGNAL_HANDLE_REVISIONS); + this.eClass = eClass; + this.branch = branch; + this.exactBranch = exactBranch; + this.timeStamp = timeStamp; + this.exactTime = exactTime; + this.handler = handler; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (eClass != null) + { + out.writeBoolean(true); + if (TRACER.isEnabled()) + { + TRACER.format("Writing eClass: {0}", eClass); //$NON-NLS-1$ + } + + out.writeCDOClassifierRef(eClass); + } + else + { + out.writeBoolean(false); + } + + if (branch != null) + { + out.writeBoolean(true); + if (TRACER.isEnabled()) + { + TRACER.format("Writing branch: {0}", branch); //$NON-NLS-1$ + } + + out.writeCDOBranch(branch); + if (TRACER.isEnabled()) + { + TRACER.format("Writing exactBranch: {0}", exactBranch); //$NON-NLS-1$ + } + + out.writeBoolean(exactBranch); + } + else + { + out.writeBoolean(false); + } + if (TRACER.isEnabled()) + { + TRACER.format("Writing timeStamp: {0}", CDOCommonUtil.formatTimeStamp(timeStamp)); //$NON-NLS-1$ + } + + out.writeXLong(timeStamp); + if (TRACER.isEnabled()) + { + TRACER.format("Writing exactTime: {0}", exactTime); //$NON-NLS-1$ + } + + out.writeBoolean(exactTime); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + while (in.readBoolean()) + { + CDORevision revision = in.readCDORevision(); + if (TRACER.isEnabled()) + { + TRACER.format("Read revision: {0}", revision); //$NON-NLS-1$ + } + + handler.handleRevision(revision); + } + + return true; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadBranchRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadBranchRequest.java new file mode 100644 index 000000000..54bfb200e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadBranchRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.spi.common.branch.InternalCDOBranchManager.BranchLoader.BranchInfo; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadBranchRequest extends CDOClientRequest +{ + private int branchID; + + public LoadBranchRequest(CDOClientProtocol protocol, int branchID) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_BRANCH); + this.branchID = branchID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(branchID); + } + + @Override + protected BranchInfo confirming(CDODataInput in) throws IOException + { + return new BranchInfo(in); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadBranchesRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadBranchesRequest.java new file mode 100644 index 000000000..7aa20632b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadBranchesRequest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +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 java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadBranchesRequest extends CDOClientRequest +{ + private int startID; + + private int endID; + + private CDOBranchHandler handler; + + public LoadBranchesRequest(CDOClientProtocol protocol, int startID, int endID, CDOBranchHandler handler) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_BRANCHES); + this.startID = startID; + this.endID = endID; + this.handler = handler; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(startID); + out.writeXInt(endID); + } + + @Override + protected Integer confirming(CDODataInput in) throws IOException + { + int count = 0; + while (in.readByte() == CDOProtocolConstants.REPLICATE_BRANCH) + { + CDOBranch branch = in.readCDOBranch(); + handler.handleBranch(branch); + ++count; + } + + return count; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java new file mode 100644 index 000000000..307053968 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChangeSetsRequest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPointRange; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +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.spi.common.branch.CDOBranchUtil; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadChangeSetsRequest extends CDOClientRequest +{ + private CDOBranchPointRange[] ranges; + + public LoadChangeSetsRequest(CDOClientProtocol protocol, CDOBranchPointRange... ranges) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_CHANGE_SETS); + this.ranges = ranges; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(ranges.length); + for (CDOBranchPointRange range : ranges) + { + CDOBranchUtil.writeRange(out, range); + } + } + + @Override + protected CDOChangeSetData[] confirming(CDODataInput in) throws IOException + { + CDOChangeSetData[] result = new CDOChangeSetData[ranges.length]; + for (int i = 0; i < result.length; i++) + { + CDOChangeSetData changeSetData = in.readCDOChangeSetData(); + result[i] = changeSetData; + } + + return result; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java new file mode 100644 index 000000000..629df514b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadChunkRequest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2009-2012, 2016-2018 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.CDOType; +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.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadChunkRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LoadChunkRequest.class); + + private InternalCDORevision revision; + + private EStructuralFeature feature; + + private int accessIndex; + + private int fromIndex; + + private int toIndex; + + private int fetchIndex; + + public LoadChunkRequest(CDOClientProtocol protocol, InternalCDORevision revision, EStructuralFeature feature, int accessIndex, int fetchIndex, int fromIndex, + int toIndex) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_CHUNK); + this.revision = revision; + this.feature = feature; + this.accessIndex = accessIndex; + this.fetchIndex = fetchIndex; + this.fromIndex = fromIndex; + this.toIndex = toIndex; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + CDOID id = revision.getID(); + if (TRACER.isEnabled()) + { + TRACER.format("Writing revision ID: {0}", id); //$NON-NLS-1$ + } + + out.writeCDOID(id); + CDOBranch branch = revision.getBranch(); + if (TRACER.isEnabled()) + { + TRACER.format("Writing branch: {0}", branch); //$NON-NLS-1$ + } + + out.writeCDOBranch(branch); + int version = revision.getVersion(); + if (TRACER.isEnabled()) + { + TRACER.format("Writing version: {0}", version); //$NON-NLS-1$ + } + + out.writeXInt(version); + if (TRACER.isEnabled()) + { + TRACER.format("Writing feature: {0}", feature); //$NON-NLS-1$ + } + + out.writeCDOClassifierRef(feature.getEContainingClass()); + out.writeXInt(feature.getFeatureID()); + if (TRACER.isEnabled()) + { + TRACER.format("Writing fromIndex: {0}", fromIndex); //$NON-NLS-1$ + } + + int diffIndex = accessIndex - fetchIndex; + out.writeXInt(fromIndex - diffIndex); + if (TRACER.isEnabled()) + { + TRACER.format("Writing toIndex: {0}", toIndex); //$NON-NLS-1$ + } + + out.writeXInt(toIndex - diffIndex); + } + + @Override + protected Object confirming(CDODataInput in) throws IOException + { + CDOType type = CDOModelUtil.getType(feature); + Object accessID = null; + InternalCDOList list = (InternalCDOList)revision.getListOrNull(feature); + for (int i = fromIndex; i <= toIndex; i++) + { + Object value = type.readValue(in); + list.setWithoutFrozenCheck(i, value); + if (i == accessIndex) + { + accessID = value; + } + } + + return accessID; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitDataRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitDataRequest.java new file mode 100644 index 000000000..eb1e65613 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitDataRequest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +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.internal.common.revision.delta.CDOSetFeatureDeltaImpl; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadCommitDataRequest extends CDOClientRequest +{ + private long timeStamp; + + public LoadCommitDataRequest(CDOClientProtocol protocol, long timeStamp) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_COMMIT_DATA); + this.timeStamp = timeStamp; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXLong(timeStamp); + } + + @Override + protected CDOCommitData confirming(CDODataInput in) throws IOException + { + try + { + CDOSetFeatureDeltaImpl.transferOldValue(true); + return in.readCDOCommitData(); + } + finally + { + CDOSetFeatureDeltaImpl.transferOldValue(false); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java new file mode 100644 index 000000000..89403bba6 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadCommitInfosRequest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +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.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadCommitInfosRequest extends CDOClientRequest +{ + private CDOBranch branch; + + private long startTime; + + private long endTime; + + private CDOCommitInfoHandler handler; + + public LoadCommitInfosRequest(CDOClientProtocol protocol, CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_COMMIT_INFOS); + this.branch = branch; + this.startTime = startTime; + this.endTime = endTime; + this.handler = handler; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (branch == null) + { + out.writeBoolean(false); + } + else + { + out.writeBoolean(true); + out.writeCDOBranch(branch); + } + + out.writeXLong(startTime); + out.writeXLong(endTime); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + InternalCDOCommitInfoManager manager = getSession().getCommitInfoManager(); + while (in.readBoolean()) + { + long id = in.readXLong(); + CDOBranch branch = this.branch == null ? in.readCDOBranch() : this.branch; + long timeStamp = in.readXLong(); + String userID = in.readString(); + String comment = in.readString(); + CDOBranchPoint mergeSource = CDOBranchUtil.readBranchPointOrNull(in); + + try + { + CDOCommitInfo commitInfo = manager.createCommitInfo(branch, timeStamp, id, userID, comment, mergeSource, null); + handler.handleCommitInfo(commitInfo); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + + return true; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadLobRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadLobRequest.java new file mode 100644 index 000000000..1c86ae306 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadLobRequest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2012, 2016 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.lob.CDOLobInfo; +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.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.IOUtil; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Writer; + +/** + * @author Eike Stepper + */ +public class LoadLobRequest extends CDOClientRequest +{ + private CDOLobInfo info; + + private Object out; + + public LoadLobRequest(CDOClientProtocol protocol, CDOLobInfo info, Object out) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_LOB); + this.info = info; + this.out = out; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeByteArray(info.getID()); + } + + @Override + protected Boolean confirming(ExtendedDataInputStream in) throws Exception + { + try + { + if (out instanceof OutputStream) + { + IOUtil.copyBinary(in, (OutputStream)out, info.getSize()); + } + else + { + IOUtil.copyCharacter(new InputStreamReader(in), (Writer)out, info.getSize()); + } + } + finally + { + ((Closeable)out).close(); + } + + return true; + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + throw new UnsupportedOperationException(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java new file mode 100644 index 000000000..b6a53545f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadMergeDataRequest.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2010-2012, 2014, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.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.CDORevisionKey; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.MergeDataResult; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class LoadMergeDataRequest extends CDOClientRequestWithMonitoring +{ + private CDORevisionAvailabilityInfo targetInfo; + + private CDORevisionAvailabilityInfo sourceInfo; + + private CDORevisionAvailabilityInfo targetBaseInfo; + + private CDORevisionAvailabilityInfo sourceBaseInfo; + + private int infos; + + private boolean auto; + + public LoadMergeDataRequest(CDOClientProtocol protocol, CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_MERGE_DATA); + this.targetInfo = targetInfo; + this.sourceInfo = sourceInfo; + this.targetBaseInfo = targetBaseInfo; + this.sourceBaseInfo = sourceBaseInfo; + infos = 2 + (targetBaseInfo != null ? 1 : 0) + (sourceBaseInfo != null ? 1 : 0); + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + out.writeXInt(infos); + monitor.begin(infos); + + try + { + writeRevisionAvailabilityInfo(out, targetInfo, monitor.fork()); + writeRevisionAvailabilityInfo(out, sourceInfo, monitor.fork()); + + if (infos > 2) + { + writeRevisionAvailabilityInfo(out, targetBaseInfo, monitor.fork()); + } + + if (infos > 3) + { + writeRevisionAvailabilityInfo(out, sourceBaseInfo, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + private void writeRevisionAvailabilityInfo(CDODataOutput out, CDORevisionAvailabilityInfo info, OMMonitor monitor) throws IOException + { + CDOBranchPoint branchPoint = info.getBranchPoint(); + if (branchPoint != CDOBranchUtil.AUTO_BRANCH_POINT) + { + out.writeBoolean(true); + + Set availableRevisions = info.getAvailableRevisions().keySet(); + int size = availableRevisions.size(); + + out.writeCDOBranchPoint(branchPoint); + out.writeXInt(size); + + monitor.begin(size); + + try + { + for (CDOID id : availableRevisions) + { + out.writeCDOID(id); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + else + { + out.writeBoolean(false); + auto = true; + } + } + + @Override + protected MergeDataResult confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + MergeDataResult result = new MergeDataResult(); + + monitor.begin(1 + infos); + + try + { + // Read IDs of objects that are changed only in target. + for (;;) + { + CDOID id = in.readCDOID(); + if (CDOIDUtil.isNull(id)) + { + break; + } + + result.getTargetIDs().add(id); + } + + // Read IDs of objects that are changed in both target and source. + for (;;) + { + CDOID id = in.readCDOID(); + if (CDOIDUtil.isNull(id)) + { + break; + } + + result.getTargetIDs().add(id); + result.getSourceIDs().add(id); + } + + // Read IDs of objects that are changed only in source. + for (;;) + { + CDOID id = in.readCDOID(); + if (CDOIDUtil.isNull(id)) + { + break; + } + + result.getSourceIDs().add(id); + } + + monitor.worked(); + + if (auto) + { + targetBaseInfo.setBranchPoint(in.readCDOBranchPoint()); + if (in.readBoolean()) + { + sourceBaseInfo.setBranchPoint(in.readCDOBranchPoint()); + } + else + { + sourceBaseInfo.setBranchPoint(targetBaseInfo.getBranchPoint()); + infos = 3; + } + } + + readRevisionAvailabilityInfo(in, targetInfo, result.getTargetIDs(), monitor.fork()); + readRevisionAvailabilityInfo(in, sourceInfo, result.getSourceIDs(), monitor.fork()); + + if (infos > 2) + { + readRevisionAvailabilityInfo(in, targetBaseInfo, result.getTargetIDs(), monitor.fork()); + } + + if (infos > 3) + { + readRevisionAvailabilityInfo(in, sourceBaseInfo, result.getSourceIDs(), monitor.fork()); + } + + result.setResultBase(CDOBranchUtil.readBranchPointOrNull(in)); + return result; + } + finally + { + monitor.done(); + } + } + + private void readRevisionAvailabilityInfo(CDODataInput in, CDORevisionAvailabilityInfo info, Set result, OMMonitor monitor) throws IOException + { + int size = in.readXInt(); + monitor.begin(size + 1); + + try + { + for (int i = 0; i < size; i++) + { + CDORevision revision; + if (in.readBoolean()) + { + revision = in.readCDORevision(); + } + else + { + CDORevisionKey key = in.readCDORevisionKey(); + revision = getRevision(key, targetInfo); + + if (revision == null && sourceInfo != null) + { + revision = getRevision(key, sourceInfo); + } + + if (revision == null && targetBaseInfo != null) + { + revision = getRevision(key, targetBaseInfo); + } + + if (revision == null) + { + throw new IllegalStateException("Missing revision: " + key); + } + } + + info.addRevision(revision); + monitor.worked(); + } + + Set> entrySet = info.getAvailableRevisions().entrySet(); + for (Iterator> it = entrySet.iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + if (!result.contains(entry.getKey())) + { + it.remove(); + } + } + + monitor.worked(); + } + finally + { + monitor.done(); + } + } + + private CDORevision getRevision(CDORevisionKey key, CDORevisionAvailabilityInfo info) + { + CDORevisionKey revision = info.getRevision(key.getID()); + if (revision instanceof CDORevision) + { + if (key.equals(revision)) + { + return (CDORevision)revision; + } + } + + return null; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java new file mode 100644 index 000000000..739dc1ca7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadObjectLifetimeRequest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, 2016 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchPointRange; +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.text.MessageFormat; + +/** + * @author Eike Stepper + */ +public class LoadObjectLifetimeRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LoadObjectLifetimeRequest.class); + + private CDOID id; + + private CDOBranchPoint branchPoint; + + public LoadObjectLifetimeRequest(CDOClientProtocol protocol, CDOID id, CDOBranchPoint branchPoint) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_OBJECT_LIFETIME); + this.id = id; + this.branchPoint = branchPoint; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing id: {0}", id); //$NON-NLS-1$ + } + + out.writeCDOID(id); + if (TRACER.isEnabled()) + { + TRACER.format("Writing branchPoint: {0}", branchPoint); //$NON-NLS-1$ + } + + out.writeCDOBranchPoint(branchPoint); + } + + @Override + protected CDOBranchPointRange confirming(CDODataInput in) throws IOException + { + return CDOBranchUtil.readRangeOrNull(in); + } + + @Override + public String toString() + { + return MessageFormat.format("LoadFirstRevisionsRequest(id={0}, branchPoint={1})", id, branchPoint); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadPackagesRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadPackagesRequest.java new file mode 100644 index 000000000..32e51675a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadPackagesRequest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2009-2012, 2015 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.resource.ResourceSet; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadPackagesRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LoadPackagesRequest.class); + + private InternalCDOPackageUnit packageUnit; + + public LoadPackagesRequest(CDOClientProtocol protocol, InternalCDOPackageUnit packageUnit) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_PACKAGES); + this.packageUnit = packageUnit; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + String packageUnitID = packageUnit.getID(); + if (TRACER.isEnabled()) + { + TRACER.format("Writing packageUnitID: {0}", packageUnitID); //$NON-NLS-1$ + } + + out.writeCDOPackageURI(packageUnitID); + } + + @Override + protected EPackage[] confirming(CDODataInput in) throws IOException + { + ResourceSet resourceSet = EMFUtil.newEcoreResourceSet(packageUnit.getPackageRegistry()); + EPackage ePackage = CDOModelUtil.readPackage(in, resourceSet, false); + return EMFUtil.getAllPackages(ePackage); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadPermissionsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadPermissionsRequest.java new file mode 100644 index 000000000..fbb69f0ec --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadPermissionsRequest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2013, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.security.CDOPermission; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public class LoadPermissionsRequest extends CDOClientRequest> +{ + private InternalCDORevision[] revisions; + + public LoadPermissionsRequest(CDOClientProtocol protocol, InternalCDORevision[] revisions) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_PERMISSIONS); + this.revisions = revisions; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + int length = revisions.length; + out.writeXInt(length); + + for (int i = 0; i < length; i++) + { + InternalCDORevision revision = revisions[i]; + CDOID id = revision.getID(); + out.writeCDOID(id); + + byte bits = revision.getPermission().getBits(); + out.writeByte(bits); + } + + int referenceChunk = getSession().options().getCollectionLoadingPolicy().getInitialChunkSize(); + out.writeXInt(referenceChunk); + } + + @Override + protected Map confirming(CDODataInput in) throws IOException + { + Map oldPermissions = null; + + int length = revisions.length; + for (int i = 0; i < length; i++) + { + byte bits = in.readByte(); + if (bits == CDOProtocolConstants.REVISION_DOES_NOT_EXIST) + { + continue; + } + + InternalCDORevision revision = revisions[i]; + CDOPermission oldPermission = revision.getPermission(); + CDOPermission newPermission = CDOPermission.get(bits); + + if (oldPermission != newPermission) + { + boolean bypassPermissionChecks = revision.bypassPermissionChecks(true); + + try + { + if (oldPermission == CDOPermission.NONE) + { + revision.readValues(in); + } + else if (newPermission == CDOPermission.NONE) + { + // TODO Handle newPermission == CDOPermission.NONE + // clearValues() also wipes out CDOResourceNode.name and CDOResourceFolder.nodes! + // revision.clearValues(); + } + } + finally + { + revision.bypassPermissionChecks(bypassPermissionChecks); + } + + revision.setPermission(newPermission); + + if (oldPermissions == null) + { + oldPermissions = new HashMap(); + } + + oldPermissions.put(revision, oldPermission); + } + } + + return oldPermissions; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadRevisionByVersionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadRevisionByVersionRequest.java new file mode 100644 index 000000000..50a6decd6 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadRevisionByVersionRequest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.text.MessageFormat; + +/** + * @author Eike Stepper + */ +public class LoadRevisionByVersionRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LoadRevisionByVersionRequest.class); + + private CDOID id; + + private CDOBranchVersion branchVersion; + + private int referenceChunk; + + public LoadRevisionByVersionRequest(CDOClientProtocol protocol, CDOID id, CDOBranchVersion branchVersion, int referenceChunk) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_REVISION_BY_VERSION); + this.id = id; + this.branchVersion = branchVersion; + this.referenceChunk = referenceChunk; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing id: {0}", id); //$NON-NLS-1$ + } + + out.writeCDOID(id); + if (TRACER.isEnabled()) + { + TRACER.format("Writing branchVersion: {0}", branchVersion); //$NON-NLS-1$ + } + + out.writeCDOBranchVersion(branchVersion); + if (TRACER.isEnabled()) + { + TRACER.format("Writing referenceChunk: {0}", referenceChunk); //$NON-NLS-1$ + } + + out.writeXInt(referenceChunk); + } + + @Override + protected InternalCDORevision confirming(CDODataInput in) throws IOException + { + return RevisionInfo.readResult(in, id, branchVersion.getBranch()); + } + + @Override + public String toString() + { + return MessageFormat.format("LoadRevisionByVersionRequest(id={0}, branchVersion={1}, referenceChunk={2})", id, branchVersion, referenceChunk); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadRevisionsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadRevisionsRequest.java new file mode 100644 index 000000000..5d316d660 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadRevisionsRequest.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2010-2012, 2014-2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.util.CDOFetchRule; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.session.CDOCollectionLoadingPolicy; +import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo; +import org.eclipse.emf.cdo.view.CDOFetchRuleManager; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class LoadRevisionsRequest extends CDOClientRequest> +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LoadRevisionsRequest.class); + + private List infos; + + private CDOBranchPoint branchPoint; + + private int referenceChunk; + + private int prefetchDepth; + + public LoadRevisionsRequest(CDOClientProtocol protocol, List infos, CDOBranchPoint branchPoint, int referenceChunk, int prefetchDepth) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_REVISIONS); + this.infos = infos; + this.branchPoint = branchPoint; + this.referenceChunk = referenceChunk; + this.prefetchDepth = prefetchDepth; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing branchPoint: {0}", branchPoint); //$NON-NLS-1$ + } + + out.writeCDOBranchPoint(branchPoint); + if (TRACER.isEnabled()) + { + TRACER.format("Writing referenceChunk: {0}", referenceChunk); //$NON-NLS-1$ + } + + out.writeXInt(referenceChunk); + int size = infos.size(); + if (TRACER.isEnabled()) + { + TRACER.format("Writing {0} infos", size); //$NON-NLS-1$ + } + + if (prefetchDepth == 0) + { + out.writeXInt(size); + } + else + { + out.writeXInt(-size); + if (TRACER.isEnabled()) + { + TRACER.format("Writing prefetchDepth: {0}", prefetchDepth); //$NON-NLS-1$ + } + + out.writeXInt(prefetchDepth); + } + + Collection ids = new ArrayList(size); + for (RevisionInfo info : infos) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing info: {0}", info); //$NON-NLS-1$ + } + + info.write(out); + ids.add(info.getID()); + } + + CDOFetchRuleManager ruleManager = getSession().getFetchRuleManager(); + CDOCollectionLoadingPolicy collectionLoadingPolicy = ruleManager.getCollectionLoadingPolicy(); + List fetchRules = ruleManager.getFetchRules(ids); + if (fetchRules == null || fetchRules.size() <= 0) + { + out.writeXInt(0); + } + else + { + // At this point, fetch size is more than one. + int fetchSize = fetchRules.size(); + CDOID contextID = ruleManager.getContext(); + + out.writeXInt(fetchSize); + out.writeXInt(collectionLoadingPolicy != null ? collectionLoadingPolicy.getInitialChunkSize() : CDORevision.UNCHUNKED); + out.writeCDOID(contextID); + + for (CDOFetchRule fetchRule : fetchRules) + { + fetchRule.write(out); + } + } + } + + @Override + protected List confirming(CDODataInput in) throws IOException + { + int size = infos.size(); + if (TRACER.isEnabled()) + { + TRACER.format("Reading {0} revisions", size); //$NON-NLS-1$ + } + + for (RevisionInfo info : infos) + { + info.readResult(in); + } + + List additionalRevisionInfos = null; + int additionalSize = in.readXInt(); + if (additionalSize != 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading {0} additional revision infos", additionalSize); //$NON-NLS-1$ + } + + additionalRevisionInfos = new ArrayList(additionalSize); + for (int i = 0; i < additionalSize; i++) + { + RevisionInfo info = RevisionInfo.read(in, branchPoint); + info.readResult(in); + additionalRevisionInfos.add(info); + } + } + + return additionalRevisionInfos; + } + + @Override + public String toString() + { + return MessageFormat.format("LoadRevisionsRequest(infos={0}, branchPoint={1}, referenceChunk={2}, prefetchDepth={3})", infos, branchPoint, referenceChunk, + prefetchDepth); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadSubBranchesRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadSubBranchesRequest.java new file mode 100644 index 000000000..9bd17b966 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LoadSubBranchesRequest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.spi.common.branch.InternalCDOBranchManager.BranchLoader.SubBranchInfo; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LoadSubBranchesRequest extends CDOClientRequest +{ + private int branchID; + + public LoadSubBranchesRequest(CDOClientProtocol protocol, int branchID) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOAD_SUB_BRANCHES); + this.branchID = branchID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(branchID); + } + + @Override + protected SubBranchInfo[] confirming(CDODataInput in) throws IOException + { + int size = in.readXInt(); + SubBranchInfo[] infos = new SubBranchInfo[size]; + for (int i = 0; i < infos.length; i++) + { + infos[i] = new SubBranchInfo(in); + } + + return infos; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockAreaRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockAreaRequest.java new file mode 100644 index 000000000..7295e8706 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockAreaRequest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011, 2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.view.CDOView; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class LockAreaRequest extends CDOClientRequest +{ + private CDOView view; + + private boolean create; + + public LockAreaRequest(CDOClientProtocol protocol, CDOView view, boolean create) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_AREA); + this.view = view; + this.create = create; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(view.getViewID()); + out.writeBoolean(create); + } + + @Override + protected String confirming(CDODataInput in) throws IOException + { + return in.readString(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockDelegationRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockDelegationRequest.java new file mode 100644 index 000000000..365157889 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockDelegationRequest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import java.io.IOException; +import java.util.List; + +/** + * @author Caspar De Groot + */ +public class LockDelegationRequest extends LockObjectsRequest +{ + private String lockAreaID; + + private CDOBranch viewedBranch; + + public LockDelegationRequest(CDOClientProtocol protocol, String lockAreaID, List revisionKeys, CDOBranch viewedBranch, LockType lockType, + boolean recursive, long timeout) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_DELEGATION, revisionKeys, 0, lockType, recursive, timeout); + this.lockAreaID = lockAreaID; + this.viewedBranch = viewedBranch; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeString(lockAreaID); + out.writeCDOBranch(viewedBranch); + super.requesting(out); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockNotificationIndication.java new file mode 100644 index 000000000..fd09f7777 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockNotificationIndication.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class LockNotificationIndication extends CDOClientIndication +{ + public LockNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + CDOLockChangeInfo lockChangeInfo = in.readCDOLockChangeInfo(); + InternalCDOSession session = getSession(); + session.handleLockNotification(lockChangeInfo, null); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java new file mode 100644 index 000000000..69d313ba6 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2009-2012, 2016, 2017 Eike Stepper (Loehne, 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 + * Caspar De Groot - maintenance + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.lock.CDOLockState; +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.CDORevisionKey; + +import org.eclipse.net4j.util.concurrent.IRWLockManager; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; + +import java.io.IOException; +import java.util.List; + +/** + * @author Eike Stepper, Caspar De Groot + */ +public class LockObjectsRequest extends CDOClientRequest +{ + private int viewID; + + private IRWLockManager.LockType lockType; + + private long timeout; + + private List revisionKeys; + + private boolean recursive; + + public LockObjectsRequest(CDOClientProtocol protocol, List revisionKeys, int viewID, LockType lockType, boolean recursive, long timeout) + { + this(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS, revisionKeys, viewID, lockType, recursive, timeout); + } + + protected LockObjectsRequest(CDOClientProtocol protocol, short signalID, List revisionKeys, int viewID, LockType lockType, boolean recursive, + long timeout) + { + super(protocol, signalID); + + this.viewID = viewID; + this.lockType = lockType; + this.timeout = timeout; + this.revisionKeys = revisionKeys; + this.recursive = recursive; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(viewID); + out.writeCDOLockType(lockType); + out.writeBoolean(recursive); + out.writeXLong(timeout); + + out.writeXInt(revisionKeys.size()); + for (CDORevisionKey revKey : revisionKeys) + { + out.writeCDORevisionKey(revKey); + } + } + + @Override + protected LockObjectsResult confirming(CDODataInput in) throws IOException + { + boolean succesful = in.readBoolean(); + boolean timeout = in.readBoolean(); + boolean waitForUpdate = in.readBoolean(); + long requiredTimestamp = in.readXLong(); + + int nStaleRevisions = in.readXInt(); + CDORevisionKey[] staleRevisions = new CDORevisionKey[nStaleRevisions]; + for (int i = 0; i < nStaleRevisions; i++) + { + staleRevisions[i] = in.readCDORevisionKey(); + } + + long timestamp = in.readXLong(); + + int n = in.readXInt(); + CDOLockState[] newLockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + newLockStates[i] = in.readCDOLockState(); + } + + return new LockObjectsResult(succesful, timeout, waitForUpdate, requiredTimestamp, staleRevisions, newLockStates, timestamp); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockStateRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockStateRequest.java new file mode 100644 index 000000000..963a98e54 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockStateRequest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +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 java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; + +/** + * @author Caspar De Groot + */ +public class LockStateRequest extends CDOClientRequest +{ + private int viewID; + + private Collection ids; + + private int prefetchDepth; + + public LockStateRequest(CDOClientProtocol protocol, int viewID, Collection ids, int prefetchDepth) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_STATE); + this.viewID = viewID; + this.ids = ids; + this.prefetchDepth = prefetchDepth; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(viewID); + + if (prefetchDepth == CDOLockState.DEPTH_NONE) + { + out.writeXInt(ids.size()); + } + else + { + out.writeXInt(-ids.size()); + out.writeXInt(prefetchDepth); + } + + for (CDOID id : ids) + { + out.writeCDOID(id); + } + } + + @Override + protected CDOLockState[] confirming(CDODataInput in) throws IOException + { + int n = in.readXInt(); + CDOLockState[] lockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + lockStates[i] = in.readCDOLockState(); + } + + return lockStates; + } + + @Override + public String toString() + { + return MessageFormat.format("LockStateRequest(viewID={0}, ids={1}, prefetchDepth={2})", viewID, ids, prefetchDepth); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ObjectLockedRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ObjectLockedRequest.java new file mode 100644 index 000000000..a916c1a16 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ObjectLockedRequest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009-2012, 2016, 2017 Eike Stepper (Loehne, 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 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.CDOObject; +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.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; + +/** + * @author Simon McDuff + */ +public class ObjectLockedRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, ObjectLockedRequest.class); + + private CDOView view; + + private CDOObject object; + + private LockType lockType; + + private boolean byOthers; + + public ObjectLockedRequest(CDOClientProtocol protocol, CDOView view, CDOObject object, LockType lockType, boolean byOthers) + { + super(protocol, CDOProtocolConstants.SIGNAL_OBJECT_LOCKED); + this.view = view; + this.object = object; + this.lockType = lockType; + this.byOthers = byOthers; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Requesting if object {0} has of lock for object {1}", object.cdoID(), //$NON-NLS-1$ + lockType == LockType.READ ? "read" : "write"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + out.writeXInt(view.getViewID()); + out.writeCDOLockType(lockType); + out.writeCDOID(object.cdoID()); + out.writeBoolean(byOthers); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenSessionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenSessionRequest.java new file mode 100644 index 000000000..16c63c072 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenSessionRequest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2009-2013, 2015-2017 Eike Stepper (Loehne, 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.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; +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.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.OpenSessionResult; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class OpenSessionRequest extends CDOClientRequestWithMonitoring +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, OpenSessionRequest.class); + + private String repositoryName; + + private String userID; + + private boolean passiveUpdateEnabled; + + private PassiveUpdateMode passiveUpdateMode; + + private LockNotificationMode lockNotificationMode; + + public OpenSessionRequest(CDOClientProtocol protocol, String repositoryName, String userID, boolean passiveUpdateEnabled, PassiveUpdateMode passiveUpdateMode, + LockNotificationMode lockNotificationMode) + { + super(protocol, CDOProtocolConstants.SIGNAL_OPEN_SESSION); + this.repositoryName = repositoryName; + this.userID = userID; + this.passiveUpdateEnabled = passiveUpdateEnabled; + this.passiveUpdateMode = passiveUpdateMode; + this.lockNotificationMode = lockNotificationMode; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing repositoryName: {0}", repositoryName); //$NON-NLS-1$ + } + + out.writeString(repositoryName); + + if (TRACER.isEnabled()) + { + TRACER.format("Writing userID: {0}", userID); //$NON-NLS-1$ + } + + out.writeString(userID); + + if (TRACER.isEnabled()) + { + TRACER.format("Writing passiveUpdateEnabled: {0}", passiveUpdateEnabled); //$NON-NLS-1$ + } + + out.writeBoolean(passiveUpdateEnabled); + + if (TRACER.isEnabled()) + { + TRACER.format("Writing passiveUpdateMode: {0}", passiveUpdateMode); //$NON-NLS-1$ + } + + out.writeEnum(passiveUpdateMode); + + if (TRACER.isEnabled()) + { + TRACER.format("Writing lockNotificationMode: {0}", lockNotificationMode); //$NON-NLS-1$ + } + + out.writeEnum(lockNotificationMode); + } + + @Override + protected OpenSessionResult confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + int sessionID = in.readXInt(); + if (sessionID == 0) + { + // The user has canceled the authentication + return null; + } + + return new OpenSessionResult(in, sessionID); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenViewRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenViewRequest.java new file mode 100644 index 000000000..1ecef87e0 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenViewRequest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException; +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 java.io.IOException; + +/** + * @author Eike Stepper + */ +public class OpenViewRequest extends CDOClientRequest +{ + private int viewID; + + private boolean readOnly; + + private CDOBranchPoint branchPoint; + + private String durableLockingID; + + public OpenViewRequest(CDOClientProtocol protocol, int viewID, boolean readOnly, CDOBranchPoint branchPoint) + { + super(protocol, CDOProtocolConstants.SIGNAL_OPEN_VIEW); + this.viewID = viewID; + this.readOnly = readOnly; + this.branchPoint = branchPoint; + } + + public OpenViewRequest(CDOClientProtocol protocol, int viewID, boolean readOnly, String durableLockingID) + { + super(protocol, CDOProtocolConstants.SIGNAL_OPEN_VIEW); + this.viewID = viewID; + this.readOnly = readOnly; + this.durableLockingID = durableLockingID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(viewID); + out.writeBoolean(readOnly); + + if (branchPoint != null) + { + out.writeBoolean(true); + out.writeCDOBranchPoint(branchPoint); + } + else + { + out.writeBoolean(false); + out.writeString(durableLockingID); + } + } + + @Override + protected CDOBranchPoint confirming(CDODataInput in) throws IOException + { + if (in.readBoolean()) + { + return in.readCDOBranchPoint(); + } + + if (durableLockingID != null) + { + String message = in.readString(); + if (message != null) + { + throw new IllegalStateException(message); + } + + throw new LockAreaNotFoundException(durableLockingID); + } + + return null; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenedSessionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenedSessionRequest.java new file mode 100644 index 000000000..f540f9870 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/OpenedSessionRequest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 Eike Stepper (Loehne, 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.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.net4j.signal.Request; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; + +/** + * @author Eike Stepper + */ +public class OpenedSessionRequest extends Request +{ + public OpenedSessionRequest(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_OPENED_SESSION); + } + + @Override + protected void requesting(ExtendedDataOutputStream out) throws Exception + { + out.writeBoolean(true); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryCancelRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryCancelRequest.java new file mode 100644 index 000000000..168767f62 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryCancelRequest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2009-2012, 2015, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; + +/** + * @author Simon McDuff + */ +public class QueryCancelRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, QueryCancelRequest.class); + + private int queryID; + + public QueryCancelRequest(CDOClientProtocol protocol, int queryID) + { + super(protocol, CDOProtocolConstants.SIGNAL_QUERY_CANCEL); + this.queryID = queryID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.trace("Cancel query " + queryID); //$NON-NLS-1$ + } + + out.writeXInt(queryID); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + boolean exception = in.readBoolean(); + if (exception) + { + String message = in.readString(); + throw new RuntimeException(message); + } + + return true; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryLobsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryLobsRequest.java new file mode 100644 index 000000000..4bd4639ac --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryLobsRequest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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 java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class QueryLobsRequest extends CDOClientRequest> +{ + private Collection ids; + + public QueryLobsRequest(CDOClientProtocol protocol, Collection ids) + { + super(protocol, CDOProtocolConstants.SIGNAL_QUERY_LOBS); + this.ids = ids; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(ids.size()); + for (byte[] id : ids) + { + out.writeByteArray(id); + } + } + + @Override + protected List confirming(CDODataInput in) throws IOException + { + int size = in.readXInt(); + List result = new ArrayList(size); + for (int i = 0; i < size; i++) + { + result.add(in.readByteArray()); + } + + return result; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryRequest.java new file mode 100644 index 000000000..15411784f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/QueryRequest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2009-2013, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +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.util.CDOQueryQueue; +import org.eclipse.emf.cdo.internal.common.CDOQueryInfoImpl; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.emf.internal.cdo.object.CDOObjectReferenceImpl; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.AbstractQueryIterator; +import org.eclipse.emf.spi.cdo.InternalCDOView; + +import java.io.IOException; + +/** + * @author Simon McDuff + */ +public class QueryRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, QueryRequest.class); + + private CDOView view; + + private AbstractQueryIterator queryResult; + + public QueryRequest(CDOClientProtocol protocol, CDOView view, AbstractQueryIterator queryResult) + { + super(protocol, CDOProtocolConstants.SIGNAL_QUERY); + this.view = view; + this.queryResult = queryResult; + } + + @Override + protected CDOIDProvider getIDProvider() + { + return (InternalCDOView)view; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(view.getViewID()); + ((CDOQueryInfoImpl)queryResult.getQueryInfo()).write(out); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + int queryID = in.readXInt(); + queryResult.setQueryID(queryID); + CDOQueryQueue resultQueue = queryResult.getQueue(); + + boolean xrefs = queryResult.getQueryInfo().getQueryLanguage().equals(CDOProtocolConstants.QUERY_LANGUAGE_XREFS); + + try + { + int numberOfObjectsReceived = 0; + while (in.readBoolean()) + { + Object element; + if (xrefs) + { + CDOIDReference delegate = in.readCDOIDReference(); + element = new CDOObjectReferenceImpl(view, delegate); + } + else + { + element = in.readCDORevisionOrPrimitive(); + } + + resultQueue.add(element); + ++numberOfObjectsReceived; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Query executed [{0} elements received]", numberOfObjectsReceived); //$NON-NLS-1$ + } + } + catch (RuntimeException ex) + { + resultQueue.setException(ex); + } + catch (Throwable throwable) + { + resultQueue.setException(new RuntimeException(throwable.getMessage(), throwable)); + } + finally + { + resultQueue.close(); + } + + return true; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RefreshSessionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RefreshSessionRequest.java new file mode 100644 index 000000000..1c8bb77a3 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RefreshSessionRequest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2010-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult; + +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class RefreshSessionRequest extends CDOClientRequest +{ + private long lastUpdateTime; + + private Map> viewedRevisions; + + private int initialChunkSize; + + private boolean enablePassiveUpdates; + + public RefreshSessionRequest(CDOClientProtocol protocol, long lastUpdateTime, Map> viewedRevisions, + int initialChunkSize, boolean enablePassiveUpdates) + { + this(protocol, CDOProtocolConstants.SIGNAL_REFRESH_SESSION, lastUpdateTime, viewedRevisions, initialChunkSize, enablePassiveUpdates); + } + + protected RefreshSessionRequest(CDOClientProtocol protocol, short signalID, long lastUpdateTime, + Map> viewedRevisions, int initialChunkSize, boolean enablePassiveUpdates) + { + super(protocol, signalID); + this.lastUpdateTime = lastUpdateTime; + this.viewedRevisions = viewedRevisions; + this.initialChunkSize = initialChunkSize; + this.enablePassiveUpdates = enablePassiveUpdates; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXLong(lastUpdateTime); + out.writeXInt(initialChunkSize); + out.writeBoolean(enablePassiveUpdates); + + out.writeXInt(viewedRevisions.size()); + for (Entry> entry : viewedRevisions.entrySet()) + { + CDOBranch branch = entry.getKey(); + Map revisions = entry.getValue(); + + out.writeCDOBranch(branch); + out.writeXInt(revisions.size()); + for (InternalCDORevision revision : revisions.values()) + { + out.writeCDORevisionKey(revision); + } + } + } + + @Override + protected RefreshSessionResult confirming(CDODataInput in) throws IOException + { + lastUpdateTime = in.readXLong(); + RefreshSessionResult result = new RefreshSessionResult(lastUpdateTime); + + ResourceSet resourceSet = EMFUtil.newEcoreResourceSet(); + for (;;) + { + byte type = in.readByte(); + switch (type) + { + case CDOProtocolConstants.REFRESH_PACKAGE_UNIT: + { + CDOPackageUnit packageUnit = in.readCDOPackageUnit(resourceSet); + result.addPackageUnit(packageUnit); + break; + } + + case CDOProtocolConstants.REFRESH_CHANGED_OBJECT: + { + InternalCDORevision revision = (InternalCDORevision)in.readCDORevision(); + result.addChangedObject(revision); + break; + } + + case CDOProtocolConstants.REFRESH_DETACHED_OBJECT: + { + CDORevisionKey key = in.readCDORevisionKey(); + result.addDetachedObject(key); + break; + } + + case CDOProtocolConstants.REFRESH_FINISHED: + return result; + + default: + throw new IOException("Invalid refresh type: " + type); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteMessageNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteMessageNotificationIndication.java new file mode 100644 index 000000000..fe72c3996 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteMessageNotificationIndication.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2009-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class RemoteMessageNotificationIndication extends CDOClientIndication +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, RemoteMessageNotificationIndication.class); + + public RemoteMessageNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_REMOTE_MESSAGE_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int senderID = in.readXInt(); + if (TRACER.isEnabled()) + { + TRACER.trace("Read senderID: " + senderID); //$NON-NLS-1$ + } + + CDORemoteSessionMessage message = new CDORemoteSessionMessage(in); + if (TRACER.isEnabled()) + { + TRACER.trace("Read message: " + message); //$NON-NLS-1$ + } + + InternalCDORemoteSessionManager remoteSessionManager = getSession().getRemoteSessionManager(); + remoteSessionManager.handleRemoteSessionMessage(senderID, message); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteMessageRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteMessageRequest.java new file mode 100644 index 000000000..885a85d28 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteMessageRequest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009-2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.session.remote.CDORemoteSession; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class RemoteMessageRequest extends CDOClientRequest> +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, RemoteMessageRequest.class); + + private CDORemoteSessionMessage message; + + private List recipients; + + public RemoteMessageRequest(CDOClientProtocol protocol, CDORemoteSessionMessage message, List recipients) + { + super(protocol, CDOProtocolConstants.SIGNAL_REMOTE_MESSAGE); + this.message = message; + this.recipients = recipients; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.trace("Writing message: " + message); //$NON-NLS-1$ + } + + message.write(out); + if (TRACER.isEnabled()) + { + TRACER.format("Writing {0} recipients", recipients.size()); //$NON-NLS-1$ + } + + out.writeXInt(recipients.size()); + for (CDORemoteSession recipient : recipients) + { + if (TRACER.isEnabled()) + { + TRACER.trace("Writing recipient: " + recipient); //$NON-NLS-1$ + } + + out.writeXInt(recipient.getSessionID()); + } + } + + @Override + protected Set confirming(CDODataInput in) throws IOException + { + Set sessionIDs = new HashSet(); + int count = in.readXInt(); + for (int i = 0; i < count; i++) + { + int sessionID = in.readXInt(); + sessionIDs.add(sessionID); + } + + return sessionIDs; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteSessionNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteSessionNotificationIndication.java new file mode 100644 index 000000000..a648e22ff --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RemoteSessionNotificationIndication.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2009-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class RemoteSessionNotificationIndication extends CDOClientIndication +{ + public RemoteSessionNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_REMOTE_SESSION_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int sessionID = in.readXInt(); + byte opcode = in.readByte(); + switch (opcode) + { + case CDOProtocolConstants.REMOTE_SESSION_OPENED: + String userID = in.readString(); + getSession().getRemoteSessionManager().handleRemoteSessionOpened(sessionID, userID); + break; + + case CDOProtocolConstants.REMOTE_SESSION_CLOSED: + getSession().getRemoteSessionManager().handleRemoteSessionClosed(sessionID); + break; + + case CDOProtocolConstants.REMOTE_SESSION_SUBSCRIBED: + getSession().getRemoteSessionManager().handleRemoteSessionSubscribed(sessionID, true); + break; + + case CDOProtocolConstants.REMOTE_SESSION_UNSUBSCRIBED: + getSession().getRemoteSessionManager().handleRemoteSessionSubscribed(sessionID, false); + break; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RenameBranchRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RenameBranchRequest.java new file mode 100644 index 000000000..b36fa276c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RenameBranchRequest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013-2015, 2017 Eike Stepper (Loehne, 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: + * Mathieu Velten - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +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 java.io.IOException; + +/** + * @author Mathieu Velten + */ +public class RenameBranchRequest extends CDOClientRequest +{ + private int branchID; + + private String oldName; + + private String newName; + + public RenameBranchRequest(CDOClientProtocol protocol, int branchID, String oldName, String newName) + { + super(protocol, CDOProtocolConstants.SIGNAL_RENAME_BRANCH); + this.branchID = branchID; + this.oldName = oldName; + this.newName = newName; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(branchID); + out.writeString(oldName); + out.writeString(newName); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ReplicateRepositoryRawRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ReplicateRepositoryRawRequest.java new file mode 100644 index 000000000..b211aaa8b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ReplicateRepositoryRawRequest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.spi.common.CDORawReplicationContext; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class ReplicateRepositoryRawRequest extends CDOClientRequestWithMonitoring +{ + private CDORawReplicationContext context; + + public ReplicateRepositoryRawRequest(CDOClientProtocol protocol, CDORawReplicationContext context) + { + super(protocol, CDOProtocolConstants.SIGNAL_REPLICATE_REPOSITORY_RAW); + this.context = context; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + int lastReplicatedBranchID = context.getLastReplicatedBranchID(); + long lastReplicatedCommitTime = context.getLastReplicatedCommitTime(); + + out.writeXInt(lastReplicatedBranchID); + out.writeXLong(lastReplicatedCommitTime); + } + + @Override + protected Boolean confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + context.replicateRaw(in, monitor); + return true; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ReplicateRepositoryRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ReplicateRepositoryRequest.java new file mode 100644 index 000000000..bedb5a1d8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ReplicateRepositoryRequest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2012, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea; +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.spi.common.CDOReplicationContext; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class ReplicateRepositoryRequest extends CDOClientRequest +{ + private CDOReplicationContext context; + + public ReplicateRepositoryRequest(CDOClientProtocol protocol, CDOReplicationContext context, OMMonitor monitor) + { + super(protocol, CDOProtocolConstants.SIGNAL_REPLICATE_REPOSITORY); + this.context = context; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(context.getLastReplicatedBranchID()); + out.writeXLong(context.getLastReplicatedCommitTime()); + + String[] lockAreaIDs = context.getLockAreaIDs(); + out.writeXInt(lockAreaIDs.length); + for (int i = 0; i < lockAreaIDs.length; i++) + { + out.writeString(lockAreaIDs[i]); + } + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + for (;;) + { + byte opcode = in.readByte(); + switch (opcode) + { + case CDOProtocolConstants.REPLICATE_FINISHED: + return true; + + case CDOProtocolConstants.REPLICATE_BRANCH: + context.handleBranch(in.readCDOBranch()); + break; + + case CDOProtocolConstants.REPLICATE_COMMIT: + context.handleCommitInfo(in.readCDOCommitInfo()); + break; + + case CDOProtocolConstants.REPLICATE_LOCKAREA: + boolean missing = !in.readBoolean(); + if (missing) + { + String missingLockAreaID = in.readString(); + LockArea area = CDOLockUtil.createLockArea(missingLockAreaID); + context.handleLockArea(area); + } + else + { + context.handleLockArea(in.readCDOLockArea()); + } + break; + + default: + throw new IllegalStateException("Invalid replicate opcode: " + opcode); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryStateNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryStateNotificationIndication.java new file mode 100644 index 000000000..897af5eb1 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryStateNotificationIndication.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionConfigurationImpl.RepositoryInfo; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionImpl; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class RepositoryStateNotificationIndication extends CDOClientIndication +{ + public RepositoryStateNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_REPOSITORY_STATE_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + CDOCommonRepository.State oldState = in.readEnum(CDOCommonRepository.State.class); + CDOCommonRepository.State newState = in.readEnum(CDOCommonRepository.State.class); + CDOID rootResourceID = in.readCDOID(); + + CDONet4jSessionImpl session = (CDONet4jSessionImpl)getSession(); + RepositoryInfo repositoryInfo = (RepositoryInfo)session.getRepositoryInfo(); + repositoryInfo.setState(newState); + repositoryInfo.setRootResourceID(rootResourceID); + + session.handleRepositoryStateChanged(oldState, newState); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryTimeRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryTimeRequest.java new file mode 100644 index 000000000..1f0c061fb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryTimeRequest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2009-2012, 2015 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RepositoryTimeResult; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class RepositoryTimeRequest extends CDOTimeRequest +{ + public RepositoryTimeRequest(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_REPOSITORY_TIME); + } + + @Override + protected RepositoryTimeResult confirming(CDODataInput in) throws IOException + { + super.confirming(in); + return getRepositoryTimeResult(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryTypeNotificationIndication.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryTypeNotificationIndication.java new file mode 100644 index 000000000..e3d67ae1e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/RepositoryTypeNotificationIndication.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionConfigurationImpl.RepositoryInfo; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionImpl; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class RepositoryTypeNotificationIndication extends CDOClientIndication +{ + public RepositoryTypeNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_REPOSITORY_TYPE_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + CDOCommonRepository.Type oldType = in.readEnum(CDOCommonRepository.Type.class); + CDOCommonRepository.Type newType = in.readEnum(CDOCommonRepository.Type.class); + + CDONet4jSessionImpl session = (CDONet4jSessionImpl)getSession(); + RepositoryInfo repositoryInfo = (RepositoryInfo)session.getRepositoryInfo(); + repositoryInfo.setType(newType); + session.handleRepositoryTypeChanged(oldType, newType); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ResetTransactionRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ResetTransactionRequest.java new file mode 100644 index 000000000..90eedfff7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/ResetTransactionRequest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +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.internal.net4j.bundle.OM; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class ResetTransactionRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, ResetTransactionRequest.class); + + private int transactionID; + + private int commitNumber; + + public ResetTransactionRequest(CDOClientProtocol protocol, int transactionID, int commitNumber) + { + super(protocol, CDOProtocolConstants.SIGNAL_RESET_TRANSACTION); + this.transactionID = transactionID; + this.commitNumber = commitNumber; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing transactionID: {0}", transactionID); //$NON-NLS-1$ + } + + out.writeXInt(transactionID); + + if (TRACER.isEnabled()) + { + TRACER.format("Writing commitNumber: {0}", commitNumber); //$NON-NLS-1$ + } + + out.writeXInt(commitNumber); + } + + @Override + protected CDOCommitInfo confirming(CDODataInput in) throws IOException + { + if (in.readBoolean()) + { + long timeStamp = in.readXLong(); + long previousTimeStamp = in.readXLong(); + + InternalCDOCommitInfoManager commitInfoManager = getSession().getCommitInfoManager(); + return commitInfoManager.createCommitInfo(null, timeStamp, previousTimeStamp, null, null, null, null); + } + + return null; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SetLockNotificationModeRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SetLockNotificationModeRequest.java new file mode 100644 index 000000000..861c131fc --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SetLockNotificationModeRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +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 java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class SetLockNotificationModeRequest extends CDOClientRequest +{ + private LockNotificationMode mode; + + public SetLockNotificationModeRequest(CDOClientProtocol protocol, LockNotificationMode mode) + { + super(protocol, CDOProtocolConstants.SIGNAL_SET_LOCK_NOTIFICATION_MODE); + this.mode = mode; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeEnum(mode); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SetPassiveUpdateModeRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SetPassiveUpdateModeRequest.java new file mode 100644 index 000000000..004407c64 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SetPassiveUpdateModeRequest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; +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 java.io.IOException; + +/** + * @author Eike Stepper + */ +public class SetPassiveUpdateModeRequest extends CDOClientRequest +{ + private PassiveUpdateMode mode; + + public SetPassiveUpdateModeRequest(CDOClientProtocol protocol, PassiveUpdateMode mode) + { + super(protocol, CDOProtocolConstants.SIGNAL_SET_PASSIVE_UPDATE_MODE); + this.mode = mode; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeByte(mode.ordinal()); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SwitchTargetRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SwitchTargetRequest.java new file mode 100644 index 000000000..b2f68d1dd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/SwitchTargetRequest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011, 2012, 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.id.CDOIDUtil; +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.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.spi.cdo.InternalCDOObject; + +import java.io.IOException; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class SwitchTargetRequest extends CDOClientRequestWithMonitoring +{ + private int viewID; + + private CDOBranchPoint branchPoint; + + private List invalidObjects; + + private List allChangedObjects; + + private List allDetachedObjects; + + public SwitchTargetRequest(CDOClientProtocol protocol, int viewID, CDOBranchPoint branchPoint, List invalidObjects, + List allChangedObjects, List allDetachedObjects) + { + super(protocol, CDOProtocolConstants.SIGNAL_SWITCH_TARGET); + this.viewID = viewID; + this.branchPoint = branchPoint; + this.invalidObjects = invalidObjects; + this.allChangedObjects = allChangedObjects; + this.allDetachedObjects = allDetachedObjects; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + out.writeXInt(viewID); + out.writeCDOBranchPoint(branchPoint); + + out.writeXInt(invalidObjects.size()); + for (InternalCDOObject object : invalidObjects) + { + out.writeCDOID(object.cdoID()); + } + } + + @Override + protected boolean[] confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + int size = in.readXInt(); + for (int i = 0; i < size; i++) + { + allChangedObjects.add(in.readCDORevisionDelta()); + } + + size = in.readXInt(); + for (int i = 0; i < size; i++) + { + CDOID id = in.readCDOID(); + allDetachedObjects.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION)); + } + + return null; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnitRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnitRequest.java new file mode 100644 index 000000000..6945e00d2 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnitRequest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016, 2017 Eike Stepper (Loehne, 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.net4j.protocol; + +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.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants.UnitOpcode; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class UnitRequest extends CDOClientRequestWithMonitoring +{ + private int viewID; + + private CDOID rootID; + + private UnitOpcode opcode; + + private CDORevisionHandler revisionHandler; + + public UnitRequest(CDOClientProtocol protocol, int viewID, CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNIT); + this.viewID = viewID; + this.rootID = rootID; + this.opcode = opcode; + this.revisionHandler = revisionHandler; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + out.writeXInt(viewID); + out.writeCDOID(rootID); + out.writeByte(opcode.ordinal()); + } + + @Override + protected Boolean confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + if (opcode.isOpen()) + { + for (;;) + { + CDORevision revision = in.readCDORevision(); + if (revision == null) + { + break; + } + + revisionHandler.handleRevision(revision); + } + } + + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockDelegationRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockDelegationRequest.java new file mode 100644 index 000000000..d89819adf --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockDelegationRequest.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Caspar De Groot + */ +public class UnlockDelegationRequest extends UnlockObjectsRequest +{ + private String lockAreaID; + + public UnlockDelegationRequest(CDOClientProtocol protocol, String lockAreaID, Collection objectIDs, LockType lockType, boolean recursive) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_DELEGATION, 0, objectIDs, lockType, recursive); + this.lockAreaID = lockAreaID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeString(lockAreaID); + super.requesting(out); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java new file mode 100644 index 000000000..c4136799a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2009-2012, 2016, 2017 Eike Stepper (Loehne, 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 + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +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.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Simon McDuff + */ +public class UnlockObjectsRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, UnlockObjectsRequest.class); + + private int viewID; + + private Collection objectIDs; + + private LockType lockType; + + private boolean recursive; + + public UnlockObjectsRequest(CDOClientProtocol protocol, int viewID, Collection objects, LockType lockType, boolean recursive) + { + this(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS, viewID, objects, lockType, recursive); + } + + protected UnlockObjectsRequest(CDOClientProtocol protocol, short signalID, int viewID, Collection objectIDs, LockType lockType, boolean recursive) + { + super(protocol, signalID); + this.viewID = viewID; + this.objectIDs = objectIDs; + this.lockType = lockType; + this.recursive = recursive; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeXInt(viewID); + out.writeCDOLockType(lockType); + out.writeBoolean(recursive); + if (objectIDs == null) + { + if (TRACER.isEnabled()) + { + TRACER.format("Unlocking all objects for view {0}", viewID); //$NON-NLS-1$ + } + + out.writeXInt(CDOProtocolConstants.RELEASE_ALL_LOCKS); + } + else + { + if (TRACER.isEnabled()) + { + TRACER.format("Unlocking of type {0} requested for view {1}", lockType == LockType.READ ? "read" //$NON-NLS-1$ //$NON-NLS-2$ + : "write", viewID); //$NON-NLS-1$ + } + + out.writeXInt(objectIDs.size()); + for (CDOID id : objectIDs) + { + if (TRACER.isEnabled()) + { + TRACER.format("Unlocking requested for object {0}", id); //$NON-NLS-1$ + } + + out.writeCDOID(id); + } + } + } + + @Override + protected UnlockObjectsResult confirming(CDODataInput in) throws IOException + { + long timestamp = in.readXLong(); + int n = in.readXInt(); + CDOLockState[] newLockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + newLockStates[i] = in.readCDOLockState(); + } + return new UnlockObjectsResult(newLockStates, timestamp); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnsubscribeRemoteSessionsRequest.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnsubscribeRemoteSessionsRequest.java new file mode 100644 index 000000000..eb5f2e256 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnsubscribeRemoteSessionsRequest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j.protocol; + +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.internal.net4j.bundle.OM; + +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class UnsubscribeRemoteSessionsRequest extends CDOClientRequest +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, UnsubscribeRemoteSessionsRequest.class); + + public UnsubscribeRemoteSessionsRequest(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNSUBSCRIBE_REMOTE_SESSIONS); + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (TRACER.isEnabled()) + { + TRACER.trace("Unsubscribing"); //$NON-NLS-1$ + } + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java new file mode 100644 index 000000000..72e120f49 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorder.java @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.testrecorder; + +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.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.CDOType; +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.CDOFeatureDelta; +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.common.util.CDOTimeProvider; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.eresource.CDOResourceNode; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.session.CDOSession; +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.revision.CDOFeatureDeltaVisitorImpl; +import org.eclipse.emf.cdo.transaction.CDOMerger; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.transaction.CDOTransactionHandler1; +import org.eclipse.emf.cdo.util.CDOUtil; + +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.event.EventUtil; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.ILifecycle; +import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter; +import org.eclipse.net4j.util.om.OMPlatform; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EEnumLiteral; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.FSMUtil; +import org.eclipse.emf.spi.cdo.InternalCDOView; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + */ +@SuppressWarnings("restriction") +public final class TestRecorder implements CDOTransactionHandler1.WithUndo +{ + public static final TestRecorder INSTANCE = new TestRecorder(); + + private static final String OUTPUT_FOLDER = OMPlatform.INSTANCE.getProperty("org.eclipse.emf.cdo.test.recorder.outputFolder"); + + private static final String CLASS_NAME = OMPlatform.INSTANCE.getProperty("org.eclipse.emf.cdo.test.recorder.className"); + + private static final String DESCRIPTION = OMPlatform.INSTANCE.getProperty("org.eclipse.emf.cdo.test.recorder.description"); + + private static final boolean RECORD_VIEWS = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.test.recorder.recordViews"); + + private static final Map TYPES = new HashMap(); + static + { + TYPES.put("mango", "getMangoFactory()"); + TYPES.put("model1", "getModel1Factory()"); + TYPES.put("model2", "getModel2Factory()"); + TYPES.put("model3", "getModel3Factory()"); + TYPES.put("subpackage", "getModel3SubpackageFactory()"); + TYPES.put("model4", "getModel4Factory()"); + TYPES.put("model5", "getModel5Factory()"); + TYPES.put("model6", "getModel6Factory()"); + + TYPES.put(EresourcePackage.Literals.CDO_RESOURCE_FOLDER, "folder"); + TYPES.put(EresourcePackage.Literals.CDO_RESOURCE, "resource"); + TYPES.put(EresourcePackage.Literals.CDO_TEXT_RESOURCE, "text"); + TYPES.put(EresourcePackage.Literals.CDO_BINARY_RESOURCE, "file"); + } + + private final Map variables = new HashMap(); + + private final Map typeCounters = new HashMap(); + + private PrintStream out; + + private TestRecorder() + { + } + + private void println(String line) + { + if (out == null) + { + String className = CLASS_NAME; + String packageName; + + if (StringUtil.isEmpty(className)) + { + className = "RecordedTest"; + packageName = ""; + } + else + { + int lastDot = className.lastIndexOf('.'); + if (lastDot != -1) + { + packageName = className.substring(0, lastDot); + className = className.substring(lastDot + 1); + } + else + { + packageName = ""; + } + } + + if (StringUtil.isEmpty(OUTPUT_FOLDER)) + { + out = System.out; + } + else + { + String prefix; + String suffix; + + int star = className.indexOf('*'); + if (star != -1) + { + prefix = className.substring(0, star); + suffix = className.substring(star + 1); + } + else + { + prefix = className; + suffix = ""; + } + + className = prefix + suffix; + + File folder = new File(OUTPUT_FOLDER, packageName.replace('.', '/')); + File file = new File(folder, className + ".java"); + char c = 'a'; + + while (file.exists()) + { + className = prefix + Character.toString(c++) + suffix; + file = new File(folder, className + ".java"); + } + + try + { + out = new PrintStream(file); + + Runtime.getRuntime().addShutdownHook(new Thread() + { + @Override + public void run() + { + if (out != null) + { + out.println("\t}"); + out.println("}"); + IOUtil.close(out); + } + } + }); + } + catch (FileNotFoundException ex) + { + ex.printStackTrace(); + out = System.out; + } + } + + if (packageName.length() != 0) + { + out.println("package " + packageName + ";"); + out.println(); + } + + out.println("import org.eclipse.emf.cdo.common.branch.CDOBranch;"); + out.println("import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;"); + out.println("import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;"); + out.println("import org.eclipse.emf.cdo.eresource.CDOResource;"); + out.println("import org.eclipse.emf.cdo.session.CDOSession;"); + out.println("import org.eclipse.emf.cdo.tests.AbstractCDOTest;"); + out.println("import org.eclipse.emf.cdo.transaction.CDOTransaction;"); + out.println("import org.eclipse.emf.spi.cdo.DefaultCDOMerger;"); + out.println(); + + if (!StringUtil.isEmpty(DESCRIPTION)) + { + out.println("/**"); + out.println(" * " + DESCRIPTION); + out.println(" */"); + } + + out.println("public class " + className + " extends AbstractCDOTest"); + out.println("{"); + out.println("\tpublic void testCase() throws Exception"); + out.println("\t{"); + } + + out.println("\t\t" + line); + } + + public synchronized void openSession(final TestRecorderSession session) + { + Variable variable = createVariable("session", session); + println("CDOSession " + variable + " = openSession();"); + + addCloseListener(variable); + } + + public synchronized void createBranch(TestRecorderBranch branch, long originalBaseTimeStamp) + { + Variable variable = createVariable("branch", branch); + + String lhs = "CDOBranch " + variable + " = "; + String create = ".createBranch(" + list(quot(branch.getName()), formatTimeStamp(originalBaseTimeStamp, true)) + ");"; + + CDOBranch baseBranch = branch.getBase().getBranch(); + if (baseBranch.isMainBranch()) + { + println(lhs + variables.get(getSession(branch)) + ".getBranchManager().getMainBranch()" + create); + } + else + { + Variable baseBranchVariable = variables.get(baseBranch); + if (baseBranchVariable != null) + { + println(lhs + baseBranchVariable + create); + } + else + { + println(lhs + variables.get(getSession(branch)) + ".getBranchManager().getBranch(" + quot(baseBranch.getPathName()) + ")" + create); + } + } + } + + public synchronized void renameBranch(TestRecorderBranch branch) + { + } + + public synchronized void openView(final TestRecorderView view, CDOSession session, CDOBranch branch, long timeStamp) + { + if (RECORD_VIEWS) + { + Variable variable = createVariable("view", view); + println( + "CDOView " + variable + " = " + variables.get(session) + ".openView(" + list(formatBranch(branch, true), formatTimeStamp(timeStamp, true)) + ");"); + + addCloseListener(variable); + } + } + + public synchronized void openTransaction(TestRecorderTransaction transaction, CDOSession session, CDOBranch branch) + { + Variable variable = createVariable("transaction", transaction); + println("CDOTransaction " + variable + " = " + variables.get(session) + ".openTransaction(" + formatBranch(branch, true) + ");"); + + addCloseListener(variable); + transaction.addTransactionHandler(this); + } + + public synchronized void attachingObject(CDOTransaction transaction, CDOObject object) + { + if (object instanceof CDOResourceNode) + { + CDOResourceNode resourceNode = (CDOResourceNode)object; + + EClass eClass = resourceNode.eClass(); + String typeName = eClass.getName(); + String type = TYPES.get(eClass); + Variable variable = createVariable(type, resourceNode); + + println(typeName + " " + variable + " = " + variables.get(transaction) + "." + typeName.replace("CDO", "create") + "(getResourcePath(" + + quot(resourceNode.getPath()) + "));"); + } + } + + public synchronized void detachingObject(CDOTransaction transaction, CDOObject object) + { + if (object instanceof CDOResourceNode) + { + CDOResourceNode resourceNode = (CDOResourceNode)object; + println(formatResourceNode(resourceNode) + ".delete(null);"); + + variables.remove(resourceNode); + } + } + + public synchronized void modifyingObject(CDOTransaction transaction, final CDOObject object, CDOFeatureDelta featureDelta) + { + if (object instanceof CDOResourceNode) + { + // TODO Handle path modifications. + // CDOResourceNode resourceNode = (CDOResourceNode)object; + return; + } + + final EStructuralFeature feature = featureDelta.getFeature(); + final String featureName = StringUtil.cap(feature.getName()); + + featureDelta.accept(new CDOFeatureDeltaVisitorImpl() + { + @Override + public void visit(CDOClearFeatureDelta delta) + { + if (filter(feature)) + { + return; + } + + Object value = object.eGet(feature); + if (value == null || value instanceof List && ((List)value).isEmpty()) + { + return; + } + + println(formatGet() + ".clear();"); + } + + @Override + public void visit(CDOUnsetFeatureDelta delta) + { + if (filter(feature)) + { + return; + } + + println(formatGet() + ".unset();"); + } + + @Override + public void visit(CDOSetFeatureDelta delta) + { + if (filter(feature)) + { + return; + } + + String value = formatValue(object, feature, delta.getValue()); + println(formatObject(object) + ".set" + featureName + "(" + value + ");"); + } + + @Override + public void visit(CDORemoveFeatureDelta delta) + { + if (filter(feature)) + { + return; + } + + println(formatGet() + ".remove(" + delta.getIndex() + ");"); + } + + @Override + public void visit(CDOAddFeatureDelta delta) + { + if (filter(feature)) + { + return; + } + + List list = (List)object.eGet(feature); + Integer index = delta.getIndex() >= list.size() - 1 ? null : delta.getIndex(); + String value = formatValue(object, feature, delta.getValue()); + println(formatGet() + ".add(" + list(index, value) + ");"); + } + + @Override + public void visit(CDOMoveFeatureDelta delta) + { + println(formatGet() + ".move(" + delta.getOldPosition() + ", " + delta.getNewPosition() + ");"); + } + + private boolean filter(EStructuralFeature feature) + { + if (feature instanceof EReference) + { + EReference reference = (EReference)feature; + + EReference oppositeReference = reference.getEOpposite(); + if (oppositeReference != null) + { + if (reference.isMany()) + { + if (oppositeReference.isMany()) + { + if (pick(reference, oppositeReference)) + { + return true; + } + } + } + else + { + if (oppositeReference.isMany()) + { + return true; + } + + if (pick(reference, oppositeReference)) + { + return true; + } + } + } + } + + return false; + } + + private boolean pick(EReference r1, EReference r2) + { + return getID(r1).compareTo(getID(r2)) > 0; + } + + private String getID(EReference r) + { + EClass c = r.getEContainingClass(); + return c.getEPackage().getNsURI() + "#" + c.getName() + "#" + r.getName(); + } + + private String formatGet() + { + return formatObject(object) + ".get" + featureName + "()"; + } + }); + } + + public synchronized void undoingObject(CDOTransaction transaction, CDOObject object, CDOFeatureDelta featureDelta) + { + modifyingObject(transaction, object, featureDelta); + } + + public synchronized void mergeTransaction(TestRecorderTransaction transaction, CDOBranch source, CDOMerger merger) + { + println(variables.get(transaction) + ".merge(" + formatBranch(source, false) + ", new " + simpleClassName(merger) + "());"); + } + + public synchronized void mergeTransaction(TestRecorderTransaction transaction, CDOBranchPoint source, CDOMerger merger) + { + println(variables.get(transaction) + ".merge(" + formatBranchPoint(source, false) + ", new " + simpleClassName(merger) + "());"); + } + + public synchronized void commitTransaction(TestRecorderTransaction transaction, CDOCommitInfo commitInfo) + { + Variable variable = createVariable("commit", commitInfo); + + long timeStamp = commitInfo.getTimeStamp(); + Variable oldVariable = variables.put(timeStamp, variable); + if (oldVariable != null) + { + variables.put(timeStamp, oldVariable); + } + + CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(commitInfo); + oldVariable = variables.put(branchPoint, variable); + if (oldVariable != null) + { + variables.put(branchPoint, oldVariable); + } + + StringBuilder builder = new StringBuilder("CDOCommitInfo " + variable + " = commitAndSync(" + variables.get(transaction)); + + for (InternalCDOView view : transaction.getSession().getViews()) + { + if (view != transaction) + { + Variable viewVariable = variables.get(view); + if (viewVariable != null) + { + builder.append(", "); + builder.append(viewVariable); + } + } + } + + builder.append(");"); + println(builder.toString()); + } + + public synchronized void rollbackTransaction(TestRecorderTransaction transaction) + { + println(variables.get(transaction) + ".rollback();"); + } + + private Variable createVariable(String type, Object object) + { + AtomicInteger counter = typeCounters.get(type); + if (counter == null) + { + counter = new AtomicInteger(); + typeCounters.put(type, counter); + } + + int number = counter.incrementAndGet(); + + Variable variable = new Variable(type, number, object); + variables.put(object, variable); + return variable; + } + + private void addCloseListener(final Variable variable) + { + final Object object = variable.getObject(); + EventUtil.addListener(object, new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + variables.remove(object); + println(variable + ".close();"); + } + }); + } + + private String formatBranchPoint(CDOBranchPoint branchPoint, boolean optional) + { + CDOBranch branch = branchPoint.getBranch(); + long timeStamp = branchPoint.getTimeStamp(); + + if (branch.isMainBranch() && timeStamp == CDOBranchPoint.UNSPECIFIED_DATE && optional) + { + return ""; + } + + Variable branchPointVariable = variables.get(branchPoint); + if (branchPointVariable == null) + { + branchPointVariable = createVariable("point", branchPoint); + println("CDOBranchPoint " + branchPointVariable + " = " + formatBranch(branch, false) + ".getPoint(" + formatTimeStamp(timeStamp, false) + ");"); + } + + return branchPointVariable.toString(); + } + + private String formatBranch(CDOBranch branch, boolean optional) + { + if (branch.isMainBranch() && optional) + { + return ""; + } + + Variable branchVariable = variables.get(branch); + if (branchVariable == null) + { + Variable sessionVariable = variables.get(getSession(branch)); + + if (branch.isMainBranch()) + { + return sessionVariable + ".getBranchManager().getMainBranch()"; + } + + branchVariable = createVariable("branch", branch); + println("CDOBranch " + branchVariable + " = " + sessionVariable + ".getBranchManager().getBranch(" + quot(branch.getPathName()) + ");"); + } + + return branchVariable.toString(); + } + + private String formatTimeStamp(long timeStamp, boolean optional) + { + if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + if (optional) + { + return ""; + } + + return "CDOBranchPoint.UNSPECIFIED_DATE"; + } + + Variable variable = variables.get(timeStamp); + if (variable != null && variable.getObject() instanceof CDOTimeProvider) + { + return variable + ".getTimeStamp()"; + } + + return timeStamp + "L"; + } + + private String formatResourceNode(CDOResourceNode resourceNode) + { + Variable variable = variables.get(resourceNode); + if (variable == null) + { + EClass eClass = resourceNode.eClass(); + String typeName = eClass.getName(); + String type = TYPES.get(eClass); + variable = createVariable(type, resourceNode); + + println(typeName + " " + variable + " = " + variables.get(resourceNode.cdoView()) + "." + typeName.replace("CDO", "get") + "(getResourcePath(" + + quot(resourceNode.getPath()) + "));"); + } + + return variable.toString(); + } + + private String formatObject(CDOObject object) + { + Variable variable = variables.get(object); + if (variable == null) + { + EClass eClass = object.eClass(); + String typeName = eClass.getName(); + String type = StringUtil.uncap(eClass.getName()); + variable = createVariable(type, object); + + if (FSMUtil.isTransient(object)) + { + String packageName = eClass.getEPackage().getName(); + String factory = TYPES.get(packageName); + if (factory == null) + { + factory = StringUtil.cap(packageName) + "Factory.eINSTANCE"; + } + + println(typeName + " " + variable + " = " + factory + ".create" + eClass.getName() + "();"); + + for (EStructuralFeature feature : eClass.getEAllStructuralFeatures()) + { + Object value = object.eGet(feature); + if (value instanceof List) + { + List list = (List)value; + for (Object element : list) + { + println(variable + ".get" + StringUtil.cap(feature.getName()) + "().add(" + formatValue(object, feature, element) + ");"); + } + } + else if (value != null) + { + println(variable + ".set" + StringUtil.cap(feature.getName()) + "(" + formatValue(object, feature, value) + ");"); + } + } + } + else + { + String rhs; + + CDOObject container = CDOUtil.getCDOObject(object.eContainer()); + if (container != null) + { + EReference reference = (EReference)object.eContainingFeature(); + rhs = formatObject(container) + ".get" + StringUtil.cap(reference.getName()) + "()"; + + if (reference.isMany()) + { + List list = (List)container.eGet(reference); + int index = list.indexOf(object); + rhs += ".get(" + index + ")"; + } + } + else + { + CDOResource resource = (CDOResource)object.eResource(); + int index = resource.getContents().indexOf(object); + rhs = "(" + typeName + ")" + formatResourceNode(resource) + ".getContents().get(" + index + ")"; + } + + println(typeName + " " + variable + " = " + rhs + ";"); + } + } + + return variable.toString(); + } + + private String formatValue(CDOObject object, EStructuralFeature feature, Object value) + { + if (value != null) + { + EClassifier eType = feature.getEType(); + + if (eType instanceof EEnum) + { + EEnumLiteral literal = ((EEnum)eType).getEEnumLiteral(value.toString()); + return literal.getEEnum().getName() + "." + literal.toString().toUpperCase(); + } + + CDOType type = CDOModelUtil.getType(eType); + if (type != null) + { + value = type.convertToEMF(eType, value); + } + + if (value instanceof String) + { + return quot((String)value); + } + + if (value instanceof CDOID) + { + value = object.cdoView().getObject((CDOID)value); + } + + if (value instanceof EObject) + { + return formatObject(CDOUtil.getCDOObject((EObject)value)); + } + } + + return String.valueOf(value); + + } + + private static TestRecorderSession getSession(CDOBranch branch) + { + InternalCDOBranchManager branchManager = (InternalCDOBranchManager)branch.getBranchManager(); + return (TestRecorderSession)((CDOSessionProtocol)branchManager.getBranchLoader()).getSession(); + } + + private static String simpleClassName(Object object) + { + String name = object.getClass().getName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) + { + name = name.substring(lastDot + 1); + } + + return name.replace('$', '.'); + } + + private static String list(Object... values) + { + StringBuilder builder = new StringBuilder(); + for (Object value : values) + { + if (value != null) + { + String str = value.toString(); + if (str.length() != 0) + { + if (builder.length() != 0) + { + builder.append(", "); + } + + builder.append(str); + } + } + } + + return builder.toString(); + } + + private static String quot(String value) + { + StringBuilder builder = new StringBuilder(); + builder.append("\""); + + if (value != null && value.length() != 0) + { + builder.append(value); + } + + builder.append("\""); + return builder.toString(); + } + + /** + * @author Eike Stepper + */ + public static final class Variable + { + private final String type; + + private final int number; + + private final Object object; + + private Variable(String type, int number, Object object) + { + this.type = type; + this.number = number; + this.object = object; + } + + public String getType() + { + return type; + } + + public int getNumber() + { + return number; + } + + public Object getObject() + { + return object; + } + + @Override + public String toString() + { + return type + number; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderBranch.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderBranch.java new file mode 100644 index 000000000..c85751331 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderBranch.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.testrecorder; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.internal.common.branch.CDOBranchImpl; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; + +/** + * @author Eike Stepper + */ +@SuppressWarnings("restriction") +public class TestRecorderBranch extends CDOBranchImpl +{ + public TestRecorderBranch(InternalCDOBranchManager branchManager, int id, String name, CDOBranchPoint base) + { + super(branchManager, id, name, base); + } + + @Override + public void setName(String name) + { + super.setName(name); + TestRecorder.INSTANCE.renameBranch(this); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderBranchManager.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderBranchManager.java new file mode 100644 index 000000000..ba9c2d249 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderBranchManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.testrecorder; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.internal.common.branch.CDOBranchManagerImpl; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; + +/** + * @author Eike Stepper + */ +@SuppressWarnings("restriction") +public class TestRecorderBranchManager extends CDOBranchManagerImpl +{ + public TestRecorderBranchManager() + { + } + + @Override + protected InternalCDOBranch createBranch(int branchID, String name, CDOBranchPoint base, long originalBaseTimeStamp) + { + TestRecorderBranch branch = new TestRecorderBranch(this, branchID, name, base); + TestRecorder.INSTANCE.createBranch(branch, originalBaseTimeStamp); + return branch; + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderSession.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderSession.java new file mode 100644 index 000000000..b4f3403d4 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderSession.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.testrecorder; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionImpl; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; + +import org.eclipse.emf.spi.cdo.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOView; + +/** + * @author Eike Stepper + */ +public class TestRecorderSession extends CDONet4jSessionImpl +{ + public TestRecorderSession() + { + TestRecorder.INSTANCE.openSession(this); + } + + @Override + protected InternalCDOBranchManager createBranchManager() + { + return new TestRecorderBranchManager(); + } + + @Override + protected InternalCDOView createView(CDOBranch branch, long timeStamp) + { + return new TestRecorderView(this, branch, timeStamp); + } + + @Override + protected InternalCDOTransaction createTransaction(CDOBranch branch) + { + return new TestRecorderTransaction(this, branch); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java new file mode 100644 index 000000000..717f035ec --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderTransaction.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.testrecorder; + +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.session.CDOSession; +import org.eclipse.emf.cdo.transaction.CDOMerger; +import org.eclipse.emf.cdo.util.CommitException; + +import org.eclipse.emf.internal.cdo.transaction.CDOTransactionImpl; + +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * @author Eike Stepper + */ +public class TestRecorderTransaction extends CDOTransactionImpl +{ + public TestRecorderTransaction(CDOSession session, CDOBranch branch) + { + super(session, branch); + TestRecorder.INSTANCE.openTransaction(this, session, branch); + } + + @Override + public TestRecorderSession getSession() + { + return (TestRecorderSession)super.getSession(); + } + + @Override + public CDOChangeSetData merge(CDOBranch source, CDOMerger merger) + { + TestRecorder.INSTANCE.mergeTransaction(this, source, merger); + return super.merge(source, merger); + } + + @Override + public CDOChangeSetData merge(CDOBranchPoint source, CDOMerger merger) + { + TestRecorder.INSTANCE.mergeTransaction(this, source, merger); + return super.merge(source, merger); + } + + @Override + public CDOCommitInfo commit(IProgressMonitor progressMonitor) throws CommitException + { + CDOCommitInfo commitInfo = super.commit(progressMonitor); + TestRecorder.INSTANCE.commitTransaction(this, commitInfo); + return commitInfo; + } + + @Override + public void rollback() + { + super.rollback(); + TestRecorder.INSTANCE.rollbackTransaction(this); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderView.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderView.java new file mode 100644 index 000000000..83f1652be --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/testrecorder/TestRecorderView.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.testrecorder; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.session.CDOSession; + +import org.eclipse.emf.internal.cdo.view.CDOViewImpl; + +/** + * @author Eike Stepper + */ +public class TestRecorderView extends CDOViewImpl +{ + public TestRecorderView(CDOSession session, CDOBranch branch, long timeStamp) + { + super(session, branch, timeStamp); + TestRecorder.INSTANCE.openView(this, session, branch, timeStamp); + } + + @Override + public TestRecorderSession getSession() + { + return (TestRecorderSession)super.getSession(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jSession.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jSession.java new file mode 100644 index 000000000..20a5ca7aa --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jSession.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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 - maintenance + * Victor Roldan Betancort - maintenance + */ +package org.eclipse.emf.cdo.net4j; + +import org.eclipse.emf.cdo.session.CDOSession; + +import org.eclipse.net4j.signal.ISignalProtocol; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; + +/** + * A Net4j-specific CDO {@link CDOSession session}. + * + * @since 4.1 + * @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 CDONet4jSession.Options} + */ +public interface CDONet4jSession extends org.eclipse.emf.cdo.session.CDOSession +{ + /** + * Returns the {@link Options options} of this session. + */ + public Options options(); + + /** + * Encapsulates a set of notifying {@link CDONet4jSession session} configuration options. + * + * @since 4.1 + * @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 Options extends org.eclipse.emf.cdo.session.CDOSession.Options + { + /** + * Returns the Net4j {@link CDOSessionProtocol protocol} instance that represents the underlying + * signalling connection to the repository of this session. + */ + public ISignalProtocol getNet4jProtocol(); + + /** + * Returns the timeout for commit operations in seconds. + */ + public int getCommitTimeout(); + + /** + * Sets the timeout for commit operations in seconds. + */ + public void setCommitTimeout(int commitTimeout); + + /** + * Returns the interval for progress reports of commit operations in seconds. + */ + public int getProgressInterval(); + + /** + * Sets the interval for progress reports of commit operations in seconds. + */ + public void setProgressInterval(int progressInterval); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jSessionConfiguration.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jSessionConfiguration.java new file mode 100644 index 000000000..01412b392 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jSessionConfiguration.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.session.CDOSession; + +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.util.io.IStreamWrapper; + +/** + * Configures and opens new Net4j-specific CDO {@link CDOSession sessions}. + * + * @since 4.1 + * @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.uses {@link CDONet4jSession} - - opens + */ +public interface CDONet4jSessionConfiguration extends org.eclipse.emf.cdo.session.CDOSessionConfiguration +{ + public String getRepositoryName(); + + public void setRepositoryName(String repositoryName); + + public IConnector getConnector(); + + public void setConnector(IConnector connector); + + public IStreamWrapper getStreamWrapper(); + + public void setStreamWrapper(IStreamWrapper streamWrapper); + + /** + * @since 4.0 + */ + public long getSignalTimeout(); + + /** + * @since 4.0 + */ + public void setSignalTimeout(long timeout); + + /** + * @see CDONet4jSession#getPackageRegistry() + */ + public CDOPackageRegistry getPackageRegistry(); + + /** + * A special package registry can be set before the session is opened and can not be changed thereafter. + * + * @see CDONet4jSession#getPackageRegistry() + */ + public void setPackageRegistry(CDOPackageRegistry packageRegistry); + + public CDOBranchManager getBranchManager(); + + public void setBranchManager(CDOBranchManager branchManager); + + /** + * @see CDONet4jSession#getRevisionManager() + * @since 3.0 + */ + public CDORevisionManager getRevisionManager(); + + /** + * @see CDONet4jSession#getRevisionManager() + * @since 3.0 + */ + public void setRevisionManager(CDORevisionManager revisionManager); + + /** + * @since 4.1 + */ + public CDONet4jSession openNet4jSession(); + + /** + * @deprecated Use {@link #openNet4jSession() openNet4jSession()}. + */ + @Deprecated + public org.eclipse.emf.cdo.net4j.CDOSession openSession(); +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jUtil.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jUtil.java new file mode 100644 index 000000000..03c3052da --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jUtil.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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 - maintenance + * Victor Roldan Betancort - maintenance + */ +package org.eclipse.emf.cdo.net4j; + +import org.eclipse.emf.cdo.eresource.CDOResourceFactory; +import org.eclipse.emf.cdo.internal.net4j.CDONet4jSessionConfigurationImpl; +import org.eclipse.emf.cdo.internal.net4j.FailoverCDOSessionConfigurationImpl; +import org.eclipse.emf.cdo.internal.net4j.Net4jConnectorInjector; +import org.eclipse.emf.cdo.internal.net4j.Net4jSessionFactory; +import org.eclipse.emf.cdo.internal.net4j.ReconnectingCDOSessionConfigurationImpl; +import org.eclipse.emf.cdo.internal.net4j.protocol.CDOClientProtocolFactory; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.view.CDOViewProvider; +import org.eclipse.emf.cdo.view.CDOViewProviderRegistry; + +import org.eclipse.emf.internal.cdo.session.CDOSessionFactory; + +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.om.OMPlatform; + +import org.eclipse.emf.ecore.resource.Resource; + +import java.util.Map; + +/** + * Various static methods that may help with Net4j-specific CDO {@link CDONet4jSession sessions}. + * + * @since 2.0 + * @author Eike Stepper + * @apiviz.uses {@link CDONet4jSessionConfiguration} - - creates + * @apiviz.uses {@link ReconnectingCDOSessionConfiguration} - - creates + * @apiviz.uses {@link FailoverCDOSessionConfiguration} - - creates + */ +public final class CDONet4jUtil +{ + /** + * @since 4.0 + */ + public static final String PROTOCOL_TCP = "cdo.net4j.tcp"; + + /** + * @since 4.0 + */ + public static final String PROTOCOL_SSL = "cdo.net4j.ssl"; + + /** + * @since 4.0 + */ + public static final String PROTOCOL_JVM = "cdo.net4j.jvm"; + + static + { + try + { + if (!OMPlatform.INSTANCE.isOSGiRunning()) + { + CDOUtil.registerResourceFactory(null); // Ensure that the normal resource factory is registered + + Map map = Resource.Factory.Registry.INSTANCE.getProtocolToFactoryMap(); + if (!map.containsKey(PROTOCOL_TCP)) + { + map.put(PROTOCOL_TCP, CDOResourceFactory.INSTANCE); + } + + if (!map.containsKey(PROTOCOL_SSL)) + { + map.put(PROTOCOL_SSL, CDOResourceFactory.INSTANCE); + } + + if (!map.containsKey(PROTOCOL_JVM)) + { + map.put(PROTOCOL_JVM, CDOResourceFactory.INSTANCE); + } + + int priority = CDOViewProvider.DEFAULT_PRIORITY - 100; + CDOViewProviderRegistry.INSTANCE.addViewProvider(new CDONet4jViewProvider.TCP(priority)); + CDOViewProviderRegistry.INSTANCE.addViewProvider(new CDONet4jViewProvider.SSL(priority)); + CDOViewProviderRegistry.INSTANCE.addViewProvider(new CDONet4jViewProvider.JVM(priority)); + } + } + catch (RuntimeException ex) + { + ex.printStackTrace(); + throw ex; + } + catch (Error ex) + { + ex.printStackTrace(); + throw ex; + } + } + + private CDONet4jUtil() + { + } + + /** + * @since 4.1 + */ + public static CDONet4jSessionConfiguration createNet4jSessionConfiguration() + { + return new CDONet4jSessionConfigurationImpl(); + } + + /** + * @deprecated Use {@link #createNet4jSessionConfiguration() createNet4jSessionConfiguration()}. + */ + @Deprecated + public static CDOSessionConfiguration createSessionConfiguration() + { + return (CDOSessionConfiguration)createNet4jSessionConfiguration(); + } + + /** + * @since 4.0 + */ + public static ReconnectingCDOSessionConfiguration createReconnectingSessionConfiguration(String hostAndPort, String repoName, IManagedContainer container) + { + return new ReconnectingCDOSessionConfigurationImpl(hostAndPort, repoName, container); + } + + /** + * @since 4.0 + */ + public static FailoverCDOSessionConfiguration createFailoverSessionConfiguration(String monitorConnectorDescription, String repositoryGroup) + { + return createFailoverSessionConfiguration(monitorConnectorDescription, repositoryGroup, IPluginContainer.INSTANCE); + } + + /** + * @since 4.0 + */ + public static FailoverCDOSessionConfiguration createFailoverSessionConfiguration(String monitorConnectorDescription, String repositoryGroup, + IManagedContainer container) + { + return new FailoverCDOSessionConfigurationImpl(monitorConnectorDescription, repositoryGroup, container); + } + + public static void prepareContainer(IManagedContainer container) + { + container.registerFactory(new CDOClientProtocolFactory()); + container.registerFactory(new Net4jSessionFactory()); + container.addPostProcessor(new Net4jConnectorInjector()); + } + + /** + * @since 4.1 + */ + public static CDONet4jSession getNet4jSession(IManagedContainer container, String description) + { + return (CDONet4jSession)container.getElement(CDOSessionFactory.PRODUCT_GROUP, Net4jSessionFactory.TYPE, description); + } + + /** + * @since 4.0 + * @deprecated Use {@link #getNet4jSession(IManagedContainer, String) getNet4jSession()}. + */ + @Deprecated + public static CDOSession getSession(IManagedContainer container, String description) + { + return (CDOSession)getNet4jSession(container, description); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jViewProvider.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jViewProvider.java new file mode 100644 index 000000000..d9596cd4a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDONet4jViewProvider.java @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.util.CDOURIData; +import org.eclipse.emf.cdo.view.AbstractCDOViewProvider; +import org.eclipse.emf.cdo.view.CDOView; +import org.eclipse.emf.cdo.view.CDOViewProvider; + +import org.eclipse.net4j.Net4jUtil; +import org.eclipse.net4j.channel.IChannel; +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.util.container.FactoryNotFoundException; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.security.CredentialsProviderFactory; +import org.eclipse.net4j.util.security.IPasswordCredentialsProvider; +import org.eclipse.net4j.util.security.PasswordCredentialsProvider; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.ResourceSet; + +import org.eclipse.core.runtime.Path; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * A {@link CDOViewProvider view provider} that uses Net4j-specific CDO {@link CDONet4jSession sessions} to open views. + * + * @author Eike Stepper + * @since 4.0 + */ +public abstract class CDONet4jViewProvider extends AbstractCDOViewProvider +{ + private String transport; + + public CDONet4jViewProvider(String transport, int priority) + { + super("cdo\\.net4j\\." + transport + "://.*", priority); + this.transport = transport; + } + + public CDOView getView(URI uri, ResourceSet resourceSet) + { + CDOURIData data = new CDOURIData(uri); + + IConnector connector = getConnector(data.getAuthority()); + CDONet4jSession session = getNet4jSession(connector, data.getUserName(), data.getPassWord(), data.getRepositoryName()); + + String viewID = data.getViewID(); + if (viewID != null) + { + if (data.isTransactional()) + { + return session.openTransaction(viewID, resourceSet); + } + + return session.openView(viewID, resourceSet); + } + + String branchPath = data.getBranchPath().toPortableString(); + CDOBranch branch = session.getBranchManager().getBranch(branchPath); + long timeStamp = data.getTimeStamp(); + + if (data.isTransactional()) + { + return session.openTransaction(branch, resourceSet); + } + + return session.openView(branch, timeStamp, resourceSet); + } + + @Override + public String getPath(URI uri) + { + return new Path(uri.path()).makeAbsolute().removeFirstSegments(1).toString(); + } + + @Override + public URI getViewURI(URI uri) + { + CDOURIData uriData = new CDOURIData(uri); + uriData.setResourcePath(null); + uriData.setExtraParameters(null); + return uriData.toURI(); + } + + @Override + public URI getResourceURI(CDOView view, String path) + { + StringBuilder builder = new StringBuilder(); + builder.append("cdo.net4j."); + builder.append(transport); + builder.append("://"); + + CDONet4jSession session = (CDONet4jSession)view.getSession(); + + // CDOAuthenticator authenticator = ((InternalCDOSession)session).getAuthenticator(); + // IPasswordCredentialsProvider credentialsProvider = authenticator.getCredentialsProvider(); + // if (credentialsProvider != null) + // { + // IPasswordCredentials credentials = credentialsProvider.getCredentials(); + // builder.append(credentials.getUserID()); + // + // char[] password = credentials.getPassword(); + // if (password != null) + // { + // builder.append(":"); + // builder.append(password); + // } + // + // builder.append("@"); + // } + + IChannel channel = session.options().getNet4jProtocol().getChannel(); + if (channel == null) + { + return null; + } + IConnector connector = (IConnector)channel.getMultiplexer(); + String repositoryName = session.getRepositoryInfo().getName(); + append(builder, connector, repositoryName); + + if (path != null) + { + if (!path.startsWith("/")) + { + builder.append("/"); + } + + builder.append(path); + } + + int params = 0; + + String branchPath = view.getBranch().getPathName(); + if (!CDOBranch.MAIN_BRANCH_NAME.equalsIgnoreCase(branchPath)) + { + builder.append(params++ == 0 ? "?" : "&"); + builder.append(CDOURIData.BRANCH_PARAMETER); + builder.append("="); + builder.append(branchPath); + } + + long timeStamp = view.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(params++ == 0 ? "?" : "&"); + builder.append(CDOURIData.TIME_PARAMETER); + builder.append("="); + builder.append(new SimpleDateFormat().format(new Date(timeStamp))); + } + + if (!view.isReadOnly()) + { + builder.append(params++ == 0 ? "?" : "&"); + builder.append(CDOURIData.TRANSACTIONAL_PARAMETER); + builder.append("=true"); + } + + return URI.createURI(builder.toString()); + } + + protected String getURIAuthority(IConnector connector) + { + String url = connector.getURL().toString(); + return URI.createURI(url).authority(); + } + + /** + * @since 4.1 + */ + protected CDONet4jSession getNet4jSession(IConnector connector, String userName, String passWord, String repositoryName) + { + CDONet4jSessionConfiguration configuration = getNet4jSessionConfiguration(connector, userName, passWord, repositoryName); + return configuration.openNet4jSession(); + } + + /** + * @since 4.1 + */ + protected CDONet4jSessionConfiguration getNet4jSessionConfiguration(IConnector connector, String userName, String passWord, String repositoryName) + { + CDONet4jSessionConfiguration configuration = CDONet4jUtil.createNet4jSessionConfiguration(); + configuration.setConnector(connector); + configuration.setRepositoryName(repositoryName); + + IPasswordCredentialsProvider credentialsProvider = null; + if (userName != null && passWord != null) + { + credentialsProvider = new PasswordCredentialsProvider(userName, passWord); + } + else + { + StringBuilder builder = new StringBuilder(); + append(builder, connector, repositoryName); + String resource = builder.toString(); + + try + { + credentialsProvider = (IPasswordCredentialsProvider)getContainer().getElement(CredentialsProviderFactory.PRODUCT_GROUP, "password", resource); + } + catch (FactoryNotFoundException ex) + { + // Ignore + } + + // The following is to stay compatible with the formerly wrong product group (".security" was missing). + if (credentialsProvider == null) + { + try + { + credentialsProvider = (IPasswordCredentialsProvider)getContainer().getElement("org.eclipse.net4j.util.credentialsProviders", "password", resource); + } + catch (FactoryNotFoundException ex) + { + // Ignore + } + } + } + + configuration.setCredentialsProvider(credentialsProvider); + return configuration; + } + + /** + * @deprecated Use {@link #getNet4jSession(IConnector, String, String, String) getNet4jSession()}. + */ + @Deprecated + protected CDOSession getSession(IConnector connector, String userName, String passWord, String repositoryName) + { + return (CDOSession)getNet4jSession(connector, userName, passWord, repositoryName); + } + + /** + * @deprecated Use {@link #getNet4jSessionConfiguration(IConnector, String, String, String) + * getNet4jSessionConfiguration()}. + */ + @Deprecated + protected CDOSessionConfiguration getSessionConfiguration(IConnector connector, String userName, String passWord, String repositoryName) + { + return (CDOSessionConfiguration)getNet4jSessionConfiguration(connector, userName, passWord, repositoryName); + } + + protected IManagedContainer getContainer() + { + return IPluginContainer.INSTANCE; + } + + protected IConnector getConnector(String authority) + { + IManagedContainer container = getContainer(); + String description = getConnectorDescription(authority); + return Net4jUtil.getConnector(container, transport, description); + } + + protected String getConnectorDescription(String authority) + { + return authority; + } + + private void append(StringBuilder builder, IConnector connector, String repositoryName) + { + String authority = getURIAuthority(connector); + builder.append(authority); + + builder.append("/"); + builder.append(repositoryName); + } + + /** + * A TCP-based {@link CDONet4jViewProvider view provider}. + * + * @author Eike Stepper + */ + public static class TCP extends CDONet4jViewProvider + { + public TCP(int priority) + { + super("tcp", priority); + } + + public TCP() + { + this(DEFAULT_PRIORITY); + } + + } + + /** + * An SSL-based {@link CDONet4jViewProvider view provider}. + * + * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.) + */ + public static class SSL extends CDONet4jViewProvider + { + public SSL(int priority) + { + super("ssl", priority); + } + + public SSL() + { + this(DEFAULT_PRIORITY); + } + + } + + /** + * A JVM-based {@link CDONet4jViewProvider view provider}. + * + * @author Eike Stepper + */ + public static class JVM extends CDONet4jViewProvider + { + public JVM(int priority) + { + super("jvm", priority); + } + + public JVM() + { + this(DEFAULT_PRIORITY); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSession.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSession.java new file mode 100644 index 000000000..c1770fa87 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSession.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.net4j.signal.ISignalProtocol; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; + +/** + * Deprecated, use {@link org.eclipse.emf.cdo.net4j.CDONet4jSession CDONet4jSession}. + * + * @since 2.0 + * @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. + * @deprecated Use {@link org.eclipse.emf.cdo.net4j.CDONet4jSession CDONet4jSession}. + */ +@Deprecated +public interface CDOSession extends CDONet4jSession +{ + /** + * Returns the {@link Options options} of this session. + * + * @deprecated Use {@link org.eclipse.emf.cdo.net4j.CDONet4jSession#options() CDONet4jSession.options()}. + */ + @Deprecated + public Options options(); + + /** + * Deprecated, use {@link org.eclipse.emf.cdo.net4j.CDONet4jSession.Options CDONet4jSession.Options}. + * + * @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. + * @deprecated Use {@link org.eclipse.emf.cdo.net4j.CDONet4jSession.Options CDONet4jSession.Options}. + */ + @Deprecated + public interface Options extends CDONet4jSession.Options + { + /** + * Returns the Net4j {@link CDOSessionProtocol protocol} instance that represents the underlying + * signalling connection to the repository of this session. + * + * @see #getNet4jProtocol() + * @deprecated Kept for 4.0 compatibility. Newer code should call {@link #getNet4jProtocol()}. + */ + @Deprecated + public ISignalProtocol getProtocol(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSessionConfiguration.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSessionConfiguration.java new file mode 100644 index 000000000..655e14dab --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSessionConfiguration.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2009-2012, 2015 Eike Stepper (Loehne, 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.net4j; + +/** + * Deprecated, use {@link org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration CDONet4jSessionConfiguration}. + * + * @since 3.0 + * @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. + * @deprecated Use {@link org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration CDONet4jSessionConfiguration}. + */ +@Deprecated +public interface CDOSessionConfiguration extends CDONet4jSessionConfiguration +{ + +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSessionRecoveryEvent.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSessionRecoveryEvent.java new file mode 100644 index 000000000..fcb03c6fe --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/CDOSessionRecoveryEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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 + * Caspar De Groot - maintenance + */ +package org.eclipse.emf.cdo.net4j; + +import org.eclipse.emf.cdo.session.CDOSessionEvent; + +/** + * A {@link CDOSessionEvent session event} fired from {@link RecoveringCDOSessionConfiguration recovering session} when + * recovery has started or finished. + * + * @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 CDOSessionRecoveryEvent extends CDOSessionEvent +{ + public Type getType(); + + /** + * Enumerates the possible types of {@link CDOSessionRecoveryEvent session recovery events}. + * + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + */ + public enum Type + { + STARTED, FINISHED + } +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/FailoverCDOSessionConfiguration.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/FailoverCDOSessionConfiguration.java new file mode 100644 index 000000000..ab5868521 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/FailoverCDOSessionConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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.net4j; + +/** + * A {@link RecoveringCDOSessionConfiguration session configuration} that recovers from network problems by failing over + * to backup repositories as directed by a fail-over monitor. + * + * @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 FailoverCDOSessionConfiguration extends RecoveringCDOSessionConfiguration +{ + public String getMonitorConnectorDescription(); + + public String getRepositoryGroup(); +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/ReconnectingCDOSessionConfiguration.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/ReconnectingCDOSessionConfiguration.java new file mode 100644 index 000000000..9371aee59 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/ReconnectingCDOSessionConfiguration.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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.net4j; + +/** + * A {@link RecoveringCDOSessionConfiguration session configuration} that recovers from network problems by attempting + * to reconnect to the same repository in specific intervals. + * + * @author Caspar De Groot + * @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 ReconnectingCDOSessionConfiguration extends RecoveringCDOSessionConfiguration +{ + public long getReconnectInterval(); + + public void setReconnectInterval(long interval); + + public int getMaxReconnectAttempts(); + + public void setMaxReconnectAttempts(int attempts); +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/RecoveringCDOSessionConfiguration.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/RecoveringCDOSessionConfiguration.java new file mode 100644 index 000000000..3da1e6d04 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/RecoveringCDOSessionConfiguration.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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.net4j; + +import org.eclipse.net4j.signal.heartbeat.HeartBeatProtocol; + +/** + * A {@link CDONet4jSessionConfiguration session configuration} that uses a {@link HeartBeatProtocol heart beat + * protocol} to detect network problems. Subtypes specify the exact behavior to recover from these problems. + * + * @author Caspar De Groot + * @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. + */ +@SuppressWarnings("deprecation") +public interface RecoveringCDOSessionConfiguration extends CDOSessionConfiguration +{ + public boolean isHeartBeatEnabled(); + + public void setHeartBeatEnabled(boolean enabled); + + public long getHeartBeatPeriod(); + + public void setHeartBeatPeriod(long period); + + public long getHeartBeatTimeout(); + + public void setHeartBeatTimeout(long timeout); + + public long getConnectorTimeout(); + + public void setConnectorTimeout(long timeout); +} diff --git a/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/package-info.java b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/package-info.java new file mode 100644 index 000000000..646633b3d --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/net4j/package-info.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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 + */ + +/** + * Client concepts for dealing with Net4j-based sessions to remote repositories. + * + * @apiviz.exclude .*Event + * @apiviz.exclude .*Event\.Type + */ +package org.eclipse.emf.cdo.net4j; diff --git a/bundles/org.eclipse.emf.cdo.server.db/.classpath b/bundles/org.eclipse.emf.cdo.server.db/.classpath new file mode 100644 index 000000000..b263de401 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.eclipse.emf.cdo.server.db/.gitignore b/bundles/org.eclipse.emf.cdo.server.db/.gitignore new file mode 100644 index 000000000..57b341172 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/generated/ diff --git a/bundles/org.eclipse.emf.cdo.server.db/.project b/bundles/org.eclipse.emf.cdo.server.db/.project new file mode 100644 index 000000000..152ad8bdb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/.project @@ -0,0 +1,23 @@ + + + org.eclipse.emf.cdo.server.db + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/bundles/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..2c715ef3b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/bundles/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..bb35fa0a8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.eclipse.emf.cdo.server.db/bnd.bnd b/bundles/org.eclipse.emf.cdo.server.db/bnd.bnd new file mode 100644 index 000000000..37f8e8c5c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/bnd.bnd @@ -0,0 +1,22 @@ +Bundle-Version: 4.5.0 +-buildpath: \ + org.eclipse.emf.cdo.server;version=latest,\ + org.eclipse.net4j.util,\ + org.eclipse.emf.cdo.common,\ + org.eclipse.emf.ecore,\ + org.eclipse.emf.cdo,\ + org.eclipse.emf.common,\ + osgi.core,\ + org.eclipse.net4j.db,\ + org.eclipse.equinox.common,\ + org.eclipse.equinox.registry,\ + org.eclipse.core.runtime +Private-Package: \ + org.eclipse.emf.cdo.server.internal.db,\ + org.eclipse.emf.cdo.server.internal.db.bundle,\ + org.eclipse.emf.cdo.server.internal.db.mapping,\ + org.eclipse.emf.cdo.server.internal.db.mapping.horizontal,\ + org.eclipse.emf.cdo.server.internal.db.messages +Export-Package: \ + org.eclipse.emf.cdo.server.db,\ + org.eclipse.emf.cdo.server.db.mapping \ No newline at end of file diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java new file mode 100644 index 000000000..4e8e0a5fa --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 Eike Stepper (Loehne, 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.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.IDBConnection; +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; + +import java.util.Map; + +/** + * Various static methods that may help in setting up and dealing with {@link IDBStore DB stores}. + * + * @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"; + + /** + * @since 4.1 + */ + public static final String PROP_ZEROBASED_INDEX = "forceZeroBasedIndex"; + + 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) + { + return createStore(mappingStrategy, dbAdapter, dbConnectionProvider, null); + } + + /** + * @since 4.2 + */ + public static IDBStore createStore(IMappingStrategy mappingStrategy, IDBAdapter dbAdapter, IDBConnectionProvider dbConnectionProvider, + Map properties) + { + DBStore store = new DBStore(); + store.setMappingStrategy(mappingStrategy); + store.setDBAdapter(dbAdapter); + store.setDBConnectionProvider(dbConnectionProvider); + store.setProperties(properties); + 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; + } + + /** + * @since 2.0 + * @deprecated As of 4.2 use {@link IDBConnection#prepareStatement(String, org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability)}. + */ + @Deprecated + public static IPreparedStatementCache createStatementCache() + { + throw new UnsupportedOperationException(); + } + + /** + * @since 2.0 + * @deprecated As of 4.2 use {@link IDBConnection#prepareStatement(String, org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability)}. + */ + @Deprecated + public static IPreparedStatementCache createStatementCache(int capacity) + { + throw new UnsupportedOperationException(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java new file mode 100644 index 000000000..74fb701ea --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2007-2014, 2016 Eike Stepper (Loehne, 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.IRepository; +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.IDBDatabase; +import org.eclipse.net4j.db.ddl.IDBSchema; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; + +/** + * The main entry point to the API of CDO's proprietary object/relational mapper. + * + * @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(); + + /** + * @since 4.2 + */ + public IDBDatabase getDatabase(); + + public IDBAdapter getDBAdapter(); + + public IDBSchema getDBSchema(); + + /** + * @since 4.2 + */ + public int getIDColumnLength(); + + /** + * @since 4.4 + */ + public int getJDBCFetchSize(); + + /** + * @since 4.2 + */ + public Map getProperties(); + + /** + * @since 4.2 + */ + public void visitAllTables(Connection connection, TableVisitor visitor); + + /** + * 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); + + /** + * Called back from {@link IDBStore#visitAllTables(Connection, TableVisitor)} for all tables in the database. + * + * @author Eike Stepper + * @since 4.2 + */ + public interface TableVisitor + { + public void visitTable(Connection connection, String name) throws SQLException; + } + + /** + * Contains symbolic constants that specifiy valid keys of {@link IRepository#getProperties() DB store properties}. + * + * @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 Props + { + /** + * In minutes. + */ + public static final String CONNECTION_KEEPALIVE_PERIOD = "connectionKeepAlivePeriod"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String ID_COLUMN_LENGTH = "idColumnLength"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String READER_POOL_CAPACITY = "readerPoolCapacity"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String WRITER_POOL_CAPACITY = "writerPoolCapacity"; //$NON-NLS-1$ + + /** + * @since 4.3 + */ + public static final String FIELD_CONSTRUCTION_TRACKING = "fieldConstructionTracking"; //$NON-NLS-1$ + + /** + * @since 4.4 + */ + public static final String DROP_ALL_DATA_ON_ACTIVATE = "dropAllDataOnActivate"; //$NON-NLS-1$ + + /** + * @since 4.4 + */ + public static final String JDBC_FETCH_SIZE = "jdbcFetchSize"; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java new file mode 100644 index 000000000..e21f8d52d --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2007-2013, 2016, 2018 Eike Stepper (Loehne, 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.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.UnitSupport; + +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.ddl.IDBTable; + +import org.eclipse.emf.ecore.EClass; + +import java.sql.Connection; + +/** + * A {@link IStoreAccessor store accessor} for CDO's proprietary object/relational mapper. + * + * @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.Raw2, UnitSupport +{ + public IDBStore getStore(); + + /** + * @since 4.2 + */ + public IDBConnection getDBConnection(); + + public Connection getConnection(); + + /** + * @since 4.5 + */ + public EClass getObjectType(CDOID id); + + /** + * @since 2.0 + * @deprecated As of 4.2 use {@link IDBConnection#prepareStatement(String, org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability)}. + */ + @Deprecated + public IPreparedStatementCache getStatementCache(); + + /** + * @since 4.6 + */ + public void tableCreated(IDBTable table); +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java new file mode 100644 index 000000000..f9785815a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * A {@link IStoreChunkReader chunk reader} for CDO's proprietary object/relational mapper. + * + * @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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java new file mode 100644 index 000000000..5679c5c06 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011, 2012, 2016 Eike Stepper (Loehne, 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; + +/** + * Abstracts the handling of {@link CDOID IDs} of different {@link ObjectType ID types}. + * + * @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 +{ + public IDBStore getStore(); + + public DBType getDBType(); + + public Set getObjectIDTypes(); + + public ITypeMapping getObjectTypeMapping(); + + public CDOID createCDOID(String val); + + /** + * @deprecated Not used anymore. + */ + @Deprecated + 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); + + /** + * @since 4.1 + */ + public void setCDOIDRaw(PreparedStatement stmt, int column, Object id) throws SQLException; + + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java new file mode 100644 index 000000000..23d2074d4 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2009-2012, 2016 Eike Stepper (Loehne, 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.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.IRepository; +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; + +/** + * Manages the {@link CDOPackageUnit elements} of the meta model level of a CDO {@link IRepository repository}. + * + * @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}. getMetaID(getMetaInstance(x)) yields + * x + * + * @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. getMetaInstance(getMetaID(m)) yields + * m + * + * @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 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 rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime, OMMonitor monitor) + throws IOException; + +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java new file mode 100644 index 000000000..1154a05f8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009, 2011-2013 Eike Stepper (Loehne, 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 org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBPreparedStatement; + +import java.sql.Connection; +import java.sql.PreparedStatement; + +/** + * Caches JDBC {@link IDBPreparedStatement statements} according to given {@link ReuseProbability reuse probabilities}. + * + * @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. + * @deprecated As of 4.2 use {@link IDBConnection}. + */ +@Deprecated +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: + *
    + *
  • For global statements which are used regularly (such as lookup object in cdo_objects) use + * {@link ReuseProbability#MAX MAX}. + *
  • For constant object-specific statements which are used regularly use {@link ReuseProbability#HIGH HIGH}. + *
  • For object-specific statements which are assembled from constants which are used regularly use + * {@link ReuseProbability#MEDIUM MEDIUM}. + *
  • For all other dynamic statements, like queries, use {@link ReuseProbability#LOW LOW} + *
+ * + * @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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java new file mode 100644 index 000000000..3e08245b4 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Loehne, 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.IDBAdapter; +import org.eclipse.net4j.db.IDBPreparedStatement; +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; +import java.text.MessageFormat; + +/** + * 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 + * factories. + * + * @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 DBType dbType; + + private IDBField field; + + /** + * Create a new type mapping + */ + public AbstractTypeMapping() + { + } + + public final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final void setMappingStrategy(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + } + + public final EStructuralFeature getFeature() + { + return feature; + } + + public final void setFeature(EStructuralFeature feature) + { + this.feature = feature; + } + + public final void setDBType(DBType dbType) + { + this.dbType = dbType; + } + + public DBType getDBType() + { + return dbType; + } + + 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); + } + } + + @Deprecated + 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 IDBField getField() + { + return field; + } + + public final void setDBField(IDBTable table, String fieldName) + { + field = table.getFieldSafe(fieldName); + } + + 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", //$NON-NLS-1$ + feature.getName()); + } + + 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; + } + + @Override + public String toString() + { + Object mappedElement = field != null ? field : dbType; + return MessageFormat.format("{0}[{1}.{2} --> {3}]", getClass().getSimpleName(), //$NON-NLS-1$ + feature.getEContainingClass().getName(), feature.getName(), mappedElement); + } + + 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 IDBPreparedStatement} 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(); + } + + 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()); + } + } + + IDBAdapter adapter = mappingStrategy.getStore().getDBAdapter(); + return adapter.getFieldLength(type); + } + + /** + * Subclasses should implement this method to read the value from the result set. Typical implementations should look + * similar to this one: resultSet.getString(getField().getName()) + * + * @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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java new file mode 100644 index 000000000..59aaf9f85 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ColumnTypeModifier.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ColumnTypeModifier.java new file mode 100644 index 000000000..ba001c957 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ColumnTypeModifier.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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.Provider; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.util.factory.ProductCreationException; + +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * Can modify the column type of a {@link ITypeMapping type mapping} that is created by the {@link ITypeMapping.Registry type mapping registry}. + * + * @author Eike Stepper + * @since 4.2 + */ +public abstract class ColumnTypeModifier +{ + public static final ColumnTypeModifier NOOP = new ColumnTypeModifier() + { + @Override + public DBType modify(Provider provider, IMappingStrategy mappingStrategy, EStructuralFeature feature, DBType dbType) + { + return dbType; + } + }; + + public ColumnTypeModifier() + { + } + + /** + * Can modify the column type of a {@link ITypeMapping type mapping} that is created by the {@link ITypeMapping.Registry type mapping registry}. + */ + public abstract DBType modify(ITypeMapping.Provider provider, IMappingStrategy mappingStrategy, EStructuralFeature feature, DBType dbType); + + /** + * Creates {@link ColumnTypeModifier} instances. + * + * @author Eike Stepper + */ + public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory + { + /** + * The Net4j factory product group for column type modifiers. + */ + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.db.columnTypeModifiers"; + + public Factory(String type) + { + super(PRODUCT_GROUP, type); + } + + public abstract ColumnTypeModifier create(String description) throws ProductCreationException; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java new file mode 100644 index 000000000..e462b0946 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2009-2013, 2016 Eike Stepper (Loehne, 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 getDBTables(); + + /** + * Get the mapping of the many-valued feature. + * + * @param feature + * the feature for which the mapping should be returned. feature.isMany() has to be + * true. + * @return the list mapping corresponding to the feature. + */ + public IListMapping getListMapping(EStructuralFeature feature); + + /** + * @since 3.0 + */ + public List getListMappings(); + + /** + * @since 4.0 + */ + public List 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 true, if the revision has been found and read correctly. false if the revision + * could not be found. In this case, the content of revision 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 + * true if the type of the object is supposed to be mapped, false otherwise. + * @param revise + * true if the previous revision is supposed to be revised, false 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 result.executeQuery(). + * @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. 0 means none. + * @param name + * the name of the resource node to look up + * @param exactMatch + * if true, name must match exactly, otherwise all resource nodes starting with + * name 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 result.executeQuery(). + * @throws ImplementationError + * if called on a mapping which does not map an EClass instanceof CDOResourceNode. + * @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 all of the following + * conditions are met: + *
    + *
  • The branch parameter is null or equal to revision.getBranch(). + *
  • The timeStamp parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to + * revision.getTimeStamp(). + *
+ * + * @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 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java new file mode 100644 index 000000000..63ea230b9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2012, 2015 Eike Stepper (Loehne, 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 true it is guaranteed that + * revision.getVersion() == version + * + * @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 true, if the revision has been found and read correctly. false if the revision + * could not be found. In this case, the content of revision is undefined. + * @since 3.0 + */ + public boolean readRevisionByVersion(IDBStoreAccessor storeAccessor, InternalCDORevision revision, int listChunk); +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java new file mode 100644 index 000000000..d65c79251 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009-2012, 2015, 2016 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingUnitSupport.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingUnitSupport.java new file mode 100644 index 000000000..616f7bc83 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingUnitSupport.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; + +import java.sql.SQLException; + +/** + * An extension interface for {@link IClassMapping class mappings} that support units. + * + * @author Eike Stepper + * @since 4.4 + */ +public interface IClassMappingUnitSupport extends IClassMapping +{ + public void readUnitRevisions(IDBStoreAccessor accessor, CDOBranchPoint branchPoint, CDOID rootID, CDORevisionHandler revisionHandler) throws SQLException; +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java new file mode 100644 index 000000000..045e63850 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2009-2012, 2015, 2016 Eike Stepper (Loehne, 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 isMany() == true. + * + * @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 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 listChunk >= 0, the list is filled with {@link InternalCDOList#UNINITIALIZED} + * and only the first listChunk 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 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping2.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping2.java new file mode 100644 index 000000000..a4d4c0255 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping2.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2016 Eike Stepper (Loehne, 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.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; + +/** + * Extension interface to {@link IListMapping}. + * + * @author Eike Stepper + * @since 4.1 + */ +public interface IListMapping2 extends IListMapping +{ + public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index); + + public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex, int toIndex); +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping3.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping3.java new file mode 100644 index 000000000..edac02317 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping3.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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; + +/** + * Extension interface to {@link IListMapping2}. + * + * @author Eike Stepper + * @since 4.4 + */ +public interface IListMapping3 extends IListMapping2 +{ + public void setClassMapping(IClassMapping classMapping); +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java new file mode 100644 index 000000000..d705a05c0 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009-2012, 2015, 2016 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java new file mode 100644 index 000000000..6af2defeb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; + +import org.eclipse.net4j.util.collection.MoveableList; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Interface to complement {@link IListMapping} in order to provide unit support. + * + * @author Eike Stepper + * @since 4.4 + */ +public interface IListMappingUnitSupport extends IListMapping +{ + public ResultSet queryUnitEntries(IDBStoreAccessor accessor, IIDHandler idHandler, long timeStamp, CDOID rootID) throws SQLException; + + public void readUnitEntries(ResultSet resultSet, IIDHandler idHandler, CDOID id, MoveableList list) throws SQLException; +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java new file mode 100644 index 000000000..da87d29f3 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.EPackage; +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. + * + * @deprecated As of 4.4 use {@link Props#MAX_TABLE_NAME_LENGTH}. + */ + @Deprecated + public static final String PROP_MAX_TABLE_NAME_LENGTH = Props.MAX_TABLE_NAME_LENGTH; + + /** + * 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. + * + * @deprecated As of 4.4 use {@link Props#MAX_FIELD_NAME_LENGTH}. + */ + @Deprecated + public static final String PROP_MAX_FIELD_NAME_LENGTH = Props.MAX_FIELD_NAME_LENGTH; + + /** + * Name of the String property that specifies a common prefix for table names. + * + * @deprecated As of 4.4 use {@link Props#TABLE_NAME_PREFIX}. + */ + @Deprecated + public static final String PROP_TABLE_NAME_PREFIX = Props.TABLE_NAME_PREFIX; + + /** + * Name of the boolean property that configures whether the table names are made of simple class names or of qualified + * class names. + * + * @deprecated As of 4.4 use {@link Props#QUALIFIED_NAMES}. + */ + @Deprecated + public static final String PROP_QUALIFIED_NAMES = Props.QUALIFIED_NAMES; + + /** + * 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. + * + * @deprecated As of 4.4 use {@link Props#FORCE_NAMES_WITH_ID}. + */ + @Deprecated + public static final String PROP_FORCE_NAMES_WITH_ID = Props.FORCE_NAMES_WITH_ID; + + /** + * Name of the integer property that configures the size of the object type in-memory cache. Possible configuration + * values are: + *
    + *
  • 0 (zero). Don't use memory caching. + *
  • >0. Use memory caching with the cache size given. + *
+ * Default is a memory cache size of 10,000,000. + *

+ * + * @since 4.0 + * @deprecated As of 4.4 use {@link Props#OBJECT_TYPE_CACHE_SIZE}. + */ + @Deprecated + public static final String PROP_OBJECT_TYPE_CACHE_SIZE = Props.OBJECT_TYPE_CACHE_SIZE; + + /** + * Name of a String property that specifies the name of a {@link ColumnTypeModifier column type modifier}. + * + * @since 4.2 + * @deprecated As of 4.4 use {@link Props#COLUMN_TYPE_MODIFIER}. + */ + @Deprecated + public static final String PROP_COLUMN_TYPE_MODIFIER = Props.COLUMN_TYPE_MODIFIER; + + /** + * @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 feature.isMany() == false. + * @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 feature.isMany() == true. + */ + 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: + * element instanceof EClass || element instanceof EPackage. + * @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 + */ + // Bug 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 getClassMappings(); + + /** + * Returns all class mappings of this strategy. + * + * @since 4.0 + */ + public Map getClassMappings(boolean createOnDemand); + + /** + * Query if this mapping supports revision deltas.
+ * If this method returns true, it is guaranteed that all class mappings returned by + * {@link #getClassMapping(EClass)} implement {@link IClassMappingDeltaSupport}. + * + * @return true if revision deltas are supported, false else. + */ + public boolean hasDeltaSupport(); + + /** + * Query if this mapping supports audits.
+ * If this method returns true, it is guaranteed that all class mappings returned by + * {@link #getClassMapping(EClass)} implement {@link IClassMappingAuditSupport}. + * + * @return true if audits are supported, false else. + */ + public boolean hasAuditSupport(); + + /** + * Query if this mapping supports branches.
+ * + * @return true if branches are supported, false 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 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 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 properties); + + /** + * Passes all revisions of the store to the {@link CDORevisionHandler handler} if all of the following + * conditions are met: + *

    + *
  • The eClass parameter is null or equal to revision.getEClass(). + *
  • The branch parameter is null or equal to revision.getBranch(). + *
  • The timeStamp parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to + * revision.getTimeStamp(). + *
+ * + * @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 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); + + /** + * Contains symbolic constants that specifiy valid keys of {@link IMappingStrategy#getProperties() mapping strategy properties}. + * + * @author Eike Stepper + * @since 4.4 + * @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 + { + /** + * 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 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 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 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 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 FORCE_NAMES_WITH_ID = "forceNamesWithID"; //$NON-NLS-1$ + + /** + * Name of the String property that configures on what types of {@link EStructuralFeature structural features} additional + * indexes are to be created. + */ + public static final String FORCE_INDEXES = "forceIndexes"; //$NON-NLS-1$ + + /** + * Name of the integer property that configures the size of the object type in-memory cache. Possible configuration + * values are: + *
    + *
  • 0 (zero). Don't use memory caching. + *
  • >0. Use memory caching with the cache size given. + *
+ * Default is a memory cache size of 10,000,000. + */ + public static final String OBJECT_TYPE_CACHE_SIZE = "objectTypeCacheSize"; //$NON-NLS-1$ + + /** + * Name of a String property that specifies the name of a {@link ColumnTypeModifier column type modifier}. + */ + public static final String COLUMN_TYPE_MODIFIER = "columnTypeModifier"; //$NON-NLS-1$ + + /** + * Name of a boolean property that configures whether all tables for a {@link EPackage package} are created eagerly. + * + * @since 4.6 + */ + public static final String EAGER_TABLE_CREATION = "eagerTableCreation"; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy2.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy2.java new file mode 100644 index 000000000..8697f6432 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy2.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * Interface to complement {@link IMappingStrategy}. + * + * @author Eike Stepper + * @since 4.5 + */ +public interface IMappingStrategy2 extends IMappingStrategy +{ + public boolean needsRevisionPostProcessing(); + + public void postProcessRevisions(IDBStoreAccessor accessor, CommitContext context, OMMonitor monitor); +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java new file mode 100644 index 000000000..a7f21053e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2009-2013 Eike Stepper (Loehne, 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.IDBPreparedStatement; +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); + + /** + * @deprecated As of 4.2 use {@link #createDBField(IDBTable, String)}. + */ + @Deprecated + 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 IDBPreparedStatement} using an appropriate setXxx method. + * + * @param stmt + * the prepared statement to set the value + * @param index + * the index to use for the setXxx method. + * @param value + * the value to set. + * @throws SQLException + * if the setXxx throws it. + */ + public void setValue(PreparedStatement stmt, int index, Object value) throws SQLException; + + /** + * Set the feature's default value to the JDBC {@link IDBPreparedStatement} using an appropriate setXxx + * method. + * + * @param stmt + * the prepared statement to set the value + * @param index + * the index to use for the setXxx method. + * @throws SQLException + * if the setXxx 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 IDBPreparedStatement} using an appropriate setXxx + * 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 setXxx method. + * @param value + * the revision to get the value to set from. + * @throws SQLException + * if the setXxx 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 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java new file mode 100644 index 000000000..1a763c6f8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java new file mode 100644 index 000000000..307f97848 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java new file mode 100644 index 000000000..354249ff5 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 Eike Stepper (Loehne, 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.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.internal.db.ddl.DBNamedElement; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; + +/** + * @author Eike Stepper + */ +public class CDODBSchema +{ + public static final IDBSchema INSTANCE = DBUtil.createSchema("CDO"); + + /** + * 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, true); //$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, true); //$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, true); //$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, true); //$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_RENAME_BRANCH = "UPDATE " + BRANCHES + " SET " + BRANCHES_NAME + "=?" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + " WHERE " + BRANCHES_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ + + 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_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, true); //$NON-NLS-1$ + + public static final IDBField LOBS_SIZE = // + LOBS.addField("lsize", 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 " + LOBS + " WHERE " + LOBS_ID //$NON-NLS-1$ //$NON-NLS-2$ + + "=?"; //$NON-NLS-1$ + + public static final String SQL_HANDLE_LOBS = "SELECT " + LOBS_ID + ", " + LOBS_SIZE + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + LOBS_BDATA + ", " + LOBS_CDATA + " FROM " + LOBS; //$NON-NLS-1$ //$NON-NLS-2$ + + public static final String SQL_LOAD_LOB = "SELECT " + LOBS_SIZE + ", " + LOBS_BDATA + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + LOBS_CDATA + " FROM " + LOBS + " WHERE " + LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + public static final String SQL_WRITE_BLOB = "INSERT INTO " + LOBS + "(" + LOBS_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + LOBS_SIZE + ", " + LOBS_BDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + + public static final String SQL_WRITE_CLOB = "INSERT INTO " + LOBS + "(" + LOBS_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + LOBS_SIZE + ", " + LOBS_CDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Name of object table + */ + public static final String CDO_OBJECTS = DBNamedElement.name("cdo_objects"); //$NON-NLS-1$ + + static + { + ((InternalDBSchema)INSTANCE).lock(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CommitInfoTable.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CommitInfoTable.java new file mode 100644 index 000000000..24ae2ac16 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CommitInfoTable.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.CDOCommonRepository.CommitInfoStorage; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +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.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +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.CDOCommitInfoUtil; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +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.IDBDatabase; +import org.eclipse.net4j.db.IDBDatabase.RunnableWithSchema; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @author Eike Stepper + * @since 4.6 + */ +public class CommitInfoTable extends Lifecycle +{ + private static final String COMMIT_INFOS = "cdo_commit_infos"; //$NON-NLS-1$ + + private static final String TIMESTAMP = "commit_time"; //$NON-NLS-1$ + + private static final String PREVIOUS_TIMESTAMP = "previous_time"; + + private static final String BRANCH = "branch_id"; //$NON-NLS-1$ + + private static final String USER = "user_id"; //$NON-NLS-1$ + + private static final String COMMENT = "commit_comment"; //$NON-NLS-1$ + + private static final String MERGED_BRANCH = "merged_branch"; //$NON-NLS-1$ + + private static final String MERGED_TIMESTAMP = "merged_time"; //$NON-NLS-1$ + + private DBStore store; + + private boolean withMergeSource; + + private IDBTable table; + + private String sqlInsert; + + public CommitInfoTable(DBStore store) + { + this.store = store; + } + + public void writeCommitInfo(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + CDOBranchPoint mergeSource, OMMonitor monitor) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.HIGH); + + try + { + stmt.setLong(1, timeStamp); + stmt.setLong(2, previousTimeStamp); + stmt.setInt(3, branch.getID()); + stmt.setString(4, userID); + stmt.setString(5, comment); + + if (withMergeSource) + { + if (mergeSource != null) + { + stmt.setInt(6, mergeSource.getBranch().getID()); + stmt.setLong(7, mergeSource.getTimeStamp()); + } + else + { + stmt.setNull(6, DBType.INTEGER.getCode()); + stmt.setNull(7, DBType.BIGINT.getCode()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + public void loadCommitInfos(IDBStoreAccessor accessor, CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + int count = CDOCommitInfoUtil.decodeCount(endTime); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(PREVIOUS_TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(USER); + builder.append(", "); //$NON-NLS-1$ + builder.append(COMMENT); + + if (branch == null) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(BRANCH); + } + + if (withMergeSource) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(MERGED_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(MERGED_TIMESTAMP); + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(COMMIT_INFOS); + boolean where = false; + + if (branch != null) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(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(TIMESTAMP); + builder.append(count < 0 ? "<=" : ">="); //$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(TIMESTAMP); + builder.append("<="); //$NON-NLS-1$ + builder.append(endTime); + where = true; + } + + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(TIMESTAMP); + builder.append(count < 0 || CDOBranchPoint.UNSPECIFIED_DATE <= endTime && endTime <= startTime ? " DESC" : " ASC"); //$NON-NLS-1$ + String sql = builder.toString(); + + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + InternalRepository repository = store.getRepository(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + count = Math.abs(count); + + try + { + resultSet = stmt.executeQuery(); + while (count-- > 0 && resultSet.next()) + { + int column = 0; + + long timeStamp = resultSet.getLong(++column); + long previousTimeStamp = resultSet.getLong(++column); + String userID = resultSet.getString(++column); + String comment = resultSet.getString(++column); + + CDOBranch infoBranch = branch; + if (infoBranch == null) + { + int id = resultSet.getInt(++column); + infoBranch = branchManager.getBranch(id); + } + + CDOBranchPoint mergeSource = null; + if (withMergeSource) + { + int id = resultSet.getInt(++column); + if (!resultSet.wasNull()) + { + InternalCDOBranch mergedBranch = branchManager.getBranch(id); + + long mergedTimeStamp = resultSet.getLong(++column); + mergeSource = mergedBranch.getPoint(mergedTimeStamp); + } + } + + CDOCommitInfo commitInfo = commitInfoManager.createCommitInfo(infoBranch, timeStamp, previousTimeStamp, userID, comment, mergeSource, null); + handler.handleCommitInfo(commitInfo); + } + } + 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 + { + out.writeBoolean(withMergeSource); + + String where = " WHERE " + TIMESTAMP + " 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 + { + boolean actualWithMergeSource = in.readBoolean(); + if (actualWithMergeSource != withMergeSource) + { + throw new IllegalStateException("Commit info data mismatch. Expected: " + (withMergeSource ? "with" : "without") + " merge source. Actual: " + + (actualWithMergeSource ? "with" : "without") + " merge source."); + } + + DBUtil.deserializeTable(in, connection, table, monitor.fork()); + } + + public void repairAfterCrash(Connection connection) + { + IDBField timeStampField = table.getField(TIMESTAMP); + + long lastCommitTime = DBUtil.selectMaximumLong(connection, timeStampField); + long lastNonLocalCommitTime = DBUtil.selectMaximumLong(connection, timeStampField, CDOBranch.MAIN_BRANCH_ID + "<=" + BRANCH); + + if (lastNonLocalCommitTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + lastNonLocalCommitTime = lastCommitTime; + } + + store.setLastCommitTime(lastCommitTime); + store.setLastNonLocalCommitTime(lastNonLocalCommitTime); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + InternalRepository repository = store.getRepository(); + withMergeSource = repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE; + + IDBDatabase database = store.getDatabase(); + table = database.getSchema().getTable(COMMIT_INFOS); + if (table == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + table = schema.addTable(COMMIT_INFOS); + table.addField(TIMESTAMP, DBType.BIGINT, true); + table.addField(PREVIOUS_TIMESTAMP, DBType.BIGINT); + table.addField(BRANCH, DBType.INTEGER); + table.addField(USER, DBType.VARCHAR); + table.addField(COMMENT, DBType.VARCHAR); + table.addIndex(IDBIndex.Type.PRIMARY_KEY, TIMESTAMP); + table.addIndex(IDBIndex.Type.NON_UNIQUE, BRANCH); + + if (withMergeSource) + { + table.addField(MERGED_BRANCH, DBType.INTEGER); + table.addField(MERGED_TIMESTAMP, DBType.BIGINT); + table.addIndex(IDBIndex.Type.NON_UNIQUE, MERGED_BRANCH, MERGED_TIMESTAMP); + } + } + }); + } + else + { + if (withMergeSource && table.getField(MERGED_BRANCH) == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + IDBTable table = schema.getTable(COMMIT_INFOS); + table.addField(MERGED_BRANCH, DBType.INTEGER); + table.addField(MERGED_TIMESTAMP, DBType.BIGINT); + table.addIndex(IDBIndex.Type.NON_UNIQUE, MERGED_BRANCH, MERGED_TIMESTAMP); + } + }); + } + } + + StringBuilder builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(COMMIT_INFOS); + builder.append("("); //$NON-NLS-1$ + builder.append(TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(PREVIOUS_TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(USER); + builder.append(", "); //$NON-NLS-1$ + builder.append(COMMENT); + + if (withMergeSource) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(MERGED_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(MERGED_TIMESTAMP); + } + + builder.append(") VALUES (?, ?, ?, ?, ?"); //$NON-NLS-1$ + if (withMergeSource) + { + builder.append(", ?, ?"); //$NON-NLS-1$ + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsert = builder.toString(); + } + + @Override + protected void doDeactivate() throws Exception + { + sqlInsert = null; + table = null; + super.doDeactivate(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java new file mode 100644 index 000000000..468e8522b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009-2012, 2016 Eike Stepper (Loehne, 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_PREFIX("tableNamePrefix"), // + TABLE_NAME("tableName"), // + COLUMN_NAME("columnName"), // + COLUMN_TYPE("columnType"), // + COLUMN_LENGTH("columnLength"), // + TYPE_MAPPING("typeMapping"), // + INVERSE_LIST("inverseList"); + + public static final String SOURCE_URI = "http://www.eclipse.org/CDO/DBStore"; + + public static final 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 null. + */ + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java new file mode 100644 index 000000000..fe6b898f7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Loehne, 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", "DB 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("\r\n"); + out.print("\r\n"); + + out.print("\r\n"); + + if (table != null) + { + out.print("\r\n"); + } + + out.print("\r\n"); + out.print("
\r\n"); + String table = showTables(browser, out, connection, repository.getName()); + out.print("\r\n"); + showTable(browser, out, connection, table); + out.print("
\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"); + boolean used = browser.isParam("used"); + boolean schema = browser.isParam("schema"); + + pout.print("\r\n"); + pout.print( + "\r\n"); + pout.print("\r\n"); + pout.print("
Empty tables:" + browser.href(used ? "Hidden" : "Shown", getName(), "used", String.valueOf(!used)) + "
Row data:" + browser.href(schema ? "Hidden" : "Shown", getName(), "schema", String.valueOf(!schema)) + + "

\r\n"); + + int totalRows = 0; + int usedTables = 0; + + List allTableNames = DBUtil.getAllTableNames(connection, repo); + for (String tableName : allTableNames) + { + if (table == null) + { + table = tableName; + } + + String label = browser.escape(tableName)/* .toLowerCase() */; + + int rowCount = DBUtil.getRowCount(connection, tableName); + if (rowCount > 0) + { + // label += " (" + rowCount + ")"; + totalRows += rowCount; + ++usedTables; + } + else if (used) + { + continue; + } + + if (tableName.equals(table)) + { + pout.print("" + label + ""); + } + else + { + pout.print(browser.href(label, getName(), "table", tableName, "order", null, "direction", null)); + } + + if (rowCount > 0) + { + pout.print(" (" + rowCount + ")"); + } + + pout.print("
\r\n"); + } + + if (totalRows != 0) + { + int totalTables = allTableNames.size(); + int emptyTables = totalTables - usedTables; + + pout.print("
" + totalTables + " tables total\r\n"); + pout.print("
" + usedTables + " tables used (" + totalRows + " rows)\r\n"); + pout.print("
" + emptyTables + " tables empty
\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, table, + "SELECT * FROM " + table + (order == null ? "" : " ORDER BY " + order + " " + browser.getParam("direction"))); + } + catch (Exception ex) + { + browser.removeParam("order"); + browser.removeParam("direction"); + executeQuery(browser, pout, connection, table, "SELECT * FROM " + table); + } + } + + protected void executeQuery(CDOServerBrowser browser, PrintStream pout, Connection connection, String table, String sql) + { + String order = browser.getParam("order"); + String direction = browser.getParam("direction"); + String highlight = browser.getParam("highlight"); + boolean schema = browser.isParam("schema"); + + Statement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = connection.createStatement(); + resultSet = stmt.executeQuery(sql); + + ResultSetMetaData metaData = resultSet.getMetaData(); + int columns = metaData.getColumnCount(); + + pout.print("\r\n"); + pout.print("\r\n"); + pout.print("\r\n"); + pout.print("\r\n"); + for (int i = 0; i < columns; i++) + { + String column = metaData.getColumnLabel(1 + i); + String type = metaData.getColumnTypeName(1 + i).toLowerCase() + "(" + metaData.getPrecision(1 + i) + ")"; + + String dir = column.equals(order) && "ASC".equals(direction) ? "DESC" : "ASC"; + pout.print("\r\n"); + } + + pout.print("\r\n"); + + if (!schema) + { + int row = 0; + while (resultSet.next()) + { + ++row; + pout.print("\r\n"); + pout.print("\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("" + browser.href(value, getName(), "highlight", value) + "\r\n"); + } + + pout.print("\r\n"); + } + } + + pout.print("
" + table + "
 " + browser.href(column, getName(), "order", column, "direction", dir)); + pout.print("
" + type + "
" + row + "
\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 = "db"; + + public Factory() + { + super(PRODUCT_GROUP, TYPE); + } + + public DBBrowserPage create(String description) throws ProductCreationException + { + return new DBBrowserPage(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBIndexAnnotation.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBIndexAnnotation.java new file mode 100644 index 000000000..2d120155a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBIndexAnnotation.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.model.EMFUtil; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; + +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * @author Kai Schlamp + */ +public final class DBIndexAnnotation +{ + public static final String SOURCE_URI = "http://www.eclipse.org/CDO/DBIndex"; + + public static final String FEATURES = "features"; + + private DBIndexAnnotation() + { + } + + public static Set> getIndices(EClass eClass, EStructuralFeature[] allPersistentFeatures) + { + Set> indices = new HashSet>(); + + for (EAnnotation annotation : EMFUtil.getAnnotations(eClass, SOURCE_URI)) + { + List features = new ArrayList(); + + String featureNames = annotation.getDetails().get(FEATURES); + if (featureNames != null && featureNames.length() != 0) + { + StringTokenizer tokenizer = new StringTokenizer(featureNames, ","); + while (tokenizer.hasMoreTokens()) + { + String featureName = tokenizer.nextToken().trim(); + if (featureName.length() != 0) + { + EStructuralFeature feature = getPersistentFeature(featureName, allPersistentFeatures); + if (feature == null) + { + OM.LOG.warn("Feature '" + featureName + "' not found in class '" + eClass.getName() + "' in package '" + eClass.getEPackage().getNsURI() + "'"); + continue; + } + + features.add(feature); + } + } + } + else + { + for (EObject reference : annotation.getReferences()) + { + if (reference instanceof EStructuralFeature) + { + EStructuralFeature feature = (EStructuralFeature)reference; + if (!isPersistentFeature(feature, allPersistentFeatures)) + { + OM.LOG.warn("Feature '" + feature.getName() + "' is not a persistent feature of class '" + eClass.getName() + "' in package '" + + eClass.getEPackage().getNsURI() + "'"); + continue; + } + + features.add(feature); + } + else + { + OM.LOG.warn("Reference '" + reference + "' is not a feature"); + } + } + } + + int size = features.size(); + if (size > 0) + { + if (size > 1) + { + for (EStructuralFeature feature : features) + { + if (feature.isMany()) + { + OM.LOG.warn("Many-valued feature '" + feature.getName() + "' not allowed in composed index on class '" + eClass.getName() + "' in package '" + + eClass.getEPackage().getNsURI() + "'"); + continue; + } + } + } + + indices.add(features); + } + } + + for (EStructuralFeature feature : allPersistentFeatures) + { + if (feature.getEAnnotation(SOURCE_URI) != null) + { + indices.add(Collections.singletonList(feature)); + } + } + + return indices; + } + + private static EStructuralFeature getPersistentFeature(String featureName, EStructuralFeature[] allPersistentFeatures) + { + for (int i = 0; i < allPersistentFeatures.length; i++) + { + EStructuralFeature feature = allPersistentFeatures[i]; + if (feature.getName().equals(featureName)) + { + return feature; + } + } + + return null; + } + + private static boolean isPersistentFeature(EStructuralFeature feature, EStructuralFeature[] allPersistentFeatures) + { + for (int i = 0; i < allPersistentFeatures.length; i++) + { + if (allPersistentFeatures[i] == feature) + { + return true; + } + } + + return false; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java new file mode 100644 index 000000000..ca9593398 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2012, 2016 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java new file mode 100644 index 000000000..1c0e69143 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java @@ -0,0 +1,1074 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 Eike Stepper (Loehne, 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.CommitInfoStorage; +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.branch.CDOBranchVersion; +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.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.mapping.horizontal.IMappingConstants; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.UnitMappingTable; +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.InternalSession; +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.IDBConnection; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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, IMappingConstants, CDOAllRevisionsProvider +{ + public static final String TYPE = "db"; //$NON-NLS-1$ + + public static final int SCHEMA_VERSION = 4; + + // public static final int SCHEMA_VERSION = 3; // Bug 404047: Indexed columns must be NOT NULL. + // public static final int SCHEMA_VERSION = 2; // Bug 344232: Rename cdo_lobs.size to cdo_lobs.lsize. + // public static final int SCHEMA_VERSION = 1; // Bug 351068: Delete detached objects from non-auditing stores. + + private static final int FIRST_START = -1; + + private static final String PROP_SCHEMA_VERSION = "org.eclipse.emf.cdo.server.db.schemaVersion"; //$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 properties; + + private int idColumnLength = IDBField.DEFAULT; + + private int jdbcFetchSize = 100000; + + private IIDHandler idHandler; + + private IMetaDataManager metaDataManager = new MetaDataManager(this); + + private DurableLockingManager durableLockingManager = new DurableLockingManager(this); + + private CommitInfoTable commitInfoTable; + + private UnitMappingTable unitMappingTable; + + private IMappingStrategy mappingStrategy; + + private IDBDatabase database; + + 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 properties) + { + checkInactive(); + this.properties = properties; + } + + public Map getProperties() + { + return properties; + } + + public int getJDBCFetchSize() + { + return jdbcFetchSize; + } + + public int getIDColumnLength() + { + return idColumnLength; + } + + public IIDHandler getIDHandler() + { + return idHandler; + } + + public IDBDatabase getDatabase() + { + return database; + } + + 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 IMetaDataManager getMetaDataManager() + { + return metaDataManager; + } + + public DurableLockingManager getDurableLockingManager() + { + return durableLockingManager; + } + + public CommitInfoTable getCommitInfoTable() + { + return commitInfoTable; + } + + public UnitMappingTable getUnitMappingTable() + { + return unitMappingTable; + } + + public Timer getConnectionKeepAliveTimer() + { + return connectionKeepAliveTimer; + } + + @Override + public Set getSupportedChangeFormats() + { + if (mappingStrategy.hasDeltaSupport()) + { + return set(ChangeFormat.DELTA); + } + + return set(ChangeFormat.REVISION); + } + + public ProgressDistributor getAccessorWriteDistributor() + { + return accessorWriteDistributor; + } + + public IDBSchema getDBSchema() + { + return database.getSchema(); + } + + public void visitAllTables(Connection connection, IDBStore.TableVisitor visitor) + { + for (String name : DBUtil.getAllTableNames(connection, getRepository().getName())) + { + try + { + visitor.visitTable(connection, name); + connection.commit(); + } + catch (SQLException ex) + { + try + { + connection.rollback(); + } + catch (SQLException ex1) + { + throw new DBException(ex1); + } + + if (!dbAdapter.isColumnNotFoundException(ex)) + { + throw new DBException(ex); + } + } + } + } + + public Map getPersistentProperties(Set names) + { + IDBConnection connection = database.getConnection(); + IDBPreparedStatement stmt = null; + String sql = null; + + try + { + Map result = new HashMap(); + boolean allProperties = names == null || names.isEmpty(); + if (allProperties) + { + sql = CDODBSchema.SQL_SELECT_ALL_PROPERTIES; + stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.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; + stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); + for (String name : names) + { + stmt.setString(1, name); + ResultSet resultSet = null; + + try + { + resultSet = stmt.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(stmt); + DBUtil.close(connection); + } + } + + public void setPersistentProperties(Map properties) + { + IDBConnection connection = database.getConnection(); + IDBPreparedStatement deleteStmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES, ReuseProbability.MEDIUM); + IDBPreparedStatement insertStmt = connection.prepareStatement(CDODBSchema.SQL_INSERT_PROPERTIES, ReuseProbability.MEDIUM); + String sql = null; + + try + { + for (Entry 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 putPersistentProperty(String key, String value) + { + Map map = new HashMap(); + map.put(key, value); + + setPersistentProperties(map); + } + + public void removePersistentProperties(Set names) + { + IDBConnection connection = database.getConnection(); + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES, ReuseProbability.MEDIUM); + + try + { + for (String name : names) + { + stmt.setString(1, name); + stmt.executeUpdate(); + } + + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex, CDODBSchema.SQL_DELETE_PROPERTIES); + } + finally + { + DBUtil.close(stmt); + 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> getAllRevisions() + { + final Map> result = new HashMap>(); + + InternalSession session = null; + if (!StoreThreadLocal.hasSession()) + { + session = getRepository().getSessionManager().openSession(null); + StoreThreadLocal.setSession(session); + } + + try + { + StoreThreadLocal.getAccessor().handleRevisions(null, null, CDOBranchPoint.UNSPECIFIED_DATE, true, + new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + CDOBranch branch = revision.getBranch(); + List list = result.get(branch); + if (list == null) + { + list = new ArrayList(); + result.put(branch, list); + } + + list.add(revision); + return true; + } + })); + } + finally + { + if (session != null) + { + StoreThreadLocal.release(); + session.close(); + } + } + + return result; + } + + public CDOID createObjectID(String val) + { + return idHandler.createCDOID(val); + } + + @Deprecated + public boolean isLocal(CDOID id) + { + throw new UnsupportedOperationException(); + } + + 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 map = new HashMap(); + 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(); + + InternalRepository repository = getRepository(); + IDGenerationLocation idGenerationLocation = repository.getIDGenerationLocation(); + if (idGenerationLocation == IDGenerationLocation.CLIENT) + { + idHandler = new UUIDHandler(this); + } + else + { + idHandler = new LongIDHandler(this); + } + + setObjectIDTypes(idHandler.getObjectIDTypes()); + connectionKeepAliveTimer = new Timer("Connection-Keep-Alive-" + this); //$NON-NLS-1$ + + if (properties != null) + { + if (idGenerationLocation == IDGenerationLocation.CLIENT) + { + String prop = properties.get(IDBStore.Props.ID_COLUMN_LENGTH); + if (prop != null) + { + idColumnLength = Integer.parseInt(prop); + } + } + + configureAccessorPool(readerPool, IDBStore.Props.READER_POOL_CAPACITY); + configureAccessorPool(writerPool, IDBStore.Props.WRITER_POOL_CAPACITY); + + String prop = properties.get(IDBStore.Props.DROP_ALL_DATA_ON_ACTIVATE); + if (prop != null) + { + setDropAllDataOnActivate(Boolean.parseBoolean(prop)); + } + + prop = properties.get(IDBStore.Props.JDBC_FETCH_SIZE); + if (prop != null) + { + jdbcFetchSize = Integer.parseInt(prop); + } + } + + Connection connection = getConnection(); + int schemaVersion; + + try + { + if (isDropAllDataOnActivate()) + { + OM.LOG.info("Dropping all tables from repository " + repository.getName() + "..."); + DBUtil.dropAllTables(connection, null); + connection.commit(); + } + + schemaVersion = selectSchemaVersion(connection); + if (0 <= schemaVersion && schemaVersion < SCHEMA_VERSION) + { + migrateSchema(schemaVersion); + } + + // CDODBSchema.INSTANCE.create(dbAdapter, connection); + connection.commit(); + } + finally + { + DBUtil.close(connection); + } + + String schemaName = repository.getName(); + boolean fixNullableIndexColumns = schemaVersion != FIRST_START && schemaVersion < FIRST_VERSION_WITH_NULLABLE_CHECKS; + + database = DBUtil.openDatabase(dbAdapter, dbConnectionProvider, schemaName, fixNullableIndexColumns); + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + + try + { + schemaTransaction.ensureSchema(CDODBSchema.INSTANCE); + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + + LifecycleUtil.activate(idHandler); + LifecycleUtil.activate(metaDataManager); + LifecycleUtil.activate(durableLockingManager); + LifecycleUtil.activate(mappingStrategy); + + if (repository.getCommitInfoStorage() != CommitInfoStorage.NO) + { + commitInfoTable = new CommitInfoTable(this); + commitInfoTable.activate(); + } + + if (repository.isSupportingUnits()) + { + unitMappingTable = new UnitMappingTable(mappingStrategy); + unitMappingTable.activate(); + } + + setRevisionTemporality(mappingStrategy.hasAuditSupport() ? RevisionTemporality.AUDITING : RevisionTemporality.NONE); + setRevisionParallelism(mappingStrategy.hasBranchingSupport() ? RevisionParallelism.BRANCHING : RevisionParallelism.NONE); + + if (schemaVersion == FIRST_START) + { + firstStart(); + } + else + { + reStart(); + } + + putPersistentProperty(PROP_SCHEMA_VERSION, Integer.toString(SCHEMA_VERSION)); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(unitMappingTable); + LifecycleUtil.deactivate(commitInfoTable); + LifecycleUtil.deactivate(mappingStrategy); + LifecycleUtil.deactivate(durableLockingManager); + LifecycleUtil.deactivate(metaDataManager); + LifecycleUtil.deactivate(idHandler); + + Map map = new HashMap(); + 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 createdTables) + { + if (createdTables.contains(CDODBSchema.PROPERTIES)) + { + return true; + } + + Set names = new HashSet(); + names.add(PROP_REPOSITORY_CREATED); + + Map map = getPersistentProperties(names); + return map.get(PROP_REPOSITORY_CREATED) == null; + } + + protected void firstStart() + { + InternalRepository repository = getRepository(); + setCreationTime(repository.getTimeStamp()); + firstTime = true; + } + + protected void reStart() throws Exception + { + Set names = new HashSet(); + names.add(PROP_REPOSITORY_CREATED); + names.add(PROP_GRACEFULLY_SHUT_DOWN); + + Map 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() + { + InternalRepository repository = getRepository(); + String name = repository.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 = repository.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); + + if (commitInfoTable != null) + { + commitInfoTable.repairAfterCrash(connection); + } + else + { + boolean branching = repository.isSupportingBranches(); + + long lastCommitTime = CDOBranchPoint.UNSPECIFIED_DATE; + long lastNonLocalCommitTime = CDOBranchPoint.UNSPECIFIED_DATE; + + // Unfortunately the package registry is still inactive, so the class mappings can not be used at this point. + // Use all tables with a "CDO_CREATED" field instead. + for (String tableName : DBUtil.getAllTableNames(connection, repository.getName())) + { + try + { + if (CDODBSchema.CDO_OBJECTS.equals(tableName)) + { + continue; + } + + IDBTable table = database.getSchema().getTable(tableName); + IDBField createdField = table.getField(IMappingConstants.ATTRIBUTES_CREATED); + if (createdField == null) + { + continue; + } + + if (branching) + { + IDBField branchField = table.getField(IMappingConstants.ATTRIBUTES_BRANCH); + if (branchField == null) + { + continue; + } + + lastNonLocalCommitTime = Math.max(lastNonLocalCommitTime, + DBUtil.selectMaximumLong(connection, branchField, CDOBranch.MAIN_BRANCH_ID + "<=" + IMappingConstants.ATTRIBUTES_BRANCH)); + } + + lastCommitTime = Math.max(lastCommitTime, DBUtil.selectMaximumLong(connection, createdField)); + } + catch (Exception ex) + { + OM.LOG.warn(ex.getMessage()); + } + } + + if (lastNonLocalCommitTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + lastNonLocalCommitTime = lastCommitTime; + } + + setLastCommitTime(lastCommitTime); + setLastNonLocalCommitTime(lastNonLocalCommitTime); + } + + if (storeIDs) + { + OM.LOG.info(MessageFormat.format(Messages.getString("DBStore.10"), name, lastObjectID, nextLocalObjectID, //$NON-NLS-1$ + getLastBranchID(), getLastCommitTime(), getLastNonLocalCommitTime())); + } + else + { + OM.LOG.info(MessageFormat.format(Messages.getString("DBStore.10b"), name, getLastBranchID(), //$NON-NLS-1$ + getLastCommitTime(), getLastNonLocalCommitTime())); + } + } + 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 void configureAccessorPool(StoreAccessorPool pool, String property) + { + if (pool != null) + { + String value = properties.get(property); + if (value != null) + { + int capacity = Integer.parseInt(value); + pool.setCapacity(capacity); + } + } + } + + protected int selectSchemaVersion(Connection connection) throws SQLException + { + Statement statement = null; + ResultSet resultSet = null; + + try + { + statement = connection.createStatement(); + resultSet = statement.executeQuery("SELECT " + CDODBSchema.PROPERTIES_VALUE + " FROM " + CDODBSchema.PROPERTIES + " WHERE " + CDODBSchema.PROPERTIES_NAME + + "='" + PROP_SCHEMA_VERSION + "'"); + + if (resultSet.next()) + { + String value = resultSet.getString(1); + return Integer.parseInt(value); + } + + return 0; + } + catch (SQLException ex) + { + connection.rollback(); + if (dbAdapter.isTableNotFoundException(ex)) + { + return FIRST_START; + } + + throw ex; + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(statement); + } + } + + protected void migrateSchema(int fromVersion) throws Exception + { + Connection connection = null; + + try + { + connection = getConnection(); + + for (int version = fromVersion; version < SCHEMA_VERSION; version++) + { + if (SCHEMA_MIGRATORS[version] != null) + { + int nextVersion = version + 1; + OM.LOG.info("Migrating schema from version " + version + " to version " + nextVersion + "..."); + SCHEMA_MIGRATORS[version].migrateSchema(this, connection); + } + } + + connection.commit(); + } + finally + { + DBUtil.close(connection); + } + } + + /** + * @author Eike Stepper + */ + private static abstract class SchemaMigrator + { + public abstract void migrateSchema(DBStore store, Connection connection) throws Exception; + } + + private static final int FIRST_VERSION_WITH_NULLABLE_CHECKS = 4; + + private static final SchemaMigrator NO_MIGRATION_NEEDED = null; + + private static final SchemaMigrator NON_AUDIT_MIGRATION = new SchemaMigrator() + { + @Override + public void migrateSchema(DBStore store, Connection connection) throws Exception + { + InternalRepository repository = store.getRepository(); + if (!repository.isSupportingAudits()) + { + store.visitAllTables(connection, new IDBStore.TableVisitor() + { + public void visitTable(Connection connection, String name) throws SQLException + { + Statement statement = null; + + try + { + statement = connection.createStatement(); + + String from = " FROM " + name + " WHERE " + ATTRIBUTES_VERSION + "<" + CDOBranchVersion.FIRST_VERSION; + + statement.executeUpdate("DELETE FROM " + CDODBSchema.CDO_OBJECTS + " WHERE " + ATTRIBUTES_ID + " IN (SELECT " + ATTRIBUTES_ID + from + ")"); + + statement.executeUpdate("DELETE" + from); + } + finally + { + DBUtil.close(statement); + } + } + }); + } + } + }; + + private static final SchemaMigrator LOB_SIZE_MIGRATION = new SchemaMigrator() + { + @Override + public void migrateSchema(DBStore store, final Connection connection) throws Exception + { + Statement statement = null; + + try + { + statement = connection.createStatement(); + + IDBAdapter dbAdapter = store.getDBAdapter(); + String sql = dbAdapter.sqlRenameField(CDODBSchema.LOBS_SIZE, "size"); + statement.execute(sql); + } + finally + { + DBUtil.close(statement); + } + } + }; + + private static final SchemaMigrator NULLABLE_COLUMNS_MIGRATION = null; + + private static final SchemaMigrator[] SCHEMA_MIGRATORS = { NO_MIGRATION_NEEDED, NON_AUDIT_MIGRATION, LOB_SIZE_MIGRATION, NULLABLE_COLUMNS_MIGRATION }; + + static + { + if (SCHEMA_MIGRATORS.length != SCHEMA_VERSION) + { + throw new Error("There must be exactly " + SCHEMA_VERSION + " schema migrators provided"); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java new file mode 100644 index 000000000..99bfa0f93 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java @@ -0,0 +1,1588 @@ +/* + * Copyright (c) 2007-2016, 2018 Eike Stepper (Loehne, 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.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.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.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.db.mapping.IMappingStrategy2; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.AbstractHorizontalClassMapping; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.UnitMappingTable; +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.BranchLoader3; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +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.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager.InternalObjectAttacher; +import org.eclipse.emf.cdo.spi.server.StoreAccessor; + +import org.eclipse.net4j.db.BatchedStatement; +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.internal.db.ddl.DBField; +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.concurrent.TrackableTimerTask; +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.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; + +/** + * @author Eike Stepper + */ +public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, BranchLoader3, DurableLocking2 +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, DBStoreAccessor.class); + + private IDBConnection connection; + + private ConnectionKeepAliveTask connectionKeepAliveTask; + + private CDOID maxID = CDOID.NULL; + + private InternalObjectAttacher objectAttacher; + + private List createdTables; + + public DBStoreAccessor(DBStore store, ISession session) throws DBException + { + super(store, session); + } + + public DBStoreAccessor(DBStore store, ITransaction transaction) throws DBException + { + super(store, transaction); + } + + @Override + public final DBStore getStore() + { + return (DBStore)super.getStore(); + } + + public final IDBConnection getDBConnection() + { + return connection; + } + + public final Connection getConnection() + { + return connection; + } + + @Deprecated + public org.eclipse.emf.cdo.server.db.IPreparedStatementCache getStatementCache() + { + return new org.eclipse.emf.cdo.server.db.IPreparedStatementCache() + { + public void setConnection(Connection connection) + { + // Do nothing + } + + public IDBPreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability) + { + org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability converted = // + org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability.values()[reuseProbability.ordinal()]; + + return connection.prepareStatement(sql, converted); + } + + public void releasePreparedStatement(PreparedStatement ps) + { + DBUtil.close(ps); + } + }; + } + + 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 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); + } + + public EClass getObjectType(CDOID id) + { + IRepository repository = getStore().getRepository(); + if (id.equals(repository.getRootResourceID())) + { + return EresourcePackage.Literals.CDO_RESOURCE; + } + + EClass result = repository.getRevisionManager().getObjectType(id); + if (result != null) + { + return result; + } + + CommitContext commitContext = StoreThreadLocal.getCommitContext(); + if (commitContext != null) + { + InternalCDORevision revision = commitContext.getNewRevisions().get(id); + if (revision != null) + { + return revision.getEClass(); + } + } + + 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); // This is part of the search criterion, being replaced later + + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + if (mapping.readRevision(this, revision, listChunk)) + { + int version = revision.getVersion(); + if (version < CDOBranchVersion.UNSPECIFIED_VERSION) + { + 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 ids) + { + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_QUERY_LOBS, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + for (Iterator 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 + { + DBUtil.close(stmt); + } + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_LOAD_LOB, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + stmt.setString(1, HexUtil.bytesToHex(id)); + resultSet = stmt.executeQuery(); + resultSet.next(); + + long size = resultSet.getLong(1); + InputStream inputStream = resultSet.getBinaryStream(2); + if (resultSet.wasNull()) + { + Reader reader = resultSet.getCharacterStream(3); + IOUtil.copyCharacter(reader, new OutputStreamWriter(out), size); + } + else + { + IOUtil.copyBinary(inputStream, out, size); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_HANDLE_LOBS, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + byte[] id = HexUtil.hexToBytes(resultSet.getString(1)); + long size = resultSet.getLong(2); + InputStream inputStream = resultSet.getBinaryStream(3); + if (resultSet.wasNull()) + { + Reader reader = resultSet.getCharacterStream(4); + Writer out = handler.handleClob(id, size); + if (out != null) + { + try + { + IOUtil.copyCharacter(reader, out, size); + } + finally + { + IOUtil.close(out); + } + } + } + else + { + OutputStream out = handler.handleBlob(id, size); + if (out != null) + { + try + { + IOUtil.copyBinary(inputStream, out, size); + } + finally + { + IOUtil.close(out); + } + } + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + @Override + protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor) + { + super.applyIDMappings(context, monitor); + + DBStore store = getStore(); + IIDHandler idHandler = store.getIDHandler(); + + // 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() && store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; + + // Remember CDOIDs of new objects. They are cleared after writeRevisions() + for (InternalCDORevision revision : context.getNewObjects()) + { + CDOID id = revision.getID(); + + if (adjustMaxID && idHandler.compare(id, maxID) > 0) + { + maxID = id; + } + } + } + + @Deprecated + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, null, monitor); + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor) + { + CommitInfoTable commitInfoTable = getStore().getCommitInfoTable(); + if (commitInfoTable != null) + { + commitInfoTable.writeCommitInfo(this, branch, timeStamp, previousTimeStamp, userID, comment, mergeSource, monitor); + } + } + + @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 writeNewObjectRevisions(InternalCommitContext context, InternalCDORevision[] newObjects, CDOBranch branch, OMMonitor monitor) + { + writeRevisions(context, true, newObjects, branch, monitor); + } + + @Override + protected void writeDirtyObjectRevisions(InternalCommitContext context, InternalCDORevision[] dirtyObjects, CDOBranch branch, OMMonitor monitor) + { + writeRevisions(context, false, dirtyObjects, branch, monitor); + } + + protected void writeRevisions(InternalCommitContext context, boolean attachNewObjects, InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + for (InternalCDORevision revision : revisions) + { + writeRevision(revision, attachNewObjects, true, monitor.fork()); + } + + if (attachNewObjects) + { + InternalRepository repository = getStore().getRepository(); + if (repository.isSupportingUnits()) + { + InternalUnitManager unitManager = repository.getUnitManager(); + objectAttacher = unitManager.attachObjects(context); + } + } + } + finally + { + monitor.done(); + } + } + + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + 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); + } + + @Override + protected boolean needsRevisionPostProcessing() + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + if (mappingStrategy instanceof IMappingStrategy2) + { + return ((IMappingStrategy2)mappingStrategy).needsRevisionPostProcessing(); + } + + return super.needsRevisionPostProcessing(); + } + + @Override + protected void postProcessRevisions(InternalCommitContext context, OMMonitor monitor) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + if (mappingStrategy instanceof IMappingStrategy2) + { + ((IMappingStrategy2)mappingStrategy).postProcessRevisions(this, context, 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() + 1 : 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(); + } + } + + @Override + protected CDOID getNextCDOID(CDORevision revision) + { + return getStore().getIDHandler().getNextCDOID(revision); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_WRITE_BLOB, ReuseProbability.MEDIUM); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_WRITE_CLOB, ReuseProbability.MEDIUM); + + try + { + 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 + { + DBUtil.close(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; + } + + if (objectAttacher != null) + { + objectAttacher.finishedCommit(true); + objectAttacher = 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) + { + if (objectAttacher != null) + { + objectAttacher.finishedCommit(false); + objectAttacher = null; + } + + IDBStore store = getStore(); + IMetaDataManager metaDataManager = store.getMetaDataManager(); + metaDataManager.clearMetaIDMappings(); + + if (TRACER.isEnabled()) + { + TRACER.format("--- DB ROLLBACK ---"); //$NON-NLS-1$ + } + + try + { + connection.rollback(); + + // Bug 298632: Must rollback DBSchema to its prior state and drop the tables + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + mappingStrategy.removeMapping(connection, commitContext.getNewPackageUnits()); + + if (createdTables != null) + { + IDBDatabase database = store.getDatabase(); + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + + try + { + for (IDBTable table : createdTables) + { + table.remove(); + } + + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + DBStore store = getStore(); + connection = store.getDatabase().getConnection(); + connectionKeepAliveTask = new ConnectionKeepAliveTask(this); + objectAttacher = null; + + long keepAlivePeriod = ConnectionKeepAliveTask.EXECUTION_PERIOD; + Map 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); + } + + @Override + protected void doDeactivate() throws Exception + { + connectionKeepAliveTask.cancel(); + connectionKeepAliveTask = null; + + DBUtil.close(connection); + connection = null; + + super.doDeactivate(); + } + + @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) + getConnection().rollback(); + + if (createdTables != null) + { + createdTables.clear(); + createdTables = null; + } + } + + @Override + protected void doUnpassivate() throws Exception + { + // do nothing + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + IMetaDataManager metaDataManager = getStore().getMetaDataManager(); + return metaDataManager.loadPackageUnit(getConnection(), packageUnit); + } + + public Collection readPackageUnits() + { + IMetaDataManager metaDataManager = getStore().getMetaDataManager(); + return metaDataManager.readPackageUnits(getConnection()); + } + + @Override + protected void doWrite(InternalCommitContext context, OMMonitor monitor) + { + boolean wasTrackConstruction = DBField.isTrackConstruction(); + + try + { + Map properties = getStore().getProperties(); + if (properties != null) + { + String prop = properties.get(IDBStore.Props.FIELD_CONSTRUCTION_TRACKING); + if (prop != null) + { + DBField.trackConstruction(Boolean.valueOf(prop)); + } + } + + super.doWrite(context, monitor); + } + finally + { + DBField.trackConstruction(wasTrackConstruction); + } + } + + 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 createBranch(int branchID, BranchInfo branchInfo) + { + checkBranchingSupport(); + if (branchID == NEW_BRANCH) + { + branchID = getStore().getNextBranchID(); + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = getStore().getNextLocalBranchID(); + } + + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_CREATE_BRANCH, ReuseProbability.LOW); + + try + { + 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 Pair.create(branchID, branchInfo.getBaseTimeStamp()); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + public BranchInfo loadBranch(int branchID) + { + checkBranchingSupport(); + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_LOAD_BRANCH, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public SubBranchInfo[] loadSubBranches(int baseID) + { + checkBranchingSupport(); + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_LOAD_SUB_BRANCHES, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + stmt.setInt(1, baseID); + + resultSet = stmt.executeQuery(); + List result = new ArrayList(); + 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); + DBUtil.close(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; + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_LOAD_BRANCHES, ReuseProbability.HIGH); + ResultSet resultSet = null; + + InternalRepository repository = getSession().getManager().getRepository(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + + try + { + 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); + DBUtil.close(stmt); + } + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + checkBranchingSupport(); + + IDBPreparedStatement stmt = connection.prepareStatement(CDODBSchema.SQL_RENAME_BRANCH, ReuseProbability.LOW); + + try + { + stmt.setString(1, newName); + stmt.setInt(2, branchID); + + DBUtil.update(stmt, true); + getConnection().commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + CommitInfoTable commitInfoTable = getStore().getCommitInfoTable(); + if (commitInfoTable != null) + { + commitInfoTable.loadCommitInfos(this, branch, startTime, endTime, handler); + } + } + + public Set 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(); + InternalRepository repository = store.getRepository(); + + if (repository.getIDGenerationLocation() == IDGenerationLocation.STORE) + { + out.writeCDOID(store.getIDHandler().getLastObjectID()); // See bug 325097 + } + + Connection connection = getConnection(); + + String where = " WHERE " + CDODBSchema.BRANCHES_ID + " BETWEEN " + fromBranchID + " AND " + toBranchID; + DBUtil.serializeTable(out, connection, CDODBSchema.BRANCHES, null, where); + + CommitInfoTable commitInfoTable = store.getCommitInfoTable(); + if (commitInfoTable != null) + { + out.writeBoolean(true); + commitInfoTable.rawExport(connection, out, fromCommitTime, toCommitTime); + } + else + { + out.writeBoolean(false); + } + + DurableLockingManager durableLockingManager = store.getDurableLockingManager(); + durableLockingManager.rawExport(connection, out, fromCommitTime, toCommitTime); + + 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(); + IIDHandler idHandler = store.getIDHandler(); + if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + idHandler.setLastObjectID(in.readCDOID()); // See bug 325097 + } + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + int size = mappingStrategy.getClassMappings().size(); + int commitWork = 5; + monitor.begin(commitWork + size + commitWork); + + Collection packageUnits = new HashSet(); + Connection connection = getConnection(); + + try + { + DBUtil.deserializeTable(in, connection, CDODBSchema.BRANCHES, monitor.fork()); + + CommitInfoTable commitInfoTable = store.getCommitInfoTable(); + if (in.readBoolean()) + { + if (commitInfoTable == null) + { + throw new IllegalStateException("Commit info table is missing"); + } + + commitInfoTable.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + } + else + { + if (commitInfoTable != null) + { + throw new IllegalStateException("Commit info data is expected but missing"); + } + } + + DurableLockingManager durableLockingManager = store.getDurableLockingManager(); + durableLockingManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + + idHandler.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + + // rawImportPackageUnits(in, fromCommitTime, toCommitTime, packageUnits, monitor.fork()); + + IDBSchemaTransaction schemaTransaction = store.getDatabase().openSchemaTransaction(); + + try + { + mappingStrategy.rawImport(this, in, fromCommitTime, toCommitTime, monitor.fork(size)); + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + + rawCommit(commitWork, monitor); + } + catch (RuntimeException ex) + { + rawRollback(packageUnits); + throw ex; + } + catch (IOException ex) + { + rawRollback(packageUnits); + throw ex; + } + finally + { + monitor.done(); + } + } + + private void rawRollback(Collection packageUnits) + { + try + { + Connection connection = getConnection(); + 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 packageUnits, + OMMonitor monitor) throws IOException + { + monitor.begin(2); + + try + { + DBStore store = getStore(); + IMetaDataManager metaDataManager = store.getMetaDataManager(); + Connection connection = getConnection(); + + Collection imported = // + metaDataManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + packageUnits.addAll(imported); + + if (!packageUnits.isEmpty()) + { + InternalRepository repository = store.getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + packageRegistry.putPackageUnit(packageUnit); + } + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + + // Use 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); + } + } + else + { + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + CDOID id = revision.getID(); + EClass eClass = revision.getEClass(); + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + CDOClassifierRef classifierRef = mappingStrategy.readObjectType(this, id); + + boolean isFirstRevision = classifierRef == null; + if (!isFirstRevision) + { + 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, null, monitor); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource, monitor); + } + + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + if (eClass == null) + { + eClass = getObjectType(id); + } + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + if (mapping instanceof AbstractHorizontalClassMapping) + { + AbstractHorizontalClassMapping m = (AbstractHorizontalClassMapping)mapping; + m.rawDelete(this, id, version, branch, monitor); + } + else + { + throw new UnsupportedOperationException("rawDelete() is not supported by " + mapping.getClass().getName()); + } + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + Connection connection = getConnection(); + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + async.stop(); + monitor.done(); + } + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 objectsToLock) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.lock(this, durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection objectsToUnlock) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID); + } + + public List readUnitRoots() + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + return unitMappingTable.readUnitRoots(this); + } + + public void readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.readUnitRevisions(this, view, rootID, revisionHandler, monitor); + } + + public Object initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, Set initializedIDs, long timeStamp, OMMonitor monitor) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + return unitMappingTable.initUnit(this, timeStamp, view, rootID, revisionHandler, initializedIDs, monitor); + } + + public void finishUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, long timeStamp, Object initResult, List ids) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.finishUnit((BatchedStatement)initResult, rootID, ids, timeStamp); + } + + public void writeUnits(Map unitMappings, long timeStamp) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.writeUnitMappings(this, unitMappings, timeStamp); + } + + public void tableCreated(IDBTable table) + { + if (createdTables == null) + { + createdTables = new ArrayList(); + } + + createdTables.add(table); + } + + /** + * @author Stefan Winkler + */ + private static final class ConnectionKeepAliveTask extends TrackableTimerTask + { + public static final long EXECUTION_PERIOD = 1000 * 60 * 60 * 4; // 4 hours + + private DBStoreAccessor accessor; + + public ConnectionKeepAliveTask(DBStoreAccessor accessor) + { + this.accessor = accessor; + } + + @Override + public void run() + { + if (accessor == null) + { + return; + } + + Statement stmt = null; + + try + { + if (TRACER.isEnabled()) + { + TRACER.trace("DB connection keep-alive task activated"); //$NON-NLS-1$ + } + + Connection connection = accessor.getConnection(); + 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(accessor); + LifecycleUtil.activate(accessor); + } + 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); + } + } + + @Override + public boolean cancel() + { + accessor = null; + return super.cancel(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java new file mode 100644 index 000000000..0d6a9b557 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2007-2013, 2016 Eike Stepper (Loehne, 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.IListMapping2; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.IMappingConstants; +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, IMappingConstants +{ + 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(); + + if (referenceMapping instanceof IListMapping2) + { + ((IListMapping2)referenceMapping).addSimpleChunkWhere(getAccessor(), getRevision().getID(), builder, index); + } + else + { + builder.append(LIST_IDX); + builder.append('='); + builder.append(index); + } + } + + @Override + public void addRangedChunk(int fromIndex, int toIndex) + { + super.addRangedChunk(fromIndex, toIndex); + prepareAddition(); + + if (referenceMapping instanceof IListMapping2) + { + ((IListMapping2)referenceMapping).addRangedChunkWhere(getAccessor(), getRevision().getID(), builder, fromIndex, toIndex); + } + else + { + builder.append(LIST_IDX); + builder.append(" BETWEEN "); //$NON-NLS-1$ + builder.append(fromIndex); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(toIndex - 1); + } + } + + public List executeRead() + { + List 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java new file mode 100644 index 000000000..c9d2e06be --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2007-2009, 2011-2013, 2015, 2016 Eike Stepper (Loehne, 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 repositoryProperties, Element storeConfig) + { + IMappingStrategy mappingStrategy = getMappingStrategy(repositoryName, repositoryProperties, storeConfig); + IDBAdapter dbAdapter = getDBAdapter(storeConfig); + DataSource dataSource = getDataSource(storeConfig); + IDBConnectionProvider connectionProvider = dbAdapter.createConnectionProvider(dataSource); + + DBStore store = new DBStore(); + store.setMappingStrategy(mappingStrategy); + store.setDBAdapter(dbAdapter); + store.setDBConnectionProvider(connectionProvider); + + Map storeProperties = RepositoryConfigurator.getProperties(storeConfig, 1); + store.setProperties(storeProperties); + + return store; + } + + private IMappingStrategy getMappingStrategy(String repositoryName, Map 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 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java new file mode 100644 index 000000000..b1b86f114 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2011-2013, 2015, 2016 Eike Stepper (Loehne, 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.CDOIDUtil; +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.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.db.IIDHandler; +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.IDBDatabase; +import org.eclipse.net4j.db.IDBDatabase.RunnableWithSchema; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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 org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author Eike Stepper + */ +public class DurableLockingManager extends Lifecycle +{ + private static final String LOCK_AREAS = "CDO_LOCK_AREAS"; + + private static final String LOCK_AREAS_ID = "ID"; + + private static final String LOCK_AREAS_USER_ID = "USER_ID"; + + private static final String LOCK_AREAS_VIEW_BRANCH = "VIEW_BRANCH"; + + private static final String LOCK_AREAS_VIEW_TIME = "VIEW_TIME"; + + private static final String LOCK_AREAS_READ_ONLY = "READ_ONLY"; + + private static final String LOCKS = "CDO_LOCKS"; + + private static final String LOCKS_AREA_ID = "AREA_ID"; + + private static final String LOCKS_OBJECT_ID = "OBJECT_ID"; + + private static final String LOCKS_LOCK_GRADE = "LOCK_GRADE"; + + private DBStore store; + + private InternalCDOBranchManager branchManager; + + private IIDHandler idHandler; + + private IDBTable lockAreasTable; + + private IDBTable locksTable; + + private String sqlInsertLockArea; + + private String sqlSelectLockArea; + + private String sqlSelectAllLockAreas; + + private String sqlSelectLockAreas; + + private String sqlDeleteLockArea; + + private String sqlDeleteLockAreas; + + 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 locks) + { + 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) + { + } + } + + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertLockArea, ReuseProbability.LOW); + + try + { + 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 + { + DBUtil.close(stmt); + } + + if (!locks.isEmpty()) + { + insertLocks(accessor, durableLockingID, locks); + } + + commit(accessor); + + return CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + } + + private void insertLocks(DBStoreAccessor accessor, String durableLockingID, Map locks) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertLock, ReuseProbability.MEDIUM); + + try + { + stmt.setString(1, durableLockingID); + + for (Entry 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 + { + DBUtil.close(stmt); + } + } + + public LockArea getLockArea(DBStoreAccessor accessor, String durableLockingID) throws LockAreaNotFoundException + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectLockArea, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void getLockAreas(DBStoreAccessor accessor, String userIDPrefix, Handler handler) + { + IDBPreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + if (userIDPrefix.length() == 0) + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectAllLockAreas, ReuseProbability.MEDIUM); + } + else + { + stmt = accessor.getDBConnection().prepareStatement(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); + DBUtil.close(stmt); + } + } + + public void deleteLockArea(DBStoreAccessor accessor, String durableLockingID) + { + unlockWithoutCommit(accessor, durableLockingID); + + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteLockArea, ReuseProbability.LOW); + + try + { + stmt.setString(1, durableLockingID); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + + commit(accessor); + } + + public void updateLockArea(DBStoreAccessor accessor, LockArea area) + { + String areaID = area.getDurableLockingID(); + unlockWithoutCommit(accessor, areaID); + insertLocks(accessor, areaID, area.getLocks()); + commit(accessor); + } + + public void lock(DBStoreAccessor accessor, String durableLockingID, LockType type, Collection objectsToLock) + { + changeLocks(accessor, durableLockingID, type, objectsToLock, true); + } + + public void unlock(DBStoreAccessor accessor, String durableLockingID, LockType type, Collection objectsToUnlock) + { + changeLocks(accessor, durableLockingID, type, objectsToUnlock, false); + } + + public void unlock(DBStoreAccessor accessor, String durableLockingID) + { + unlockWithoutCommit(accessor, durableLockingID); + commit(accessor); + } + + private void unlockWithoutCommit(DBStoreAccessor accessor, String durableLockingID) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteLocks, ReuseProbability.MEDIUM); + + try + { + stmt.setString(1, durableLockingID); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + branchManager = store.getRepository().getBranchManager(); + idHandler = store.getIDHandler(); + IDBDatabase database = store.getDatabase(); + + // Lock areas + lockAreasTable = database.getSchema().getTable(LOCK_AREAS); + if (lockAreasTable == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + lockAreasTable = schema.addTable(LOCK_AREAS); + lockAreasTable.addField(LOCK_AREAS_ID, DBType.VARCHAR, true); + lockAreasTable.addField(LOCK_AREAS_USER_ID, DBType.VARCHAR); + lockAreasTable.addField(LOCK_AREAS_VIEW_BRANCH, DBType.INTEGER); + lockAreasTable.addField(LOCK_AREAS_VIEW_TIME, DBType.BIGINT); + lockAreasTable.addField(LOCK_AREAS_READ_ONLY, DBType.BOOLEAN); + lockAreasTable.addIndex(IDBIndex.Type.PRIMARY_KEY, LOCK_AREAS_ID); + lockAreasTable.addIndex(IDBIndex.Type.NON_UNIQUE, LOCK_AREAS_USER_ID); + } + }); + } + + sqlInsertLockArea = "INSERT INTO " + LOCK_AREAS + "(" + LOCK_AREAS_ID + "," + LOCK_AREAS_USER_ID + "," + LOCK_AREAS_VIEW_BRANCH + "," + LOCK_AREAS_VIEW_TIME + + "," + LOCK_AREAS_READ_ONLY + ") VALUES (?, ?, ?, ?, ?)"; + sqlSelectLockArea = "SELECT " + LOCK_AREAS_USER_ID + "," + LOCK_AREAS_VIEW_BRANCH + "," + LOCK_AREAS_VIEW_TIME + "," + LOCK_AREAS_READ_ONLY + " FROM " + + LOCK_AREAS + " WHERE " + LOCK_AREAS_ID + "=?"; + sqlSelectAllLockAreas = "SELECT " + LOCK_AREAS_ID + "," + LOCK_AREAS_USER_ID + "," + LOCK_AREAS_VIEW_BRANCH + "," + LOCK_AREAS_VIEW_TIME + "," + + LOCK_AREAS_READ_ONLY + " FROM " + LOCK_AREAS; + sqlSelectLockAreas = sqlSelectAllLockAreas + " WHERE " + LOCK_AREAS_USER_ID + " LIKE ?"; + sqlDeleteLockArea = "DELETE FROM " + LOCK_AREAS + " WHERE " + LOCK_AREAS_ID + "=?"; + + /** BEGIN SPEMATE PATCH */ + sqlDeleteLockAreas = "DELETE FROM " + LOCK_AREAS;// + " WHERE EXISTS (SELECT * FROM " + LOCKS + " WHERE " + LOCKS + "." + LOCKS_AREA_ID + "=" + LOCK_AREAS + //+ "." + LOCK_AREAS_ID + ")"; + /** END SPECMATE PATCH */ + + // Locks + locksTable = database.getSchema().getTable(LOCKS); + if (locksTable == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + locksTable = schema.addTable(LOCKS); + locksTable.addField(LOCKS_AREA_ID, DBType.VARCHAR, true); + locksTable.addField(LOCKS_OBJECT_ID, idHandler.getDBType(), store.getIDColumnLength(), true); + locksTable.addField(LOCKS_LOCK_GRADE, DBType.INTEGER); + locksTable.addIndex(IDBIndex.Type.PRIMARY_KEY, LOCKS_AREA_ID, LOCKS_OBJECT_ID); + locksTable.addIndex(IDBIndex.Type.NON_UNIQUE, LOCKS_AREA_ID); + } + }); + } + + sqlSelectLocks = "SELECT " + LOCKS_OBJECT_ID + "," + LOCKS_LOCK_GRADE + " FROM " + LOCKS + " WHERE " + LOCKS_AREA_ID + "=?"; + sqlSelectLock = "SELECT " + LOCKS_LOCK_GRADE + " FROM " + LOCKS + " WHERE " + LOCKS_AREA_ID + "=? AND " + LOCKS_OBJECT_ID + "=?"; + sqlInsertLock = "INSERT INTO " + LOCKS + "(" + LOCKS_AREA_ID + "," + LOCKS_OBJECT_ID + "," + LOCKS_LOCK_GRADE + ") VALUES (?, ?, ?)"; + sqlUpdateLock = "UPDATE " + LOCKS + " SET " + LOCKS_LOCK_GRADE + "=? " + " WHERE " + LOCKS_AREA_ID + "=? AND " + LOCKS_OBJECT_ID + "=?"; + sqlDeleteLock = "DELETE FROM " + LOCKS + " WHERE " + LOCKS_AREA_ID + "=? AND " + LOCKS_OBJECT_ID + "=?"; + sqlDeleteLocks = "DELETE FROM " + LOCKS + " WHERE " + LOCKS_AREA_ID + "=?"; + } + + 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 lockMap = getLockMap(accessor, durableLockingID); + return CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, lockMap); + } + + private Map getLockMap(DBStoreAccessor accessor, String durableLockingID) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectLocks, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + stmt.setString(1, durableLockingID); + resultSet = stmt.executeQuery(); + + Map lockMap = CDOIDUtil.createMap(); + 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); + DBUtil.close(stmt); + } + } + + private void changeLocks(DBStoreAccessor accessor, String durableLockingID, LockType type, Collection keys, boolean on) + { + if (keys.isEmpty()) + { + return; + } + + String sql = on ? sqlInsertLock : sqlDeleteLock; + + IDBPreparedStatement stmtSelect = accessor.getDBConnection().prepareStatement(sqlSelectLock, ReuseProbability.MEDIUM); + IDBPreparedStatement stmtInsertOrDelete = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + IDBPreparedStatement stmtUpdate = accessor.getDBConnection().prepareStatement(sqlUpdateLock, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + stmtSelect.setString(1, durableLockingID); + stmtInsertOrDelete.setString(1, durableLockingID); + stmtUpdate.setString(2, durableLockingID); + + InternalLockManager lockManager = accessor.getStore().getRepository().getLockingManager(); + 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.getDBConnection().commit(); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmtUpdate); + DBUtil.close(stmtInsertOrDelete); + DBUtil.close(stmtSelect); + } + } + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) throws IOException + { + DBUtil.serializeTable(out, connection, lockAreasTable, null, null); + DBUtil.serializeTable(out, connection, locksTable, null, null); + } + + public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime, OMMonitor monitor) throws IOException + { + monitor.begin(4); + + try + { + // Delete all non-empty lock areas + DBUtil.update(connection, sqlDeleteLockAreas); + monitor.worked(); + + DBUtil.deserializeTable(in, connection, lockAreasTable, monitor.fork()); + + DBUtil.clearTable(connection, locksTable); + monitor.worked(); + + DBUtil.deserializeTable(in, connection, locksTable, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + private static void commit(DBStoreAccessor accessor) + { + try + { + accessor.getDBConnection().commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java new file mode 100644 index 000000000..9478898cb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2009-2013, 2016 Eike Stepper (Loehne, 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.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.IDBDatabase; +import org.eclipse.net4j.db.IDBDatabase.RunnableWithSchema; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.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.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author Stefan Winkler + */ +public class ExternalReferenceManager extends Lifecycle +{ + private static final String EXTERNAL_REFS = "cdo_external_refs"; + + private static final String EXTERNAL_REFS_ID = "ID"; + + private static final String EXTERNAL_REFS_URI = "URI"; + + private static final String EXTERNAL_REFS_COMMITTIME = "COMMITTIME"; + + private static final int NULL = 0; + + private IDBTable table; + + 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) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectByLongID, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public long lookupByURI(IDBStoreAccessor accessor, String uri) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectByURI, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) throws IOException + { + String where = " WHERE " + EXTERNAL_REFS_COMMITTIME + " 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(); + + final IDBStore store = idHandler.getStore(); + IDBDatabase database = store.getDatabase(); + + table = database.getSchema().getTable(EXTERNAL_REFS); + if (table == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + table = schema.addTable(EXTERNAL_REFS); + table.addField(EXTERNAL_REFS_ID, idHandler.getDBType(), store.getIDColumnLength(), true); + table.addField(EXTERNAL_REFS_URI, DBType.VARCHAR, 1024); + table.addField(EXTERNAL_REFS_COMMITTIME, DBType.BIGINT); + table.addIndex(IDBIndex.Type.PRIMARY_KEY, EXTERNAL_REFS_ID); + table.addIndex(IDBIndex.Type.NON_UNIQUE, EXTERNAL_REFS_URI); + } + }); + } + else + { + String sql = "SELECT MIN(" + EXTERNAL_REFS_ID + ") FROM " + table; + + IDBStoreAccessor writer = store.getWriter(null); + IDBPreparedStatement stmt = writer.getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + if (resultSet.next()) + { + lastMappedID.set(resultSet.getLong(1)); + } + + // else: resultSet is empty => table is empty + // and lastMappedId stays 0 - as initialized. + } + catch (SQLException ex) + { + writer.getDBConnection().rollback(); + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + writer.release(); + } + } + + sqlInsert = "INSERT INTO " + table + "(" + EXTERNAL_REFS_ID + "," + EXTERNAL_REFS_URI + "," + EXTERNAL_REFS_COMMITTIME + ") VALUES (?, ?, ?)"; + sqlSelectByURI = "SELECT " + EXTERNAL_REFS_ID + " FROM " + table + " WHERE " + EXTERNAL_REFS_URI + "=?"; + sqlSelectByLongID = "SELECT " + EXTERNAL_REFS_URI + " FROM " + table + " WHERE " + EXTERNAL_REFS_ID + "=?"; + } + + private long insertNew(IDBStoreAccessor accessor, String uri, long commitTime) + { + long newMappedID = lastMappedID.decrementAndGet(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.MEDIUM); + + try + { + 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 + { + DBUtil.close(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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java new file mode 100644 index 000000000..642ecdd06 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011-2013, 2016 Eike Stepper (Loehne, 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 boolean putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type); + + public boolean 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java new file mode 100644 index 000000000..68dc24d7b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2011, 2012, 2016 Eike Stepper (Loehne, 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 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; + } + + @Deprecated + public boolean isLocalCDOID(CDOID id) + { + throw new UnsupportedOperationException(); + } + + public DBType getDBType() + { + return DBType.BIGINT; + } + + public ITypeMapping getObjectTypeMapping() + { + return new CoreTypeMappings.TMObject(); + } + + public void appendCDOID(StringBuilder builder, CDOID id) + { + long value; + if (id != null && id.isExternal()) + { + IDBStoreAccessor accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + String uri = CDOIDUtil.getString(id); + value = externalReferenceManager.lookupByURI(accessor, uri); + } + else + { + value = value(id); + } + + builder.append(value); + } + + public void setCDOIDRaw(PreparedStatement stmt, int column, Object rawID) throws SQLException + { + stmt.setLong(column, (Long)rawID); + } + + 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 != null && 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 != null && 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java new file mode 100644 index 000000000..6dc45c79b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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.id.CDOIDUtil; +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.IDBConnection; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.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 modelElementToMetaID = new HashMap(); + + private Map metaIDToModelElement = CDOIDUtil.createMap(); + + 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 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((IDBConnection)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 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(IDBConnection 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 + " (" + CDODBSchema.PACKAGE_UNITS_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ + + CDODBSchema.PACKAGE_UNITS_ORIGINAL_TYPE + ", " + CDODBSchema.PACKAGE_UNITS_TIME_STAMP + ", " + CDODBSchema.PACKAGE_UNITS_PACKAGE_DATA + + ") VALUES (?, ?, ?, ?)"; + DBUtil.trace(sql); + + IDBPreparedStatement stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); + + try + { + async = monitor.forkAsync(); + 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(IDBConnection 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(IDBConnection 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 + " (" + CDODBSchema.PACKAGE_INFOS_URI + ", " //$NON-NLS-1$ //$NON-NLS-2$ + + CDODBSchema.PACKAGE_INFOS_PARENT + ", " + CDODBSchema.PACKAGE_INFOS_UNIT + ") VALUES (?, ?, ?)"; + DBUtil.trace(sql); + + IDBPreparedStatement stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); + Async async = monitor.forkAsync(); + + try + { + 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 readPackageUnits(Connection connection, long fromCommitTime, long toCommitTime, OMMonitor monitor) + { + final Map packageUnits = new HashMap(); + IDBRowHandler unitRowHandler = new IDBRowHandler() + { + public boolean handle(int row, final Object... values) + { + int index = DBUtil.asInt(values[1]); + long timestamp = DBUtil.asLong(values[2]); + + InternalCDOPackageUnit packageUnit = createPackageUnit(); + packageUnit.setOriginalType(CDOPackageUnit.Type.values()[index]); + packageUnit.setTimeStamp(timestamp); + + 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> packageInfos = new HashMap>(); + 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 list = packageInfos.get(unit); + if (list == null) + { + list = new ArrayList(); + 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 entry : packageUnits.entrySet()) + { + String id = entry.getKey(); + InternalCDOPackageUnit packageUnit = entry.getValue(); + + List 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java new file mode 100644 index 000000000..7a5260d27 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2007-2012 Eike Stepper (Loehne, 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 +{ + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java new file mode 100644 index 000000000..295d2188e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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 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 org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; + +import java.sql.Clob; +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 accessor; + + public SQLQueryHandler(DBStoreAccessor storeAccessor) + { + accessor = storeAccessor; + } + + public DBStoreAccessor getStoreAccessor() + { + return accessor; + } + + /** + * Executes SQL queries. Gets the connection from {@link DBStoreAccessor}, creates a SQL query and sets the parameters + * taken from the {@link CDOQueryInfo#getParameters()}. + *

+ * Takes into account the {@link CDOQueryInfo#getMaxResults()} and the {@link SQLQueryHandler#FIRST_RESULT} (numbered + * from 0) values for paging. + *

+ * 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. + *

+ * By default (parameter {@link SQLQueryHandler#QUERY_STATEMENT} == true) query statements are executed. Set this + * parameter to false for update/DDL statements. + *

+ * 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); + } + + HashMap> paramMap = new HashMap>(); + String query = parse(info.getQueryString(), paramMap); + + int firstResult = -1; + boolean queryStatement = true; + boolean objectQuery = true; + boolean mapQuery = false; + + IIDHandler idHandler = accessor.getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, + ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + 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); + // parameter = convertToSQL(parameter); + stmt.setObject(indexes[i], parameter); + } + } + } + + if (queryStatement) + { + resultSet = stmt.executeQuery(); + if (firstResult > -1) + { + resultSet.absolute(1 + 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 (firstResult > -1 || 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 = convertFromSQL(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] = convertFromSQL(resultSet.getObject(i + 1)); + } + + context.addResult(mapQuery ? toMap(columnNames, results) : results); + } + } + } + } + else + { + int result = stmt.executeUpdate(); + context.addResult(result); + } + } + catch (SQLException ex) + { + throw new DBException("Problem while executing SQL query: " + query, ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + @SuppressWarnings("unused") + private Object convertToSQL(Object value) + { + if (value instanceof java.util.Date) + { + java.util.Date date = (java.util.Date)value; + value = new java.sql.Date(date.getTime()); + } + + return value; + } + + private Object convertFromSQL(Object value) + { + // Conversion of java.sql.Date not needed in this direction + + 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 toMap(String[] columnNames, Object[] results) + { + Map ret = new HashMap(); + + 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> 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 indexList = paramMap.get(name); + if (indexList == null) + { + indexList = new ArrayList(); + paramMap.put(name, indexList); + } + + indexList.add(new Integer(index)); + index++; + } + } + + builder.append(c); + } + + return builder.toString(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java new file mode 100644 index 000000000..623cdd668 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2011, 2012, 2016 Eike Stepper (Loehne, 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 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 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); + } + + @Deprecated + public boolean isLocalCDOID(CDOID id) + { + throw new UnsupportedOperationException(); + } + + public ITypeMapping getObjectTypeMapping() + { + return new CoreTypeMappings.TMObject(); + } + + public void appendCDOID(StringBuilder builder, CDOID id) + { + builder.append("'"); + builder.append(value(id)); + builder.append("'"); + } + + public void setCDOIDRaw(PreparedStatement stmt, int column, Object rawID) throws SQLException + { + stmt.setString(column, (String)rawID); + } + + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java new file mode 100644 index 000000000..0dfcf1bde --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2011, 2012, 2016 Eike Stepper (Loehne, 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 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) + { + // UUIDs are not generated in strictly ordered form. + throw new UnsupportedOperationException(); + } + + public DBType getDBType() + { + return DBType.VARCHAR; + } + + public Set 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(); + } + + @Deprecated + public boolean isLocalCDOID(CDOID id) + { + throw new UnsupportedOperationException(); + } + + public ITypeMapping getObjectTypeMapping() + { + return new CoreTypeMappings.TMObject(); + } + + public void appendCDOID(StringBuilder builder, CDOID id) + { + builder.append("'"); + builder.append(value(id)); + builder.append("'"); + } + + public void setCDOIDRaw(PreparedStatement stmt, int column, Object rawID) throws SQLException + { + stmt.setString(column, (String)rawID); + } + + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java new file mode 100644 index 000000000..c8233b3a3 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2007, 2008, 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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 Operations & Maintenance 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 OMTracer DEBUG_UNITS = DEBUG.tracer("units"); //$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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java new file mode 100644 index 000000000..3b5a0fc2f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2009-2016, 2018 Eike Stepper (Loehne, 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.CDOFeatureType; +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.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.server.internal.db.bundle.OM; +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.lifecycle.LifecycleUtil; +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.EModelElement; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +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 properties; + + private ConcurrentMap classMappings; + + private boolean allClassMappingsCreated; + + private Set forceIndexes; + + public AbstractMappingStrategy() + { + classMappings = new ConcurrentHashMap(); + } + + // -- property related methods ----------------------------------------- + + public synchronized Map getProperties() + { + if (properties == null) + { + properties = new HashMap(); + } + + return properties; + } + + public synchronized void setProperties(Map properties) + { + this.properties = properties; + } + + public int getMaxTableNameLength() + { + String value = getProperties().get(Props.MAX_TABLE_NAME_LENGTH); + return value == null ? store.getDBAdapter().getMaxTableNameLength() : Integer.valueOf(value); + } + + public int getMaxFieldNameLength() + { + String value = getProperties().get(Props.MAX_FIELD_NAME_LENGTH); + return value == null ? store.getDBAdapter().getMaxFieldNameLength() : Integer.valueOf(value); + } + + public boolean isQualifiedNames() + { + String value = getProperties().get(Props.QUALIFIED_NAMES); + return value == null ? false : Boolean.valueOf(value); + } + + public boolean isForceNamesWithID() + { + String value = getProperties().get(Props.FORCE_NAMES_WITH_ID); + return value == null ? false : Boolean.valueOf(value); + } + + public String getTableNamePrefix() + { + String value = getProperties().get(Props.TABLE_NAME_PREFIX); + return StringUtil.safe(value); + } + + public Set getForceIndexes() + { + if (forceIndexes == null) + { + forceIndexes = doGetForceIndexes(this); + } + + return forceIndexes; + } + + // -- 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 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 readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments) + { + Set result = new HashSet(); + Collection classMappings = getClassMappings().values(); + + monitor.begin(classMappings.size()); + + try + { + for (IClassMapping mapping : classMappings) + { + Async async = monitor.forkAsync(); + + try + { + Set ids = mapping.readChangeSet(accessor, segments); + result.addAll(ids); + } + finally + { + async.stop(); + } + } + + return result; + } + finally + { + monitor.done(); + } + } + + public CloseableIterator readObjectIDs(IDBStoreAccessor accessor) + { + Collection classes = getClassesWithObjectInfo(); + final Iterator 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() + { + DBUtil.close(currentStatement); + currentStatement = null; + } + }; + } + + protected abstract Collection getClassesWithObjectInfo(); + + // -- database name demangling methods --------------------------------- + + private String getTableNamePrefix(EModelElement element) + { + String prefix = StringUtil.safe(DBAnnotation.TABLE_NAME_PREFIX.getValue(element)); + if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR)) + { + prefix += NAME_SEPARATOR; + } + + EObject eContainer = element.eContainer(); + if (eContainer instanceof EModelElement) + { + EModelElement parent = (EModelElement)eContainer; + prefix = getTableNamePrefix(parent) + prefix; + } + + return prefix; + } + + 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; + } + + prefix += getTableNamePrefix(element); + + String suffix = typePrefix + getUniqueID(element); + int maxTableNameLength = getMaxTableNameLength(); + + return getName(prefix + name, suffix, maxTableNameLength); + } + + 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; + } + + prefix += getTableNamePrefix(feature); + + /** BEGIN SPECMATE PATCH */ + //String suffix = TYPE_PREFIX_FEATURE + getUniqueID(feature); + String uniqueId = getUniqueID(feature); + + //FIXME: inserted by junkerm to make this work with Oracle + if(uniqueId.length()>5){ + uniqueId = Integer.toString(uniqueId.hashCode()).substring(1,5); + } + // END + String suffix = TYPE_PREFIX_FEATURE + uniqueId; + /** END SPECMATE PATCH*/ + int maxTableNameLength = getMaxTableNameLength(); + + return getName(prefix + name, suffix, maxTableNameLength); + } + + 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()); + if (length < 0) + { + // Most likely CDOIDs are client side-assigned, i.e., meta IDs are extrefs. See getUniqueID() + throw new IllegalStateException("Suffix is too long: " + suffix); + } + + 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(); + } + + IMetaDataManager metaDataManager = getMetaDataManager(); + CDOID result = metaDataManager.getMetaID(element, timeStamp); + + StringBuilder builder = new StringBuilder(); + CDOIDUtil.write(builder, result); + return builder.toString(); + } + + public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + monitor.begin(); + Async async = null; + + try + { + if (packageUnits != null && packageUnits.length != 0) + { + async = monitor.forkAsync(); + + mapPackageUnits(packageUnits, connection, false); + } + } + finally + { + if (async != null) + { + async.stop(); + } + + monitor.done(); + } + } + + public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits) + { + mapPackageUnits(packageUnits, connection, true); + } + + protected Set mapPackageUnits(InternalCDOPackageUnit[] packageUnits, Connection connection, boolean unmap) + { + Set classMappings = new HashSet(); + + if (packageUnits != null && packageUnits.length != 0) + { + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + InternalCDOPackageInfo[] packageInfos = packageUnit.getPackageInfos(); + mapPackageInfos(packageInfos, connection, unmap, classMappings); + } + } + + return classMappings; + } + + private void mapPackageInfos(InternalCDOPackageInfo[] packageInfos, Connection connection, boolean unmap, Set classMappings) + { + for (InternalCDOPackageInfo packageInfo : packageInfos) + { + EPackage ePackage = packageInfo.getEPackage(); + EClass[] persistentClasses = EMFUtil.getPersistentClasses(ePackage); + mapClasses(connection, unmap, persistentClasses, classMappings); + } + } + + private void mapClasses(Connection connection, boolean unmap, EClass[] eClasses, Set classMappings) + { + 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; + } + + IClassMapping classMapping = unmap ? removeClassMapping(eClass) : createClassMapping(eClass); + if (classMapping != null) + { + classMappings.add(classMapping); + } + } + } + } + + private IClassMapping createClassMapping(EClass eClass) + { + IClassMapping classMapping = doCreateClassMapping(eClass); + if (classMapping != null) + { + classMappings.put(eClass, classMapping); + } + + return classMapping; + } + + private IClassMapping removeClassMapping(EClass eClass) + { + IClassMapping classMapping = classMappings.get(eClass); + if (classMapping != null) + { + IDBSchema schema = getStore().getDBSchema(); + for (IDBTable table : classMapping.getDBTables()) + { + schema.removeTable(table.getName()); + } + + classMappings.remove(eClass); + } + + return classMapping; + } + + 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 getClassMappings() + { + return getClassMappings(true); + } + + public final Map getClassMappings(boolean createOnDemand) + { + return doGetClassMappings(createOnDemand); + } + + public final Map 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()) + { + 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$ + return doCreateListMapping(containingClass, feature); + } + + public final IListMapping createFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + checkArg(FeatureMapUtil.isFeatureMap(feature), "Only FeatureMaps allowed"); //$NON-NLS-1$ + return doCreateFeatureMapMapping(containingClass, feature); + } + + public abstract IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature); + + /** + * @deprecated As 4.5 feature maps are no longer supported. + */ + @Deprecated + public abstract IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature); + + @Override + protected void doDeactivate() throws Exception + { + deactivateClassMappings(); + super.doDeactivate(); + } + + protected void deactivateClassMappings() + { + for (IClassMapping classMapping : classMappings.values()) + { + Exception exception = LifecycleUtil.deactivate(classMapping); + if (exception != null) + { + OM.LOG.warn(exception); + } + } + } + + private static Set doGetForceIndexes(IMappingStrategy mappingStrategy) + { + return CDOFeatureType.readCombination(mappingStrategy.getProperties().get(Props.FORCE_INDEXES)); + } + + public static Set getForceIndexes(IMappingStrategy mappingStrategy) + { + if (mappingStrategy instanceof AbstractMappingStrategy) + { + return ((AbstractMappingStrategy)mappingStrategy).getForceIndexes(); + } + + return doGetForceIndexes(mappingStrategy); + } + + public static boolean isEagerTableCreation(IMappingStrategy mappingStrategy) + { + String value = mappingStrategy.getProperties().get(Props.EAGER_TABLE_CREATION); + return value == null ? false : Boolean.valueOf(value); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java new file mode 100644 index 000000000..ed0ccbf23 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Loehne, 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() + { + // return getFeature().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
+ */ + public static class TMObject extends AbstractTypeMapping + { + @Override + protected int getDBLength(DBType type) + { + return getMappingStrategy().getStore().getIDColumnLength(); + } + + @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; + } + + if (str.length() == 0) + { + // Bug 397318: Work around a bug in H2 that trims a space from CHAR(1) + return ' '; + } + + 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 Stefan Winkler + */ + public static class TMCharacter2Integer extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory( + TypeMappingUtil.createDescriptor(ID_PREFIX + ".Character2Integer", EcorePackage.eINSTANCE.getEChar(), DBType.INTEGER)); + + public static final Factory FACTORY_OBJECT = new Factory( + TypeMappingUtil.createDescriptor(ID_PREFIX + ".CharacterObject2Integer", EcorePackage.eINSTANCE.getECharacterObject(), DBType.INTEGER)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + int intRepresentation = resultSet.getInt(getField().getName()); + if (resultSet.wasNull()) + { + return getFeature().isUnsettable() ? CDORevisionData.NIL : null; + } + + return Character.valueOf((char)intRepresentation); + } + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + stmt.setInt(index, /* (int) */((Character)value).charValue()); + } + + /** + * @author Stefan Winkler + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMCharacter2Integer(); + } + } + } + + /** + * @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 TMBytesVarbinary extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory( + TypeMappingUtil.createDescriptor(ID_PREFIX + ".ByteArrayVarbinary", EcorePackage.eINSTANCE.getEByteArray(), DBType.VARBINARY)); + + @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 TMBytesVarbinary(); + } + } + } + + /** + * @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 Eike Stepper + */ + public static class TMJavaClass extends AbstractTypeMapping + { + public static final Factory FACTORY_VARCHAR = new Factory( + TypeMappingUtil.createDescriptor(ID_PREFIX + ".JavaClassVarchar", EcorePackage.eINSTANCE.getEJavaClass(), DBType.VARCHAR)); + + @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 TMJavaClass(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMJavaObject extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory( + TypeMappingUtil.createDescriptor(ID_PREFIX + ".JavaObjectBlob", EcorePackage.eINSTANCE.getEJavaObject(), 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 TMJavaObject(); + } + } + } + + /** + * @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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java new file mode 100644 index 000000000..82a7ed200 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java new file mode 100644 index 000000000..2920f0a11 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Loehne, 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.ColumnTypeModifier; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Provider; +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.FactoryNotFoundException; +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.factory.ProductCreationException; +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 classifierDefaultMapping; + + /** + * The main TypeMapping index. For any known pair of model and db types the {@link ITypeMapping.Descriptor} is + * registered here. + */ + private Map, ITypeMapping.Descriptor> typeMappingByTypes; + + /** + * ID-based index. Can be used to lookup an {@link ITypeMapping.Descriptor} for a given ID. + */ + private Map typeMappingsById; + + /** + * A set of all known mapped DBTypes. This is needed for the feature map mappings. + */ + private Set 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(); + + registerColumnTypeModifier("postgresql", new ColumnTypeModifier() + { + @Override + public DBType modify(Provider provider, IMappingStrategy mappingStrategy, EStructuralFeature feature, DBType dbType) + { + EClassifier classifier = feature.getEType(); + if (classifier == EcorePackage.eINSTANCE.getEChar()) + { + return DBType.INTEGER; + } + + if (classifier == EcorePackage.eINSTANCE.getECharacterObject()) + { + return DBType.INTEGER; + } + + return dbType; + } + }); + + defaultFeatureMapDBTypes = new HashSet(); + typeMappingsById = new HashMap(); + typeMappingByTypes = new HashMap, ITypeMapping.Descriptor>(); + classifierDefaultMapping = new HashMap(); + + registerCoreTypeMappings(); + populator.connect(); + } + + public void registerColumnTypeModifier(String factoryType, final ColumnTypeModifier columnTypeModifier) + { + getContainer().registerFactory(new ColumnTypeModifier.Factory(factoryType) + { + @Override + public ColumnTypeModifier create(String description) throws ProductCreationException + { + return columnTypeModifier; + } + }); + } + + /** + * 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.TMBytesVarbinary.FACTORY); + container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY); + container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMCharacter2Integer.FACTORY); + container.registerFactory(CoreTypeMappings.TMCharacter2Integer.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.TMJavaClass.FACTORY_VARCHAR); + container.registerFactory(CoreTypeMappings.TMJavaObject.FACTORY); + 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(EcorePackage.eINSTANCE.getEJavaClass(), DBType.VARCHAR); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEJavaObject(), DBType.BLOB); + + 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 sourceTargetPair = Pair.create(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 + { + DBType dbType = getDBType(mappingStrategy, feature); + + 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(IMappingStrategy mappingStrategy, EStructuralFeature feature) + { + DBType dbType; + + String typeKeyword = DBAnnotation.COLUMN_TYPE.getValue(feature); + if (typeKeyword != null) + { + dbType = DBType.getTypeByKeyword(typeKeyword); + if (dbType == null) + { + throw new IllegalArgumentException("Unsupported columnType (" + typeKeyword + ") annotation of feature " + feature.getName()); + } + } + else + { + // No annotation present - lookup default DB type. + IDBAdapter dbAdapter = mappingStrategy.getStore().getDBAdapter(); + dbType = getDefaultDBType(getEType(feature), dbAdapter); + } + + ColumnTypeModifier columnTypeModifier = getColumnTypeModifier(mappingStrategy); + if (columnTypeModifier != null) + { + dbType = columnTypeModifier.modify(this, mappingStrategy, feature, dbType); + } + + return dbType; + } + + private ColumnTypeModifier getColumnTypeModifier(IMappingStrategy mappingStrategy) + { + String factoryType = mappingStrategy.getProperties().get(IMappingStrategy.Props.COLUMN_TYPE_MODIFIER); + if (factoryType == null) + { + factoryType = mappingStrategy.getStore().getDBAdapter().getName(); + } + + ColumnTypeModifier columnTypeModifier = null; + + try + { + columnTypeModifier = (ColumnTypeModifier)IPluginContainer.INSTANCE.getElement(ColumnTypeModifier.Factory.PRODUCT_GROUP, factoryType, null); + } + catch (FactoryNotFoundException ex) + { + //$FALL-THROUGH$ + } + catch (ProductCreationException ex) + { + //$FALL-THROUGH$ + } + return columnTypeModifier; + } + + 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(Pair.create(feature.getEType(), dbType)); + + if (descriptor == null) + { + // Second try: lookup general mapping + descriptor = typeMappingByTypes.get(Pair.create(getEType(feature), dbType)); + if (descriptor == null) + { + // Lookup failed. Give up + return null; + } + } + + return descriptor; + } + + public Collection 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 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> ev = (ContainerEvent>)event; + + for (IContainerDelta> delta : ev.getDeltas()) + { + IFactoryKey key = delta.getElement().getKey(); + String productGroup = key.getProductGroup(); + if (productGroup.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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java new file mode 100644 index 000000000..6403400bb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java new file mode 100644 index 000000000..3ef1687ec --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java @@ -0,0 +1,1011 @@ +/* + * Copyright (c) 2013, 2016, 2018 Eike Stepper (Loehne, 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.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOFeatureType; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +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.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping3; +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.DBIndexAnnotation; +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.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.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; + +/** + * @author Stefan Winkler + */ +public abstract class AbstractBasicListTableMapping implements IListMapping3, IMappingConstants +{ + private IMappingStrategy mappingStrategy; + + private EClass containingClass; + + private EStructuralFeature feature; + + public AbstractBasicListTableMapping(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; + } + + public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index) + { + builder.append(LIST_IDX); + builder.append('='); + builder.append(index); + } + + public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex, int toIndex) + { + builder.append(LIST_IDX); + builder.append(" BETWEEN "); //$NON-NLS-1$ + builder.append(fromIndex); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(toIndex - 1); + } + + public void setClassMapping(IClassMapping classMapping) + { + // Subclasses may override. + } + + public abstract void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version); + + protected final boolean needsIndexOnValueField(EStructuralFeature feature) + { + IMappingStrategy mappingStrategy = getMappingStrategy(); + Set forceIndexes = AbstractMappingStrategy.getForceIndexes(mappingStrategy); + + if (CDOFeatureType.matchesCombination(feature, forceIndexes)) + { + return true; + } + + EClass eClass = getContainingClass(); + EStructuralFeature[] allPersistentFeatures = CDOModelUtil.getClassInfo(eClass).getAllPersistentFeatures(); + + for (List features : DBIndexAnnotation.getIndices(eClass, allPersistentFeatures)) + { + if (features.size() == 1) + { + if (features.get(0) == feature) + { + return true; + } + } + } + + return false; + } + + /** + * @author Eike Stepper + */ + public static abstract class AbstractListDeltaWriter implements CDOFeatureDeltaVisitor + { + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractListDeltaWriter.class); + + private static final int UNBOUNDED_SHIFT = -1; + + private static final int NO_INDEX = Integer.MIN_VALUE; + + private static final int NONE = 0; + + private static final int SET = 1 << 1; + + private static final int MOVE = 1 << 2; + + private static final int INSERT = 1 << 3; + + private static final int DELETE = 1 << 4; + + protected final IDBStoreAccessor accessor; + + protected final CDOID id; + + private final List listChanges; + + private final List manipulations; + + private boolean clearFirst; + + private int offsetBefore; + + /** + * Start of a range [tempIndex, tempIndex-1, ...] which lies outside of the normal list indexes and which serve as + * temporary space to move items temporarily to get them out of the way of other operations. + */ + private int tmpIndex = -1; + + private int newListSize; + + public AbstractListDeltaWriter(IDBStoreAccessor accessor, CDOID id, List listChanges, int oldListSize) + { + this.accessor = accessor; + this.id = id; + this.listChanges = listChanges; + + manipulations = createManipulations(id, listChanges, oldListSize); + newListSize = oldListSize; + } + + public void writeListDeltas() + { + if (TRACER.isEnabled()) + { + TRACER.trace("Processing list deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : listChanges) + { + listDelta.accept(this); + } + + // boolean zeroBasedIndex = + // ((HorizontalNonAuditMappingStrategy)accessor.getStore().getMappingStrategy()).shallForceZeroBasedIndex(); + // if (!zeroBasedIndex) + if (!isZeroBasedIndex()) + { + if (TRACER.isEnabled()) + { + TRACER.trace("Optimizing list indexes..."); //$NON-NLS-1$ + } + + optimizeListIndexes(); + } + + if (TRACER.isEnabled()) + { + TRACER.trace("Result to be written to DB:"); + for (Manipulation manipulation : manipulations) + { + TRACER.trace(manipulation.toString()); + } + } + + try + { + writeResultToDatabase(); + } + catch (SQLException e) + { + throw new DBException(e); + } + + throw new NewListSizeResult(newListSize); + } + + 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(Manipulation.createInsertedElement(delta.getIndex(), delta.getValue())); + ++newListSize; + } + + public void visit(CDORemoveFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - remove at {0}", delta.getIndex()); //$NON-NLS-1$ + } + + Manipulation e = findManipulation(delta.getIndex()); + deleteItem(e); + + // Fill the gap by shifting all subsequent items down + shiftIndexes(delta.getIndex() + 1, UNBOUNDED_SHIFT, -1); + --newListSize; + } + + public void visit(CDOSetFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - set at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$ + } + + Manipulation manipulation = findManipulation(delta.getIndex()); + + // Set the new value + manipulation.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 (!manipulation.is(INSERT)) + { + // Else mark the existing item to be set to a new value + manipulation.addType(SET); + } + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (!delta.getFeature().isUnsettable()) + { + throw new IllegalArgumentException("Feature is not unsettable: " + delta); + } + + if (TRACER.isEnabled()) + { + TRACER.format(" - unset list"); //$NON-NLS-1$ + } + + // Set the clear-flag + clearFirst = true; + + // And also clear all manipulation items + manipulations.clear(); + newListSize = 0; + } + + 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(); + newListSize = 0; + } + + 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; + } + + Manipulation manipulation = findManipulation(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 + manipulation.dstIndex = toIdx; + + // If it is a new element, no MOVE mark needed, because we insert it at the new position + if (!manipulation.is(INSERT)) + { + // Else we need to handle the move of an existing item + manipulation.addType(MOVE); + } + } + + @Deprecated + public void visit(CDOListFeatureDelta delta) + { + throw new UnsupportedOperationException("Should never be called"); + } + + @Deprecated + public void visit(CDOContainerFeatureDelta delta) + { + throw new UnsupportedOperationException("Should never be called"); + } + + protected boolean isZeroBasedIndex() + { + return false; + } + + protected List createManipulations(CDOID id, List listChanges, int oldListSize) + { + List manipulations = new ArrayList(oldListSize); + + // Create list and initialize with original indexes + for (int i = 0; i < oldListSize; i++) + { + manipulations.add(Manipulation.createOriginalElement(i)); + } + + return manipulations; + } + + /** + * 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 (Manipulation manipulation : manipulations) + { + if (manipulation.dstIndex >= from && (to == UNBOUNDED_SHIFT || manipulation.dstIndex <= to)) + { + manipulation.dstIndex += offset; + } + } + } + + /** + * Find a manipulation item by destination index). + */ + private Manipulation findManipulation(int index) + { + for (Manipulation manipulation : manipulations) + { + if (manipulation.dstIndex == index) + { + return manipulation; + } + } + + throw new IllegalStateException("Should never be reached"); + } + + /** + * Delete an element (used in remove and clear) + */ + private void deleteItem(Manipulation manipulation) + { + if (manipulation.is(INSERT)) + { + // Newly inserted items are simply removed, as removing inserted items is equal to no change at all. + manipulations.remove(manipulation); + } + else + { + // Mark the existing item as to be deleted. + // Previous MOVE and SET conditions are overridden by setting the exclusive DELETE type. + manipulation.types = DELETE; + manipulation.dstIndex = NO_INDEX; + } + } + + /** + * Called after all deltas are applied and before the results are written to the database. This method post-processes + * the manipulation elements in order to minimize database access. + */ + private void optimizeListIndexes() + { + /* + * This is an optimization which reduces the amount of modifications on the database to maintain list indexes. For the + * optimization, we let go of the assumption that indexes are zero-based. Instead, we work with an offset at the + * database level which can change with every change to the list (e.g. if the second element is removed from a list with + * 1000 elements, instead of shifting down indexes 2 to 1000 by 1, we shift up index 0 by 1 and have now a list with + * indexes starting at 1 instead of 0. This optimization is applied by modifying the list of Manipulations, which can be + * seen as the database modification plan. + */ + + // First, get the current offset. + offsetBefore = getCurrentIndexOffset(); + if (TRACER.isEnabled()) + { + TRACER.trace("Offset optimization."); //$NON-NLS-1$ + TRACER.trace("Current offset = " + offsetBefore); //$NON-NLS-1$ + } + + applyOffsetToSourceIndexes(offsetBefore); + + int offsetAfter; + + if ((long)Math.abs(offsetBefore) + (long)manipulations.size() > Integer.MAX_VALUE) + { + // Safety belt for really huge collections or for collections that have been manipulated lots of times + // -> Do not optimize after this border is crossed. Instead, reset offset for the whole list to a zero-based + // index. + offsetAfter = 0; + } + else + { + offsetAfter = calculateOptimalOffset(); + } + + if (TRACER.isEnabled()) + { + TRACER.trace("New offset = " + offsetAfter); //$NON-NLS-1$ + } + + applyOffsetToDestinationIndexes(offsetAfter); + + // Make sure temporary indexes do not get in the way of the other operations. + tmpIndex = Math.min(offsetBefore, offsetAfter) - 1; + } + + /** + * Calculate the optimal offset w.r.t. the manipulations planned. The optimal offset is the offset which occurs the + * most in the manipulations (because letting this offset be neutral leads to the least manipulations. Note: the + * zero offset is also regarded as an offset as any other, because selecting an offset != 0 would also lead to + * elements with original offset 0 to be moved. + */ + private int calculateOptimalOffset() + { + HashMap occurrences = new HashMap(); + int bestOffset = 0; + int bestOffsetOccurrence = 0; + + for (Manipulation manipulation : manipulations) + { + int srcIndex = manipulation.srcIndex; + int dstIndex = manipulation.dstIndex; + + if (srcIndex != NO_INDEX && dstIndex != NO_INDEX) + { + int offset = dstIndex - srcIndex; + Integer oldOccurrence = occurrences.get(offset); + + int newOccurrence; + if (oldOccurrence == null) + { + newOccurrence = 1; + } + else + { + newOccurrence = oldOccurrence + 1; + } + + occurrences.put(offset, newOccurrence); + + // Remember maximum along the way + if (newOccurrence > bestOffsetOccurrence) + { + bestOffsetOccurrence = newOccurrence; + bestOffset = offset; + } + } + } + + // The offset which has occurred the most has to be applied negatively to normalize the list + // therefore return the negative offset as the new offset to be applied + return -bestOffset; + } + + private void applyOffsetToSourceIndexes(int offsetBefore) + { + if (offsetBefore != 0) + { + for (Manipulation manipulation : manipulations) + { + if (manipulation.srcIndex != NO_INDEX) + { + manipulation.srcIndex += offsetBefore; + } + } + } + } + + private void applyOffsetToDestinationIndexes(int offsetAfter) + { + if (offsetAfter != 0) + { + for (Manipulation manipulation : manipulations) + { + if (manipulation.dstIndex != NO_INDEX) + { + // Apply the offset to all indices to make them relative to the new offset + manipulation.dstIndex += offsetAfter; + } + } + } + } + + protected final int getOffsetBefore() + { + return offsetBefore; + } + + protected final int getNextTmpIndex() + { + return --tmpIndex; + } + + /** + * Write calculated changes to the database + */ + protected void writeResultToDatabase() throws SQLException + { + IIDHandler idHandler = accessor.getStore().getIDHandler(); + if (TRACER.isEnabled()) + { + TRACER.trace("Writing to database:"); //$NON-NLS-1$ + } + + if (clearFirst) + { + if (TRACER.isEnabled()) + { + TRACER.trace(" - clear list"); //$NON-NLS-1$ + } + + clearList(); + } + + for (Manipulation manipulation : manipulations) + { + if (manipulation.is(DELETE)) + { + /* + * Step 1: DELETE all elements e which have e.is(DELETE) by e.srcIndex + */ + dbDelete(idHandler, manipulation.srcIndex); + + if (TRACER.isEnabled()) + { + TRACER.format(" - delete at {0} ", manipulation.srcIndex); //$NON-NLS-1$ + } + } + + if (manipulation.is(MOVE)) + { + /* + * Step 2: MOVE all elements e (by e.srcIndex) which have e.is(MOVE) to tmpIndex (-1, -2, -3, -4, ...) and store + * tmpIndex in e.tempIndex + */ + manipulation.tmpIndex = getNextTmpIndex(); + dbMove(idHandler, manipulation.srcIndex, manipulation.tmpIndex, manipulation.srcIndex); + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", manipulation.srcIndex, manipulation.tmpIndex); //$NON-NLS-1$ + } + } + } + + writeShifts(idHandler); + + ITypeMapping typeMapping = getTypeMapping(); + for (Manipulation manipulation : manipulations) + { + if (manipulation.is(MOVE)) + { + /* + * Step 4: MOVE all elements e have e.is(MOVE) from e.tempIdx to e.dstIndex (because we have moved them before, moveStmt + * is always initialized + */ + dbMove(idHandler, manipulation.tmpIndex, manipulation.dstIndex, manipulation.srcIndex); + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", manipulation.tmpIndex, manipulation.dstIndex); //$NON-NLS-1$ + } + } + + if (manipulation.is(SET)) + { + /* + * Step 5: SET all elements which have e.type == SET by index == e.dstIndex + */ + dbSet(idHandler, typeMapping, manipulation.dstIndex, manipulation.value, manipulation.srcIndex); + + if (TRACER.isEnabled()) + { + TRACER.format(" - set value at {0} to {1} ", manipulation.dstIndex, manipulation.value); //$NON-NLS-1$ + } + } + + if (manipulation.is(INSERT)) + { + /* + * Step 6: INSERT all elements which have e.type == INSERT. + */ + dbInsert(idHandler, typeMapping, manipulation.dstIndex, manipulation.value); + + if (TRACER.isEnabled()) + { + TRACER.format(" - insert value at {0} : value {1} ", manipulation.dstIndex, manipulation.value); //$NON-NLS-1$ + } + } + } + } + + /** + * Perform the shift operations to adjust indexes resulting from remove, insert, and move operations. + * + * @see #writeResultToDatabase(IDBStoreAccessor, CDOID) + * @throws SQLException + */ + protected void writeShifts(IIDHandler idHandler) throws SQLException + { + /* + * Step 3: shift 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 and shift + * up operations have to be executed in top to bottom order. + */ + LinkedList shiftOperations = new LinkedList(); + + /* + * If a necessary shift is detected (source and destination indices differ), firstIndex is set to the current index and + * currentOffset is set to the offset of the shift operation. When a new offset is detected or the range is interrupted, + * we record the range and start a new one if needed. + */ + int rangeStartIndex = NO_INDEX; + int rangeOffset = 0; + int lastElementIndex = NO_INDEX; + + // Iterate through the manipulationElements and collect the necessary operations + for (Manipulation manipulation : manipulations) + { + /* + * Shift applies only to elements which are not moved, inserted or deleted (i.e. only plain SET and NONE are affected) + */ + if (manipulation.types == NONE || manipulation.types == SET) + { + int elementOffset = manipulation.dstIndex - manipulation.srcIndex; + + /* + * First make sure if we have to close a previous range. This is the case, if the current element's offset differs from + * the rangeOffset and a range is open. + */ + if (elementOffset != rangeOffset && rangeStartIndex != NO_INDEX) + { + // There is an open range but the rangeOffset differs. We have to close the open range + shiftOperations.add(new Shift(rangeStartIndex, lastElementIndex, rangeOffset)); + + // And reset the state + rangeStartIndex = NO_INDEX; + rangeOffset = 0; + } + + /* + * At this point, either a range is open, which means that the current element also fits in the range (i.e. the offsets + * match) or no range is open. In the latter case, we have to open one if the current element's offset is not 0. + */ + if (elementOffset != 0 && rangeStartIndex == NO_INDEX) + { + rangeStartIndex = manipulation.srcIndex; + rangeOffset = elementOffset; + } + } + else + { + // Shift does not apply to this element because of its type + if (rangeStartIndex != NO_INDEX) + { + // If there is an open range, we have to close and remember it + shiftOperations.add(new Shift(rangeStartIndex, lastElementIndex, rangeOffset)); + + // And reset the state + rangeStartIndex = NO_INDEX; + rangeOffset = 0; + } + } + + lastElementIndex = manipulation.srcIndex; + } + + // After the iteration, we have to make sure that we remember the last open range, if it is there + if (rangeStartIndex != NO_INDEX) + { + shiftOperations.add(new Shift(rangeStartIndex, lastElementIndex, rangeOffset)); + } + + /* + * Now process the operations. Move down operations can be performed directly, move up operations need to be performed + * later in the reverse direction + */ + ListIterator operationIt = shiftOperations.listIterator(); + writeShiftsDown(idHandler, operationIt); + writeShiftsUp(idHandler, operationIt); + } + + protected void writeShiftsDown(IIDHandler idHandler, ListIterator operationIt) throws SQLException + { + while (operationIt.hasNext()) + { + Shift operation = operationIt.next(); + if (operation.offset < 0) + { + dbShiftDown(idHandler, operation.offset, operation.startIndex, operation.endIndex); + + if (TRACER.isEnabled()) + { + TRACER.format(" - shift down {0} ", operation); //$NON-NLS-1$ + } + + operationIt.remove(); + } + } + } + + protected void writeShiftsUp(IIDHandler idHandler, ListIterator operationIt) throws SQLException + { + while (operationIt.hasPrevious()) + { + Shift operation = operationIt.previous(); + dbShiftUp(idHandler, operation.offset, operation.startIndex, operation.endIndex); + + if (TRACER.isEnabled()) + { + TRACER.format(" - shift up {0} ", operation); //$NON-NLS-1$ + } + } + } + + protected abstract void dbDelete(IIDHandler idHandler, int index) throws SQLException; + + protected abstract void dbMove(IIDHandler idHandler, int fromIndex, int toIndex, int srcIndex) throws SQLException; + + protected abstract void dbSet(IIDHandler idHandler, ITypeMapping typeMapping, int index, Object value, int srcIndex) throws SQLException; + + protected abstract void dbInsert(IIDHandler idHandler, ITypeMapping typeMapping, int index, Object value) throws SQLException; + + protected abstract void dbShiftDown(IIDHandler idHandler, int offset, int startIndex, int endIndex) throws SQLException; + + protected abstract void dbShiftUp(IIDHandler idHandler, int offset, int startIndex, int endIndex) throws SQLException; + + protected static void close(PreparedStatement... stmts) + { + Throwable t = null; + + for (PreparedStatement stmt : stmts) + { + try + { + if (stmt != null) + { + try + { + stmt.clearBatch(); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(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); + } + } + + protected abstract ITypeMapping getTypeMapping(); + + protected abstract int getCurrentIndexOffset(); + + protected abstract void clearList(); + + /** + * @author Eike Stepper + */ + public static final class NewListSizeResult extends RuntimeException + { + private static final long serialVersionUID = 1L; + + private final int newListSize; + + public NewListSizeResult(int newListSize) + { + this.newListSize = newListSize; + } + + public int getNewListSize() + { + return newListSize; + } + } + + /** + * @author Eike Stepper + */ + public static final class Manipulation + { + private static final Object NIL = new Object() + { + @Override + public String toString() + { + return "NIL"; + } + }; + + public int types; + + public int srcIndex; + + public int tmpIndex; + + public int dstIndex; + + public Object value; + + public Manipulation(int types, int srcIndex, int dstIndex, Object value) + { + this.types = types; + this.srcIndex = srcIndex; + tmpIndex = NO_INDEX; + this.dstIndex = dstIndex; + this.value = value; + } + + public boolean is(int type) + { + return type == NONE ? types == NONE : (types & type) != 0; + } + + public void addType(int type) + { + types |= type; + } + + @Override + public String toString() + { + return MessageFormat.format("Manipulation[types={0}, srcIndex={1}, tmpIndex={2}, dstIndex={3}, value={4}]", formatTypes(types), formatIndex(srcIndex), + formatIndex(tmpIndex), formatIndex(dstIndex), String.valueOf(value)); + } + + /** + * Create a Manipulation which represents an element which already is in the list. + */ + public static Manipulation createOriginalElement(int index) + { + return new Manipulation(NONE, index, index, NIL); + } + + /** + * Create a Manipulation which represents an element which is inserted in the list. + */ + public static Manipulation createInsertedElement(int index, Object value) + { + return new Manipulation(INSERT, NO_INDEX, index, value); + } + + private static String formatTypes(int types) + { + StringBuilder builder = new StringBuilder(); + formatType(types, DELETE, "DELETE", builder); + formatType(types, INSERT, "INSERT", builder); + formatType(types, MOVE, "MOVE", builder); + formatType(types, SET, "SET", builder); + + if (builder.length() != 0) + { + return builder.toString(); + } + + return "NONE"; + } + + private static void formatType(int types, int type, String label, StringBuilder builder) + { + if ((types & type) != 0) + { + if (builder.length() != 0) + { + builder.append("|"); + } + + builder.append(label); + } + } + + private static String formatIndex(int index) + { + if (index == NO_INDEX) + { + return "NONE"; + } + + return Integer.toString(index); + } + } + + /** + * @author Eike Stepper + */ + public static final class Shift + { + public final int startIndex; + + public final int endIndex; + + public final int offset; + + public Shift(int startIndex, int endIndex, int offset) + { + this.startIndex = startIndex; + this.endIndex = endIndex; + this.offset = offset; + } + + @Override + public String toString() + { + return "Shift[" + startIndex + ".." + endIndex + ", offset=" + offset + "]"; + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java new file mode 100644 index 000000000..d91023c22 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.id.CDOIDUtil; +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.IMetaDataManager; +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.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.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +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.Collection; +import java.util.Collections; +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 + * @deprecated As 4.5 feature maps are no longer supported. + */ +@Deprecated +public abstract class AbstractFeatureMapTableMapping extends AbstractBasicListTableMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractFeatureMapTableMapping.class); + + /** + * The table of this mapping. + */ + private IDBTable table; + + private FieldInfo[] keyFields; + + /** + * The tags mapped to column names. + */ + private Map tagMap = CDOIDUtil.createMap(); + + /** + * Column names. + */ + private List columnNames = new ArrayList(); + + /** + * The type mappings for the value fields. + */ + private Map typeMappings = CDOIDUtil.createMap(); + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + protected String sqlInsert; + + private List 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(registry.getDefaultFeatureMapDBTypes()); + } + + protected ITypeMapping.Registry getTypeMappingRegistry() + { + return ITypeMapping.Registry.INSTANCE; + } + + private void initTable() + { + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + DBType idType = getMappingStrategy().getStore().getIDHandler().getDBType(); + int idLength = getMappingStrategy().getStore().getIDColumnLength(); + + IDBDatabase database = getMappingStrategy().getStore().getDatabase(); + table = database.getSchema().getTable(tableName); + if (table == null) + { + table = database.getSchemaTransaction().getWorkingCopy().addTable(tableName); + + IDBIndex index = table.addIndexEmpty(Type.NON_UNIQUE); + for (FieldInfo fieldInfo : getKeyFields()) + { + IDBField field = table.addField(fieldInfo.getName(), fieldInfo.getType(), fieldInfo.getPrecision()); + index.addIndexField(field); + } + + // Add field for list index + table.addField(FEATUREMAP_IDX, DBType.INTEGER); + + // Add field for FeatureMap tag (MetaID for Feature in CDO registry) + table.addField(FEATUREMAP_TAG, idType, idLength); + + // Create columns for all DBTypes + initTypeColumns(true); + + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_IDX); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_TAG); + } + else + { + initTypeColumns(false); + } + } + + private void initTypeColumns(boolean create) + { + for (DBType type : getDBTypes()) + { + String column = FEATUREMAP_VALUE + "_" + type.name(); + if (create) + { + table.addField(column, type); + } + + columnNames.add(column); + } + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + FieldInfo[] fields = getKeyFields(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); + + builder.append(FEATUREMAP_TAG); + builder.append(", "); + + Iterator 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 " + 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(FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(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 final FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + List list = new ArrayList(3); + + IDBStore store = getMappingStrategy().getStore(); + DBType type = store.getIDHandler().getDBType(); + int precision = store.getIDColumnLength(); + list.add(new FieldInfo(FEATUREMAP_REVISION_ID, type, precision)); + + addKeyFields(list); + + keyFields = list.toArray(new FieldInfo[list.size()]); + } + + return keyFields; + } + + protected abstract void addKeyFields(List list); + + protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException; + + public Collection getDBTables() + { + return Collections.singleton(table); + } + + protected List getDBTypes() + { + return dbTypes; + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List getColumnNames() + { + return columnNames; + } + + protected final Map getTypeMappings() + { + return typeMappings; + } + + protected final Map getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList list = revision.getListOrNull(getFeature()); + if (list == null) + { + // Nothing to read take shortcut. + return; + } + + 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()); + } + + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + 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}", currentIndex, value); + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(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 = FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); + + tagMap.put(tag, column); + typeMapping.setDBField(table, column); + typeMappings.put(tag, typeMapping); + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List 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()); + } + + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = chunkReader.getAccessor().getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getListOrNull(getFeature()); + if (values != null) + { + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + } + } + + 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(), //$NON-NLS-1$ + getFeature(), idx, revision, value); + } + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, revision.getTimeStamp()); + ITypeMapping typeMapping = getTypeMapping(tag); + String columnName = getColumnName(tag); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.HIGH); + + try + { + setKeyFields(stmt, revision); + int column = getKeyFields().length + 1; + + for (int i = 0; i < columnNames.size(); i++) + { + if (columnNames.get(i).equals(columnName)) + { + typeMapping.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 + { + DBUtil.close(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) + { + IMetaDataManager metaDataManager = getMappingStrategy().getStore().getMetaDataManager(); + return (EStructuralFeature)metaDataManager.getMetaInstance(tag); + } + + /** + * @param feature + * The EStructuralFeature + * @return The feature's MetaID in CDO + */ + protected CDOID getTagByFeature(EStructuralFeature feature, long timeStamp) + { + IMetaDataManager metaDataManager = getMappingStrategy().getStore().getMetaDataManager(); + return metaDataManager.getMetaID(feature, timeStamp); + } + + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java new file mode 100644 index 000000000..fbd69b3ee --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java @@ -0,0 +1,1232 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.CDOFeatureType; +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.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +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.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.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping3; +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.DBIndexAnnotation; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.AbstractMappingStrategy; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +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.InternalCDOList; +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.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +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.ImplementationError; +import org.eclipse.net4j.util.lifecycle.IDeactivateable; +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 java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +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.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, IMappingConstants, IDeactivateable +{ + protected static final int UNSET_LIST = -1; + + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class); + + private EClass eClass; + + private IDBTable table; + + private AbstractHorizontalMappingStrategy mappingStrategy; + + private List valueMappings; + + private List listMappings; + + private Map listSizeFields; + + private Map unsettableFields; + + private String sqlSelectForHandle; + + private String sqlSelectForChangeSet; + + public AbstractHorizontalClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + this.mappingStrategy = mappingStrategy; + this.eClass = eClass; + + IDBStoreAccessor accessor = null; + if (AbstractHorizontalMappingStrategy.isEagerTableCreation(mappingStrategy)) + { + accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + } + + initTable(accessor); + } + + protected void initTable(IDBStoreAccessor accessor) + { + IDBStore store = mappingStrategy.getStore(); + IDBDatabase database = store.getDatabase(); + String tableName = mappingStrategy.getTableName(eClass); + + table = database.getSchema().getTable(tableName); + if (table == null) + { + if (accessor != null) + { + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + + try + { + DBType idType = store.getIDHandler().getDBType(); + int idLength = store.getIDColumnLength(); + + IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); + IDBTable table = workingCopy.addTable(tableName); + table.addField(ATTRIBUTES_ID, idType, idLength, true); + table.addField(ATTRIBUTES_VERSION, DBType.INTEGER, true); + + IDBField branchField = addBranchField(table); + + table.addField(ATTRIBUTES_CREATED, DBType.BIGINT, true); + table.addField(ATTRIBUTES_REVISED, DBType.BIGINT, true); + table.addField(ATTRIBUTES_RESOURCE, idType, idLength, true); + addContainerField(table, idType, idLength); + table.addField(ATTRIBUTES_FEATURE, DBType.INTEGER, true); + + IDBIndex primaryKey = table.addIndex(IDBIndex.Type.PRIMARY_KEY, ATTRIBUTES_ID, ATTRIBUTES_VERSION); + if (branchField != null) + { + primaryKey.addIndexField(branchField); + } + + table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_REVISED); + + EStructuralFeature[] allPersistentFeatures = CDOModelUtil.getClassInfo(eClass).getAllPersistentFeatures(); + Map valueMappings = new HashMap(); + List unsettableFeatures = new ArrayList(); + + for (EStructuralFeature feature : allPersistentFeatures) + { + String fieldName = mappingStrategy.getFieldName(feature); + if (feature.isMany()) + { + IListMapping mapping = createListMapping(feature); + if (mapping != null) + { + // Add field for list sizes. + table.addField(fieldName, DBType.INTEGER); + } + } + else + { + ITypeMapping valueMapping = mappingStrategy.createValueMapping(feature); + valueMapping.createDBField(table, fieldName); + valueMappings.put(feature, valueMapping); + + Set forceIndexes = AbstractMappingStrategy.getForceIndexes(mappingStrategy); + if (CDOFeatureType.matchesCombination(feature, forceIndexes)) + { + IDBField field = table.getField(fieldName); + if (!table.hasIndexFor(field)) + { + IDBIndex index = table.addIndex(IDBIndex.Type.NON_UNIQUE, field); + DBUtil.setOptional(index, true); // Creation might fail for unsupported column type! + } + } + + if (feature.isUnsettable()) + { + unsettableFeatures.add(feature); + } + } + } + + // Add unsettable fields to end of table. + for (EStructuralFeature feature : unsettableFeatures) + { + String fieldName = mappingStrategy.getUnsettableFieldName(feature); + table.addField(fieldName, DBType.BOOLEAN); + } + + // Create optional feature indices. + for (List features : DBIndexAnnotation.getIndices(eClass, allPersistentFeatures)) + { + int size = features.size(); + IDBField[] fields = new IDBField[size]; + + for (int i = 0; i < size; i++) + { + EStructuralFeature feature = features.get(i); + + ITypeMapping valueMapping = valueMappings.get(feature); + IDBField field = valueMapping.getField(); + fields[i] = field; + } + + if (!table.hasIndexFor(fields)) + { + IDBIndex index = table.addIndex(IDBIndex.Type.NON_UNIQUE, fields); + DBUtil.setOptional(index, true); // Creation might fail for unsupported column type! + } + } + + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + + initTable(null); + accessor.tableCreated(table); + } + } + else + { + valueMappings = null; + listMappings = null; + listSizeFields = null; + unsettableFields = null; + + EStructuralFeature[] allPersistentFeatures = CDOModelUtil.getClassInfo(eClass).getAllPersistentFeatures(); + List unsettableFeatures = new ArrayList(); + + for (EStructuralFeature feature : allPersistentFeatures) + { + String fieldName = mappingStrategy.getFieldName(feature); + if (feature.isMany()) + { + IListMapping mapping = createListMapping(feature); + if (mapping != null) + { + if (mapping instanceof IListMapping3) + { + ((IListMapping3)mapping).setClassMapping(this); + } + + if (listMappings == null) + { + listMappings = new ArrayList(); + } + + listMappings.add(mapping); + + IDBField listSizeField = table.getField(fieldName); + + if (listSizeFields == null) + { + listSizeFields = new LinkedHashMap(); + } + + listSizeFields.put(feature, listSizeField); + } + } + else + { + ITypeMapping mapping = mappingStrategy.createValueMapping(feature); + mapping.setDBField(table, fieldName); + + if (valueMappings == null) + { + valueMappings = new ArrayList(); + } + + valueMappings.add(mapping); + + if (feature.isUnsettable()) + { + unsettableFeatures.add(feature); + } + } + } + + // Register unsettable fields. + if (!unsettableFeatures.isEmpty()) + { + unsettableFields = new LinkedHashMap(); + for (EStructuralFeature feature : unsettableFeatures) + { + String fieldName = mappingStrategy.getUnsettableFieldName(feature); + IDBField field = table.getField(fieldName); + unsettableFields.put(feature, field); + } + } + + if (valueMappings == null) + { + valueMappings = Collections.emptyList(); + } + + if (listMappings == null) + { + listMappings = Collections.emptyList(); + } + + initSQLStrings(); + } + } + + private IListMapping createListMapping(EStructuralFeature feature) + { + if (FeatureMapUtil.isFeatureMap(feature)) + { + return mappingStrategy.createFeatureMapMapping(eClass, feature); + } + + return mappingStrategy.createListMapping(eClass, feature); + } + + protected void initSQLStrings() + { + // ----------- Select all revisions (for handleRevisions) --- + StringBuilder builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + appendSelectForHandleFields(builder); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + sqlSelectForHandle = builder.toString(); + + // ----------- Select all revisions (for readChangeSet) --- + builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + builder.append(" WHERE "); //$NON-NLS-1$ + sqlSelectForChangeSet = builder.toString(); + } + + protected void appendSelectForHandleFields(StringBuilder builder) + { + // Do nothing. + } + + protected String getSQLSelectForHandle() + { + return sqlSelectForHandle; + } + + protected String getSQLSelectForChangeSet() + { + return sqlSelectForChangeSet; + } + + protected IDBField addContainerField(IDBTable table, DBType idType, int idLength) + { + return table.addField(ATTRIBUTES_CONTAINER, idType, idLength, true); + } + + protected IDBField addBranchField(IDBTable table) + { + return null; + } + + /** + * Read the revision's values from the DB. + * + * @return true if the revision has been read successfully.
+ * false 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(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + if (!readValuesFromResultSet(resultSet, idHandler, revision, false)) + { + if (TRACER.isEnabled()) + { + TRACER.format("Resultset was empty"); //$NON-NLS-1$ + } + + return false; + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + } + } + + /** + * Read the revision's values from the DB. + * + * @return true if the revision has been read successfully.
+ * false if the revision does not exist in the DB. + */ + protected final boolean readValuesFromResultSet(ResultSet resultSet, IIDHandler idHandler, InternalCDORevision revision, boolean forUnit) + { + try + { + if (resultSet.next()) + { + long timeStamp = resultSet.getLong(ATTRIBUTES_CREATED); + CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp); + + if (forUnit) + { + revision.setID(idHandler.getCDOID(resultSet, ATTRIBUTES_ID)); + } + + revision.setBranchPoint(branchPoint); + revision.setVersion(resultSet.getInt(ATTRIBUTES_VERSION)); + revision.setRevised(resultSet.getLong(ATTRIBUTES_REVISED)); + revision.setResourceID(idHandler.getCDOID(resultSet, ATTRIBUTES_RESOURCE)); + revision.setContainerID(idHandler.getCDOID(resultSet, ATTRIBUTES_CONTAINER)); + revision.setContainingFeatureID(resultSet.getInt(ATTRIBUTES_FEATURE)); + + for (ITypeMapping mapping : valueMappings) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + IDBField field = unsettableFields.get(feature); + if (!resultSet.getBoolean(field.getName())) + { + // isSet==false -- setValue: null + revision.setValue(feature, null); + continue; + } + } + + mapping.readValueToRevision(resultSet, revision); + } + + if (listSizeFields != null) + { + for (Map.Entry listSizeEntry : listSizeFields.entrySet()) + { + EStructuralFeature feature = listSizeEntry.getKey(); + IDBField field = listSizeEntry.getValue(); + + int size = resultSet.getInt(field.getName()); + if (size == UNSET_LIST) + { + // Leave the list slot in the revision null. + continue; + } + + // Ensure the list size. + CDOList list = revision.getOrCreateList(feature, size); + for (int i = 0; i < size; i++) + { + list.add(InternalCDOList.UNINITIALIZED); + } + } + } + + return true; + } + + return false; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + 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 getUnsettableFields() + { + return unsettableFields; + } + + protected final Map getListSizeFields() + { + return listSizeFields; + } + + public final List getValueMappings() + { + return valueMappings; + } + + public final ITypeMapping getValueMapping(EStructuralFeature feature) + { + for (ITypeMapping mapping : valueMappings) + { + if (mapping.getFeature() == feature) + { + return mapping; + } + } + + return null; + } + + public final List 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 getDBTables() + { + List tables = new ArrayList(); + tables.add(table); + + if (listMappings != null) + { + for (IListMapping listMapping : listMappings) + { + tables.addAll(listMapping.getDBTables()); + } + } + + return tables; + } + + @Override + public String toString() + { + return MessageFormat.format("{0}[{1} -> {2}]", getClass().getSimpleName(), eClass, table); + } + + 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 node in folder " + folderID + ": " + name); //$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) + { + if (table == null) + { + initTable(accessor); + } + + CDOID id = revision.getID(); + InternalCDOBranch branch = revision.getBranch(); + long timeStamp = revision.getTimeStamp(); + + // A DetachedCDORevision can only come from DBStoreAccessor.rawStore(). + if (revision instanceof DetachedCDORevision) + { + int version = revision.getVersion(); + detachAttributes(accessor, id, version, branch, timeStamp, monitor); + + long revised = revision.getRevised(); + if (revised != CDOBranchPoint.UNSPECIFIED_DATE) + { + reviseOldRevision(accessor, id, branch, revised); + } + + return; + } + + // If the repository's root resource ID is not yet set, then this must be the initial initRootResource() + // commit. The duplicate check is certainly not needed in this case, and it appears that Mysql has problems + // with it (Table definition has changed, please retry transaction), see bug 482886. + boolean duplicateResourcesCheckNeeded = revision.isResourceNode() && mappingStrategy.getStore().getRepository().getRootResourceID() != null; + + monitor.begin(duplicateResourcesCheckNeeded ? 10 : 9); + Async async = null; + + try + { + try + { + async = monitor.forkAsync(); + if (mapType) + { + mappingStrategy.putObjectType(accessor, timeStamp, id, eClass); + } + else if (revise) + { + long revised = timeStamp - 1; + reviseOldRevision(accessor, id, branch, revised); + for (IListMapping mapping : getListMappings()) + { + mapping.objectDetached(accessor, id, revised); + } + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + if (duplicateResourcesCheckNeeded) + { + try + { + async = monitor.forkAsync(); + 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) + { + if (table == null) + { + return; + } + + // 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(); + CDOBranch mainBranch = branchManager.getMainBranch(); + + // 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(ATTRIBUTES_CREATED); + builder.append("=?"); //$NON-NLS-1$ + timeParameters = 1; + } + } + else + { + builder.append(" WHERE "); //$NON-NLS-1$ + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(ATTRIBUTES_CREATED); + builder.append("<=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=? OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + timeParameters = 2; + } + else + { + builder.append(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + } + } + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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) + { + CDOBranchVersion branchVersion = mainBranch.getVersion(version); + InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchVersion, CDORevision.UNCHUNKED, true); + + if (!handler.handleRevision(revision)) + { + break; + } + } + else + { + EClass eClass = getEClass(); + long created = resultSet.getLong(3); + long revised = resultSet.getLong(4); + + // Tell handler about detached IDs + InternalCDORevision revision = new DetachedCDORevision(eClass, id, mainBranch, -version, created, revised); + if (!handler.handleRevision(revision)) + { + break; + } + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + public Set readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments) + { + Set result = new HashSet(); + if (table == null) + { + return result; + } + + 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(ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + if (table == null) + { + return; + } + + Async async = null; + monitor.begin(1 + listMappings.size()); + + try + { + if (version >= CDOBranchVersion.FIRST_VERSION) + { + reviseOldRevision(accessor, id, branch, timeStamp - 1); + } + + detachAttributes(accessor, id, version, 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 void rawDelete(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, OMMonitor monitor) + { + if (table == null) + { + return; + } + + Async async = null; + monitor.begin(1 + listMappings.size()); + + try + { + rawDeleteAttributes(accessor, id, branch, version, monitor.fork()); + + // notify list mappings so they can clean up + for (IListMapping mapping : getListMappings()) + { + if (mapping instanceof AbstractBasicListTableMapping) + { + try + { + async = monitor.forkAsync(); + + AbstractBasicListTableMapping m = (AbstractBasicListTableMapping)mapping; + m.rawDeleted(accessor, id, branch, version); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + else + { + throw new UnsupportedOperationException("rawDeleted() is not supported by " + mapping.getClass().getName()); + } + } + } + finally + { + monitor.done(); + } + } + + protected abstract void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor fork); + + public final boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString) + { + if (table == null) + { + return true; + } + + String tableName = table.getName(); + List refs = context.getSourceCandidates().get(eClass); + List scalarRefs = new ArrayList(); + + 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 scalarRefs, QueryXRefsContext context, String idString) + { + String tableName = table.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(ATTRIBUTES_ID); + builder.append(", "); + builder.append(valueField); + builder.append(" FROM "); + builder.append(tableName); + builder.append(" WHERE "); + builder.append(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(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (attributes): {0}", sql); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + 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); + + public Exception deactivate() + { + return null; + } + + protected static void appendTypeMappingNames(StringBuilder builder, Collection typeMappings) + { + if (typeMappings != null) + { + for (ITypeMapping typeMapping : typeMappings) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(typeMapping.getField()); + } + } + } + + protected static void appendFieldNames(StringBuilder builder, Map fields) + { + if (fields != null) + { + for (IDBField field : fields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(field); + } + } + } + + protected static void appendTypeMappingParameters(StringBuilder builder, Collection typeMappings) + { + if (typeMappings != null) + { + for (int i = 0; i < typeMappings.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + } + + protected static void appendFieldParameters(StringBuilder builder, Map fields) + { + if (fields != null) + { + for (int i = 0; i < fields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + } + + /** + * @author Eike Stepper + */ + protected abstract class AbstractFeatureDeltaWriter implements CDOFeatureDeltaVisitor + { + protected IDBStoreAccessor accessor; + + protected long created; + + protected CDOID id; + + public final void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created) + { + this.accessor = accessor; + this.created = created; + id = delta.getID(); + + if (table == null) + { + initTable(accessor); + } + + doProcess(delta); + } + + protected abstract void doProcess(InternalCDORevisionDelta delta); + + @Deprecated + public final void visit(CDOAddFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + @Deprecated + public final void visit(CDORemoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + @Deprecated + public final void visit(CDOMoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + @Deprecated + public final void visit(CDOClearFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java new file mode 100644 index 000000000..9fb78eb46 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2009-2013, 2015-2018 Eike Stepper (Loehne, 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.IRepository; +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.DBUtil.DeserializeRowHandler; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBSchemaTransaction; +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: + *
    + *
  • object type cache (table cdo_objects) + *
  • resource query handling + *
+ * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractHorizontalMappingStrategy extends AbstractMappingStrategy implements IMappingConstants { + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalMappingStrategy.class); + + /** + * The associated object type mapper. + */ + private IObjectTypeMapper objectTypeMapper; + + public AbstractHorizontalMappingStrategy() { + } + + public IObjectTypeMapper getObjectTypeMapper() { + return objectTypeMapper; + } + + public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id) { + return objectTypeMapper.getObjectType(accessor, id); + } + + public boolean putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type) { + return objectTypeMapper.putObjectType(accessor, timeStamp, id, type); + } + + public boolean removeObjectType(IDBStoreAccessor accessor, CDOID id) { + return objectTypeMapper.removeObjectType(accessor, id); + } + + public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection) { + IDBStore store = getStore(); + IRepository repository = store.getRepository(); + if (repository.getIDGenerationLocation() == IDGenerationLocation.STORE) { + IIDHandler idHandler = store.getIDHandler(); + + if (repository.isSupportingBranches()) { + 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(ATTRIBUTES_CREATED); + builder.append(" BETWEEN "); //$NON-NLS-1$ + builder.append(fromCommitTime); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(toCommitTime); + + String attrSuffix = builder.toString(); + IDBConnection connection = accessor.getDBConnection(); + + Collection classMappings = getClassMappings(true).values(); + out.writeXInt(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, IDBConnection connection, IListMapping listMapping, + IDBTable attrTable, String attrSuffix) throws IOException { + for (IDBTable table : listMapping.getDBTables()) { + String listSuffix = ", " + attrTable + " a_t" + attrSuffix; + String listJoin = getListJoinForRawExport("a_t", "l_t"); + if (listJoin != null) { + listSuffix += listJoin; + } + + DBUtil.serializeTable(out, connection, table, "l_t", listSuffix); + } + } + + protected String getListJoinForRawExport(String attrTable, String listTable) { + return getListJoin(attrTable, listTable); + } + + public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException { + int size = in.readXInt(); + if (size == 0) { + return; + } + + int objectTypeMapperWork = 10; + monitor.begin(3 * size + objectTypeMapperWork); + + try { + IDBConnection connection = accessor.getDBConnection(); + + for (int i = 0; i < size; i++) { + EClass eClass = (EClass) in.readCDOClassifierRefAndResolve(); + + /** BEGIN SPECMATE PATCH */ + //IClassMapping classMapping = getClassMapping(eClass); + + IClassMapping classMapping; + IDBSchemaTransaction schemaTransaction = null; + + try { + schemaTransaction = accessor.getStore().getDatabase().openSchemaTransaction(); + classMapping = getClassMapping(eClass); + schemaTransaction.commit(); + } catch (RuntimeException ex) { + throw ex; + } catch (Error ex) { + throw ex; + } finally { + if (schemaTransaction != null) { + schemaTransaction.close(); + } + } + /** END SPECMATE PATCH */ + + 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 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(IDBConnection connection, IDBTable table, long fromCommitTime, + long toCommitTime, OMMonitor monitor) { + throw new UnsupportedOperationException("Must be overridden"); + } + + protected void rawImportReviseOldRevisions(IDBConnection connection, IDBTable table, OMMonitor monitor) { + throw new UnsupportedOperationException("Must be overridden"); + } + + protected void rawImportList(CDODataInput in, IDBConnection connection, IListMapping listMapping, OMMonitor monitor) + throws IOException { + Collection 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(), getImportListHandler()); + } + } finally { + monitor.done(); + } + } + + protected DeserializeRowHandler getImportListHandler() { + // Only needed with ranges + return null; + } + + public String getListJoin(String attrTable, String listTable) { + return " AND " + attrTable + "." + ATTRIBUTES_ID + "=" + listTable + "." + LIST_REVISION_ID; + } + + @Override + protected boolean isMapped(EClass eClass) { + return !eClass.isAbstract() && !eClass.isInterface(); + } + + @Override + protected Collection 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(Props.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 class + * mappings 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 true 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) { + CDOID folderID = context.getFolderID(); + String name = context.getName(); + boolean exactMatch = context.exactMatch(); + + IIDHandler idHandler = getStore().getIDHandler(); + PreparedStatement stmt = classMapping.createResourceQueryStatement(accessor, folderID, name, exactMatch, + context); + if (stmt == null) { + return true; + } + + ResultSet resultSet = null; + + try { + 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); + DBUtil.close(stmt); + } + } + + private CDOID getMinLocalID(Connection connection) { + final IIDHandler idHandler = getStore().getIDHandler(); + final CDOID[] min = { idHandler.getMaxCDOID() }; + + final String prefix = "SELECT MIN(t." + ATTRIBUTES_ID + ") FROM " + CDODBSchema.CDO_OBJECTS + " o, "; + + final String suffix = " t WHERE t." + ATTRIBUTES_BRANCH + "<0 AND t." + ATTRIBUTES_ID + "=o." + ATTRIBUTES_ID + + " AND t." + ATTRIBUTES_CREATED + "=o." + ATTRIBUTES_CREATED; + + getStore().visitAllTables(connection, new IDBStore.TableVisitor() { + public void visitTable(Connection connection, String name) throws SQLException { + 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]) < 0) { + min[0] = id; + } + } + } finally { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + }); + + return min[0]; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java new file mode 100644 index 000000000..28b23aead --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.StoreThreadLocal; +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.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +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.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBSchema; +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.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +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 AbstractBasicListTableMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractListTableMapping.class); + + /** + * The table of this mapping. + */ + private IDBTable table; + + private FieldInfo[] keyFields; + + /** + * 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); + + IDBStoreAccessor accessor = null; + if (AbstractHorizontalMappingStrategy.isEagerTableCreation(mappingStrategy)) + { + accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + } + + initTable(accessor); + } + + protected void initTable(IDBStoreAccessor accessor) + { + IMappingStrategy mappingStrategy = getMappingStrategy(); + EStructuralFeature feature = getFeature(); + + String tableName = mappingStrategy.getTableName(getContainingClass(), feature); + typeMapping = mappingStrategy.createValueMapping(feature); + + IDBDatabase database = mappingStrategy.getStore().getDatabase(); + table = database.getSchema().getTable(tableName); + if (table == null) + { + if (accessor != null) + { + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + + try + { + IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); + IDBTable table = workingCopy.addTable(tableName); + + IDBIndex primaryKey = table.addIndexEmpty(Type.PRIMARY_KEY); + for (FieldInfo info : getKeyFields()) + { + IDBField field = table.addField(info.getName(), info.getType(), info.getPrecision(), true); + primaryKey.addIndexField(field); + } + + // Add field for list index. + IDBField listIndexField = table.addField(LIST_IDX, DBType.INTEGER, true); + primaryKey.addIndexField(listIndexField); + + // Add field for value. + typeMapping.createDBField(table, LIST_VALUE); + + if (needsIndexOnValueField(feature)) + { + IDBField field = table.getField(LIST_VALUE); + + if (!table.hasIndexFor(field)) + { + IDBIndex index = table.addIndex(IDBIndex.Type.NON_UNIQUE, field); + DBUtil.setOptional(index, true); // Creation might fail for unsupported column type! + } + } + + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + + initTable(null); + accessor.tableCreated(table); + } + } + else + { + typeMapping.setDBField(table, LIST_VALUE); + initSQLStrings(); + } + } + + protected void initSQLStrings() + { + String tableName = table.getName(); + FieldInfo[] fields = getKeyFields(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(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 " + 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(LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(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 FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + List list = new ArrayList(3); + + IDBStore store = getMappingStrategy().getStore(); + DBType type = store.getIDHandler().getDBType(); + int precision = store.getIDColumnLength(); + list.add(new FieldInfo(LIST_REVISION_ID, type, precision)); + + addKeyFields(list); + + keyFields = list.toArray(new FieldInfo[list.size()]); + } + + return keyFields; + } + + protected abstract void addKeyFields(List list); + + protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException; + + public Collection getDBTables() + { + return Collections.singleton(table); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + if (table == null) + { + // Nothing to read. Take shortcut. + return; + } + + if (listChunk == 0) + { + // Nothing to read. Take shortcut. + return; + } + + MoveableList list = revision.getListOrNull(getFeature()); + if (list == null || 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()); + } + + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + setKeyFields(stmt, revision); + + if (TRACER.isEnabled()) + { + TRACER.trace(stmt.toString()); + } + + if (listChunk != CDORevision.UNCHUNKED) + { + if (stmt.getMaxRows() != listChunk) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + } + else + { + if (stmt.getMaxRows() != 0) + { + stmt.setMaxRows(0); // No limit. + } + } + + 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}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(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 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()); + } + + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + String sql = builder.toString(); + + IDBPreparedStatement stmt = chunkReader.getAccessor().getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getListOrNull(getFeature()); + if (values != null && !values.isEmpty()) + { + if (table == null) + { + initTable(accessor); + } + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + } + } + + 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}v{4} : {5}", getContainingClass().getName(), getFeature().getName(), idx, + revision.getID(), revision.getVersion(), value); + } + + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertEntry, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, QueryXRefsContext context, String idString) + { + if (table == null) + { + // Nothing to read. Take shortcut. + return true; + } + + String tableName = table.getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" 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(LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID srcId = idHandler.getCDOID(resultSet, 1); + CDOID targetId = idHandler.getCDOID(resultSet, 2); + int idx = resultSet.getInt(3); + + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", srcId, targetId, idx); + } + + boolean more = context.addXRef(targetId, srcId, (EReference)getFeature(), 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java new file mode 100644 index 000000000..5f7579590 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java new file mode 100644 index 000000000..2f714e707 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2009-2013, 2018 Eike Stepper (Loehne, 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.branch.CDOBranch; +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.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * 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 + * @deprecated As 4.5 feature maps are no longer supported. + */ +@Deprecated +public class AuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping +{ + private String sqlClear; + + public AuditFeatureMapTableMapping(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(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION); + builder.append("=? "); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected void addKeyFields(List list) + { + list.add(new FieldInfo(FEATUREMAP_VERSION, DBType.INTEGER)); + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + idHandler.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 + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClear, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java new file mode 100644 index 000000000..4825a7d29 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java @@ -0,0 +1,1203 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.id.CDOIDUtil; +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.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.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.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +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 IMappingConstants#LIST_REVISION_VERSION_ADDED cdo_version_added} and + * {@link IMappingConstants#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 + * @deprecated As 4.5 feature maps are no longer supported. + */ +@Deprecated +public class AuditFeatureMapTableMappingWithRanges extends AbstractBasicListTableMapping 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; + + /** + * Column names. + */ + private List columnNames = new ArrayList(); + + /** + * The type mappings for the value fields. + */ + private Map typeMappings = CDOIDUtil.createMap(); + + /** + * The tags mapped to column names + */ + private Map tagMap = CDOIDUtil.createMap(); + + private List 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(registry.getDefaultFeatureMapDBTypes()); + } + + private void initTable() + { + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + IDBStore store = getMappingStrategy().getStore(); + DBType idType = store.getIDHandler().getDBType(); + int idLength = store.getIDColumnLength(); + + IDBDatabase database = getMappingStrategy().getStore().getDatabase(); + table = database.getSchema().getTable(tableName); + if (table == null) + { + table = database.getSchemaTransaction().getWorkingCopy().addTable(tableName); + table.addField(FEATUREMAP_REVISION_ID, idType, idLength); + table.addField(FEATUREMAP_VERSION_ADDED, DBType.INTEGER); + table.addField(FEATUREMAP_VERSION_REMOVED, DBType.INTEGER); + table.addField(FEATUREMAP_IDX, DBType.INTEGER); + table.addField(FEATUREMAP_TAG, idType, idLength); + + initTypeColumns(true); + + // TODO think about indices + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_REVISION_ID); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_VERSION_ADDED); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_VERSION_REMOVED); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_IDX); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_TAG); + } + else + { + initTypeColumns(false); + } + } + + private void initTypeColumns(boolean create) + { + for (DBType type : getDBTypes()) + { + String column = FEATUREMAP_VALUE + "_" + type.name(); + if (create) + { + table.addField(column, type); + } + + columnNames.add(column); + } + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + + builder.append(FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + Iterator 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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + 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(FEATUREMAP_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlDeleteList = builder.toString(); + } + + protected List getDBTypes() + { + return dbTypes; + } + + public Collection getDBTables() + { + return Collections.singleton(table); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List getColumnNames() + { + return columnNames; + } + + protected final Map getTypeMappings() + { + return typeMappings; + } + + protected final Map getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList list = revision.getListOrNull(getFeature()); + if (list == null) + { + // Nothing to read take shortcut. + return; + } + + 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()); + } + + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + 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}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(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 = 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 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()); + } + + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = chunkReader.getAccessor().getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getListOrNull(getFeature()); + if (values != null) + { + 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(), //$NON-NLS-1$ + getFeature(), idx, revision, value); + } + + 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(); + IDBPreparedStatement stmtDeleteTemp = accessor.getDBConnection().prepareStatement(sqlDeleteList, ReuseProbability.HIGH); + IDBPreparedStatement stmtClear = accessor.getDBConnection().prepareStatement(sqlClearList, ReuseProbability.HIGH); + + try + { + // delete temporary entries + 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.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 + { + DBUtil.close(stmtClear); + DBUtil.close(stmtDeleteTemp); + } + } + + 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); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + + 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.size(getFeature()); + + 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.size(getFeature()) - 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(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value, long timestamp) + { + 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); + } + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + ITypeMapping typeMapping = getTypeMapping(tag); + String columnName = getColumnName(tag); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.HIGH); + + try + { + 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)) + { + typeMapping.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 + { + DBUtil.close(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + try + { + // try to delete a temporary entry first + 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 + DBUtil.close(stmt); + stmt = accessor.getDBConnection().prepareStatement(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 + { + DBUtil.close(stmt); + } + } + + private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlGetValue, ReuseProbability.HIGH); + + try + { + 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); + FeatureMap.Entry result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value); + + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + + return result; + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java new file mode 100644 index 000000000..236607a9d --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009-2013, 2018 Eike Stepper (Loehne, 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.branch.CDOBranch; +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.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBTable; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * 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 String sqlClear; + + public AuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected void initSQLStrings() + { + super.initSQLStrings(); + + IDBTable table = getTable(); + + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(table); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected void addKeyFields(List list) + { + list.add(new FieldInfo(LIST_REVISION_VERSION, DBType.INTEGER)); + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + idHandler.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 -> NOOP + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + if (getTable() == null) + { + initTable(accessor); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClear, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java new file mode 100644 index 000000000..8a27cfa92 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java @@ -0,0 +1,1231 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.StoreThreadLocal; +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.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingUnitSupport; +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.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +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.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +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.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.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Collections; +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 AbstractBasicListTableMapping implements IListMappingDeltaSupport, IListMappingUnitSupport +{ + 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; + + private static final String SQL_ORDER_BY_INDEX = " ORDER BY " + LIST_IDX; + + private static final boolean CHECK_UNIT_ENTRIES = Boolean.getBoolean("org.eclipse.emf.cdo.server.db.checkUnitEntries"); + + /** + * 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 sqlSelectUnitEntries; + + 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); + + IDBStoreAccessor accessor = null; + if (AbstractHorizontalMappingStrategy.isEagerTableCreation(mappingStrategy)) + { + accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + } + + initTable(accessor); + } + + private void initTable(IDBStoreAccessor accessor) + { + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + typeMapping = getMappingStrategy().createValueMapping(getFeature()); + + IDBStore store = getMappingStrategy().getStore(); + DBType idType = store.getIDHandler().getDBType(); + int idLength = store.getIDColumnLength(); + + IDBDatabase database = getMappingStrategy().getStore().getDatabase(); + table = database.getSchema().getTable(tableName); + if (table == null) + { + if (accessor != null) + { + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + + try + { + IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); + IDBTable table = workingCopy.addTable(tableName); + + table.addField(LIST_REVISION_ID, idType, idLength, true); + table.addField(LIST_REVISION_VERSION_ADDED, DBType.INTEGER); + table.addField(LIST_REVISION_VERSION_REMOVED, DBType.INTEGER); + table.addField(LIST_IDX, DBType.INTEGER, true); + + // TODO think about indexes + table.addIndex(Type.NON_UNIQUE, LIST_REVISION_ID, LIST_REVISION_VERSION_ADDED, LIST_REVISION_VERSION_REMOVED, LIST_IDX); + + typeMapping.createDBField(table, LIST_VALUE); + + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + + initTable(null); + accessor.tableCreated(table); + } + } + else + { + typeMapping.setDBField(table, LIST_VALUE); + initSQLStrings(); + } + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + // ----------------- insert entry ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(","); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlDeleteList = builder.toString(); + } + + @Override + public void setClassMapping(IClassMapping classMapping) + { + IMappingStrategy mappingStrategy = getMappingStrategy(); + InternalRepository repository = (InternalRepository)mappingStrategy.getStore().getRepository(); + if (repository.isSupportingUnits()) + { + String listTableName = mappingStrategy.getTableName(getContainingClass(), getFeature()); + String attributesTableName = classMapping.getDBTables().get(0).getName(); + + sqlSelectUnitEntries = "SELECT " + (CHECK_UNIT_ENTRIES ? ATTRIBUTES_ID + ", " : "") + "cdo_list." + LIST_VALUE + // + " FROM " + listTableName + " cdo_list, " + attributesTableName + ", " + UnitMappingTable.UNITS + // + " WHERE " + UnitMappingTable.UNITS_ELEM + "=" + ATTRIBUTES_ID + // + " AND " + ATTRIBUTES_ID + "=cdo_list." + LIST_REVISION_ID + // + " AND " + UnitMappingTable.UNITS_UNIT + "=?" + // + " AND " + ATTRIBUTES_CREATED + "<=?" + // + " AND (" + ATTRIBUTES_REVISED + "=0 OR " + ATTRIBUTES_REVISED + ">=?)" + // + " AND cdo_list." + LIST_REVISION_VERSION_ADDED + "<=" + ATTRIBUTES_VERSION + // + " AND (cdo_list." + LIST_REVISION_VERSION_REMOVED + " IS NULL OR cdo_list." + LIST_REVISION_VERSION_REMOVED + ">" + ATTRIBUTES_VERSION + + ") ORDER BY cdo_list." + LIST_REVISION_ID + ", cdo_list." + LIST_IDX; + } + } + + public Collection getDBTables() + { + return Collections.singleton(table); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + if (table == null) + { + // Nothing to read. Take shortcut. + return; + } + + MoveableList list = revision.getListOrNull(getFeature()); + if (list == null) + { + // Nothing to read take shortcut. + return; + } + + 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()); + } + + String sql = sqlSelectChunksPrefix + SQL_ORDER_BY_INDEX; + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); + ResultSet resultSet = null; + + try + { + 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}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + } + + 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()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List 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()); + } + + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(SQL_ORDER_BY_INDEX); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = chunkReader.getAccessor().getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getListOrNull(getFeature()); + if (values != null && !values.isEmpty()) + { + if (table == null) + { + initTable(accessor); + } + + 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(); + IDBPreparedStatement stmtDeleteTemp = accessor.getDBConnection().prepareStatement(sqlDeleteList, ReuseProbability.HIGH); + IDBPreparedStatement stmtClear = accessor.getDBConnection().prepareStatement(sqlClearList, ReuseProbability.HIGH); + + try + { + // delete temporary entries + 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.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 + { + DBUtil.close(stmtClear); + DBUtil.close(stmtDeleteTemp); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + if (table == null) + { + initTable(accessor); + } + + 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); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + + public ResultSet queryUnitEntries(IDBStoreAccessor accessor, IIDHandler idHandler, long timeStamp, CDOID rootID) throws SQLException + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectUnitEntries, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, rootID); + stmt.setLong(2, timeStamp); + stmt.setLong(3, timeStamp); + return stmt.executeQuery(); + } + + public void readUnitEntries(ResultSet resultSet, IIDHandler idHandler, CDOID id, MoveableList list) throws SQLException + { + int size = list.size(); + for (int i = 0; i < size; i++) + { + resultSet.next(); + + if (CHECK_UNIT_ENTRIES) + { + CDOID checkID = idHandler.getCDOID(resultSet, 1); + if (checkID != id) + { + throw new IllegalStateException("Result set does not deliver expected result"); + } + } + + Object value = typeMapping.readValue(resultSet); + list.set(i, value); + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertEntry, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + try + { + // try to delete a temporary entry first + 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 + DBUtil.close(stmt); + stmt = accessor.getDBConnection().prepareStatement(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 + { + DBUtil.close(stmt); + } + } + + private Object getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlGetValue, ReuseProbability.HIGH); + Object result = null; + + try + { + 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 + { + DBUtil.close(stmt); + } + + return result; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, QueryXRefsContext context, String idString) + { + if (table == null) + { + // Nothing to read. Take shortcut. + return true; + } + + 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(LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t.");//$NON-NLS-1$ + builder.append(mainTableWhere); + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + 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); + } + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, final int newVersion, long created, + CDOListFeatureDelta delta) + { + List listChanges = delta.getListChanges(); + int size = listChanges.size(); + if (size == 0) + { + // nothing to do. + return; + } + + if (table == null) + { + initTable(accessor); + } + + 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.size(getFeature()); + + 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 : listChanges) + { + listDelta.accept(visitor); + } + + visitor.finishPendingRemove(); + } + + /** + * @author Stefan Winkler + */ + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private CDOID id; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private int lastRemovedIndex; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion, int newVersion) + { + this.accessor = accessor; + id = originalRevision.getID(); + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.size(getFeature()) - 1; + lastRemovedIndex = -1; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + // optimization: a move from the end of the list to an index that was just removed requires no shifting + boolean optimizeMove = lastRemovedIndex != -1 && fromIdx == lastIndex - 1 && toIdx == lastRemovedIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + // items after a pending remove have an index offset by one + if (optimizeMove) + { + fromIdx++; + } + else + { + finishPendingRemove(); + } + + Object value = getValue(accessor, id, fromIdx); + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down + if (!optimizeMove) + { + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1); + } + } + else + { + // finish the optimized move by resetting lastRemovedIndex + lastRemovedIndex = -1; + --lastIndex; + } + + // create the item + addEntry(accessor, id, newVersion, toIdx, value); + } + + public void visit(CDOAddFeatureDelta delta) + { + finishPendingRemove(); + 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) + { + finishPendingRemove(); + lastRemovedIndex = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", lastRemovedIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, lastRemovedIndex); + } + + public void visit(CDOSetFeatureDelta delta) + { + finishPendingRemove(); + 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 (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + lastRemovedIndex = -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; + lastRemovedIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void finishPendingRemove() + { + if (lastRemovedIndex != -1) + { + int startIndex = lastRemovedIndex; + int endIndex = lastIndex; + + // make room for the new item + moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + lastRemovedIndex = -1; + } + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java new file mode 100644 index 000000000..b27913875 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2010-2013, 2018 Eike Stepper (Loehne, 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.branch.CDOBranch; +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.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * 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 + * @deprecated As 4.5 feature maps are no longer supported. + */ +@Deprecated +public class BranchingFeatureMapTableMapping extends AbstractFeatureMapTableMapping +{ + private String sqlClear; + + public BranchingFeatureMapTableMapping(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(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected void addKeyFields(List list) + { + list.add(new FieldInfo(FEATUREMAP_BRANCH, DBType.INTEGER)); + list.add(new FieldInfo(FEATUREMAP_VERSION, DBType.INTEGER)); + } + + @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 + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClear, ReuseProbability.HIGH); + + try + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, branch.getID()); + stmt.setInt(3, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java new file mode 100644 index 000000000..5db1697cf --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java @@ -0,0 +1,1496 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +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.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.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.bundle.OM; +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.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +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 IMappingConstants#LIST_REVISION_VERSION_ADDED cdo_version_added} and + * {@link IMappingConstants#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 + * @deprecated As 4.5 feature maps are no longer supported. + */ +@Deprecated +public class BranchingFeatureMapTableMappingWithRanges extends AbstractBasicListTableMapping 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 Map tagMap; + + /** + * Column name Set + */ + private List columnNames; + + /** + * The type mappings for the value fields. + */ + private Map typeMappings; + + private List 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(registry.getDefaultFeatureMapDBTypes()); + } + + private void initTable() + { + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + IDBStore store = getMappingStrategy().getStore(); + DBType idType = store.getIDHandler().getDBType(); + int idLength = store.getIDColumnLength(); + + IDBDatabase database = getMappingStrategy().getStore().getDatabase(); + table = database.getSchema().getTable(tableName); + if (table == null) + { + table = database.getSchemaTransaction().getWorkingCopy().addTable(tableName); + table.addField(FEATUREMAP_REVISION_ID, idType, idLength); + table.addField(LIST_REVISION_BRANCH, DBType.INTEGER); + table.addField(FEATUREMAP_VERSION_ADDED, DBType.INTEGER); + table.addField(FEATUREMAP_VERSION_REMOVED, DBType.INTEGER); + table.addField(FEATUREMAP_IDX, DBType.INTEGER); + table.addField(FEATUREMAP_TAG, idType, idLength); + + tagMap = CDOIDUtil.createMap(); + typeMappings = CDOIDUtil.createMap(); + columnNames = new ArrayList(); + + initTypeColumns(true); + + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_REVISION_ID); + table.addIndex(Type.NON_UNIQUE, LIST_REVISION_BRANCH); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_VERSION_ADDED); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_VERSION_REMOVED); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_IDX); + table.addIndex(Type.NON_UNIQUE, FEATUREMAP_TAG); + } + else + { + initTypeColumns(false); + } + } + + private void initTypeColumns(boolean create) + { + for (DBType type : getDBTypes()) + { + String column = FEATUREMAP_VALUE + "_" + type.name(); + if (create) + { + table.addField(column, type); + } + + columnNames.add(column); + } + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + + builder.append(FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + Iterator 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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + 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(FEATUREMAP_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(", "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + } + + public Collection getDBTables() + { + return Collections.singleton(table); + } + + protected List getDBTypes() + { + return dbTypes; + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List getColumnNames() + { + return columnNames; + } + + protected final Map getTypeMappings() + { + return typeMappings; + } + + protected final Map getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList list = revision.getListOrNull(getFeature()); + if (list == null) + { + // Nothing to read take shortcut. + return; + } + + 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); + } + + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + + try + { + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + 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); + DBUtil.close(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 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); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List 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); + } + + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = chunkReader.getAccessor().getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + IStoreChunkReader baseReader = null; + + try + { + 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, //$NON-NLS-1$ + nextListIndex); + } + + 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); + DBUtil.close(stmt); + } + + // now read missing values from base revision. + if (baseReader != null) + { + List baseChunks = baseReader.executeRead(); + + Iterator 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.getListOrNull(getFeature()); + if (values != null) + { + 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(), //$NON-NLS-1$ + getFeature(), idx, revision, value); + } + + 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; + } + + private void addFeature(CDOID tag) + { + EStructuralFeature modelFeature = getFeatureByTag(tag); + + ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature); + String column = FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); //$NON-NLS-1$ + + tagMap.put(tag, column); + typeMapping.setDBField(table, column); + typeMappings.put(tag, 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) + { + // 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); + } + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClearList, ReuseProbability.HIGH); + + try + { + // clear rest of the list + stmt.setInt(1, newVersion); + idHandler.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 + { + DBUtil.close(stmt); + } + } + + 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.size(getFeature()) - 1, revised); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, final int newVersion, long created, + CDOListFeatureDelta delta) + { + List listChanges = delta.getListChanges(); + if (listChanges.size() == 0) + { + // nothing to do. + return; + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + int oldListSize = originalRevision.size(getFeature()); + + 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.size(getFeature()) - 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(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int version, int index, Object value, long timestamp) + { + 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); + } + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded, int versionRemoved, int index, Object value, + long timestamp) + { + 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); + } + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + ITypeMapping typeMapping = getTypeMapping(tag); + String columnName = getColumnName(tag); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.HIGH); + + try + { + 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)) + { + typeMapping.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 + { + DBUtil.close(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int index, long timestamp) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + try + { + // try to delete a temporary entry first + 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 + DBUtil.close(stmt); + stmt = accessor.getDBConnection().prepareStatement(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 + { + DBUtil.close(stmt); + } + } + + private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlGetValue, ReuseProbability.HIGH); + FeatureMap.Entry result = null; + + try + { + 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 + { + DBUtil.close(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 index in revision with ID id in the parent branch at + * the base of this branch (indicated by branchID). + */ + private FeatureMap.Entry getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index) + { + IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID); + chunkReader.addSimpleChunk(index); + List chunks = chunkReader.executeRead(); + return (FeatureMap.Entry)chunks.get(0).get(0); + } + + private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID) + { + InternalRepository repository = (InternalRepository)accessor.getStore().getRepository(); + + CDOBranchManager branchManager = repository.getBranchManager(); + CDOBranch branch = branchManager.getBranch(branchID); + CDOBranchPoint base = branch.getBase(); + if (base.getBranch() == null) + { + throw new IllegalArgumentException("Base branch is null: " + branch); + } + + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + InternalCDORevision baseRevision = revisionManager.getRevision(id, base, 0, CDORevision.DEPTH_NONE, true); + + return accessor.createChunkReader(baseRevision, getFeature()); + } + + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java new file mode 100644 index 000000000..5972c1555 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010-2013, 2018 Eike Stepper (Loehne, 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.branch.CDOBranch; +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.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBTable; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * 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 String sqlClear; + + public BranchingListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected void initSQLStrings() + { + super.initSQLStrings(); + + IDBTable table = getTable(); + + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(table); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected void addKeyFields(List list) + { + list.add(new FieldInfo(LIST_REVISION_BRANCH, DBType.INTEGER)); + list.add(new FieldInfo(LIST_REVISION_VERSION, DBType.INTEGER)); + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + idHandler.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 + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + if (getTable() == null) + { + initTable(accessor); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClear, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branch.getID()); + stmt.setInt(3, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java new file mode 100644 index 000000000..8aa6f61f7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java @@ -0,0 +1,1510 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +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.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.StoreThreadLocal; +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.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.bundle.OM; +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.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +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.collection.MoveableList; +import org.eclipse.net4j.util.collection.Pair; +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.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +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 AbstractBasicListTableMapping 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); + + IDBStoreAccessor accessor = null; + if (AbstractHorizontalMappingStrategy.isEagerTableCreation(mappingStrategy)) + { + accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + } + + initTable(accessor); + } + + private void initTable(IDBStoreAccessor accessor) + { + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + typeMapping = getMappingStrategy().createValueMapping(getFeature()); + + IDBStore store = getMappingStrategy().getStore(); + DBType idType = store.getIDHandler().getDBType(); + int idLength = store.getIDColumnLength(); + + IDBDatabase database = getMappingStrategy().getStore().getDatabase(); + table = database.getSchema().getTable(tableName); + if (table == null) + { + if (accessor != null) + { + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + + try + { + IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); + IDBTable table = workingCopy.addTable(tableName); + table.addField(LIST_REVISION_ID, idType, idLength, true); + table.addField(LIST_REVISION_BRANCH, DBType.INTEGER, true); + table.addField(LIST_REVISION_VERSION_ADDED, DBType.INTEGER); + table.addField(LIST_REVISION_VERSION_REMOVED, DBType.INTEGER); + table.addField(LIST_IDX, DBType.INTEGER, true); + + // TODO think about indexes + table.addIndex(Type.NON_UNIQUE, LIST_REVISION_ID, LIST_REVISION_BRANCH, LIST_REVISION_VERSION_ADDED, LIST_REVISION_VERSION_REMOVED, LIST_IDX); + + typeMapping.createDBField(table, LIST_VALUE); + + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + + initTable(null); + accessor.tableCreated(table); + } + } + else + { + typeMapping.setDBField(table, LIST_VALUE); + initSQLStrings(); + } + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + 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(LIST_REVISION_ID); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(","); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(","); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + } + + public Collection getDBTables() + { + return Collections.singleton(table); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, final int listChunk) + { + if (table == null) + { + // Nothing to read. Take shortcut. + return; + } + + MoveableList list = revision.getListOrNull(getFeature()); + if (list == null) + { + // Nothing to read take shortcut. + return; + } + + 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); + } + + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); + ResultSet resultSet = null; + ArrayList> toReadFromBase = null; // list of chunks to be read from base revision + + try + { + 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 (toReadFromBase == null) + { + toReadFromBase = new ArrayList>(); + } + toReadFromBase.add(Pair.create(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 (toReadFromBase == null) + { + toReadFromBase = new ArrayList>(); + } + toReadFromBase.add(Pair.create(currentIndex, currentIndex + valuesToRead)); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + + // read missing values from base revision ... + if (toReadFromBase != null) + { + IStoreChunkReader baseReader = createBaseChunkReader(accessor, id, branchID); + + 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()); + } + + for (Pair range : toReadFromBase) + { + baseReader.addRangedChunk(range.getElement1(), range.getElement2()); + } + + List 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 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()); + } + + CDORevision revision = chunkReader.getRevision(); + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = chunkReader.getAccessor().getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + IStoreChunkReader baseReader = null; + + try + { + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branchID); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.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(), id, branchID); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, //$NON-NLS-1$ + nextListIndex); + } + + 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(), id, branchID); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, //$NON-NLS-1$ + chunk.getStartIndex() + chunk.size()); + } + + baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size()); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + + // now read missing values from base revision. + if (baseReader != null) + { + List baseChunks = baseReader.executeRead(); + + Iterator 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(), revision); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getListOrNull(getFeature()); + if (values != null && !values.isEmpty()) + { + if (table == null) + { + initTable(accessor); + } + + 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) + { + // 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)); + } + } + + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClearList, ReuseProbability.HIGH); + + try + { + // clear rest of the list + 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 + { + DBUtil.close(stmt); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + ITransaction transaction = accessor.getTransaction(); + InternalCDORevision revision = (InternalCDORevision)transaction.getRevision(id); + if (revision == null) + { + // This must be an attempt to resurrect an object, i.e., revise its detached revision + return; + } + + if (table == null) + { + initTable(accessor); + } + + if (TRACER.isEnabled()) + { + TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$ + } + + int branchID = transaction.getBranch().getID(); + int version = revision.getVersion(); + int lastIndex = revision.size(getFeature()) - 1; + + clearList(accessor, id, branchID, version, FINAL_VERSION, lastIndex); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, final int oldVersion, final int newVersion, long created, + CDOListFeatureDelta delta) + { + List listChanges = delta.getListChanges(); + int size = listChanges.size(); + if (size == 0) + { + // nothing to do. + return; + } + + if (table == null) + { + initTable(accessor); + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + int oldListSize = originalRevision.size(getFeature()); + + 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 = size - 1; + while (index > 0) + { + CDOFeatureDelta listDelta = listChanges.get(index); + if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta) + { + break; + } + + index--; + } + while (index < size) + { + listChanges.get(index++).accept(visitor); + } + + visitor.finishPendingRemove(); + } + + /** + * @author Stefan Winkler + * @author Andras Peteri + */ + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private CDOID id; + + private int branchID; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private int lastRemovedIndex; + + 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.size(getFeature()) - 1; + lastRemovedIndex = -1; + } + + public void visit(CDOAddFeatureDelta delta) + { + finishPendingRemove(); + 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) + { + finishPendingRemove(); + lastRemovedIndex = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", lastRemovedIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, lastRemovedIndex); + } + + public void visit(CDOSetFeatureDelta delta) + { + finishPendingRemove(); + 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 (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex); + lastIndex = -1; + lastRemovedIndex = -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; + lastRemovedIndex = -1; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + // optimization: a move from the end of the list to an index that was just removed requires no shifting + boolean optimizeMove = lastRemovedIndex != -1 && fromIdx == lastIndex - 1 && toIdx == lastRemovedIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + // items after a pending remove have an index offset by one + if (optimizeMove) + { + fromIdx++; + } + else + { + finishPendingRemove(); + } + + 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 for regular moves + if (!optimizeMove) + { + 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); + } + } + else + { + // finish the optimized move by resetting lastRemovedIndex + lastRemovedIndex = -1; + --lastIndex; + } + + // create the item + addEntry(accessor, id, branchID, newVersion, toIdx, value); + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void finishPendingRemove() + { + if (lastRemovedIndex != -1) + { + int startIndex = lastRemovedIndex; + int endIndex = lastIndex; + + // make room for the new item + moveOneUp(accessor, id, branchID, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + lastRemovedIndex = -1; + } + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int version, int index, Object value) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertEntry, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded, int versionRemoved, int index, Object value) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertEntry, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int index) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + try + { + // Try to delete a temporary entry first + 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 + DBUtil.close(stmt); + stmt = accessor.getDBConnection().prepareStatement(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 + { + DBUtil.close(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 true, 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 + * false, null is returned in the former case. + */ + private Object getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlGetValue, ReuseProbability.HIGH); + Object result = null; + + try + { + 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 + { + DBUtil.close(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 index in revision with ID id in the parent branch at + * the base of this branch (indicated by branchID). + */ + private Object getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index) + { + IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID); + chunkReader.addSimpleChunk(index); + List chunks = chunkReader.executeRead(); + return chunks.get(0).get(0); + } + + private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID) + { + InternalRepository repository = (InternalRepository)accessor.getStore().getRepository(); + + CDOBranchManager branchManager = repository.getBranchManager(); + CDOBranch branch = branchManager.getBranch(branchID); + CDOBranchPoint base = branch.getBase(); + if (base.getBranch() == null) + { + throw new IllegalArgumentException("Base branch is null: " + branch); + } + + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + InternalCDORevision baseRevision = revisionManager.getRevision(id, base, 0, CDORevision.DEPTH_NONE, true); + + return accessor.createChunkReader(baseRevision, getFeature()); + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, QueryXRefsContext context, String idString) + { + if (table == null) + { + // Nothing to read. Take shortcut. + return true; + } + + 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(LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" 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(LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java new file mode 100644 index 000000000..453b6c292 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2010-2013, 2016 Eike Stepper (Loehne, 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 boolean putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type) + { + CDOID classID = getMetaDataManager().getMetaID(type, timeStamp); + if (!doPutObjectType(accessor, id, classID)) + { + return false; + } + + return delegate.putObjectType(accessor, timeStamp, id, type); + } + + public boolean removeObjectType(IDBStoreAccessor accessor, CDOID id) + { + doRemoveObjectType(accessor, id); + return 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 doGetMaxID(Connection connection, IIDHandler idHandler); + + protected abstract CDOID doGetObjectType(IDBStoreAccessor accessor, CDOID id); + + protected abstract boolean doPutObjectType(IDBStoreAccessor accessor, CDOID id, CDOID type); + + protected abstract boolean doRemoveObjectType(IDBStoreAccessor accessor, CDOID id); +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/FieldInfo.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/FieldInfo.java new file mode 100644 index 000000000..34aeb2ea3 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/FieldInfo.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; + +/** + * Used by subclasses to indicate which fields should be in the table. I.e. just a triple of name, DBType and precision. + * + * @author Stefan Winkler + */ +public final class FieldInfo +{ + private final String name; + + private final DBType type; + + private final int precision; + + public FieldInfo(String name, DBType type, int precision) + { + this.name = name; + this.type = type; + this.precision = precision; + } + + public FieldInfo(String name, DBType type) + { + this(name, type, IDBField.DEFAULT); + } + + public String getName() + { + return name; + } + + public DBType getType() + { + return type; + } + + public int getPrecision() + { + return precision; + } + + @Override + public String toString() + { + return "FieldInfo[name=" + name + ", type=" + type + ", precision=" + precision + "]"; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java new file mode 100644 index 000000000..25d430915 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2009-2016, 2018 Eike Stepper (Loehne, 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.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +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.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.IClassMappingAuditSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingUnitSupport; +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.IListMappingUnitSupport; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.DBStore; +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.emf.cdo.spi.common.revision.StubCDORevision; +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.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBResultSet; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; +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.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping + implements IClassMappingAuditSupport, IClassMappingDeltaSupport, IClassMappingUnitSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalAuditClassMapping.class); + + private static final ContextTracer TRACER_UNITS = new ContextTracer(OM.DEBUG_UNITS, HorizontalAuditClassMapping.class); + + private String sqlInsertAttributes; + + private String sqlSelectAttributesCurrent; + + private String sqlSelectAttributesByTime; + + private String sqlSelectAttributesByVersion; + + private String sqlSelectUnitByTime; + + private String sqlSelectAllObjectIDs; + + private String sqlReviseAttributes; + + private String sqlRawDeleteAttributes; + + public HorizontalAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + } + + @Override + protected void initSQLStrings() + { + super.initSQLStrings(); + + // ----------- Select Revision --------------------------- + String[] strings = buildSQLSelects(false); + String sqlSelectAttributesPrefix = strings[0]; + sqlSelectAttributesCurrent = strings[1]; + sqlSelectAttributesByTime = strings[2]; + + StringBuilder builder = new StringBuilder(sqlSelectAttributesPrefix); + builder.append("ABS("); + builder.append(ATTRIBUTES_VERSION); + builder.append(")=?"); //$NON-NLS-1$ + sqlSelectAttributesByVersion = builder.toString(); + + InternalRepository repository = (InternalRepository)getMappingStrategy().getStore().getRepository(); + if (repository.isSupportingUnits()) + { + strings = buildSQLSelects(true); + sqlSelectUnitByTime = strings[2]; + } + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append("("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + appendTypeMappingParameters(builder, getValueMappings()); + appendFieldParameters(builder, getUnsettableFields()); + appendFieldParameters(builder, getListSizeFields()); + 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(ATTRIBUTES_REVISED); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlSelectAllObjectIDs = builder.toString(); + + // ----------- Raw delete one specific revision ------ + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlRawDeleteAttributes = builder.toString(); + } + + @Override + protected void appendSelectForHandleFields(StringBuilder builder) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + } + + private String[] buildSQLSelects(boolean forUnits) + { + String[] strings = new String[3]; + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + if (forUnits) + { + builder.append(ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + } + + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + + if (forUnits) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("="); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS); + builder.append("."); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS_ELEM); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS); + builder.append("."); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS_UNIT); + builder.append("=?"); //$NON-NLS-1$ + builder.append(" AND "); //$NON-NLS-1$ + } + else + { + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + } + + strings[0] = builder.toString(); + + builder.append(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + + if (forUnits) + { + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + } + + strings[1] = builder.toString(); + + builder = new StringBuilder(strings[0]); + builder.append("("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + + if (forUnits) + { + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + } + + strings[2] = builder.toString(); + + return strings; + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = null; + + try + { + long timeStamp = revision.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setLong(2, timeStamp); + stmt.setLong(3, timeStamp); + } + else + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectAttributesCurrent, 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 + { + DBUtil.close(stmt); + } + } + + public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + public IDBPreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, boolean exactMatch, + CDOBranchPoint branchPoint) + { + if (getTable() == null) + { + return null; + } + + 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(ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(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(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.MEDIUM); + + try + { + int column = 1; + 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); //$NON-NLS-1$ + } + + return stmt; + } + catch (Throwable ex) + { + DBUtil.close(stmt); // only release on error + throw new DBException(ex); + } + } + + public IDBPreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + return accessor.getDBConnection().prepareStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + @Override + protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + try + { + int column = 1; + 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 listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getListOrNull(feature); + int size = list == null ? UNSET_LIST : list.size(); + stmt.setInt(column++, size); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + try + { + 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 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 + { + DBUtil.close(stmt); + } + } + + @Override + protected void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor fork) + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlRawDeleteAttributes, ReuseProbability.HIGH); + + try + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlReviseAttributes, ReuseProbability.HIGH); + + try + { + stmt.setLong(1, revised); + idHandler.setCDOID(stmt, 2, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + + FeatureDeltaWriter writer = new FeatureDeltaWriter(); + 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(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + } + else + { + builder.append(ATTRIBUTES_CREATED); + builder.append("<="); + builder.append(timeStamp); + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">="); + builder.append(timeStamp); + builder.append(")"); //$NON-NLS-1$ + } + + return builder.toString(); + } + + public void readUnitRevisions(IDBStoreAccessor accessor, CDOBranchPoint branchPoint, CDOID rootID, CDORevisionHandler revisionHandler) throws SQLException + { + DBStore store = (DBStore)getMappingStrategy().getStore(); + InternalRepository repository = store.getRepository(); + + CDOBranchPoint head = repository.getBranchManager().getMainBranch().getHead(); + EClass eClass = getEClass(); + long timeStamp = branchPoint.getTimeStamp(); + + IIDHandler idHandler = store.getIDHandler(); + IDBPreparedStatement stmt = null; + + int jdbcFetchSize = store.getJDBCFetchSize(); + int oldFetchSize = -1; + + final long start1 = TRACER_UNITS.isEnabled() ? System.currentTimeMillis() : CDOBranchPoint.UNSPECIFIED_DATE; + + try + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectUnitByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, rootID); + stmt.setLong(2, timeStamp); + stmt.setLong(3, timeStamp); + + AsnychronousListFiller listFiller = new AsnychronousListFiller(accessor, timeStamp, rootID, revisionHandler); + ConcurrencyUtil.execute(repository, listFiller); + + oldFetchSize = stmt.getFetchSize(); + stmt.setFetchSize(jdbcFetchSize); + IDBResultSet resultSet = stmt.executeQuery(); + + for (;;) + { + InternalCDORevision revision = store.createRevision(eClass, null); + revision.setBranchPoint(head); + + if (!readValuesFromResultSet(resultSet, idHandler, revision, true)) + { + break; + } + + listFiller.schedule(revision); + } + + final long start2 = start1 != CDOBranchPoint.UNSPECIFIED_DATE ? System.currentTimeMillis() : start1; + + listFiller.await(); + + if (start1 != CDOBranchPoint.UNSPECIFIED_DATE) + { + TRACER_UNITS.format("Read {0} revisions of unit {1}: {2} millis + {3} millis", eClass.getName(), rootID, start2 - start1, + System.currentTimeMillis() - start2); + } + } + finally + { + if (oldFetchSize != -1) + { + stmt.setFetchSize(oldFetchSize); + } + + DBUtil.close(stmt); + } + } + + private class AsnychronousListFiller implements Runnable + { + private final BlockingQueue queue = new LinkedBlockingQueue(); + + private final CountDownLatch latch = new CountDownLatch(1); + + private final IDBStoreAccessor accessor; + + private final long timeStamp; + + private final CDOID rootID; + + private final DBStore store; + + private final IIDHandler idHandler; + + private final IListMappingUnitSupport[] listMappings; + + private final ResultSet[] resultSets; + + private final CDORevisionHandler revisionHandler; + + private Throwable exception; + + public AsnychronousListFiller(IDBStoreAccessor accessor, long timeStamp, CDOID rootID, CDORevisionHandler revisionHandler) + { + this.accessor = accessor; + this.timeStamp = timeStamp; + this.rootID = rootID; + this.revisionHandler = revisionHandler; + + store = (DBStore)accessor.getStore(); + idHandler = store.getIDHandler(); + + List tmp = getListMappings(); + int size = tmp.size(); + + listMappings = new IListMappingUnitSupport[size]; + resultSets = new ResultSet[size]; + + int i = 0; + for (IListMapping listMapping : tmp) + { + listMappings[i++] = (IListMappingUnitSupport)listMapping; + } + } + + public void schedule(InternalCDORevision revision) + { + queue.offer(revision); + } + + public void await() throws SQLException + { + // Schedule an end marker revision. + schedule(new StubCDORevision(getEClass())); + + try + { + latch.await(); + } + catch (InterruptedException ex) + { + throw new TimeoutRuntimeException(); + } + finally + { + for (ResultSet resultSet : resultSets) + { + if (resultSet != null) + { + Statement statement = resultSet.getStatement(); + DBUtil.close(statement); + } + } + } + + if (exception instanceof RuntimeException) + { + throw (RuntimeException)exception; + } + + if (exception instanceof Error) + { + throw (Error)exception; + } + + if (exception instanceof SQLException) + { + throw (SQLException)exception; + } + + if (exception instanceof Exception) + { + throw WrappedException.wrap((Exception)exception); + } + } + + public void run() + { + StoreThreadLocal.setAccessor(accessor); + + try + { + while (store.isActive()) + { + InternalCDORevision revision = queue.poll(1, TimeUnit.SECONDS); + if (revision == null) + { + continue; + } + + if (revision instanceof StubCDORevision) + { + return; + } + + readUnitEntries(revision); + } + } + catch (Throwable ex) + { + exception = ex; + } + finally + { + latch.countDown(); + StoreThreadLocal.remove(); + } + } + + private void readUnitEntries(InternalCDORevision revision) throws SQLException + { + CDOID id = revision.getID(); + + for (int i = 0; i < listMappings.length; i++) + { + IListMappingUnitSupport listMapping = listMappings[i]; + EStructuralFeature feature = listMapping.getFeature(); + + MoveableList list = revision.getListOrNull(feature); + if (list != null) + { + int size = list.size(); + if (size != 0) + { + if (resultSets[i] == null) + { + resultSets[i] = listMapping.queryUnitEntries(accessor, idHandler, timeStamp, rootID); + } + + listMapping.readUnitEntries(resultSets[i], idHandler, id, list); + } + } + } + + synchronized (revisionHandler) + { + revisionHandler.handleRevision(revision); + } + } + } + + /** + * @author Stefan Winkler + */ + private final class FeatureDeltaWriter extends AbstractFeatureDeltaWriter + { + private int oldVersion; + + private InternalCDORevision newRevision; + + private int branchId; + + @Override + protected void doProcess(InternalCDORevisionDelta delta) + { + 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(CDOSetFeatureDelta delta) + { + delta.applyTo(newRevision); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + delta.applyTo(newRevision); + } + + public void visit(CDOListFeatureDelta delta) + { + delta.applyTo(newRevision); + + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature()); + listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta); + } + + public void visit(CDOContainerFeatureDelta delta) + { + delta.applyTo(newRevision); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java new file mode 100644 index 000000000..449da9a94 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2009-2013, 2018 Eike Stepper (Loehne, 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 HorizontalAuditMappingStrategy extends AbstractHorizontalMappingStrategy +{ + public HorizontalAuditMappingStrategy() + { + } + + public boolean hasAuditSupport() + { + return true; + } + + public boolean hasBranchingSupport() + { + return false; + } + + public boolean hasDeltaSupport() + { + return false; + } + + @Override + protected IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalAuditClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditListTableMapping(this, containingClass, feature); + } + + @Deprecated + @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); + return modifyListJoin(attrTable, listTable, join); + } + + protected String modifyListJoin(String attrTable, String listTable, String join) + { + join += " AND " + attrTable + "." + ATTRIBUTES_VERSION; + join += "=" + listTable + "." + LIST_REVISION_VERSION; + return join; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java new file mode 100644 index 000000000..cb7330b32 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010-2013, 2018 Eike Stepper (Loehne, 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.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalAuditMappingStrategyWithRanges extends HorizontalAuditMappingStrategy +{ + public HorizontalAuditMappingStrategyWithRanges() + { + } + + @Override + public boolean hasDeltaSupport() + { + return true; + } + + @Override + protected IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalAuditClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditListTableMappingWithRanges(this, containingClass, feature); + } + + @Deprecated + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditFeatureMapTableMappingWithRanges(this, containingClass, feature); + } + + @Override + protected String modifyListJoin(String attrTable, String listTable, String join) + { + join += " AND " + listTable + "." + LIST_REVISION_VERSION_ADDED; + join += "<=" + attrTable + "." + ATTRIBUTES_VERSION; + join += " AND (" + listTable + "." + LIST_REVISION_VERSION_REMOVED; + join += " IS NULL OR " + listTable + "." + LIST_REVISION_VERSION_REMOVED; + join += ">" + attrTable + "." + ATTRIBUTES_VERSION + ")"; + return join; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java new file mode 100644 index 000000000..0d7287c44 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java @@ -0,0 +1,1053 @@ +/* + * Copyright (c) 2010-2016, 2018 Eike Stepper (Loehne, 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.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.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +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.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.bundle.OM; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +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.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.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 +{ + 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 sqlRawDeleteAttributes; + + public HorizontalBranchingClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + } + + @Override + protected IDBField addBranchField(IDBTable table) + { + return table.addField(ATTRIBUTES_BRANCH, DBType.INTEGER, true); + } + + @Override + protected void initSQLStrings() + { + super.initSQLStrings(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_BRANCH); + builder.append("=? AND ("); //$NON-NLS-1$ + String sqlSelectAttributesPrefix = builder.toString(); + builder.append(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + sqlSelectCurrentAttributes = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + builder.append(ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + sqlSelectAttributesByTime = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + builder.append("ABS("); //$NON-NLS-1$ + builder.append(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(ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + appendTypeMappingParameters(builder, getValueMappings()); + appendFieldParameters(builder, getUnsettableFields()); + appendFieldParameters(builder, getListSizeFields()); + 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(ATTRIBUTES_REVISED); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlSelectAllObjectIDs = builder.toString(); + + // ----------- Raw delete one specific revision ------ + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlRawDeleteAttributes = builder.toString(); + } + + @Override + protected void appendSelectForHandleFields(StringBuilder builder) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + long timeStamp = revision.getTimeStamp(); + int branchID = revision.getBranch().getID(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = null; + boolean success = false; + + try + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, branchID); + stmt.setLong(3, timeStamp); + stmt.setLong(4, timeStamp); + } + else + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, branchID); + } + + // Read singleval-attribute table always (even without modeled attributes!) + success = readValuesFromStatement(stmt, revision, accessor); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + + // Read multival tables only if revision exists + if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + + public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH); + boolean success = false; + + try + { + 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!) + success = readValuesFromStatement(stmt, revision, accessor); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + + // Read multival tables only if revision exists + if (success) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + + public IDBPreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, boolean exactMatch, + CDOBranchPoint branchPoint) + { + if (getTable() == null) + { + return null; + } + + 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(ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.MEDIUM); + + try + { + int column = 1; + 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 (Throwable ex) + { + DBUtil.close(stmt); // only release on error + throw new DBException(ex); + } + } + + public IDBPreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + return accessor.getDBConnection().prepareStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + @Override + protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + try + { + int column = 1; + 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 listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getListOrNull(feature); + int size = list == null ? UNSET_LIST : list.size(); + stmt.setInt(column++, size); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + try + { + 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 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 + { + DBUtil.close(stmt); + } + } + + @Override + protected void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor fork) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlRawDeleteAttributes, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branch.getID()); + stmt.setInt(3, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlReviseAttributes, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + @Override + public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise, OMMonitor monitor) + { + if (getTable() == null) + { + initTable(accessor); + } + + CDOID id = revision.getID(); + int version = revision.getVersion(); + InternalCDOBranch branch = revision.getBranch(); + long timeStamp = revision.getTimeStamp(); + + // A DetachedCDORevision can only come from DBStoreAccessor.rawStore(). + if (revision instanceof DetachedCDORevision) + { + detachAttributes(accessor, id, version, branch, timeStamp, monitor); + + long revised = revision.getRevised(); + if (revised != CDOBranchPoint.UNSPECIFIED_DATE) + { + reviseOldRevision(accessor, id, branch, revised); + } + + return; + } + + Async async = null; + monitor.begin(10); + + try + { + try + { + async = monitor.forkAsync(); + if (mapType) + { + // Put new objects into objectTypeMapper + EClass eClass = getEClass(); + + AbstractHorizontalMappingStrategy mappingStrategy = (AbstractHorizontalMappingStrategy)getMappingStrategy(); + if (!mappingStrategy.putObjectType(accessor, timeStamp, id, eClass)) + { + mapType = false; + } + } + + if (!mapType && revise && version > CDOBranchVersion.FIRST_VERSION) + { + // If revision is not the first one, revise the old revision + long revised = timeStamp - 1; + + reviseOldRevision(accessor, id, branch, revised); + for (IListMapping mapping : getListMappings()) + { + mapping.objectDetached(accessor, id, revised); + } + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + async = monitor.forkAsync(); + if (revision.isResourceNode()) + { + 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) + { + if (getTable() == null) + { + return; + } + + StringBuilder builder = new StringBuilder(getSQLSelectForHandle()); + boolean whereAppend = false; + + if (branch != null) + { + // TODO: Prepare this string literal + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(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(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(ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=?)"); //$NON-NLS-1$ + timeParameters = 2; + } + else + { + builder.append(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + } + } + } + + IRepository repository = accessor.getStore().getRepository(); + CDORevisionManager revisionManager = repository.getRevisionManager(); + CDOBranchManager branchManager = repository.getBranchManager(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + CDOBranch revisionBranch = branchManager.getBranch(resultSet.getInt(3)); + + if (version >= CDOBranchVersion.FIRST_VERSION) + { + CDOBranchVersion branchVersion = revisionBranch.getVersion(version); + InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchVersion, CDORevision.UNCHUNKED, true); + + if (!handler.handleRevision(revision)) + { + break; + } + } + else + { + EClass eClass = getEClass(); + long created = resultSet.getLong(4); + long revised = resultSet.getLong(5); + + // Tell handler about detached IDs + InternalCDORevision revision = new DetachedCDORevision(eClass, id, revisionBranch, -version, created, revised); + if (!handler.handleRevision(revision)) + { + break; + } + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + @Override + public Set readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments) + { + Set result = new HashSet(); + if (getTable() == null) + { + return result; + } + + StringBuilder builder = new StringBuilder(getSQLSelectForChangeSet()); + boolean isFirst = true; + + for (int i = 0; i < segments.length; i++) + { + if (isFirst) + { + isFirst = false; + } + else + { + builder.append(" OR "); //$NON-NLS-1$ + } + + builder.append(ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + + builder.append(ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.LOW); + ResultSet resultSet = null; + + try + { + 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); + DBUtil.close(stmt); + } + } + + @Override + protected String getListXRefsWhere(QueryXRefsContext context) + { + StringBuilder builder = new StringBuilder(); + builder.append(ATTRIBUTES_BRANCH); + builder.append("="); + builder.append(context.getBranch().getID()); + builder.append(" AND ("); + + long timeStamp = context.getTimeStamp(); + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(ATTRIBUTES_CREATED); + builder.append("<="); + builder.append(timeStamp); + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(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 = new FeatureDeltaWriter(); + 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 + repository.ensureChunks(oldRevision, CDORevision.UNCHUNKED); + + InternalCDORevision newRevision = oldRevision.copy(); + newRevision.adjustForCommit(accessor.getTransaction().getBranch(), created); + delta.applyTo(newRevision); + monitor.worked(); + writeRevision(accessor, newRevision, false, true, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + /** + * @author Stefan Winkler + */ + private final class FeatureDeltaWriter extends AbstractFeatureDeltaWriter + { + private CDOBranch targetBranch; + + private int oldVersion; + + private int newVersion; + + private InternalCDORevision newRevision; + + @Override + protected void doProcess(InternalCDORevisionDelta delta) + { + 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(CDOSetFeatureDelta delta) + { + delta.applyTo(newRevision); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + delta.applyTo(newRevision); + } + + public void visit(CDOListFeatureDelta delta) + { + delta.applyTo(newRevision); + + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature()); + listMapping.processDelta(accessor, id, targetBranch.getID(), oldVersion, newVersion, created, delta); + } + + public void visit(CDOContainerFeatureDelta delta) + { + delta.applyTo(newRevision); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java new file mode 100644 index 000000000..fab24dc87 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2010-2013, 2016, 2018 Eike Stepper (Loehne, 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.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.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 + protected IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalBranchingClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingListTableMapping(this, containingClass, feature); + } + + @Deprecated + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingFeatureMapTableMapping(this, containingClass, feature); + } + + @Override + public String getListJoin(String attrTable, String listTable) + { + String join = getListJoinBasic(attrTable, listTable); + return modifyListJoin(attrTable, listTable, join, false); + } + + @Override + protected String getListJoinForRawExport(String attrTable, String listTable) + { + String join = getListJoinBasic(attrTable, listTable); + return modifyListJoin(attrTable, listTable, join, true); + } + + protected String getListJoinBasic(String attrTable, String listTable) + { + return super.getListJoin(attrTable, listTable); + } + + protected String modifyListJoin(String attrTable, String listTable, String join, boolean forRawExport) + { + join += " AND " + attrTable + "." + ATTRIBUTES_VERSION; + join += "=" + listTable + "." + LIST_REVISION_VERSION; + join += " AND " + attrTable + "." + ATTRIBUTES_BRANCH; + join += "=" + listTable + "." + LIST_REVISION_BRANCH; + return join; + } + + @Override + protected void rawImportReviseOldRevisions(IDBConnection connection, IDBTable table, OMMonitor monitor) + { + String sqlUpdate = "UPDATE " + table + " SET " + ATTRIBUTES_REVISED + "=? WHERE " + ATTRIBUTES_ID + "=? AND " + ATTRIBUTES_BRANCH + "=? AND " + + ATTRIBUTES_VERSION + "=?"; + + String sqlQuery = "SELECT cdo1." + ATTRIBUTES_ID + ", cdo1." + ATTRIBUTES_BRANCH + ", cdo1." + ATTRIBUTES_VERSION + ", cdo2." + ATTRIBUTES_CREATED + + " FROM " + table + " cdo1, " + table + " cdo2 WHERE cdo1." + ATTRIBUTES_ID + "=cdo2." + ATTRIBUTES_ID + " AND cdo1." + ATTRIBUTES_BRANCH + "=cdo2." + + ATTRIBUTES_BRANCH + " AND (cdo1." + ATTRIBUTES_VERSION + "=cdo2." + ATTRIBUTES_VERSION + "-1 OR (cdo1." + ATTRIBUTES_VERSION + "+cdo2." + + ATTRIBUTES_VERSION + "=-1 AND cdo1." + ATTRIBUTES_VERSION + ">cdo2." + ATTRIBUTES_VERSION + ")) AND cdo1." + ATTRIBUTES_REVISED + "=0"; + + IIDHandler idHandler = getStore().getIDHandler(); + IDBPreparedStatement stmtUpdate = connection.prepareStatement(sqlUpdate, ReuseProbability.MEDIUM); + IDBPreparedStatement stmtQuery = connection.prepareStatement(sqlQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, + ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + 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(IDBConnection connection, IDBTable table, long fromCommitTime, long toCommitTime, OMMonitor monitor) + { + String sql = "UPDATE " + table + " SET " + ATTRIBUTES_REVISED + "=0 WHERE " + ATTRIBUTES_BRANCH + ">=0 AND " + ATTRIBUTES_CREATED + "<=" + toCommitTime + + " AND " + ATTRIBUTES_REVISED + ">" + toCommitTime + " AND " + ATTRIBUTES_VERSION + ">0"; + + IDBPreparedStatement stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); + + try + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + stmt.executeUpdate(); + } + finally + { + async.stop(); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + monitor.done(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java new file mode 100644 index 000000000..4879b8ec9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2010-2013, 2016-2018 Eike Stepper (Loehne, 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.common.branch.CDOBranchVersion; +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.server.db.CDODBUtil; +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.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.DBUtil.DeserializeRowHandler; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.io.ExtendedDataInput; +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.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalBranchingMappingStrategyWithRanges extends HorizontalBranchingMappingStrategy +{ + private boolean copyOnBranch; + + public HorizontalBranchingMappingStrategyWithRanges() + { + } + + @Override + public boolean hasDeltaSupport() + { + return true; + } + + public boolean shallCopyOnBranch() + { + return copyOnBranch; + } + + @Override + protected IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalBranchingClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingListTableMappingWithRanges(this, containingClass, feature); + } + + @Deprecated + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingFeatureMapTableMappingWithRanges(this, containingClass, feature); + } + + @Override + protected void rawExportList(CDODataOutput out, IDBConnection connection, IListMapping listMapping, IDBTable attrTable, String attrSuffix) throws IOException + { + super.rawExportList(out, connection, listMapping, attrTable, attrSuffix); + + for (IDBTable table : listMapping.getDBTables()) + { + rawExportListPostProcess(out, connection, attrTable, attrSuffix, table); + } + } + + private void rawExportListPostProcess(CDODataOutput out, IDBConnection connection, IDBTable attrTable, String attrSuffix, IDBTable table) throws IOException + { + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); + builder.append(LIST_REVISION_ID); + builder.append(", l_t."); + builder.append(LIST_REVISION_BRANCH); + builder.append(", l_t."); + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append(", l_t."); + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(", l_t."); + builder.append(LIST_IDX); + builder.append(" FROM "); + builder.append(table); + builder.append(" l_t, "); + builder.append(attrTable); + builder.append(" a_t"); + builder.append(attrSuffix); + builder.append(getListJoinForPostProcess("a_t", "l_t")); + builder.append(" AND l_t."); + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NOT NULL"); + String sql = DBUtil.trace(builder.toString()); + + IIDHandler idHandler = getStore().getIDHandler(); + IDBPreparedStatement stmt = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + + // Write resultSet size for progress monitoring + int size = DBUtil.getRowCount(resultSet); + out.writeXInt(size); + if (size == 0) + { + return; + } + + while (resultSet.next()) + { + CDOID source = idHandler.getCDOID(resultSet, 1); + int branch = resultSet.getInt(2); + int versionAdded = resultSet.getInt(3); + int versionRemoved = resultSet.getInt(4); + int idx = resultSet.getInt(5); + + out.writeCDOID(source); + out.writeXInt(branch); + out.writeXInt(versionAdded); + out.writeXInt(versionRemoved); + out.writeXInt(idx); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + @Override + protected void rawImportList(CDODataInput in, IDBConnection connection, IListMapping listMapping, OMMonitor monitor) throws IOException + { + Collection tables = listMapping.getDBTables(); + int size = tables.size(); + if (size == 0) + { + return; + } + + monitor.begin(2 * size); + + try + { + super.rawImportList(in, connection, listMapping, monitor.fork(size)); + + for (IDBTable table : tables) + { + rawImportListPostProcess(in, connection, table, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + private void rawImportListPostProcess(CDODataInput in, IDBConnection connection, IDBTable table, OMMonitor monitor) throws IOException + { + int size = in.readXInt(); + if (size == 0) + { + return; + } + + StringBuilder builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(table); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_REMOVED); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + String sql = DBUtil.trace(builder.toString()); + + IIDHandler idHandler = getStore().getIDHandler(); + IDBPreparedStatement stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); + + monitor.begin(1 + 2 * size); + + try + { + monitor.worked(); + + for (int row = 0; row < size; row++) + { + CDOID source = in.readCDOID(); + int branch = in.readXInt(); + int versionAdded = in.readXInt(); + int versionRemoved = in.readXInt(); + int idx = in.readXInt(); + + stmt.setInt(1, versionRemoved); + idHandler.setCDOID(stmt, 2, source); + stmt.setInt(3, branch); + stmt.setInt(4, versionAdded); + stmt.setInt(5, idx); + + stmt.addBatch(); + monitor.worked(); + } + + Async async = monitor.forkAsync(size); + + try + { + stmt.executeBatch(); + } + finally + { + async.stop(); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + monitor.done(); + } + } + + protected String getListJoinForPostProcess(String attrTable, String listTable) + { + String join = getListJoinBasic(attrTable, listTable); + return modifyListJoin2(attrTable, listTable, join, true, true); + } + + @Override + protected String modifyListJoin(String attrTable, String listTable, String join, boolean forRawExport) + { + return modifyListJoin2(attrTable, listTable, join, forRawExport, false); + } + + private String modifyListJoin2(String attrTable, String listTable, String join, boolean forRawExport, boolean forPostProcess) + { + join += " AND " + listTable + "."; + + if (forRawExport) + { + if (forPostProcess) + { + join += LIST_REVISION_VERSION_REMOVED; + } + else + { + join += LIST_REVISION_VERSION_ADDED; + } + + join += "=" + attrTable + "." + ATTRIBUTES_VERSION; + } + else + { + join += LIST_REVISION_VERSION_ADDED; + join += "<=" + attrTable + "." + ATTRIBUTES_VERSION; + join += " AND (" + listTable + "." + LIST_REVISION_VERSION_REMOVED; + join += " IS NULL OR " + listTable + "." + LIST_REVISION_VERSION_REMOVED; + join += ">" + attrTable + "." + ATTRIBUTES_VERSION + ")"; + } + + join += " AND " + attrTable + "." + ATTRIBUTES_BRANCH; + join += "=" + listTable + "." + LIST_REVISION_BRANCH; + + if (forRawExport && !forPostProcess) + { + join += " ORDER BY " + listTable + "." + LIST_REVISION_ID; + join += ", " + listTable + "." + LIST_REVISION_BRANCH; + join += ", " + listTable + "." + LIST_REVISION_VERSION_ADDED; + join += ", " + listTable + "." + LIST_IDX; + } + + return join; + } + + @Override + protected DeserializeRowHandler getImportListHandler() + { + return new ImportListHandler(); + } + + @Override + protected void doAfterActivate() throws Exception + { + super.doAfterActivate(); + + String value = getProperties().get(CDODBUtil.PROP_COPY_ON_BRANCH); + copyOnBranch = value == null ? false : Boolean.valueOf(value); + } + + /** + * @author Eike Stepper + */ + private final class ImportListHandler implements DeserializeRowHandler + { + private final IIDHandler idHandler = getStore().getIDHandler(); + + private IDBPreparedStatement stmt; + + public void handleRow(ExtendedDataInput in, Connection connection, IDBField[] fields, Object[] values) throws SQLException, IOException + { + int versionAdded = DBUtil.asInt(values[2]); + if (versionAdded == CDOBranchVersion.FIRST_VERSION) + { + return; + } + + if (stmt == null) + { + String sql = "UPDATE " + fields[0].getTable() // + + " SET " + LIST_REVISION_VERSION_REMOVED + "=?" // + + " WHERE " + LIST_REVISION_ID + "=?" // + + " AND " + LIST_REVISION_BRANCH + "=?" // + + " AND " + LIST_IDX + "=?" // + + " AND " + LIST_REVISION_VERSION_ADDED + " properties; + + private IDBStore store; + + private IMappingStrategy delegate; + + public HorizontalMappingStrategy() + { + } + + public IMappingStrategy getDelegate() + { + return delegate; + } + + public Map getProperties() + { + if (delegate != null) + { + return delegate.getProperties(); + } + + if (properties != null) + { + return properties; + } + + return new HashMap(); + } + + public void setProperties(Map 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 getClassMappings() + { + return delegate.getClassMappings(); + } + + public Map 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 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 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(IRepository.Props.SUPPORTING_AUDITS); + boolean branching = getBooleanProperty(IRepository.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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java new file mode 100644 index 000000000..88f115837 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.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.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +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.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.DBStore; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.AbstractBasicListTableMapping.AbstractListDeltaWriter.NewListSizeResult; +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.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBField; +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.ResultSet; +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 sqlSelectCurrentVersion; + + private String sqlInsertAttributes; + + private String sqlUpdateAffix; + + private String sqlUpdatePrefix; + + private String sqlUpdateContainerPart; + + private String sqlDelete; + + private boolean hasLists; + + public HorizontalNonAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + } + + @Override + protected void initSQLStrings() + { + hasLists = !getListMappings().isEmpty(); + + super.initSQLStrings(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=?"); //$NON-NLS-1$ + sqlSelectCurrentAttributes = builder.toString(); + + // ----------- Select Version --------------------------- + builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=?"); //$NON-NLS-1$ + sqlSelectCurrentVersion = builder.toString(); + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append("("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + appendTypeMappingParameters(builder, getValueMappings()); + appendFieldParameters(builder, getUnsettableFields()); + appendFieldParameters(builder, getListSizeFields()); + builder.append(")"); //$NON-NLS-1$ + sqlInsertAttributes = builder.toString(); + + // ----------- Select all unrevised Object IDs ------ + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(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(ATTRIBUTES_VERSION); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdatePrefix = builder.toString(); + + builder = new StringBuilder(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateContainerPart = builder.toString(); + + builder = new StringBuilder(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateAffix = builder.toString(); + + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? "); //$NON-NLS-1$ + sqlDelete = builder.toString(); + } + + @Override + protected void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + try + { + int column = 1; + 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 listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getListOrNull(feature); + int size = list == null ? UNSET_LIST : list.size(); + stmt.setInt(column++, size); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + public IDBPreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + return accessor.getDBConnection().prepareStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + public IDBPreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, boolean exactMatch, + CDOBranchPoint branchPoint) + { + if (getTable() == null) + { + return null; + } + + 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(ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(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(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.MEDIUM); + + try + { + int column = 1; + 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 (Throwable ex) + { + DBUtil.close(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$ + } + + CDOID id = revision.getID(); + + DBStore store = (DBStore)getMappingStrategy().getStore(); + IIDHandler idHandler = store.getIDHandler(); + IDBPreparedStatement stmtVersion = null; + IDBPreparedStatement stmtAttributes = accessor.getDBConnection().prepareStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + + try + { + if (hasLists) + { + // Reading all list rows of an object is not atomic. + // After all row reads are done, check the revision version again (see below). + stmtVersion = accessor.getDBConnection().prepareStatement(sqlSelectCurrentVersion, ReuseProbability.HIGH); + stmtVersion.setMaxRows(1); // Optimization: only 1 row + idHandler.setCDOID(stmtVersion, 1, id); + } + + idHandler.setCDOID(stmtAttributes, 1, id); + + for (;;) + { + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmtAttributes, revision, accessor); + + if (hasLists) + { + // Read multival tables only if revision exists + if (success) + { + int currentVersion; + + try + { + readLists(accessor, revision, listChunk); + currentVersion = readVersion(stmtVersion); + } + catch (IndexOutOfBoundsException ex) + { + // A commit has appended list rows after the list size has been read in readValuesFromStatement(). + // Trigger start from scratch below. + currentVersion = CDOBranchVersion.UNSPECIFIED_VERSION; + } + + if (currentVersion != revision.getVersion()) + { + // A commit has changed the revision while reading the lists. Start from scratch! + revision.clearValues(); // Make sure that lists are recreated + continue; + } + } + } + + return success; + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmtAttributes); + DBUtil.close(stmtVersion); + } + } + + private int readVersion(IDBPreparedStatement stmt) + { + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + if (resultSet.next()) + { + return resultSet.getInt(1); + } + + return CDOBranchVersion.UNSPECIFIED_VERSION; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + } + } + + @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 ATTRIBUTES_REVISED + "=0"; + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + rawDelete(accessor, id, version, branch, monitor); + + AbstractHorizontalMappingStrategy mappingStrategy = (AbstractHorizontalMappingStrategy)getMappingStrategy(); + mappingStrategy.removeObjectType(accessor, id); + } + + @Override + protected void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor monitor) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDelete, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + + FeatureDeltaWriter writer = new FeatureDeltaWriter(); + writer.process(accessor, delta, created); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + /** + * @author Eike Stepper + */ + private final class FeatureDeltaWriter extends AbstractFeatureDeltaWriter + { + private final List> attributeChanges = new ArrayList>(); + + private final List> listSizeChanges = new ArrayList>(); + + private int oldVersion; + + private boolean updateContainer; + + private int newContainingFeatureID; + + private CDOID newContainerID; + + private CDOID newResourceID; + + private int branchId; + + private int newVersion; + + @Override + protected void doProcess(InternalCDORevisionDelta delta) + { + // Set context + id = delta.getID(); + + branchId = delta.getBranch().getID(); + oldVersion = delta.getVersion(); + newVersion = oldVersion + 1; + + // Process revision delta tree + delta.accept(this); + + updateAttributes(); + } + + 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(Pair.create(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(Pair.create(tm, null)); + } + + public void visit(CDOListFeatureDelta delta) + { + EStructuralFeature feature = delta.getFeature(); + int oldSize = delta.getOriginSize(); + int newSize = -1; + + try + { + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(feature); + listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta); + } + catch (NewListSizeResult result) + { + newSize = result.getNewListSize(); + } + + if (oldSize != newSize) + { + listSizeChanges.add(Pair.create(feature, newSize)); + } + } + + 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(); + String sql = buildUpdateSQL(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + + try + { + int column = 1; + 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 + { + DBUtil.close(stmt); + } + } + + private String buildUpdateSQL() + { + StringBuilder builder = new StringBuilder(sqlUpdatePrefix); + if (updateContainer) + { + builder.append(sqlUpdateContainerPart); + } + + for (Pair 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 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> attributeChanges, IDBPreparedStatement stmt, int col) throws SQLException + { + for (Pair 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> attributeChanges, IDBPreparedStatement stmt, int col) throws SQLException + { + for (Pair change : listSizeChanges) + { + stmt.setInt(col++, change.getElement2()); + } + + return col; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java new file mode 100644 index 000000000..0b489f46e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009-2013, 2018 Eike Stepper (Loehne, 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.CDODBUtil; +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 +{ + private boolean forceZeroBasedIndex; + + public HorizontalNonAuditMappingStrategy() + { + } + + public boolean hasAuditSupport() + { + return false; + } + + public boolean hasBranchingSupport() + { + return false; + } + + public boolean hasDeltaSupport() + { + return true; + } + + public boolean shallForceZeroBasedIndex() + { + return forceZeroBasedIndex; + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new NonAuditListTableMapping(this, containingClass, feature); + } + + @Deprecated + @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); + } + + @Override + protected void doAfterActivate() throws Exception + { + super.doAfterActivate(); + + String value = getProperties().get(CDODBUtil.PROP_ZEROBASED_INDEX); + forceZeroBasedIndex = value == null ? false : Boolean.valueOf(value); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/IMappingConstants.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/IMappingConstants.java new file mode 100644 index 000000000..be1589574 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/IMappingConstants.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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; + +/** + * @author Eike Stepper + */ +public interface IMappingConstants +{ + /* + * 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 = LIST_REVISION_ID; + + public static final String FEATUREMAP_VERSION = LIST_REVISION_VERSION; + + public static final String FEATUREMAP_VERSION_ADDED = LIST_REVISION_VERSION_ADDED; + + public static final String FEATUREMAP_VERSION_REMOVED = LIST_REVISION_VERSION_REMOVED; + + public static final String FEATUREMAP_BRANCH = LIST_REVISION_BRANCH; + + public static final String FEATUREMAP_IDX = LIST_IDX; + + public static final String FEATUREMAP_TAG = LIST_FEATURE; + + public static final String FEATUREMAP_VALUE = LIST_VALUE; +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java new file mode 100644 index 000000000..54779486a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.branch.CDOBranch; +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.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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; +import java.util.List; + +/** + * 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 + * @deprecated As 4.5 feature maps are no longer supported. + */ +@Deprecated +public class NonAuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping implements IListMappingDeltaSupport +{ + 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(FEATUREMAP_REVISION_ID); + builder.append("=? "); //$NON-NLS-1$ + + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_TAG); + builder.append("=?,"); //$NON-NLS-1$ + + Iterator 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(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("-1 WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append(">? "); //$NON-NLS-1$ + sqlMoveDown = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(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(FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append("+1 WHERE "); //$NON-NLS-1$ + builder.append(FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append(">=? "); //$NON-NLS-1$ + sqlMoveUp = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(FEATUREMAP_IDX); + builder.append(" list) + { + // Do nothing + } + + @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) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClear, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + clearList(accessor, id); + } + + /** + * 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) + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + ITypeMapping typeMapping = getTypeMapping(tag); + String columnName = getColumnName(tag); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + int column = getKeyFields().length + 1; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + typeMapping.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 + { + DBUtil.close(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(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(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 index. Only indexes starting with + * index + 1 and ending with upperIndex are moved down. + */ + private void move1down(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit, + ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + /** + * Move references downwards to close a gap at position index. Only indexes starting with + * index + 1 and ending with upperIndex are moved down. + */ + private void move1up(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit, + ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(stmt); + } + } + + private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteItem, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(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) + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + ITypeMapping mapping = getTypeMapping(tag); + String columnName = getColumnName(tag); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlUpdateValue, ReuseProbability.HIGH); + + try + { + 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 + { + DBUtil.close(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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java new file mode 100644 index 000000000..4ea9aef2e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2009-2016, 2018 Eike Stepper (Loehne, 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.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +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.bundle.OM; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBTable; +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.List; +import java.util.ListIterator; + +/** + * 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 String sqlClear; + + private String sqlUpdateValue; + + private String sqlUpdateIndex; + + private String sqlInsertValue; + + private String sqlDeleteItem; + + private String sqlShiftDownIndex; + + private String sqlReadCurrentIndexOffset; + + private String sqlShiftUpIndex; + + public NonAuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected void initSQLStrings() + { + super.initSQLStrings(); + + IDBTable table = getTable(); + + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(table); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=?"); //$NON-NLS-1$ + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(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(LIST_VALUE); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(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(LIST_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(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(LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------- mass update item indexes -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append("+? WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(" BETWEEN ? AND ?"); //$NON-NLS-1$ + + // needed because of MySQL: + builder.append(" /*! ORDER BY "); //$NON-NLS-1$ / + builder.append(LIST_IDX); + sqlShiftDownIndex = builder.toString() + " */"; //$NON-NLS-1$ + + builder.append(" DESC"); //$NON-NLS-1$ + sqlShiftUpIndex = builder.toString() + " */"; //$NON-NLS-1$ + + // ----------- read current index offset -------------- + builder = new StringBuilder(); + builder.append("SELECT MIN("); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(") FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append("=?"); //$NON-NLS-1$ + sqlReadCurrentIndexOffset = builder.toString(); + } + + @Override + public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index) + { + int offset = getCurrentIndexOffset(accessor, cdoid); + super.addSimpleChunkWhere(accessor, cdoid, builder, index + offset); + } + + @Override + public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex, int toIndex) + { + int offset = getCurrentIndexOffset(accessor, cdoid); + super.addRangedChunkWhere(accessor, cdoid, builder, fromIndex + offset, toIndex + offset); + } + + @Override + protected void addKeyFields(List list) + { + // Do nothing. + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + idHandler.setCDOID(stmt, 1, revision.getID()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + clearList(accessor, id); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + clearList(accessor, id); + } + + public void processDelta(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, long created, CDOListFeatureDelta delta) + { + if (getTable() == null) + { + initTable(accessor); + } + + List listChanges = delta.getListChanges(); + int oldListSize = delta.getOriginSize(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for object {0} - original list size: {1}", id, //$NON-NLS-1$ + oldListSize); + } + + ListDeltaWriter writer = new ListDeltaWriter(accessor, id, listChanges, oldListSize); + writer.writeListDeltas(); + } + + /** + * 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 + */ + private void clearList(IDBStoreAccessor accessor, CDOID id) + { + if (getTable() == null) + { + return; + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlClear, ReuseProbability.HIGH); + + try + { + idHandler.setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + private int getCurrentIndexOffset(IDBStoreAccessor accessor, CDOID id) + { + if (getTable() == null) + { + // List is empty. Return the default offset of 0. + return 0; + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlReadCurrentIndexOffset, ReuseProbability.HIGH); + ResultSet rset = null; + + try + { + idHandler.setCDOID(stmt, 1, id); + rset = stmt.executeQuery(); + if (!rset.next()) + { + // List is empty. Return the default offset of 0. + return 0; + } + + // Return the minimum index which is equal to the current offset. + return rset.getInt(1); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(rset); + DBUtil.close(stmt); + } + } + + /** + * @author Eike Stepper + */ + private final class ListDeltaWriter extends AbstractListDeltaWriter + { + private IDBPreparedStatement stmtDelete; + + private IDBPreparedStatement stmtMove; + + private IDBPreparedStatement stmtSet; + + private IDBPreparedStatement stmtInsert; + + private IDBPreparedStatement stmtShiftDown; + + private IDBPreparedStatement stmtShiftUp; + + private int countDelete; + + private int countMove; + + private int countSet; + + private int countInsert; + + private int countShiftDown; + + private int countShiftUp; + + public ListDeltaWriter(IDBStoreAccessor accessor, CDOID id, List listChanges, int oldListSize) + { + super(accessor, id, listChanges, oldListSize); + } + + @Override + protected boolean isZeroBasedIndex() + { + return ((HorizontalNonAuditMappingStrategy)getMappingStrategy()).shallForceZeroBasedIndex(); + } + + @Override + protected ITypeMapping getTypeMapping() + { + return NonAuditListTableMapping.this.getTypeMapping(); + } + + @Override + protected int getCurrentIndexOffset() + { + return NonAuditListTableMapping.this.getCurrentIndexOffset(accessor, id); + } + + @Override + protected void clearList() + { + NonAuditListTableMapping.this.clearList(accessor, id); + } + + @Override + protected void writeResultToDatabase() throws SQLException + { + try + { + super.writeResultToDatabase(); + + if (countMove > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} move operations", countMove); //$NON-NLS-1$ + } + + DBUtil.executeBatch(stmtMove, countMove); + } + + if (countInsert > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} insert operations", countInsert); //$NON-NLS-1$ + } + + DBUtil.executeBatch(stmtInsert, countInsert); + } + + if (countSet > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} set operations", countSet); //$NON-NLS-1$ + } + + DBUtil.executeBatch(stmtSet, countSet); + } + } + finally + { + close(stmtDelete, stmtMove, stmtInsert, stmtSet); + } + } + + @Override + protected void writeShifts(IIDHandler idHandler) throws SQLException + { + if (countDelete > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} delete operations", countDelete); //$NON-NLS-1$ + } + + DBUtil.executeBatch(stmtDelete, countDelete); + } + + if (countMove > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} move operations", countMove); //$NON-NLS-1$ + } + + DBUtil.executeBatch(stmtMove, countMove); + countMove = 0; + } + + super.writeShifts(idHandler); + } + + @Override + protected void writeShiftsDown(IIDHandler idHandler, ListIterator operationIt) throws SQLException + { + try + { + super.writeShiftsDown(idHandler, operationIt); + + if (countShiftDown > 0) + { + DBUtil.executeBatch(stmtShiftDown, countShiftDown, false); + } + } + finally + { + close(stmtShiftDown); + } + } + + @Override + protected void writeShiftsUp(IIDHandler idHandler, ListIterator operationIt) throws SQLException + { + try + { + super.writeShiftsUp(idHandler, operationIt); + + if (countShiftUp > 0) + { + DBUtil.executeBatch(stmtShiftUp, countShiftUp, false); + } + } + finally + { + close(stmtShiftUp); + } + } + + @Override + protected void dbDelete(IIDHandler idHandler, int index) throws SQLException + { + if (stmtDelete == null) + { + stmtDelete = accessor.getDBConnection().prepareStatement(sqlDeleteItem, ReuseProbability.HIGH); + idHandler.setCDOID(stmtDelete, 1, id); + } + + stmtDelete.setInt(2, index); + stmtDelete.addBatch(); + ++countDelete; + } + + @Override + protected void dbMove(IIDHandler idHandler, int fromIndex, int toIndex, int srcIndex) throws SQLException + { + if (stmtMove == null) + { + stmtMove = accessor.getDBConnection().prepareStatement(sqlUpdateIndex, ReuseProbability.HIGH); + idHandler.setCDOID(stmtMove, 2, id); + } + + stmtMove.setInt(3, fromIndex); + stmtMove.setInt(1, toIndex); + stmtMove.addBatch(); + ++countMove; + } + + @Override + protected void dbSet(IIDHandler idHandler, ITypeMapping typeMapping, int index, Object value, int srcIndex) throws SQLException + { + if (stmtSet == null) + { + stmtSet = accessor.getDBConnection().prepareStatement(sqlUpdateValue, ReuseProbability.HIGH); + idHandler.setCDOID(stmtSet, 2, id); + } + + stmtSet.setInt(3, index); + typeMapping.setValue(stmtSet, 1, value); + stmtSet.addBatch(); + ++countSet; + } + + @Override + protected void dbInsert(IIDHandler idHandler, ITypeMapping typeMapping, int index, Object value) throws SQLException + { + if (stmtInsert == null) + { + stmtInsert = accessor.getDBConnection().prepareStatement(sqlInsertValue, ReuseProbability.HIGH); + idHandler.setCDOID(stmtInsert, 1, id); + } + + stmtInsert.setInt(2, index); + typeMapping.setValue(stmtInsert, 3, value); + stmtInsert.addBatch(); + ++countInsert; + } + + @Override + protected void dbShiftDown(IIDHandler idHandler, int offset, int startIndex, int endIndex) throws SQLException + { + if (stmtShiftDown == null) + { + stmtShiftDown = accessor.getDBConnection().prepareStatement(sqlShiftDownIndex, ReuseProbability.HIGH); + idHandler.setCDOID(stmtShiftDown, 2, id); + } + + stmtShiftDown.setInt(1, offset); + stmtShiftDown.setInt(3, startIndex); + stmtShiftDown.setInt(4, endIndex); + stmtShiftDown.addBatch(); + ++countShiftDown; + } + + @Override + protected void dbShiftUp(IIDHandler idHandler, int offset, int startIndex, int endIndex) throws SQLException + { + if (stmtShiftUp == null) + { + stmtShiftUp = accessor.getDBConnection().prepareStatement(sqlShiftUpIndex, ReuseProbability.HIGH); + idHandler.setCDOID(stmtShiftUp, 2, id); + } + + stmtShiftUp.setInt(1, offset); + stmtShiftUp.setInt(3, startIndex); + stmtShiftUp.setInt(4, endIndex); + stmtShiftUp.addBatch(); + ++countShiftUp; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java new file mode 100644 index 000000000..ded575c32 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009-2013 Eike Stepper (Loehne, 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 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 boolean doPutObjectType(IDBStoreAccessor accessor, CDOID id, CDOID type) + { + return memoryCache.put(id, type) == null; + } + + @Override + protected boolean doRemoveObjectType(IDBStoreAccessor accessor, CDOID id) + { + return memoryCache.remove(id) != null; + } + + @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 + { + 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 eldest) + { + return size() > capacity; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java new file mode 100644 index 000000000..53e2ce347 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2010-2014, 2016 Eike Stepper (Loehne, 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.internal.db.CDODBSchema; +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.IDBDatabase; +import org.eclipse.net4j.db.IDBDatabase.RunnableWithSchema; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class ObjectTypeTable extends AbstractObjectTypeMapper implements IMappingConstants +{ + private IDBTable table; + + private String sqlDelete; + + private String sqlInsert; + + private String sqlSelect; + + public ObjectTypeTable() + { + } + + public final CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelect, ReuseProbability.MAX); + + try + { + idHandler.setCDOID(stmt, 1, id); + + if (DBUtil.isTracerEnabled()) + { + DBUtil.trace(stmt.toString()); + } + + ResultSet resultSet = stmt.executeQuery(); + + if (!resultSet.next()) + { + if (DBUtil.isTracerEnabled()) + { + 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 + { + DBUtil.close(stmt); + } + } + + public final boolean putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type) + { + IDBStore store = getMappingStrategy().getStore(); + IIDHandler idHandler = store.getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsert, ReuseProbability.MAX); + + try + { + idHandler.setCDOID(stmt, 1, id); + idHandler.setCDOID(stmt, 2, getMetaDataManager().getMetaID(type, timeStamp)); + stmt.setLong(3, timeStamp); + + if (DBUtil.isTracerEnabled()) + { + DBUtil.trace(stmt.toString()); + } + + int result = stmt.executeUpdate(); + if (result != 1) + { + throw new DBException("Object type could not be inserted: " + id); //$NON-NLS-1$ + } + + return true; + } + catch (SQLException ex) + { + if (store.getDBAdapter().isDuplicateKeyException(ex)) + { + // Unique key violation can occur in rare cases (merging new objects from other branches) + return false; + } + + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + public final boolean removeObjectType(IDBStoreAccessor accessor, CDOID id) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDelete, ReuseProbability.MAX); + + try + { + idHandler.setCDOID(stmt, 1, id); + + if (DBUtil.isTracerEnabled()) + { + DBUtil.trace(stmt.toString()); + } + + int result = stmt.executeUpdate(); + return result == 1; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + public CDOID getMaxID(Connection connection, IIDHandler idHandler) + { + Statement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = connection.createStatement(); + resultSet = stmt.executeQuery("SELECT MAX(" + ATTRIBUTES_ID + ") 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 " + ATTRIBUTES_CREATED + " 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(); + + final IDBStore store = getMappingStrategy().getStore(); + final DBType idType = store.getIDHandler().getDBType(); + final int idLength = store.getIDColumnLength(); + + IDBDatabase database = store.getDatabase(); + table = database.getSchema().getTable(CDODBSchema.CDO_OBJECTS); + if (table == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + table = schema.addTable(CDODBSchema.CDO_OBJECTS); + table.addField(ATTRIBUTES_ID, idType, idLength, true); + table.addField(ATTRIBUTES_CLASS, idType, idLength); + table.addField(ATTRIBUTES_CREATED, DBType.BIGINT); + table.addIndex(IDBIndex.Type.PRIMARY_KEY, ATTRIBUTES_ID); + + InternalRepository repository = (InternalRepository)store.getRepository(); + if (repository.isSupportingUnits()) + { + table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_CLASS); + } + } + }); + } + + sqlSelect = "SELECT " + ATTRIBUTES_CLASS + " FROM " + table + " WHERE " + ATTRIBUTES_ID + "=?"; + sqlInsert = "INSERT INTO " + table + "(" + ATTRIBUTES_ID + "," + ATTRIBUTES_CLASS + "," + ATTRIBUTES_CREATED + ") VALUES (?, ?, ?)"; + sqlDelete = "DELETE FROM " + table + " WHERE " + ATTRIBUTES_ID + "=?"; + } + + @Override + protected void doDeactivate() throws Exception + { + sqlDelete = null; + sqlInsert = null; + sqlSelect = null; + table = null; + super.doDeactivate(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/UnitMappingTable.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/UnitMappingTable.java new file mode 100644 index 000000000..97712f058 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/UnitMappingTable.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.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.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.server.IView; +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.IClassMappingUnitSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.BatchedStatement; +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBDatabase.RunnableWithSchema; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +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.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class UnitMappingTable extends Lifecycle implements IMappingConstants +{ + public static final String UNITS = "CDO_UNITS"; //$NON-NLS-1$ + + public static final String UNITS_ELEM = "CDO_ELEM"; //$NON-NLS-1$ + + public static final String UNITS_UNIT = "CDO_UNIT"; //$NON-NLS-1$ + + // public static final String UNITS_CREATED = "CDO_CREATED"; //$NON-NLS-1$ + + private static final String SQL_SELECT_ROOTS = "SELECT DISTINCT " + UNITS_UNIT + " FROM " + UNITS; + + private static final String SQL_INSERT_MAPPINGS = "INSERT INTO " + UNITS + " (" + UNITS_ELEM + ", " + UNITS_UNIT + ") VALUES (?, ?)"; + + // private static final String SQL_SELECT_SIZE = "SELECT COUNT(" + UNITS_ELEM + ") FROM " + UNITS + " WHERE " + // + UNITS_UNIT + "=?"; + + private static final String SQL_SELECT_CLASSES = "SELECT " + ATTRIBUTES_CLASS + ", COUNT(" + UNITS_ELEM + ") FROM " + UNITS + ", " + CDODBSchema.CDO_OBJECTS + + " WHERE " + UNITS_ELEM + "=" + ATTRIBUTES_ID + " AND " + UNITS_UNIT + "=? GROUP BY " + ATTRIBUTES_CLASS; + + private static final int WRITE_UNIT_MAPPING_BATCH_SIZE = 100000; + + private final IMappingStrategy mappingStrategy; + + private IDBTable table; + + public UnitMappingTable(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + } + + public List readUnitRoots(IDBStoreAccessor accessor) + { + List rootIDs = new ArrayList(); + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + Statement stmt = null; + + try + { + stmt = accessor.getDBConnection().createStatement(); + + if (DBUtil.isTracerEnabled()) + { + DBUtil.trace(stmt.toString()); + } + + ResultSet resultSet = stmt.executeQuery(SQL_SELECT_ROOTS); + while (resultSet.next()) + { + CDOID rootID = idHandler.getCDOID(resultSet, 1); + rootIDs.add(rootID); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + + return rootIDs; + } + + public void readUnitRevisions(IDBStoreAccessor accessor, IView view, CDOID rootID, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + IDBStore store = mappingStrategy.getStore(); + IIDHandler idHandler = store.getIDHandler(); + IMetaDataManager metaDataManager = store.getMetaDataManager(); + + long timeStamp = view.isHistorical() ? view.getTimeStamp() : store.getRepository().getTimeStamp(); + CDOBranchPoint branchPoint = view.getBranch().getPoint(timeStamp); + + IDBConnection connection = accessor.getDBConnection(); + IDBPreparedStatement stmt = connection.prepareStatement(SQL_SELECT_CLASSES, ReuseProbability.HIGH); + + int jdbcFetchSize = store.getJDBCFetchSize(); + int oldFetchSize = -1; + + try + { + idHandler.setCDOID(stmt, 1, rootID); + + oldFetchSize = stmt.getFetchSize(); + stmt.setFetchSize(jdbcFetchSize); + ResultSet resultSet = stmt.executeQuery(); + + while (resultSet.next()) + { + CDOID classID = idHandler.getCDOID(resultSet, 1); + EClass eClass = (EClass)metaDataManager.getMetaInstance(classID); + + IClassMappingUnitSupport classMapping = (IClassMappingUnitSupport)mappingStrategy.getClassMapping(eClass); + classMapping.readUnitRevisions(accessor, branchPoint, rootID, revisionHandler); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + if (oldFetchSize != -1) + { + try + { + stmt.setFetchSize(oldFetchSize); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + DBUtil.close(stmt); + } + } + + public BatchedStatement initUnit(IDBStoreAccessor accessor, long timeStamp, IView view, CDOID rootID, CDORevisionHandler revisionHandler, + Set initializedIDs, OMMonitor monitor) + { + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + IDBConnection connection = accessor.getDBConnection(); + BatchedStatement stmt = DBUtil.batched(connection.prepareStatement(SQL_INSERT_MAPPINGS, ReuseProbability.HIGH), WRITE_UNIT_MAPPING_BATCH_SIZE); + + try + { + CDORevision revision = view.getRevision(rootID); + + initUnit(stmt, view, rootID, revisionHandler, initializedIDs, timeStamp, idHandler, revision, monitor); + return stmt; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + // Don't close the statement; that's done later in finishUnit(). + } + } + + private void initUnit(BatchedStatement stmt, IView view, CDOID rootID, CDORevisionHandler revisionHandler, Set initializedIDs, long timeStamp, + IIDHandler idHandler, CDORevision revision, OMMonitor monitor) throws SQLException + { + revisionHandler.handleRevision(revision); + + CDOID id = revision.getID(); + initializedIDs.add(id); + + writeUnitMapping(stmt, rootID, timeStamp, idHandler, id); + + List children = CDORevisionUtil.getChildRevisions(revision, view, true); + for (CDORevision child : children) + { + initUnit(stmt, view, rootID, revisionHandler, initializedIDs, timeStamp, idHandler, child, monitor); + } + } + + public void finishUnit(BatchedStatement stmt, CDOID rootID, List ids, long timeStamp) + { + IDBStore store = mappingStrategy.getStore(); + IIDHandler idHandler = store.getIDHandler(); + Connection connection = null; + + try + { + connection = stmt.getConnection(); + + for (CDOID id : ids) + { + writeUnitMapping(stmt, rootID, timeStamp, idHandler, id); + } + } + catch (SQLException ex) + { + DBUtil.rollbackSilently(connection); + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + + try + { + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + public void writeUnitMappings(IDBStoreAccessor accessor, Map unitMappings, long timeStamp) + { + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + IDBConnection connection = accessor.getDBConnection(); + BatchedStatement stmt = DBUtil.batched(connection.prepareStatement(SQL_INSERT_MAPPINGS, ReuseProbability.HIGH), WRITE_UNIT_MAPPING_BATCH_SIZE); + + try + { + for (Entry entry : unitMappings.entrySet()) + { + CDOID id = entry.getKey(); + CDOID rootID = entry.getValue(); + writeUnitMapping(stmt, rootID, timeStamp, idHandler, id); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + private void writeUnitMapping(BatchedStatement stmt, CDOID rootID, long timeStamp, IIDHandler idHandler, CDOID id) throws SQLException + { + idHandler.setCDOID(stmt, 1, id); + idHandler.setCDOID(stmt, 2, rootID); + // stmt.setLong(3, timeStamp); + stmt.executeUpdate(); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + IDBStore store = mappingStrategy.getStore(); + final DBType idType = store.getIDHandler().getDBType(); + final int idLength = store.getIDColumnLength(); + + IDBDatabase database = store.getDatabase(); + table = database.getSchema().getTable(UNITS); + if (table == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + table = schema.addTable(UNITS); + table.addField(UNITS_ELEM, idType, idLength, true); + table.addField(UNITS_UNIT, idType, idLength); + // table.addField(UNITS_CREATED, DBType.BIGINT); + table.addIndex(IDBIndex.Type.PRIMARY_KEY, UNITS_ELEM); + table.addIndex(IDBIndex.Type.NON_UNIQUE, UNITS_UNIT); + } + }); + } + } + + @Override + protected void doDeactivate() throws Exception + { + table = null; + super.doDeactivate(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java new file mode 100644 index 000000000..4ccdbc3c7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties new file mode 100644 index 000000000..c01fb1a26 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties @@ -0,0 +1,31 @@ +# Copyright (c) 2009-2012 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/.classpath b/bundles/org.eclipse.emf.cdo.server/.classpath new file mode 100644 index 000000000..b263de401 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.eclipse.emf.cdo.server/.gitignore b/bundles/org.eclipse.emf.cdo.server/.gitignore new file mode 100644 index 000000000..57b341172 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/generated/ diff --git a/bundles/org.eclipse.emf.cdo.server/.project b/bundles/org.eclipse.emf.cdo.server/.project new file mode 100644 index 000000000..46440f696 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/.project @@ -0,0 +1,23 @@ + + + org.eclipse.emf.cdo.server + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/bundles/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..2c715ef3b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/bundles/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..bb35fa0a8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.eclipse.emf.cdo.server/bnd.bnd b/bundles/org.eclipse.emf.cdo.server/bnd.bnd new file mode 100644 index 000000000..619dd72b0 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/bnd.bnd @@ -0,0 +1,25 @@ +Require-Bundle: org.eclipse.emf.cdo;bundle-version="[4.0.0,5.0.0)";visibility:=reexport + +-buildpath: \ + org.eclipse.emf.cdo,\ + org.eclipse.emf.cdo.common,\ + org.eclipse.net4j.util,\ + org.eclipse.emf.ecore,\ + org.eclipse.equinox.common,\ + org.eclipse.emf.common,\ + org.eclipse.osgi,\ + org.eclipse.equinox.app,\ + org.eclipse.equinox.registry,\ + org.eclipse.core.runtime +Export-Package: \ + org.eclipse.emf.cdo.internal.server,\ + org.eclipse.emf.cdo.internal.server.bundle,\ + org.eclipse.emf.cdo.internal.server.embedded,\ + org.eclipse.emf.cdo.internal.server.mem,\ + org.eclipse.emf.cdo.internal.server.messages,\ + org.eclipse.emf.cdo.internal.server.syncing,\ + org.eclipse.emf.cdo.server,\ + org.eclipse.emf.cdo.server.embedded,\ + org.eclipse.emf.cdo.server.mem,\ + org.eclipse.emf.cdo.spi.server +Bundle-Version: 4.6.0 \ No newline at end of file diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java new file mode 100644 index 000000000..dc61b19d5 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2008-2012, 2015, 2017 Eike Stepper (Loehne, 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.protocol.CDODataInput; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +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.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.ThreadPool; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.Closeable; +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.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 contextEntries = new ConcurrentHashMap(); + + public CommitManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + } + + public synchronized ExecutorService getExecutors() + { + if (executors == null) + { + executors = ConcurrencyUtil.getExecutorService(repository); + + if (executors == null) + { + shutdownExecutorService = true; + executors = ThreadPool.create(); + } + } + + return executors; + } + + public synchronized void setExecutors(ExecutorService executors) + { + if (shutdownExecutorService) + { + if (this.executors != null) + { + this.executors.shutdown(); + } + + shutdownExecutorService = false; + } + + this.executors = executors; + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + setExecutors(null); + } + + @Deprecated + public void preCommit(InternalCommitContext commitContext, OMMonitor monitor) + { + preCommit(commitContext, null, monitor); + } + + public void preCommit(InternalCommitContext commitContext, CDODataInput in, OMMonitor monitor) + { + TransactionCommitContextEntry contextEntry = new TransactionCommitContextEntry(in, monitor); + contextEntry.setContext(commitContext); + + Future 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 final CDODataInput in; + + private final OMMonitor monitor; + + private InternalCommitContext context; + + private Future future; + + public TransactionCommitContextEntry(CDODataInput in, OMMonitor monitor) + { + this.in = in; + this.monitor = monitor; + } + + public Callable createCallable() + { + return new Callable() + { + public Object call() throws Exception + { + try + { + StoreThreadLocal.setCommitContext(context); + context.write(monitor); + } + finally + { + StoreThreadLocal.setCommitContext(null); + if (in instanceof Closeable) + { + IOUtil.closeSilent((Closeable)in); + } + } + + return null; + } + }; + } + + public InternalCommitContext getContext() + { + return context; + } + + public void setContext(InternalCommitContext context) + { + this.context = context; + } + + public Future getFuture() + { + return future; + } + + public void setFuture(Future future) + { + this.future = future; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java new file mode 100644 index 000000000..92a811300 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2010-2013, 2016 Eike Stepper (Loehne, 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.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +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.server.IView; +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.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; + +import org.eclipse.emf.ecore.EClass; + +import java.util.List; +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 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 getDetachedObjectTypes() + { + return getDelegate().getDetachedObjectTypes(); + } + + public CDORevision getRevision(CDOID id) + { + return getDelegate().getRevision(id); + } + + public Map getIDMappings() + { + return getDelegate().getIDMappings(); + } + + public long getPreviousTimeStamp() + { + return getDelegate().getPreviousTimeStamp(); + } + + public long getLastUpdateTime() + { + return getDelegate().getLastUpdateTime(); + } + + public boolean isClearResourcePathCache() + { + return getDelegate().isClearResourcePathCache(); + } + + public boolean isUsingEcore() + { + return getDelegate().isUsingEcore(); + } + + public boolean isUsingEtypes() + { + return getDelegate().isUsingEtypes(); + } + + @Deprecated + public boolean isAutoReleaseLocksEnabled() + { + return getDelegate().isAutoReleaseLocksEnabled(); + } + + public CDOLockState[] getLocksOnNewObjects() + { + return getDelegate().getLocksOnNewObjects(); + } + + public CDOID[] getIDsToUnlock() + { + return getDelegate().getIDsToUnlock(); + } + + public CDOBranchVersion[] getDetachedObjectVersions() + { + return getDelegate().getDetachedObjectVersions(); + } + + public ExtendedDataInputStream getLobs() + { + return getDelegate().getLobs(); + } + + public CDOCommitInfo createCommitInfo() + { + return getDelegate().createCommitInfo(); + } + + public List> getPostCommmitLockStates() + { + return getDelegate().getPostCommmitLockStates(); + } + + public byte getRollbackReason() + { + return getDelegate().getRollbackReason(); + } + + public String getRollbackMessage() + { + return getDelegate().getRollbackMessage(); + } + + public List getXRefs() + { + return getDelegate().getXRefs(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/InstancesQueryHandler.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/InstancesQueryHandler.java new file mode 100644 index 000000000..4fb7f3d3e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/InstancesQueryHandler.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +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.spi.common.revision.DetachedCDORevision; +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 java.util.List; + +/** + * @author Eike Stepper + */ +public class InstancesQueryHandler implements IQueryHandler +{ + public InstancesQueryHandler() + { + } + + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + EClass type = (EClass)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_INSTANCES_TYPE); + if (type != null) + { + executeQuery(type, context); + + if (!Boolean.TRUE.equals(info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_INSTANCES_EXACT))) + { + List subTypes = context.getView().getRepository().getPackageRegistry().getSubTypes().get(type); + if (subTypes != null && !subTypes.isEmpty()) + { + for (EClass subType : subTypes) + { + if (context.getResultCount() == 0) + { + break; + } + + executeQuery(subType, context); + } + } + } + } + } + + private void executeQuery(EClass type, final IQueryContext context) + { + if (type.isInterface() || type.isAbstract()) + { + return; + } + + CDOBranch branch = context.getBranch(); + long timeStamp = context.getTimeStamp(); + + InternalRepository repository = (InternalRepository)context.getView().getRepository(); + repository.handleRevisions(type, branch, false, timeStamp, false, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + if (revision instanceof DetachedCDORevision) + { + return true; + } + + return context.addResult(revision); + } + }); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends QueryHandlerFactory + { + public Factory() + { + super(CDOProtocolConstants.QUERY_LANGUAGE_INSTANCES); + } + + @Override + public InstancesQueryHandler create(String description) throws ProductCreationException + { + return new InstancesQueryHandler(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockingManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockingManager.java new file mode 100644 index 000000000..b03202f9f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockingManager.java @@ -0,0 +1,894 @@ +/* + * Copyright (c) 2011-2013, 2015, 2016 Eike Stepper (Loehne, 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.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +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.common.revision.ManagedRevisionProvider; +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.collection.ConcurrentArray; +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 org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +import org.eclipse.core.runtime.PlatformObject; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Simon McDuff + * @since 3.0 + */ +public class LockingManager extends RWOLockManager implements InternalLockManager +{ + private InternalRepository repository; + + private Map openDurableViews = new HashMap(); + + private Map durableViews = new HashMap(); + + private ConcurrentArray durableViewHandlers = new ConcurrentArray() + { + @Override + protected DurableViewHandler[] newArray(int length) + { + return new DurableViewHandler[length]; + } + }; + + @ExcludeFromDump + private transient IListener sessionListener = new ContainerEventAdapter() + { + @Override + protected void onRemoved(IContainer container, IView view) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID == null) + { + repository.unlock((InternalView)view, null, null, false); + } + else + { + DurableView durableView = new DurableView(durableLockingID); + changeContext(view, durableView); + unregisterOpenDurableView(durableLockingID); + durableViews.put(durableLockingID, durableView); + } + } + }; + + @ExcludeFromDump + private transient IListener sessionManagerListener = new ContainerEventAdapter() + { + @Override + protected void onAdded(IContainer container, ISession session) + { + session.addListener(sessionListener); + } + + @Override + protected void onRemoved(IContainer container, ISession session) + { + session.removeListener(sessionListener); + } + }; + + public LockingManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + } + + public synchronized Object getLockEntryObject(Object key) + { + LockState 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 getLocks(final IView view) + { + final Map result = CDOIDUtil.createMap(); + + for (LockState 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 objectsToLock, long timeout) throws InterruptedException + { + lock2(explicit, type, view, objectsToLock, false, timeout); + } + + public List> lock2(boolean explicit, LockType type, IView view, Collection objectsToLock, boolean recursive, + long timeout) throws InterruptedException + { + String durableLockingID = null; + DurableLocking accessor = null; + + if (explicit) + { + durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + accessor = getDurableLocking(); + } + } + + long startTime = timeout == WAIT ? 0L : currentTimeMillis(); + + List> newLockStates; + synchronized (this) + { + if (recursive) + { + objectsToLock = createContentSet(objectsToLock, view); + } + + // Adjust timeout for delay we may have incurred on entering this synchronized block + if (timeout != WAIT) + { + timeout -= currentTimeMillis() - startTime; + } + + newLockStates = super.lock2(type, view, objectsToLock, timeout); + } + + if (accessor != null) + { + accessor.lock(durableLockingID, type, objectsToLock); + } + + return newLockStates; + } + + @Override + public void lock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Collection objectsToLock, long timeout) + throws InterruptedException + { + lock2(false, type, context, objectsToLock, false, timeout); + } + + @Override + public void lock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Object objectToLock, long timeout) throws InterruptedException + { + Collection objectsToLock = new LinkedHashSet(); + objectsToLock.add(objectToLock); + lock2(false, type, context, objectsToLock, false, timeout); + } + + @Override + public List> lock2(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, + IView context, Collection objectsToLock, long timeout) throws InterruptedException + { + return lock2(false, type, context, objectsToLock, false, timeout); + } + + private Set createContentSet(Collection objectsToLock, IView view) + { + CDOBranch branch = view.getBranch(); + boolean branching = repository.isSupportingBranches(); + + CDORevisionManager revisionManager = view.getSession().getManager().getRepository().getRevisionManager(); + CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branch.getHead()); + + Set contents = new HashSet(); + + for (Object objectToLock : objectsToLock) + { + contents.add(objectToLock); + + CDOID id = branching ? ((CDOIDAndBranch)objectToLock).getID() : (CDOID)objectToLock; + CDORevision revision = revisionProvider.getRevision(id); + createContentSet(branch, branching, revisionProvider, revision, contents); + } + + return contents; + } + + private void createContentSet(CDOBranch branch, boolean branching, CDORevisionProvider revisionProvider, CDORevision revision, Set contents) + { + for (CDORevision child : CDORevisionUtil.getChildRevisions(revision, revisionProvider)) + { + CDOID childID = child.getID(); + contents.add(branching ? CDOIDUtil.createIDAndBranch(childID, branch) : childID); + createContentSet(branch, branching, revisionProvider, child, contents); + } + } + + @Deprecated + public synchronized void unlock(boolean explicit, LockType type, IView view, Collection objectsToUnlock) + { + unlock2(explicit, type, view, objectsToUnlock, false); + } + + public synchronized List> unlock2(boolean explicit, LockType type, IView view, Collection objects, + boolean recursive) + { + List> newLockStates; + synchronized (this) + { + if (recursive) + { + objects = createContentSet(objects, view); + } + + newLockStates = super.unlock2(type, view, objects); + } + + if (explicit) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + DurableLocking accessor = getDurableLocking(); + accessor.unlock(durableLockingID, type, objects); + } + } + + return newLockStates; + } + + @Deprecated + public synchronized void unlock(boolean explicit, IView view) + { + unlock2(explicit, view); + } + + public synchronized List> unlock2(boolean explicit, IView view) + { + if (explicit) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + DurableLocking accessor = getDurableLocking(); + accessor.unlock(durableLockingID); + } + } + + return super.unlock2(view); + } + + @Override + public synchronized List> unlock2(IView context) + { + return unlock2(false, context); + } + + @Override + public synchronized List> unlock2(IView context, + Collection objectsToUnlock) + { + // If no locktype is specified, use the LockType.WRITE + return unlock2(false, LockType.WRITE, context, objectsToUnlock, false); + } + + @Override + public synchronized List> unlock2( + org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Collection objectsToUnlock) + { + return unlock2(false, type, context, objectsToUnlock, false); + } + + @Override + public synchronized void unlock(IView context) + { + unlock2(context); + } + + @Override + public synchronized void unlock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type, IView context, Collection objectsToUnlock) + { + unlock2(type, context, objectsToUnlock); + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return createLockArea(userID, branchPoint, readOnly, locks, null); + } + + private LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 locks = getLocks(view); + + LockArea area = createLockArea(userID, branchPoint, readOnly, locks, lockAreaID); + synchronized (openDurableViews) + { + openDurableViews.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); + unregisterOpenDurableView(durableLockingID); + } + + public IView openView(ISession session, int viewID, boolean readOnly, final String durableLockingID) + { + synchronized (openDurableViews) + { + InternalView view = openDurableViews.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"); + } + + for (DurableViewHandler handler : durableViewHandlers.get()) + { + try + { + handler.openingView(session, viewID, readOnly, area); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + + if (readOnly) + { + view = (InternalView)session.openView(viewID, area); + } + else + { + view = (InternalView)session.openTransaction(viewID, area); + } + + DurableView durableView = durableViews.get(durableLockingID); + changeContext(durableView, view); + view.setDurableLockingID(durableLockingID); + view.addListener(new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + synchronized (openDurableViews) + { + openDurableViews.remove(durableLockingID); + } + } + }); + + openDurableViews.put(durableLockingID, view); + return view; + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + loadLocks(); + 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()); + } + + public void reloadLocks() + { + DurableLockLoader handler = new DurableLockLoader(); + getLockAreas(null, handler); + } + + private void loadLocks() + { + InternalStore store = repository.getStore(); + IStoreAccessor reader = null; + + try + { + reader = store.getReader(null); + if (reader instanceof DurableLocking) + { + StoreThreadLocal.setAccessor(reader); + reloadLocks(); + } + } + finally + { + StoreThreadLocal.release(); + } + } + + private void unregisterOpenDurableView(String durableLockingID) + { + synchronized (openDurableViews) + { + InternalView view = openDurableViews.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); + } + + public void addDurableViewHandler(DurableViewHandler handler) + { + durableViewHandlers.add(handler); + } + + public void removeDurableViewHandler(DurableViewHandler handler) + { + durableViewHandlers.remove(handler); + } + + public DurableViewHandler[] getDurableViewHandlers() + { + return durableViewHandlers.get(); + } + + /** + * @author Eike Stepper + */ + private final class DurableView extends PlatformObject implements IView, CDOCommonView.Options + { + private String durableLockingID; + + private IRegistry properties; + + public DurableView(String durableLockingID) + { + this.durableLockingID = durableLockingID; + } + + public String getDurableLockingID() + { + return durableLockingID; + } + + public boolean isDurableView() + { + return true; + } + + public int getSessionID() + { + throw new UnsupportedOperationException(); + } + + public int getViewID() + { + throw new UnsupportedOperationException(); + } + + public boolean isReadOnly() + { + throw new UnsupportedOperationException(); + } + + public boolean isHistorical() + { + 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 synchronized IRegistry properties() + { + if (properties == null) + { + properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + } + + return properties; + } + + public boolean isLockNotificationEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + } + } + + /** + * @author Eike Stepper + */ + private final class DurableLockLoader implements LockArea.Handler + { + public DurableLockLoader() + { + } + + private IView getView(String lockAreaID) + { + IView view = openDurableViews.get(lockAreaID); + if (view == null) + { + view = durableViews.get(lockAreaID); + } + + return view; + } + + public boolean handleLockArea(LockArea area) + { + String durableLockingID = area.getDurableLockingID(); + IView view = getView(durableLockingID); + if (view != null) + { + unlock2(view); + } + + if (view == null) + { + view = new DurableView(durableLockingID); + durableViews.put(durableLockingID, (DurableView)view); + } + + Collection readLocks = new ArrayList(); + Collection writeLocks = new ArrayList(); + Collection writeOptions = new ArrayList(); + for (Entry 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 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 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); + new DurableLockLoader().handleLockArea(lockArea); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java new file mode 100644 index 000000000..4c6d8ddfc --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2008-2013, 2015, 2016 Eike Stepper (Loehne, 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.common.util.CDOQueryQueue; +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.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.ThreadPool; +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.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 queryContexts = new ConcurrentHashMap(); + + 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) + { + executors = ConcurrencyUtil.getExecutorService(repository); + + if (executors == null) + { + shutdownExecutorService = true; + executors = ThreadPool.create(); + } + } + + return executors; + } + + public synchronized void setExecutors(ExecutorService executors) + { + if (shutdownExecutorService) + { + if (this.executors != null) + { + this.executors.shutdown(); + } + + shutdownExecutorService = false; + } + + this.executors = executors; + } + + public InternalQueryResult execute(InternalView view, CDOQueryInfo queryInfo) + { + InternalQueryResult queryResult = new QueryResult(view, queryInfo, getNextQueryID()); + 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(QueryContext queryContext) + { + int queryID = queryContext.getQueryResult().getQueryID(); + queryContexts.put(queryID, queryContext); + queryContext.addListener(); + } + + public synchronized void unregister(QueryContext queryContext) + { + int queryID = queryContext.getQueryResult().getQueryID(); + queryContexts.remove(queryID); + queryContext.removeListener(); + } + + public synchronized int getNextQueryID() + { + return ++nextQuery; + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + setExecutors(null); + } + + private Future execute(QueryContext queryContext) + { + register(queryContext); + + Future future = getExecutors().submit(queryContext); + queryContext.setFuture(future); + 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$ + } + + CDOQueryQueue queue = queryResult.getQueue(); + queue.add(object); + + return !cancelled && --resultCount > 0; + } + + public void run() + { + CDOQueryQueue queue = queryResult.getQueue(); + + 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); + + try + { + handler.executeQuery(info, this); + } + catch (Throwable executionException) + { + addResult(executionException); + return; + } + } + catch (Throwable initializationException) + { + queue.setException(initializationException); + } + finally + { + queue.close(); + unregister(this); + StoreThreadLocal.release(); + } + } + + public void addListener() + { + InternalSession session = getQueryResult().getView().getSession(); + session.addListener(sessionListener); + } + + public void removeListener() + { + InternalSession session = getQueryResult().getView().getSession(); + session.removeListener(sessionListener); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java new file mode 100644 index 000000000..e6dcfc69f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008-2012, 2015, 2016 Eike Stepper (Loehne, 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 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java new file mode 100644 index 000000000..6cf597482 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java @@ -0,0 +1,2601 @@ +/* + * Copyright (c) 2007-2018 Eike Stepper (Loehne, 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.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.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.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo; +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.CDOTimeProvider; +import org.eclipse.emf.cdo.common.util.CurrentTimeProvider; +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.branch.InternalCDOBranchManager.BranchLoader3; +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.BaseCDORevision; +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.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.emf.internal.cdo.object.CDOFactoryImpl; + +import org.eclipse.net4j.util.AdapterUtil; +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.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.IExecutorServiceProvider; +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.IManagedContainer; +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.om.monitor.ProgressDistributor; +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.MergeDataResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + +import org.eclipse.core.runtime.IProgressMonitor; + +import java.io.IOException; +import java.io.OutputStream; +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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class Repository extends Container implements InternalRepository, IExecutorServiceProvider +{ + private static final int UNCHUNKED = CDORevision.UNCHUNKED; + + private static final int NONE = CDORevision.DEPTH_NONE; + + private String name; + + private String uuid; + + private InternalStore store; + + private Type type = Type.MASTER; + + private State state = State.INITIAL; + + private Map properties; + + private boolean supportingAudits; + + private boolean supportingBranches; + + private boolean supportingUnits; + + private boolean serializingCommits; + + private boolean ensuringReferentialIntegrity; + + private IDGenerationLocation idGenerationLocation; + + private CommitInfoStorage commitInfoStorage; + + private long optimisticLockingTimeout = 10000L; + + private CDOTimeProvider timeProvider; + + /** + * Must not be thread-bound to support XA commits. + */ + private final 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 lockingManager; + + private InternalUnitManager unitManager; + + private IQueryHandlerProvider queryHandlerProvider; + + private IManagedContainer container; + + private final List readAccessHandlers = new ArrayList(); + + private final List writeAccessHandlers = new ArrayList(); + + // Bug 297940 + private final TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this); + + @ExcludeFromDump + private final transient Object commitTransactionLock = new Object(); + + @ExcludeFromDump + private final transient Object createBranchLock = new Object(); + + private boolean skipInitialization; + + private EPackage[] initialPackages; + + private CDOID rootResourceID; + + private long lastTreeRestructuringCommit = -1; + + 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 boolean waitWhileInitial(IProgressMonitor monitor) + { + return CDOCommonUtil.waitWhileInitial(this, this, monitor); + } + + public synchronized Map getProperties() + { + if (properties == null) + { + properties = new HashMap(); + } + + return properties; + } + + public synchronized void setProperties(Map properties) + { + this.properties = properties; + } + + public boolean isAuthenticating() + { + if (sessionManager != null) + { + return sessionManager.getAuthenticator() != null; + } + + return false; + } + + public boolean isSupportingAudits() + { + return supportingAudits; + } + + public boolean isSupportingBranches() + { + return supportingBranches; + } + + public boolean isSupportingUnits() + { + return supportingUnits; + } + + @Deprecated + public boolean isSupportingEcore() + { + return true; + } + + public boolean isSerializingCommits() + { + return serializingCommits; + } + + public boolean isEnsuringReferentialIntegrity() + { + return ensuringReferentialIntegrity; + } + + public IDGenerationLocation getIDGenerationLocation() + { + return idGenerationLocation; + } + + public CommitInfoStorage getCommitInfoStorage() + { + return commitInfoStorage; + } + + public long getOptimisticLockingTimeout() + { + return optimisticLockingTimeout; + } + + public void setOptimisticLockingTimeout(long optimisticLockingTimeout) + { + this.optimisticLockingTimeout = optimisticLockingTimeout; + } + + public String getStoreType() + { + return store.getType(); + } + + public Set 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 createBranch(int branchID, BranchInfo branchInfo) + { + if (!isSupportingBranches()) + { + throw new IllegalStateException("Branching is not supported by " + this); + } + + long baseTimeStamp = branchInfo.getBaseTimeStamp(); + long baseTimeStampMax = timeStampAuthority.getMaxBaseTimeForNewBranch(); + + if (baseTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE || baseTimeStamp > baseTimeStampMax) + { + baseTimeStamp = baseTimeStampMax; + 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); + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + if (!isSupportingBranches()) + { + throw new IllegalStateException("Branching is not supported by " + this); + } + + if (branchID == CDOBranch.MAIN_BRANCH_ID) + { + throw new IllegalArgumentException("Renaming of the MAIN branch is not supported"); + } + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + if (!(accessor instanceof BranchLoader3)) + { + throw new UnsupportedOperationException("Branch renaming is not supported by " + this); + } + + synchronized (createBranchLock) + { + ((BranchLoader3)accessor).renameBranch(branchID, oldName, newName); + } + } + + 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 loadRevisions(List 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()) + { + InternalCDORevision target = loadRevisionTarget(id, branchPoint, referenceChunk, accessor); + if (target != null) + { + target = normalizeRevision(target, info, referenceChunk); + + 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(); + + revision = normalizeRevision(revision, info, referenceChunk); + info.setResult(revision); + } + } + + return null; + } + + private InternalCDORevision normalizeRevision(InternalCDORevision revision, RevisionInfo info, int referenceChunk) + { + if (info instanceof RevisionInfo.Available) + { + RevisionInfo.Available availableInfo = (RevisionInfo.Available)info; + + CDOBranchVersion availableBranchVersion = availableInfo.getAvailableBranchVersion(); + if (availableBranchVersion instanceof BaseCDORevision) + { + BaseCDORevision availableRevision = (BaseCDORevision)availableBranchVersion; + if (availableRevision.equals(revision)) + { + ensureChunks(availableRevision, referenceChunk); + return availableRevision; + } + } + } + + if (referenceChunk == UNCHUNKED) + { + revision.setUnchunked(); + } + + return revision; + } + + 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), 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); + } + + public CDOBranchPointRange loadObjectLifetime(CDOID id, CDOBranchPoint branchPoint) + { + CDORevision revision = revisionManager.getRevision(id, branchPoint, UNCHUNKED, NONE, true); + if (revision == null) + { + return null; + } + + CDORevision firstRevision = getFirstRevision(id, revision); + if (firstRevision == null) + { + return null; + } + + CDOBranchPoint lastPoint = getLastBranchPoint(revision, branchPoint); + return CDOBranchUtil.createRange(firstRevision, lastPoint); + } + + private CDORevision getFirstRevision(CDOID id, CDORevision revision) + { + CDOBranch branch = revision.getBranch(); + + for (int version = revision.getVersion() - 1; version >= CDOBranchVersion.FIRST_VERSION; --version) + { + CDORevision rev = revisionManager.getRevisionByVersion(id, branch.getVersion(version), UNCHUNKED, true); + if (rev == null) + { + return revision; + } + + revision = rev; + } + + if (!branch.isMainBranch()) + { + CDOBranchPoint base = branch.getBase(); + CDORevision baseRevision = revisionManager.getRevision(id, base, UNCHUNKED, NONE, true); + if (baseRevision != null) + { + return getFirstRevision(id, baseRevision); + } + } + + return revision; + } + + private CDOBranchPoint getLastBranchPoint(CDORevision revision, CDOBranchPoint branchPoint) + { + CDOBranch branch = branchPoint.getBranch(); + if (revision.getBranch() != branch) + { + return branch.getHead(); + } + + CDOID id = revision.getID(); + for (int version = revision.getVersion() + 1; version <= Integer.MAX_VALUE; ++version) + { + if (revision.getRevised() == CDOBranchPoint.UNSPECIFIED_DATE) + { + break; + } + + CDORevision rev = revisionManager.getRevisionByVersion(id, branch.getVersion(version), UNCHUNKED, true); + if (rev == null) + { + break; + } + + revision = rev; + } + + return branch.getPoint(revision.getRevised()); + } + + /** + * @deprecated Not used. + */ + @Deprecated + protected void ensureChunks(InternalCDORevision revision, int referenceChunk, IStoreAccessor accessor) + { + throw new UnsupportedOperationException(); + } + + public void ensureChunks(InternalCDORevision revision) + { + ensureChunks(revision, UNCHUNKED); + } + + public void ensureChunks(InternalCDORevision revision, int chunkSize) + { + if (revision.isUnchunked()) + { + return; + } + + IStoreAccessor accessor = null; + boolean unchunked = true; + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + if (feature.isMany()) + { + MoveableList list = revision.getListOrNull(feature); + if (list != null) + { + int size = list.size(); + if (size != 0) + { + int chunkSizeToUse = chunkSize; + if (chunkSizeToUse == UNCHUNKED) + { + chunkSizeToUse = size; + } + + int chunkEnd = Math.min(chunkSizeToUse, size); + accessor = ensureChunk(revision, feature, accessor, list, 0, chunkEnd); + + if (unchunked) + { + for (int i = chunkEnd; i < size; i++) + { + if (list.get(i) == InternalCDOList.UNINITIALIZED) + { + unchunked = false; + break; + } + } + } + } + } + } + } + + if (unchunked) + { + revision.setUnchunked(); + } + } + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, int chunkEnd) + { + if (!revision.isUnchunked()) + { + MoveableList list = revision.getListOrNull(feature); + if (list == null) + { + return null; + } + + chunkEnd = Math.min(chunkEnd, list.size()); + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + ensureChunk(revision, feature, accessor, list, chunkStart, chunkEnd); + + // TODO Expensive: if the revision is unchunked all lists/elements must be visited + if (isUnchunked(revision)) + { + revision.setUnchunked(); + } + + return accessor; + } + + return null; + } + + private boolean isUnchunked(InternalCDORevision revision) + { + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + if (feature.isMany()) + { + MoveableList list = revision.getListOrNull(feature); + if (list != null) + { + int size = list.size(); + for (int i = 0; i < size; i++) + { + if (list.get(i) == InternalCDOList.UNINITIALIZED) + { + return false; + } + } + } + } + } + + return true; + } + + protected IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, IStoreAccessor accessor, MoveableList 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 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 CDOTimeProvider getTimeProvider() + { + return timeProvider; + } + + public void setTimeProvider(CDOTimeProvider timeProvider) + { + checkInactive(); + this.timeProvider = timeProvider; + } + + 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 InternalUnitManager getUnitManager() + { + return unitManager; + } + + public void setUnitManager(InternalUnitManager unitManager) + { + checkInactive(); + this.unitManager = unitManager; + } + + 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 + * @deprecated + */ + @Deprecated + public InternalLockManager getLockManager() + { + return getLockingManager(); + } + + public InternalLockManager getLockingManager() + { + return lockingManager; + } + + /** + * @since 2.0 + */ + public void setLockingManager(InternalLockManager lockingManager) + { + checkInactive(); + this.lockingManager = lockingManager; + } + + 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); + } + + public void executeOutsideStartCommit(Runnable runnable) + { + synchronized (timeStampAuthority) + { + runnable.run(); + } + } + + public void commit(InternalCommitContext commitContext, OMMonitor monitor) + { + if (commitContext.isTreeRestructuring()) + { + synchronized (commitTransactionLock) + { + commitContext.setLastTreeRestructuringCommit(lastTreeRestructuringCommit); + commitUnsynced(commitContext, monitor); + lastTreeRestructuringCommit = commitContext.getTimeStamp(); + } + } + else if (serializingCommits) + { + synchronized (commitTransactionLock) + { + commitUnsynced(commitContext, monitor); + } + } + else + { + commitUnsynced(commitContext, monitor); + } + } + + protected void commitUnsynced(InternalCommitContext commitContext, OMMonitor monitor) + { + ProgressDistributor distributor = store.getIndicatingCommitDistributor(); + distributor.run(InternalCommitContext.OPS, commitContext, monitor); + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo) + { + sendCommitNotification(sender, commitInfo, true); + } + + @Deprecated + public CDOCommitInfoHandler[] getCommitInfoHandlers() + { + return commitInfoManager.getCommitInfoHandlers(); + } + + @Deprecated + public void addCommitInfoHandler(CDOCommitInfoHandler handler) + { + commitInfoManager.addCommitInfoHandler(handler); + } + + @Deprecated + public void removeCommitInfoHandler(CDOCommitInfoHandler handler) + { + commitInfoManager.removeCommitInfoHandler(handler); + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo info) + { + CDOCommitInfo commitInfo = info.getCommitInfo(); + boolean isFailureCommitInfo = commitInfo.getBranch() == null; + if (isFailureCommitInfo || !commitInfo.isEmpty()) + { + sessionManager.sendCommitNotification(info); + commitInfoManager.notifyCommitInfoHandlers(commitInfo); + } + } + + /** + * @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_INSTANCES.equals(language)) + { + return new InstancesQueryHandler(); + } + + 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) + { + IManagedContainer container = getContainer(); + queryHandlerProvider = new ContainerQueryHandlerProvider(container); + } + + IQueryHandler handler = queryHandlerProvider.getQueryHandler(info); + if (handler != null) + { + return handler; + } + + return null; + } + + public IManagedContainer getContainer() + { + if (container == null) + { + return IPluginContainer.INSTANCE; + } + + return container; + } + + public void setContainer(IManagedContainer container) + { + this.container = container; + } + + public ExecutorService getExecutorService() + { + IManagedContainer container = getContainer(); + return ConcurrencyUtil.getExecutorService(container); + } + + public Object[] getElements() + { + final Object[] elements = { packageRegistry, branchManager, revisionManager, sessionManager, queryManager, commitManager, commitInfoManager, + getLockingManager(), 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})", //$NON-NLS-1$ + CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(creationTimeStamp))); + } + + long currentTimeStamp = getTimeStamp(); + if (timeStamp > currentTimeStamp) + { + throw new IllegalArgumentException(MessageFormat.format("timeStamp ({0}) > current time ({1})", //$NON-NLS-1$ + CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(currentTimeStamp))); + } + } + + public long getTimeStamp() + { + return timeProvider.getTimeStamp(); + } + + public Set getHandlers() + { + Set handlers = new HashSet(); + + synchronized (readAccessHandlers) + { + handlers.addAll(readAccessHandlers); + } + + synchronized (writeAccessHandlers) + { + handlers.addAll(writeAccessHandlers); + } + + return handlers; + } + + /** + * @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 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 = store.getLastBranchID(); + + final long fromCommitTime = lastReplicatedCommitTime + 1L; + final long toCommitTime = store.getLastCommitTime(); + + out.writeXInt(toBranchID); + out.writeXLong(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); + + getLockingManager().getLockAreas(null, context); + } + + public CDOChangeSetData getChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint) + { + CDOChangeSetSegment[] segments = CDOChangeSetSegment.createFrom(startPoint, endPoint); + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + Set ids = accessor.readChangeSet(new Monitor(), segments); + + return CDORevisionUtil.createChangeSetData(ids, startPoint, endPoint, revisionManager); + } + + @Deprecated + public Set getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, CDORevisionAvailabilityInfo targetBaseInfo, + CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor) + { + MergeDataResult result = getMergeData2(targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo, monitor); + Set ids = result.getTargetIDs(); + ids.addAll(result.getSourceIDs()); + return ids; + } + + public MergeDataResult getMergeData2(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(); + + MergeDataResult result = new MergeDataResult(); + Set targetIDs = result.getTargetIDs(); + Set sourceIDs = result.getSourceIDs(); + + if (targetBaseInfo == null && sourceBaseInfo == null) + { + if (CDOBranchUtil.isContainedBy(source, target)) + { + // This is a "compare" case, see CDOSessionImpl.compareRevisions(). + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(source, target))); + } + else if (CDOBranchUtil.isContainedBy(target, source)) + { + // This is a "compare" case, see CDOSessionImpl.compareRevisions(). + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(target, source))); + } + else + { + CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source); + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, target))); + sourceIDs.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, source))); + } + } + else + { + CDOChangeSetSegment[] targetSegments; + CDOChangeSetSegment[] sourceSegments; + + if (targetBaseInfo.getBranchPoint() == CDOBranchUtil.AUTO_BRANCH_POINT) + { + CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source); + targetSegments = CDOChangeSetSegment.createFrom(ancestor, target); + sourceSegments = CDOChangeSetSegment.createFrom(ancestor, source); + + CDOBranchPoint targetBase = ancestor; + CDOBranchPoint sourceBase = ancestor; + long ancestorTime = ancestor.getTimeStamp(); + + CDOBranchPointRange latestTargetMerge = getLatestMerge(targetSegments, sourceSegments, ancestorTime); + if (latestTargetMerge != null) + { + targetBase = latestTargetMerge.getEndPoint(); + sourceBase = latestTargetMerge.getStartPoint(); + + if (!sourceBase.equals(ancestor)) + { + sourceSegments = CDOChangeSetSegment.createFrom(sourceBase, source); + } + } + + CDOBranchPointRange latestSourceMerge = getLatestMerge(sourceSegments, targetSegments, ancestorTime); + if (latestSourceMerge != null) + { + CDOBranchPoint mergeSource = latestSourceMerge.getStartPoint(); + if (targetBase.getTimeStamp() < mergeSource.getTimeStamp()) + { + targetBase = mergeSource; + } + + result.setResultBase(sourceBase); + } + + if (!targetBase.equals(ancestor)) + { + targetSegments = CDOChangeSetSegment.createFrom(targetBase, target); + } + + targetBaseInfo.setBranchPoint(targetBase); + sourceBaseInfo.setBranchPoint(sourceBase); + } + else + { + CDORevisionAvailabilityInfo sourceBaseInfoToUse = sourceBaseInfo == null ? targetBaseInfo : sourceBaseInfo; + targetSegments = CDOChangeSetSegment.createFrom(targetBaseInfo.getBranchPoint(), target); + sourceSegments = CDOChangeSetSegment.createFrom(sourceBaseInfoToUse.getBranchPoint(), source); + } + + targetIDs.addAll(accessor.readChangeSet(monitor.fork(), targetSegments)); + sourceIDs.addAll(accessor.readChangeSet(monitor.fork(), sourceSegments)); + } + + loadMergeData(targetIDs, targetInfo, monitor.fork()); + loadMergeData(sourceIDs, sourceInfo, monitor.fork()); + + if (targetBaseInfo != null) + { + loadMergeData(targetIDs, targetBaseInfo, monitor.fork()); + } + + if (sourceBaseInfo != null && !targetBaseInfo.getBranchPoint().equals(sourceBaseInfo.getBranchPoint())) + { + loadMergeData(sourceIDs, sourceBaseInfo, monitor.fork()); + } + + return result; + } + finally + { + monitor.done(); + } + } + + private CDOBranchPointRange getLatestMerge(CDOChangeSetSegment[] targetSegments, CDOChangeSetSegment[] sourceSegments, long ancestorTime) + { + for (int i = targetSegments.length - 1; i >= 0; --i) + { + CDOChangeSetSegment targetSegment = targetSegments[i]; + CDOBranch targetBranch = targetSegment.getBranch(); + long startTime = targetSegment.getTimeStamp(); + long endTime = targetSegment.getEndTime(); + + while (endTime > startTime || endTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + CDOCommitInfo commitInfo = commitInfoManager.getCommitInfo(targetBranch, endTime, false); + if (commitInfo == null) + { + break; + } + + long timeStamp = commitInfo.getTimeStamp(); + if (timeStamp <= startTime) + { + break; + } + + CDOBranchPoint mergeSource = getMergeSource(commitInfo, sourceSegments, ancestorTime); + if (mergeSource != null) + { + CDOBranchPoint endPoint = CDOBranchUtil.copyBranchPoint(commitInfo); + return CDOBranchUtil.createRange(mergeSource, endPoint); + } + + endTime = timeStamp - 1; + } + } + + return null; + } + + private CDOBranchPoint getMergeSource(CDOCommitInfo commitInfo, CDOChangeSetSegment[] sourceSegments, long ancestorTime) + { + CDOBranchPoint mergeSource = commitInfo.getMergeSource(); + if (mergeSource != null) + { + if (CDOChangeSetSegment.contains(sourceSegments, mergeSource)) + { + return mergeSource; + } + + CDOChangeSetSegment[] targetSegments = CDOChangeSetSegment.createFrom(ancestorTime, mergeSource); + CDOBranchPointRange latestMerge = getLatestMerge(targetSegments, sourceSegments, ancestorTime); + if (latestMerge != null) + { + return latestMerge.getStartPoint(); + } + } + + return null; + } + + private void loadMergeData(Set 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) + { + return revisionManager.getRevision(id, branchPoint, UNCHUNKED, NONE, true); + } + + public void queryLobs(List 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 handled = new HashSet(); + + 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 revisionKeysToObjects(List revisionKeys, CDOBranch viewedBranch, boolean isSupportingBranches) + { + List lockables = new ArrayList(); + 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 revKeys, boolean recursive, long timeout) + { + List lockables = revisionKeysToObjects(revKeys, view.getBranch(), isSupportingBranches()); + return lock(view, lockType, lockables, revKeys, recursive, timeout); + } + + protected LockObjectsResult lock(InternalView view, LockType type, List lockables, List loadedRevs, boolean recursive, long timeout) + { + List> newLockStates = null; + + try + { + newLockStates = getLockingManager().lock2(true, type, view, lockables, recursive, 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 = null; + + try + { + staleRevisionsArray = checkStaleRevisions(view, loadedRevs, lockables, type, requiredTimestamp); + } + catch (IllegalArgumentException e) + { + getLockingManager().unlock2(true, type, view, lockables, recursive); + throw e; + } + + // 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) + { + getLockingManager().unlock2(true, type, view, lockables, recursive); + 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 revisionKeys, List objectsToLock, LockType lockType, + long[] requiredTimestamp) + { + List staleRevisions = new LinkedList(); + 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(), UNCHUNKED, NONE, true); + + if (rev == null) + { + throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id, viewedBranch)); + } + + if (!revKey.equals(rev)) + { + // Send back the *expected* revision keys, so that the client can check that it really has loaded those. + staleRevisions.add(CDORevisionUtil.copyRevisionKey(rev)); + 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> lockStates) + { + CDOLockState[] cdoLockStates = new CDOLockState[lockStates.size()]; + int i = 0; + + for (LockState lockState : lockStates) + { + CDOLockState cdoLockState = CDOLockUtil.createLockState(lockState); + cdoLockStates[i++] = cdoLockState; + } + + return cdoLockStates; + } + + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List objectIDs, boolean recursive) + { + List unlockables = null; + if (objectIDs != null) + { + unlockables = new ArrayList(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, recursive); + } + + protected UnlockObjectsResult doUnlock(InternalView view, LockType lockType, List unlockables, boolean recursive) + { + List> newLockStates = null; + if (lockType == null) // Signals an unlock-all operation + { + newLockStates = getLockingManager().unlock2(true, view); + } + else + { + newLockStates = getLockingManager().unlock2(true, lockType, view, unlockables, recursive); + } + + long timestamp = getTimeStamp(); + CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates); + sendLockNotifications(view, Operation.UNLOCK, lockType, cdoLockStates); + + return new UnlockObjectsResult(cdoLockStates, timestamp); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) + { + return AdapterUtil.adapt(this, adapter, false); + } + + @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() + { + // SUPPORTING_AUDITS + String valueAudits = properties.get(Props.SUPPORTING_AUDITS); + if (valueAudits != null) + { + supportingAudits = Boolean.valueOf(valueAudits); + } + else + { + supportingAudits = store.getRevisionTemporality() == IStore.RevisionTemporality.AUDITING; + } + + // SUPPORTING_BRANCHES + String valueBranches = properties.get(Props.SUPPORTING_BRANCHES); + if (valueBranches != null) + { + supportingBranches = Boolean.valueOf(valueBranches); + } + else + { + supportingBranches = store.getRevisionParallelism() == IStore.RevisionParallelism.BRANCHING; + } + + // SUPPORTING_UNITS + String valueUnits = properties.get(Props.SUPPORTING_UNITS); + if (valueUnits != null) + { + supportingUnits = Boolean.valueOf(valueUnits); + } + + // SERIALIZE_COMMITS + String valueCommits = properties.get(Props.SERIALIZE_COMMITS); + if (valueCommits != null) + { + serializingCommits = Boolean.valueOf(valueCommits); + } + + // ENSURE_REFERENTIAL_INTEGRITY + String valueIntegrity = properties.get(Props.ENSURE_REFERENTIAL_INTEGRITY); + if (valueIntegrity != null) + { + ensuringReferentialIntegrity = Boolean.valueOf(valueIntegrity); + } + + // ID_GENERATION_LOCATION + String valueIDLocation = properties.get(Props.ID_GENERATION_LOCATION); + if (valueIDLocation != null) + { + idGenerationLocation = IDGenerationLocation.valueOf(valueIDLocation); + } + + if (idGenerationLocation == null) + { + idGenerationLocation = IDGenerationLocation.STORE; + } + + // COMMIT_INFO_STORAGE + String valueCommitInfoStorage = properties.get(Props.COMMIT_INFO_STORAGE); + if (valueCommitInfoStorage != null) + { + commitInfoStorage = CommitInfoStorage.valueOf(valueCommitInfoStorage); + } + + if (commitInfoStorage == null) + { + commitInfoStorage = CommitInfoStorage.WITH_MERGE_SOURCE; + } + + if (commitInfoStorage != CommitInfoStorage.NO && !supportingBranches) + { + commitInfoStorage = CommitInfoStorage.YES; + } + + // ENSURE_REFERENTIAL_INTEGRITY + String valueTimeout = properties.get(Props.OPTIMISTIC_LOCKING_TIMEOUT); + if (valueTimeout != null) + { + optimisticLockingTimeout = Long.valueOf(valueTimeout); + } + } + + @Deprecated + public void initSystemPackages() + { + initSystemPackages(true); + } + + public void initSystemPackages(final boolean firstStart) + { + final List list = new ArrayList(); + IStoreAccessor writer = store.getWriter(null); + StoreThreadLocal.setAccessor(writer); + + try + { + long timeStamp; + if (firstStart) + { + timeStamp = store.getCreationTime(); + list.add(initPackage(timeStamp, EcorePackage.eINSTANCE)); + list.add(initPackage(timeStamp, EresourcePackage.eINSTANCE)); + list.add(initPackage(timeStamp, EtypesPackage.eINSTANCE)); + } + else + { + timeStamp = getTimeStamp(); + } + + if (initialPackages != null) + { + for (EPackage initialPackage : initialPackages) + { + if (!packageRegistry.containsKey(initialPackage.getNsURI())) + { + InternalCDOPackageUnit packageUnit = initPackage(timeStamp, initialPackage); + list.add(packageUnit); + } + } + } + + if (!list.isEmpty()) + { + InternalCDOPackageUnit[] initialUnits = list.toArray(new InternalCDOPackageUnit[list.size()]); + writer.writePackageUnits(initialUnits, new Monitor()); + writer.commit(new Monitor()); + } + } + finally + { + StoreThreadLocal.release(); + } + + fireEvent(new PackagesInitializedEvent() + { + public InternalRepository getSource() + { + return Repository.this; + } + + public boolean isFirstStart() + { + return firstStart; + } + + public List getPackageUnits() + { + return Collections.unmodifiableList(list); + } + }); + } + + protected InternalCDOPackageUnit initPackage(long timeStamp, EPackage ePackage) + { + EMFUtil.registerPackage(ePackage, packageRegistry); + InternalCDOPackageInfo packageInfo = packageRegistry.getPackageInfo(ePackage); + + InternalCDOPackageUnit packageUnit = packageInfo.getPackageUnit(); + packageUnit.setTimeStamp(timeStamp); + 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 ""; //$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 packageUnits = reader.readPackageUnits(); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + packageRegistry.putPackageUnit(packageUnit); + } + } + finally + { + StoreThreadLocal.release(); + } + } + + protected void setPostActivateState() + { + setState(State.ONLINE); + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + + checkState(!StringUtil.isEmpty(name), "name is empty"); //$NON-NLS-1$ + checkState(store, "store"); //$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(lockingManager, "lockingManager"); //$NON-NLS-1$ + + packageRegistry.setReplacingDescriptors(true); + + if (packageRegistry.getPackageProcessor() == null) + { + packageRegistry.setPackageProcessor(this); + } + + if (packageRegistry.getPackageLoader() == null) + { + packageRegistry.setPackageLoader(this); + } + + if (branchManager.getRepository() == null) + { + branchManager.setRepository(this); + } + + if (branchManager.getBranchLoader() == null) + { + branchManager.setBranchLoader(this); + } + + if (revisionManager.getRevisionLoader() == null) + { + revisionManager.setRevisionLoader(this); + } + + if (sessionManager.getRepository() == null) + { + sessionManager.setRepository(this); + } + + if (queryManager.getRepository() == null) + { + queryManager.setRepository(this); + } + + if (commitInfoManager.getRepository() == null) + { + commitInfoManager.setRepository(this); + } + + if (commitInfoManager.getCommitInfoLoader() == null) + { + commitInfoManager.setCommitInfoLoader(this); + } + + if (commitManager.getRepository() == null) + { + commitManager.setRepository(this); + } + + if (lockingManager.getRepository() == null) + { + lockingManager.setRepository(this); + } + + if (store.getRepository() == null) + { + 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 (supportingUnits) + { + LifecycleUtil.activate(unitManager); + } + + if (!skipInitialization) + { + long creationTime = store.getCreationTime(); + initMainBranch(branchManager, creationTime); + + long lastCommitTime = Math.max(creationTime, store.getLastCommitTime()); + timeStampAuthority.setLastFinishedTimeStamp(lastCommitTime); + commitInfoManager.setLastCommitOfBranch(null, lastCommitTime); + + if (store.isFirstStart()) + { + initSystemPackages(true); + initRootResource(); + } + else + { + readPackageUnits(); + initSystemPackages(false); + + readRootResource(); + } + } + + LifecycleUtil.activate(lockingManager); // Needs an initialized main branch / branch manager + setPostActivateState(); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(unitManager); + LifecycleUtil.deactivate(lockingManager); + 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 (getTimeProvider() == null) + { + setTimeProvider(createTimeProvider()); + } + + 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) + { + setLockingManager(createLockManager()); + } + + if (getUnitManager() == null) + { + setUnitManager(createUnitManager()); + } + + super.doBeforeActivate(); + } + + protected CDOTimeProvider createTimeProvider() + { + return CurrentTimeProvider.INSTANCE; + } + + 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 InternalUnitManager createUnitManager() + { + return new UnitManager(this); + } + + @Deprecated + protected InternalLockManager createLockManager() + { + return createLockingManager(); + } + + public LockingManager createLockingManager() + { + return new LockingManager(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java new file mode 100644 index 000000000..250bea1d7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2008-2013 Eike Stepper (Loehne, 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.server.StoreThreadLocal.NoSessionRegisteredException; +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) + { + try + { + 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); + } + } + catch (NoSessionRegisteredException ex) + { + // View has been closed - do nothing + } + } + + /** + * @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 resourceIDs = new HashSet(); + + public QueryContext(CDOQueryInfo info, IQueryContext context) + { + this.info = info; + this.context = context; + branchPoint = context; + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + this.branchPoint = branchPoint; + } + + public Set 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java new file mode 100644 index 000000000..eaaedbbca --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java @@ -0,0 +1,1271 @@ +/* + * Copyright (c) 2010-2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399306 + */ +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.id.CDOIDUtil; +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.CDOProtocol.CommitNotificationInfo; +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.common.security.CDOPermission; +import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.session.CDOCollectionLoadingPolicy; +import org.eclipse.emf.cdo.session.CDORepositoryInfo; +import org.eclipse.emf.cdo.session.CDOSession; +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.CDOUnitManager; +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.emf.internal.cdo.view.CDOLockStateLoadingPolicy; + +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.KeyedReference; +import org.eclipse.net4j.util.ref.ReferenceType; +import org.eclipse.net4j.util.ref.ReferenceValueMap2; +import org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; +import org.eclipse.net4j.util.security.IPasswordCredentialsProvider; + +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.CDOPermissionUpdater; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult.Provider; +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 org.eclipse.emf.spi.cdo.InternalCDOViewSet; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.PlatformObject; + +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, CDORevisionProvider revisionProvider) + { + super(null, branchPoint); + this.session = new ServerCDOSession(session); + this.revisionProvider = revisionProvider; + + InternalCDOViewSet resourceSet = SessionUtil.prepareResourceSet(new ResourceSetImpl()); + setViewSet(resourceSet); + + Map> map = CDOIDUtil.createMap(); + setObjects(new ReferenceValueMap2.Weak(map)); + + 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 InternalCDORevision getRevision(CDOID id, boolean loadOnDemand) + { + return (InternalCDORevision)revisionProvider.getRevision(id); + } + + @Override + protected void excludeNewObject(CDOID id) + { + // Do nothing + } + + public boolean isInvalidationRunnerActive() + { + return false; + } + + public boolean setBranchPoint(CDOBranchPoint branchPoint, IProgressMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public void lockObjects(Collection objects, LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void lockObjects(Collection objects, LockType lockType, long timeout, boolean recursive) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(Collection objects, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(Collection objects, LockType lockType, boolean recursive) + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects() + { + throw new UnsupportedOperationException(); + } + + public boolean waitForUpdate(long updateTime, long timeoutMillis) + { + throw new UnsupportedOperationException(); + } + + public boolean runAfterUpdate(long updateTime, Runnable runnable) + { + throw new UnsupportedOperationException(); + } + + public void setViewID(int viewId) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setSession(InternalCDOSession session) + { + throw new UnsupportedOperationException(); + } + + public int getSessionID() + { + return session.getSessionID(); + } + + public boolean isDurableView() + { + return false; + } + + public String getDurableLockingID() + { + return null; + } + + @Deprecated + public String enableDurableLocking(boolean enable) + { + throw new UnsupportedOperationException(); + } + + public String enableDurableLocking() + { + throw new UnsupportedOperationException(); + } + + public void disableDurableLocking(boolean releaseLocks) + { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + public CDOFeatureAnalyzer getFeatureAnalyzer() + { + return CDOFeatureAnalyzer.NOOP; + } + + @SuppressWarnings("deprecation") + public void setFeatureAnalyzer(CDOFeatureAnalyzer featureAnalyzer) + { + throw new UnsupportedOperationException(); + } + + public InternalCDOTransaction toTransaction() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOBranch branch, long lastUpdateTime, List allChangedObjects, List allDetachedObjects, + Map oldRevisions, boolean async) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOBranch branch, long lastUpdateTime, List allChangedObjects, List allDetachedObjects, + Map oldRevisions, boolean async, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void invalidate(ViewInvalidationData invalidationData) + { + throw new UnsupportedOperationException(); + } + + public void handleLockNotification(InternalCDOView sender, CDOLockChangeInfo lockChangeInfo) + { + // Do nothing + } + + public void resourceLoaded(CDOResourceImpl resource, boolean loaded) + { + // 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 isDetachmentNotificationEnabled() + { + return false; + } + + public void setDetachmentNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isInvalidationNotificationEnabled() + { + return false; + } + + public void setInvalidationNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isLoadNotificationEnabled() + { + return false; + } + + public void setLoadNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isLockNotificationEnabled() + { + return false; + } + + public boolean isLockStatePrefetchEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public CDOLockStateLoadingPolicy getLockStateLoadingPolicy() + { + return null; + } + + public void setLockStateLoadingPolicy(CDOLockStateLoadingPolicy lockStateLoadingPolicy) + { + } + + public void setLockStatePrefetchEnabled(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.DEFAULT; + } + + 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 ids) + { + throw new UnsupportedOperationException(); + } + + public CDOUnitManager getUnitManager() + { + throw new UnsupportedOperationException(); + } + + /** + * @author Eike Stepper + */ + private final class ServerCDOSession extends PlatformObject implements InternalCDOSession, CDORepositoryInfo, org.eclipse.emf.cdo.session.CDOSession.Options + { + private final IRegistry properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + + private boolean generatedPackageEmulationEnabled; + + private InternalSession internalSession; + + private InternalRepository repository; + + public ServerCDOSession(InternalSession internalSession) + { + this.internalSession = internalSession; + repository = internalSession.getManager().getRepository(); + } + + public IRegistry properties() + { + return properties; + } + + public CDOSession getSession() + { + return this; + } + + public String getUserID() + { + return internalSession.getUserID(); + } + + public int getSessionID() + { + return internalSession.getSessionID(); + } + + public CDOView[] getElements() + { + return new ServerCDOView[] { ServerCDOView.this }; + } + + public InternalCDOTransaction getTransaction(int viewID) + { + return null; + } + + public InternalCDOTransaction[] getTransactions() + { + return new InternalCDOTransaction[0]; + } + + public InternalCDOTransaction[] getTransactions(CDOBranch branch) + { + return new InternalCDOTransaction[0]; + } + + public CDOView[] getViews(CDOBranch branch) + { + if (getBranch() == branch) + { + return getViews(); + } + + return new CDOView[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(); + } + + /** + * Server sessions may not be used to change the user's credentials: it must + * be done client-side by interaction with the user. + * + * @since 4.3 + */ + public void changeCredentials() + { + throw new UnsupportedOperationException(); + } + + /** + * Server sessions may not be used to reset a user's credentials: it must + * be done client-side by interaction with an adminstrator. + * + * @since 4.3 + */ + public void resetCredentials(String userID) + { + 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 getObjectIDTypes() + { + return repository.getObjectIDTypes(); + } + + public CDOID getRootResourceID() + { + return repository.getRootResourceID(); + } + + public boolean isAuthenticating() + { + return repository.isAuthenticating(); + } + + public boolean isSupportingAudits() + { + return repository.isSupportingAudits(); + } + + public boolean isSupportingBranches() + { + return repository.isSupportingBranches(); + } + + public boolean isSupportingUnits() + { + return repository.isSupportingUnits(); + } + + @Deprecated + public boolean isSupportingEcore() + { + return repository.isSupportingEcore(); + } + + public boolean isSerializingCommits() + { + return repository.isSerializingCommits(); + } + + public boolean isEnsuringReferentialIntegrity() + { + return repository.isEnsuringReferentialIntegrity(); + } + + public IDGenerationLocation getIDGenerationLocation() + { + return repository.getIDGenerationLocation(); + } + + public CommitInfoStorage getCommitInfoStorage() + { + return repository.getCommitInfoStorage(); + } + + public boolean waitWhileInitial(IProgressMonitor monitor) + { + return repository.waitWhileInitial(monitor); + } + + 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 runAfterUpdate(long updateTime, Runnable runnable) + { + throw new UnsupportedOperationException(); + } + + 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 long refresh(Provider provider) + { + throw new UnsupportedOperationException(); + } + + public long refresh() + { + throw new UnsupportedOperationException(); + } + + public Options options() + { + return this; + } + + 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 ensureChunks(InternalCDORevision revision, int chunkSize) + { + throw new UnsupportedOperationException(); + } + + public void processRefreshSessionResult(RefreshSessionResult result, CDOBranch branch, List branchViews, + Map> viewedRevisions) + { + throw new UnsupportedOperationException(); + } + + public Object startLocalCommit() + { + throw new UnsupportedOperationException(); + } + + public void endLocalCommit(Object token) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender, boolean clearResourcePathCache, byte securityImpact, + Map newPermissions) + { + throw new UnsupportedOperationException(); + } + + public void invalidate(InvalidationData invalidationData) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void handleCommitNotification(CDOCommitInfo commitInfo) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void handleCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void handleCommitNotification(CommitNotificationInfo info) + { + throw new UnsupportedOperationException(); + } + + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo, InternalCDOView sender) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void handleBranchNotification(InternalCDOBranch branch) + { + throw new UnsupportedOperationException(); + } + + public InternalCDORemoteSessionManager getRemoteSessionManager() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public org.eclipse.emf.cdo.common.protocol.CDOAuthenticator getAuthenticator() + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void setAuthenticator(org.eclipse.emf.cdo.common.protocol.CDOAuthenticator authenticator) + { + throw new UnsupportedOperationException(); + } + + public IPasswordCredentialsProvider getCredentialsProvider() + { + throw new UnsupportedOperationException(); + } + + public void setCredentialsProvider(IPasswordCredentialsProvider credentialsProvider) + { + 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(); + } + + @Deprecated + public CDORevisionAvailabilityInfo createRevisionAvailabilityInfo(CDOBranchPoint branchPoint) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void cacheRevisions(CDORevisionAvailabilityInfo info) + { + throw new UnsupportedOperationException(); + } + + public MergeData getMergeData(CDOBranchPoint target, CDOBranchPoint source, CDOBranchPoint sourceBase, boolean computeChangeSets) + { + throw new UnsupportedOperationException(); + } + + public MergeData getMergeData(CDOBranchPoint target, CDOBranchPoint source, CDOBranchPoint targetBase, CDOBranchPoint sourceBase, boolean computeChangeSets) + { + throw new UnsupportedOperationException(); + } + + public boolean isPassiveUpdateEnabled() + { + throw new UnsupportedOperationException(); + } + + public void setPassiveUpdateEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public PassiveUpdateMode getPassiveUpdateMode() + { + throw new UnsupportedOperationException(); + } + + public void setPassiveUpdateMode(PassiveUpdateMode mode) + { + throw new UnsupportedOperationException(); + } + + public LockNotificationMode getLockNotificationMode() + { + throw new UnsupportedOperationException(); + } + + public void setLockNotificationMode(LockNotificationMode mode) + { + throw new UnsupportedOperationException(); + } + + public CDOSession getContainer() + { + throw new UnsupportedOperationException(); + } + + public boolean isGeneratedPackageEmulationEnabled() + { + return generatedPackageEmulationEnabled; + } + + public void setGeneratedPackageEmulationEnabled(boolean generatedPackageEmulationEnabled) + { + this.generatedPackageEmulationEnabled = generatedPackageEmulationEnabled; + } + + public CDOCollectionLoadingPolicy getCollectionLoadingPolicy() + { + throw new UnsupportedOperationException(); + } + + public void setCollectionLoadingPolicy(CDOCollectionLoadingPolicy policy) + { + throw new UnsupportedOperationException(); + } + + public CDOLobStore getLobCache() + { + throw new UnsupportedOperationException(); + } + + public void setLobCache(CDOLobStore lobCache) + { + throw new UnsupportedOperationException(); + } + + public CDOPermissionUpdater getPermissionUpdater() + { + throw new UnsupportedOperationException(); + } + + public void setPermissionUpdater(CDOPermissionUpdater permissionUpdater) + { + throw new UnsupportedOperationException(); + } + + public boolean isDelegableViewLockEnabled() + { + throw new UnsupportedOperationException(); + } + + public void setDelegableViewLockEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java new file mode 100644 index 000000000..faefa0ea9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2007-2016 Eike Stepper (Loehne, 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.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +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.protocol.CDOProtocol.CommitNotificationInfo; +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.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.security.CDOPermission; +import org.eclipse.emf.cdo.internal.common.commit.DelegatingCommitInfo; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IPermissionManager; +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.InternalRepository; +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.AdapterUtil; +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.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + */ +public class Session extends Container 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 boolean openOnClientSide; + + private long firstUpdateTime; + + private long lastUpdateTime; + + @ExcludeFromDump + private Object lastUpdateTimeLock = new Object(); + + private Map views = new HashMap(); + + private AtomicInteger lastTempViewID = new AtomicInteger(); + + private final IRegistry properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + + @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; + } + + public final IRegistry properties() + { + return properties; + } + + /** + * @since 2.0 + */ + public CDOCommonSession getContainer() + { + return this; + } + + public InternalSessionManager getManager() + { + return manager; + } + + public CDOBranchManager getBranchManager() + { + return manager.getRepository().getBranchManager(); + } + + public ISessionProtocol getProtocol() + { + return protocol; + } + + public int getSessionID() + { + return sessionID; + } + + /** + * @since 2.0 + */ + public String getUserID() + { + return userID; + } + + public void setUserID(String userID) + { + this.userID = 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; + } + + @Deprecated + public long getLastUpdateTime() + { + synchronized (lastUpdateTimeLock) + { + return lastUpdateTime; + } + } + + public long getFirstUpdateTime() + { + return firstUpdateTime; + } + + public void setFirstUpdateTime(long firstUpdateTime) + { + this.firstUpdateTime = firstUpdateTime; + } + + public boolean isOpenOnClientSide() + { + return openOnClientSide; + } + + public void setOpenOnClientSide() + { + openOnClientSide = true; + manager.openedOnClientSide(this); + } + + public InternalView[] getElements() + { + checkActive(); + return getViews(); + } + + @Override + public boolean isEmpty() + { + checkActive(); + + synchronized (views) + { + return views.isEmpty(); + } + } + + public InternalView[] getViews() + { + checkActive(); + return getViewsArray(); + } + + private InternalView[] getViewsArray() + { + synchronized (views) + { + return views.values().toArray(new InternalView[views.size()]); + } + } + + public InternalView getView(int viewID) + { + checkActive(); + + synchronized (views) + { + 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(); + + synchronized (views) + { + views.put(viewID, view); + } + + fireElementAddedEvent(view); + } + + /** + * @since 2.0 + */ + public void viewClosed(InternalView view) + { + int viewID = view.getViewID(); + InternalView removedView; + + synchronized (views) + { + removedView = views.remove(viewID); + } + + if (removedView == 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 revisions, + List additionalRevisions) + { + InternalCDORevisionManager revisionManager = manager.getRepository().getRevisionManager(); + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + // 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 CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext) + { + IPermissionManager permissionManager = manager.getPermissionManager(); + if (permissionManager != null) + { + return permissionManager.getPermission(revision, securityContext, this); + } + + return CDORevision.PERMISSION_PROVIDER.getPermission(revision, securityContext); + } + + 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); + } + } + + @Deprecated + public void sendBranchNotification(InternalCDOBranch branch) throws Exception + { + sendBranchNotification(branch, ChangeKind.CREATED); + } + + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception + { + if (protocol != null) + { + protocol.sendBranchNotification(branch, changeKind); + } + } + + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo notificationInfo) throws Exception + { + if (protocol == null) + { + return; + } + + if (!isPassiveUpdateEnabled()) + { + return; + } + + byte securityImpact = notificationInfo.getSecurityImpact(); + if (securityImpact == CommitNotificationInfo.IMPACT_PERMISSIONS) + { + IPermissionManager permissionManager = manager.getPermissionManager(); + Set impactedRules = notificationInfo.getImpactedRules(); + + if (!permissionManager.hasAnyRule(this, impactedRules)) + { + securityImpact = CommitNotificationInfo.IMPACT_NONE; + } + } + + CommitInfo sessionCommitInfo = new CommitInfo(notificationInfo); + + CommitNotificationInfo sessionNotificationInfo = new CommitNotificationInfo(); + sessionNotificationInfo.setSender(notificationInfo.getSender()); + sessionNotificationInfo.setCommitInfo(sessionCommitInfo); + sessionNotificationInfo.setRevisionProvider(notificationInfo.getRevisionProvider()); + sessionNotificationInfo.setClearResourcePathCache(notificationInfo.isClearResourcePathCache()); + sessionNotificationInfo.setNewPermissions(sessionCommitInfo.getNewPermissions()); + sessionNotificationInfo.setSecurityImpact(securityImpact); + + CDOLockChangeInfo lockChangeInfo = notificationInfo.getLockChangeInfo(); + if (lockChangeInfo != null) + { + Object lockNotificationRequired = isLockNotificationRequired(lockChangeInfo); + if (lockNotificationRequired != null) + { + sessionNotificationInfo.setLockChangeInfo(lockChangeInfo); + } + } + + protocol.sendCommitNotification(sessionNotificationInfo); + + synchronized (lastUpdateTimeLock) + { + CDOCommitInfo originalCommitInfo = notificationInfo.getCommitInfo(); + lastUpdateTime = originalCommitInfo.getTimeStamp(); + } + } + + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception + { + if (protocol != null) + { + Object lockNotificationRequired = isLockNotificationRequired(lockChangeInfo); + if (lockNotificationRequired == Boolean.TRUE) + { + protocol.sendLockNotification(lockChangeInfo); + return; + } + + if (lockNotificationRequired instanceof InternalView) + { + InternalView view = (InternalView)lockNotificationRequired; + + try + { + protocol.sendLockNotification(lockChangeInfo); + } + catch (Exception ex) + { + if (!view.isClosed()) + { + OM.LOG.warn("A problem occured while notifying view " + view, ex); + } + } + } + } + } + + private Object isLockNotificationRequired(CDOLockChangeInfo lockChangeInfo) + { + LockNotificationMode lockNotificationMode = options().getLockNotificationMode(); + if (lockNotificationMode == LockNotificationMode.ALWAYS) + { + return Boolean.TRUE; + } + + if (lockNotificationMode == LockNotificationMode.IF_REQUIRED_BY_VIEWS) + { + // We send the lockChangeInfo only if this session has one (or more) views configured for this branch. + for (InternalView view : getViews()) + { + if (view.options().isLockNotificationEnabled()) + { + CDOBranch affectedBranch = lockChangeInfo.getBranch(); + if (view.getBranch() == affectedBranch || affectedBranch == null) + { + return view; + } + } + } + } + + return null; + } + + private boolean isDeltaNeeded(CDOID id, InternalView[] views) + { + boolean supportingUnits = manager.getRepository().isSupportingUnits(); + + for (InternalView view : views) + { + try + { + if (view.hasSubscription(id)) + { + return true; + } + + if (supportingUnits && view.isInOpenUnit(id)) + { + return true; + } + } + catch (Exception ex) + { + if (!view.isClosed()) + { + OM.LOG.warn("A problem occured while checking subscriptions of view " + view, ex); + } + } + } + + 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); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) + { + return AdapterUtil.adapt(this, adapter, false); + } + + @Override + public String toString() + { + String name = "unknown"; + if (manager != null) + { + InternalRepository repository = manager.getRepository(); + if (repository != null) + { + name = repository.getName(); + } + } + + if (userID != null && userID.length() != 0) + { + name = userID + "@" + name; + } + + return MessageFormat.format("Session{0} [{1}]", sessionID, name); //$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(); + } + + /** + * @author Eike Stepper + */ + private final class CommitInfo extends DelegatingCommitInfo + { + private final CDOCommitInfo delegate; + + private final CDORevisionProvider revisionProvider; + + private final InternalView[] views; + + private final IPermissionManager permissionManager; + + private final Map newPermissions; + + private final boolean additions; + + private final boolean changes; + + public CommitInfo(CommitNotificationInfo notificationInfo) + { + delegate = notificationInfo.getCommitInfo(); + revisionProvider = notificationInfo.getRevisionProvider(); + + views = getViews(); + permissionManager = manager.getPermissionManager(); + if (permissionManager != null) + { + newPermissions = CDOIDUtil.createMap(); + } + else + { + newPermissions = null; + } + + PassiveUpdateMode passiveUpdateMode = getPassiveUpdateMode(); + additions = passiveUpdateMode == PassiveUpdateMode.ADDITIONS; + changes = additions || passiveUpdateMode == PassiveUpdateMode.CHANGES; + } + + @Override + protected CDOCommitInfo getDelegate() + { + return delegate; + } + + protected void addNewPermission(CDOID id, CDOPermission permission) + { + newPermissions.put(id, permission); + } + + public Map getNewPermissions() + { + return newPermissions; + } + + @Override + public List getNewObjects() + { + final List newObjects = super.getNewObjects(); + return new IndexedList() + { + @Override + public CDOIDAndVersion get(int index) + { + CDORevision revision = (CDORevision)newObjects.get(index); + if (additions) + { + if (permissionManager == null) + { + // Return full revision + return revision; + } + + CDOPermission permission = permissionManager.getPermission(revision, delegate, Session.this); + CDOID id = revision.getID(); + addNewPermission(id, permission); + + if (permission != CDOPermission.NONE) + { + // Return full revision + return revision; + } + } + + // Prevent sending full revision by copying the id and version + return CDOIDUtil.createIDAndVersion(revision); + } + + @Override + public int size() + { + return newObjects.size(); + } + }; + } + + @Override + public List getChangedObjects() + { + final List changedObjects = super.getChangedObjects(); + return new IndexedList() + { + @Override + public CDORevisionKey get(int index) + { + CDORevisionDelta revisionDelta = (CDORevisionDelta)changedObjects.get(index); + CDOID id = revisionDelta.getID(); + + if (changes || isDeltaNeeded(id, views)) + { + if (permissionManager == null) + { + // Return full delta + return revisionDelta; + } + + if (revisionProvider == null) + { + // Return full delta + return revisionDelta; + } + + CDORevision newRevision = revisionProvider.getRevision(id); + CDOPermission permission = permissionManager.getPermission(newRevision, delegate, Session.this); + addNewPermission(id, permission); + + if (permission != CDOPermission.NONE) + { + // Return full delta + return revisionDelta; + } + } + + // Prevent sending full delta by copying the id and version + return CDORevisionUtil.copyRevisionKey(revisionDelta); + } + + @Override + public int size() + { + return changedObjects.size(); + } + }; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java new file mode 100644 index 000000000..d29755b0b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2007-2013, 2015-2017 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399306 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +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.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IPermissionManager; +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.server.IAuthenticationProtocol; +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.ObjectUtil; +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +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.trace.ContextTracer; +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; +import org.eclipse.net4j.util.security.DiffieHellman; +import org.eclipse.net4j.util.security.DiffieHellman.Client.Response; +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IAuthenticator2; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.UserManagerAuthenticator; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +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 implements InternalSessionManager +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SESSION, SessionManager.class); + + private InternalRepository repository; + + private DiffieHellman.Server authenticationServer; + + private IAuthenticator authenticator; + + private IPermissionManager permissionManager; + + private final Map sessions = new HashMap(); + + private final AtomicInteger lastSessionID = new AtomicInteger(); + + private final Map> commitNotificationInfoQueues = new HashMap>(); + + private final IListener sessionListener = new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + synchronized (commitNotificationInfoQueues) + { + commitNotificationInfoQueues.remove(lifecycle); + } + } + }; + + /** + * @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; + } + + @Deprecated + public IUserManager getUserManager() + { + if (authenticator instanceof UserManagerAuthenticator) + { + return ((UserManagerAuthenticator)authenticator).getUserManager(); + } + + return null; + } + + @Deprecated + public void setUserManager(IUserManager userManager) + { + UserManagerAuthenticator userManagerAuthenticator = new UserManagerAuthenticator(); + userManagerAuthenticator.setUserManager(userManager); + + setAuthenticator(userManagerAuthenticator); + } + + public DiffieHellman.Server getAuthenticationServer() + { + return authenticationServer; + } + + public void setAuthenticationServer(DiffieHellman.Server authenticationServer) + { + this.authenticationServer = authenticationServer; + } + + public IAuthenticator getAuthenticator() + { + return authenticator; + } + + public void setAuthenticator(IAuthenticator authenticator) + { + this.authenticator = authenticator; + if (isActive() && authenticator != null) + { + initAuthentication(); + } + } + + public IPermissionManager getPermissionManager() + { + return permissionManager; + } + + public void setPermissionManager(IPermissionManager permissionManager) + { + this.permissionManager = permissionManager; + } + + 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) + { + final int id = lastSessionID.incrementAndGet(); + if (TRACER.isEnabled()) + { + TRACER.trace("Opening session " + id); //$NON-NLS-1$ + } + + String userID = authenticateUser(sessionProtocol); + final InternalSession session = createSession(id, userID, sessionProtocol); + LifecycleUtil.activate(session); + + synchronized (sessions) + { + repository.executeOutsideStartCommit(new Runnable() + { + public void run() + { + long firstUpdateTime = repository.getLastCommitTimeStamp(); + session.setFirstUpdateTime(firstUpdateTime); + + 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 openedOnClientSide(InternalSession session) + { + processQueuedCommitNotifications(session); + } + + 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); + } + } + } + + @Deprecated + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch) + { + sendBranchNotification(sender, branch, ChangeKind.CREATED); + } + + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch, ChangeKind changeKind) + { + for (InternalSession session : getSessions()) + { + if (session != sender) + { + try + { + session.sendBranchNotification(branch, changeKind); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache) + { + throw new UnsupportedOperationException(); + } + + public void sendCommitNotification(CommitNotificationInfo info) + { + CDOCommonSession sender = info.getSender(); + for (InternalSession session : getSessions()) + { + if (session != sender) + { + if (session.isOpenOnClientSide()) + { + processQueuedCommitNotifications(session); + doSendCommitNotification(session, info); + } + else + { + queueCommitNotification(session, info); + } + } + } + } + + private void doSendCommitNotification(InternalSession session, CommitNotificationInfo info) + { + try + { + session.sendCommitNotification(info); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + + private void queueCommitNotification(InternalSession session, CommitNotificationInfo info) + { + synchronized (commitNotificationInfoQueues) + { + List queue = commitNotificationInfoQueues.get(session); + if (queue == null) + { + queue = new ArrayList(); + commitNotificationInfoQueues.put(session, queue); + + session.addListener(sessionListener); + } + + queue.add(info); + } + } + + private void processQueuedCommitNotifications(InternalSession session) + { + List queue; + synchronized (commitNotificationInfoQueues) + { + queue = commitNotificationInfoQueues.remove(session); + } + + if (queue != null && !session.isClosed()) + { + session.removeListener(sessionListener); + + for (CommitNotificationInfo queuedInfo : queue) + { + doSendCommitNotification(session, queuedInfo); + } + } + } + + 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 sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, int[] recipients) + { + List result = new ArrayList(); + 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) + { + if (session.isClosed()) + { + if (TRACER.isEnabled()) + { + TRACER.trace("A problem occured while notifying session " + session, t); + } + } + else + { + OM.LOG.warn("A problem occured while notifying session " + session, t); + } + } + + public String authenticateUser(IAuthenticationProtocol protocol) throws SecurityException + { + if (protocol == null) + { + return null; + } + + if (authenticationServer == null || authenticator == null) + { + return null; + } + + try + { + Challenge challenge = authenticationServer.getChallenge(); + Response response = protocol.sendAuthenticationChallenge(challenge); + if (response == null) + { + throw notAuthenticated(); + } + + ByteArrayInputStream bais = new ByteArrayInputStream(authenticationServer.handleResponse(response)); + + ExtendedDataInputStream stream = new ExtendedDataInputStream(bais); + String userID = stream.readString(); + char[] password = stream.readString().toCharArray(); + + authenticator.authenticate(userID, password); + return userID; + } + catch (SecurityException ex) + { + throw ex; + } + catch (Exception ex) + { + Throwable cause = ex.getCause(); + if (cause instanceof SecurityException) + { + throw (SecurityException)cause; + } + + throw new SecurityException(ex); + } + } + + public void changeUserCredentials(IAuthenticationProtocol sessionProtocol, String userID) + { + changeUserCredentials(sessionProtocol, userID, CredentialsUpdateOperation.CHANGE_PASSWORD); + } + + public void resetUserCredentials(IAuthenticationProtocol sessionProtocol, String userID) + { + changeUserCredentials(sessionProtocol, userID, CredentialsUpdateOperation.RESET_PASSWORD); + } + + protected void changeUserCredentials(IAuthenticationProtocol sessionProtocol, String userID, CredentialsUpdateOperation operation) + { + + if (sessionProtocol == null) + { + return; + } + + if (authenticationServer == null || authenticator == null) + { + return; + } + + if (!(authenticator instanceof IAuthenticator2)) + { + throw new SecurityException("Current authenticator does not permit password updates"); //$NON-NLS-1$ + } + + try + { + Challenge challenge = authenticationServer.getChallenge(); + Response response = sessionProtocol.sendCredentialsChallenge(challenge, userID, operation); + if (response == null) + { + throw notAuthenticated(); + } + + ByteArrayInputStream baos = new ByteArrayInputStream(authenticationServer.handleResponse(response)); + ExtendedDataInputStream stream = new ExtendedDataInputStream(baos); + + if (operation == CredentialsUpdateOperation.RESET_PASSWORD) + { + String adminID = stream.readString(); + char[] adminPassword = stream.readString().toCharArray(); + if (!ObjectUtil.equals(userID, stream.readString())) + { + throw new SecurityException("Attempt to reset password of a different user than requested"); //$NON-NLS-1$ + } + char[] newPassword = stream.readString().toCharArray(); + + // this will throw if the current credentials are not authenticated as an administrator + ((IAuthenticator2)authenticator).resetPassword(adminID, adminPassword, userID, newPassword); + } + else + { + userID = stream.readString(); // user can change any password that she can authenticate on the old password + char[] password = stream.readString().toCharArray(); + char[] newPassword = stream.readString().toCharArray(); + + // this will throw if the "old password" provided by the user is not correct + ((IAuthenticator2)authenticator).updatePassword(userID, password, newPassword); + } + } + catch (SecurityException ex) + { + throw ex; + } + catch (Exception ex) + { + Throwable cause = ex.getCause(); + if (cause instanceof SecurityException) + { + throw (SecurityException)cause; + } + + throw new SecurityException(ex); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + initAuthentication(); + } + + protected void initAuthentication() + { + if (authenticator != null) + { + if (authenticationServer == null) + { + authenticationServer = new DiffieHellman.Server(repository.getUUID()); + } + + LifecycleUtil.activate(authenticationServer); + LifecycleUtil.activate(authenticator); + } + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(authenticator); + LifecycleUtil.deactivate(authenticationServer); + + for (InternalSession session : getSessions()) + { + LifecycleUtil.deactivate(session); + } + + super.doDeactivate(); + } + + @SuppressWarnings("deprecation") + private SecurityException notAuthenticated() + { + // Existing clients may expect this deprecated exception type + return new org.eclipse.emf.cdo.common.util.NotAuthenticatedException(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java new file mode 100644 index 000000000..0fd2c14fd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2011-2013, 2016 Eike Stepper (Loehne, 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; + +/** + * Bug 297940, 290032 + * + * @author Caspar De Groot + */ +class TimeStampAuthority +{ + private InternalRepository repository; + + /** + * Holds the begin timestamp that was issued in response to the last call to {@link #startCommit(long, OMMonitor)}. + *

+ * Synchronized on TimeStampAuthority.this. + */ + @ExcludeFromDump + private transient long lastIssuedTimeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + + /** + * Holds the begin timestamp that was last reported finished by a call to {@link #endCommit(long)}. + *

+ * Synchronized on lastFinishedTimeStampLock. + */ + private long lastFinishedTimeStamp; + + private LastCommitTimeStampLock lastFinishedTimeStampLock = 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 runningTransactions = new LinkedList(); + + /** + * A set of timestamps that have been reported finished but have not yet been + */ + private SortedSet finishedTransactions = new TreeSet(); + + TimeStampAuthority(InternalRepository repository) + { + this.repository = repository; + } + + /** + * The purpose of this method is to make sure that no commit can occur at the same time as + * the base of a new branch. Otherwise that commit could change revisions of that branch base. + * See bug 506768 and bug 383602. + */ + synchronized long getMaxBaseTimeForNewBranch() + { + long now = repository.getTimeStamp(); + while (repository.getTimeStamp() == now) + { + ConcurrencyUtil.sleep(1); + } + + return now; + } + + /** + * @deprecated Not used anymore. + */ + @Deprecated + synchronized long[] startCommit(OMMonitor monitor) + { + return startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor); + } + + synchronized long[] startCommit(long timeStampOverride, OMMonitor monitor) + { + monitor.begin(); + lockIfNeeded(); + + 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; + } + + long previousTimeStamp = lastIssuedTimeStamp; + lastIssuedTimeStamp = now; + + runningTransactions.add(now); + return new long[] { now, previousTimeStamp }; + } + 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 (lastFinishedTimeStampLock) + { + long oldValue = lastFinishedTimeStamp; + while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning) + { + finishedTransactions.remove(oldestFinished); + setLastFinishedTimeStampUnsynced(oldestFinished); + } + + // If we actually changed the lastFinishedTimeStamp, we need to notify waiting threads + if (lastFinishedTimeStamp != oldValue) + { + lastFinishedTimeStampLock.notifyAll(); + } + } + + unlockIfNeeded(); + } + + synchronized void failCommit(long timeStamp) + { + // Exclude problems before TransactionCommitContext.setTimeStamp() + if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + unlockIfNeeded(); + } + else + { + endCommit(timeStamp); + } + } + + 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 (lastFinishedTimeStampLock) + { + try + { + lastFinishedTimeStampLock.wait(timeout); + } + catch (Exception ignore) + { + } + + return lastFinishedTimeStamp; + } + } + + void setLastFinishedTimeStamp(long lastCommitTimeStamp) + { + synchronized (lastFinishedTimeStampLock) + { + if (lastCommitTimeStamp > lastFinishedTimeStamp) + { + lastIssuedTimeStamp = lastCommitTimeStamp; + setLastFinishedTimeStampUnsynced(lastCommitTimeStamp); + lastFinishedTimeStampLock.notifyAll(); + } + } + } + + private void setLastFinishedTimeStampUnsynced(long lastCommitTimeStamp) + { + lastFinishedTimeStamp = lastCommitTimeStamp; + repository.getStore().setLastCommitTime(lastFinishedTimeStamp); + } + + private void lockIfNeeded() + { + if (strictOrdering) + { + strictOrderingLock.lock(); + } + } + + private void unlockIfNeeded() + { + if (strictOrdering) + { + strictOrderingLock.unlock(); + } + } + + /** + * 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java new file mode 100644 index 000000000..0dfc6dc1c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008-2013, 2015, 2016 Eike Stepper (Loehne, 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.InternalRepository; +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 +{ + private CommitAttempt lastCommitAttempt; + + 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(); + + InternalRepository repository = getRepository(); + return repository.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, UNSPECIFIED_DATE }; + } + }; + } + + public CommitAttempt getLastCommitAttempt() + { + return lastCommitAttempt; + } + + public void setLastCommitAttempt(CommitAttempt lastCommitAttempt) + { + this.lastCommitAttempt = lastCommitAttempt; + } + + @Override + protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + if (timeStamp != UNSPECIFIED_DATE) + { + throw new IllegalArgumentException("Changing the target time is not supported by transactions"); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java new file mode 100644 index 000000000..7eb8016c9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java @@ -0,0 +1,1818 @@ +/* + * Copyright (c) 2010-2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399487 + */ +package org.eclipse.emf.cdo.internal.server; + +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.branch.CDOBranchVersion; +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.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +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.security.NoPermissionException; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +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.IRepository; +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.CDOCommitInfoUtil; +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.InternalCDORevisionCache; +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.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalTransaction.CommitAttempt; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; + +import org.eclipse.net4j.util.CheckUtil; +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; +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.net4j.util.om.trace.ContextTracer; + +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.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class TransactionCommitContext implements InternalCommitContext +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, TransactionCommitContext.class); + + private static final InternalCDORevision DETACHED = new StubCDORevision(null); + + private final InternalTransaction transaction; + + private final CDOBranch branch; + + private InternalRepository repository; + + private InternalLockManager lockManager; + + private InternalCDOPackageRegistry repositoryPackageRegistry; + + private boolean packageRegistryLocked; + + private TransactionPackageRegistry packageRegistry; + + private IStoreAccessor accessor; + + private long lastUpdateTime; + + private long lastTreeRestructuringCommit; + + private Boolean treeRestructuring; + + private long timeStamp = CDORevision.UNSPECIFIED_DATE; + + private long previousTimeStamp = CDORevision.UNSPECIFIED_DATE; + + private int commitNumber; + + private String commitComment; + + private CDOBranchPoint commitMergeSource; + + private boolean usingEcore; + + private boolean usingEtypes; + + 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 detachedObjectTypes; + + private CDOBranchVersion[] detachedObjectVersions; + + private InternalCDORevision[] dirtyObjects = new InternalCDORevision[0]; + + private InternalCDORevision[] cachedDetachedRevisions = new InternalCDORevision[0]; + + private Map cachedRevisions; + + private Map oldRevisions = CDOIDUtil.createMap(); + + private Map newRevisions; + + private Set lockedObjects = new HashSet(); + + private List lockedTargets; + + private Map idMappings = CDOIDUtil.createMap(); + + private CDOReferenceAdjuster idMapper = new CDOIDMapper(idMappings); + + private byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN; + + private String rollbackMessage; + + private List xRefs; + + private boolean hasChanges; + + private boolean serializingCommits; + + private boolean ensuringReferentialIntegrity; + + private ExtendedDataInputStream lobs; + + private CDOLockState[] locksOnNewObjects = new CDOLockState[0]; + + private CDOID[] idsToUnlock = new CDOID[0]; + + private Map data; + + private CommitNotificationInfo commitNotificationInfo = new CommitNotificationInfo(); + + private CDOLockChangeInfo lockChangeInfo; + + private final List> postCommitLockStates = new ArrayList>(); + + public TransactionCommitContext(InternalTransaction transaction) + { + this.transaction = transaction; + + branch = transaction.getBranch(); + repository = transaction.getRepository(); + lockManager = repository.getLockingManager(); + serializingCommits = repository.isSerializingCommits(); + ensuringReferentialIntegrity = repository.isEnsuringReferentialIntegrity(); + + repositoryPackageRegistry = repository.getPackageRegistry(false); + } + + public InternalTransaction getTransaction() + { + return transaction; + } + + public CDOBranchPoint getBranchPoint() + { + return branch.getPoint(timeStamp); + } + + public String getUserID() + { + return transaction.getSession().getUserID(); + } + + public int getCommitNumber() + { + return commitNumber; + } + + public String getCommitComment() + { + return commitComment; + } + + public CDOBranchPoint getCommitMergeSource() + { + return commitMergeSource; + } + + public long getLastUpdateTime() + { + return lastUpdateTime; + } + + public byte getRollbackReason() + { + return rollbackReason; + } + + public String getRollbackMessage() + { + return rollbackMessage; + } + + public List getXRefs() + { + return xRefs; + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + if (packageRegistry == null) + { + packageRegistry = new TransactionPackageRegistry(repositoryPackageRegistry); + packageRegistry.activate(); + } + + return packageRegistry; + } + + public boolean isClearResourcePathCache() + { + return commitNotificationInfo.isClearResourcePathCache(); + } + + public byte getSecurityImpact() + { + return commitNotificationInfo.getSecurityImpact(); + } + + public boolean isUsingEcore() + { + return usingEcore; + } + + public boolean isUsingEtypes() + { + return usingEtypes; + } + + public InternalCDOPackageUnit[] getNewPackageUnits() + { + return newPackageUnits; + } + + public InternalCDORevision[] getNewObjects() + { + return newObjects; + } + + public InternalCDORevision[] getDirtyObjects() + { + return dirtyObjects; + } + + public CDOID[] getDetachedObjects() + { + return detachedObjects; + } + + public Map getDetachedObjectTypes() + { + return detachedObjectTypes; + } + + public CDOBranchVersion[] getDetachedObjectVersions() + { + return detachedObjectVersions; + } + + public InternalCDORevision[] getDetachedRevisions() + { + return getDetachedRevisions(true); + } + + public InternalCDORevision[] getDetachedRevisions(boolean check) + { + if (check) + { + // 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 Map getOldRevisions() + { + return oldRevisions; + } + + public Map getNewRevisions() + { + if (newRevisions == null) + { + newRevisions = CDOIDUtil.createMap(); + + for (int i = 0; i < newObjects.length; i++) + { + InternalCDORevision revision = newObjects[i]; + newRevisions.put(revision.getID(), revision); + } + } + + return newRevisions; + } + + public InternalCDORevision 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 (InternalCDORevision)transaction.getRevision(id); + } + + protected Map cacheRevisions() + { + Map cache = CDOIDUtil.createMap(); + 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 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.put(oldID, newID); + if (previousMapping != null && previousMapping != newID) + { + throw new IllegalStateException("previousMapping != null && previousMapping != newID"); //$NON-NLS-1$ + } + } + + public void applyIDMappings(OMMonitor monitor) + { + boolean mapIDs = !idMappings.isEmpty(); + monitor.begin(1 + (mapIDs ? newObjects.length + dirtyObjects.length + dirtyObjectDeltas.length : 0)); + + try + { + if (mapIDs) + { + applyIDMappings(newObjects, monitor.fork(newObjects.length)); + applyIDMappings(dirtyObjects, monitor.fork(dirtyObjects.length)); + for (CDORevisionDelta dirtyObjectDelta : dirtyObjectDeltas) + { + ((InternalCDORevisionDelta)dirtyObjectDelta).adjustReferences(idMapper); + monitor.worked(); + } + } + + // Do not notify handlers before the IDs are fully mapped! + notifyBeforeCommitting(monitor); + } + finally + { + monitor.done(); + } + } + + protected 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(); + } + } + + protected void notifyBeforeCommitting(OMMonitor monitor) + { + repository.notifyWriteAccessHandlers(transaction, this, true, monitor.fork()); + } + + 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 boolean isTreeRestructuring() + { + if (treeRestructuring == null) + { + treeRestructuring = CDORevisionUtil.isTreeRestructuring(dirtyObjectDeltas); + } + + return treeRestructuring; + } + + public void setLastTreeRestructuringCommit(long lastTreeRestructuringCommit) + { + this.lastTreeRestructuringCommit = lastTreeRestructuringCommit; + } + + public void setClearResourcePathCache(boolean clearResourcePathCache) + { + commitNotificationInfo.setClearResourcePathCache(clearResourcePathCache); + } + + public void setSecurityImpact(byte securityImpact, Set impactedRules) + { + commitNotificationInfo.setSecurityImpact(securityImpact); + commitNotificationInfo.setImpactedRules(impactedRules); + } + + public void setUsingEcore(boolean usingEcore) + { + this.usingEcore = usingEcore; + } + + public void setUsingEtypes(boolean usingEtypes) + { + this.usingEtypes = usingEtypes; + } + + 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 detachedObjectTypes) + { + this.detachedObjectTypes = detachedObjectTypes; + } + + public void setDetachedObjectVersions(CDOBranchVersion[] detachedObjectVersions) + { + this.detachedObjectVersions = detachedObjectVersions; + } + + public void setLastUpdateTime(long lastUpdateTime) + { + this.lastUpdateTime = lastUpdateTime; + } + + public void setCommitNumber(int commitNumber) + { + this.commitNumber = commitNumber; + } + + public void setCommitComment(String commitComment) + { + this.commitComment = commitComment; + } + + public void setCommitMergeSource(CDOBranchPoint commitMergeSource) + { + this.commitMergeSource = commitMergeSource; + } + + public ExtendedDataInputStream getLobs() + { + return lobs; + } + + public void setLobs(ExtendedDataInputStream in) + { + lobs = in; + } + + @Deprecated + public boolean isAutoReleaseLocksEnabled() + { + return false; + } + + @Deprecated + public void setAutoReleaseLocksEnabled(boolean on) + { + // Do nothing. + } + + public CDOLockState[] getLocksOnNewObjects() + { + return locksOnNewObjects; + } + + public void setLocksOnNewObjects(CDOLockState[] locksOnNewObjects) + { + this.locksOnNewObjects = locksOnNewObjects; + } + + public CDOID[] getIDsToUnlock() + { + return idsToUnlock; + } + + public void setIDsToUnlock(CDOID[] idsToUnlock) + { + this.idsToUnlock = idsToUnlock; + } + + public T getData(Object key) + { + if (data == null) + { + return null; + } + + @SuppressWarnings("unchecked") + T result = (T)data.get(key); + return result; + } + + public synchronized T setData(Object key, T value) + { + if (data == null) + { + data = new HashMap(); + } + + @SuppressWarnings("unchecked") + T old = (T)data.put(key, value); + return old; + } + + protected InternalCDOPackageUnit[] lockPackageRegistry(InternalCDOPackageUnit[] packageUnits) throws InterruptedException + { + if (!packageRegistryLocked) + { + repository.getPackageRegistryCommitLock().acquire(); + packageRegistryLocked = true; + } + + List noDuplicates = new ArrayList(); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + String id = packageUnit.getID(); + if (!repositoryPackageRegistry.containsKey(id)) + { + noDuplicates.add(packageUnit); + } + } + + int newSize = noDuplicates.size(); + if (packageUnits.length != newSize) + { + return noDuplicates.toArray(new InternalCDOPackageUnit[newSize]); + } + + return packageUnits; + } + + /** + * @since 2.0 + */ + public void write(OMMonitor monitor) + { + try + { + monitor.begin(107); + + hasChanges = newPackageUnits.length != 0 || newObjects.length != 0 || dirtyObjectDeltas.length != 0; + if (!hasChanges) + { + return; + } + + dirtyObjects = new InternalCDORevision[dirtyObjectDeltas.length]; + + if (newPackageUnits.length != 0) + { + newPackageUnits = lockPackageRegistry(newPackageUnits); + } + + lockObjects(); // Can take long and must come before setTimeStamp() + monitor.worked(); + + setTimeStamp(monitor.fork()); + + adjustForCommit(); + monitor.worked(); + + computeDirtyObjects(monitor.fork()); + + checkContainmentCycles(); + checkXRefs(); + checkUnitMoves(); + monitor.worked(); + + detachObjects(monitor.fork()); + writeAccessor(monitor.fork(100)); + } + catch (RollbackException ex) + { + rollbackReason = ex.getRollbackReason(); + rollback(ex.getRollbackMessage()); + } + catch (Throwable t) + { + handleException(t); + } + finally + { + finishMonitor(monitor); + } + } + + public void commit(OMMonitor monitor) + { + try + { + monitor.begin(101); + if (hasChanges) + { + accessor.commit(monitor.fork(100)); + } + else + { + monitor.worked(100); + } + + updateInfraStructure(monitor.fork()); + + if (hasChanges) + { + // Bug 297940 + repository.endCommit(timeStamp); + } + } + catch (Throwable ex) + { + handleException(ex); + } + finally + { + finishMonitor(monitor); + } + } + + public List> getPostCommmitLockStates() + { + return postCommitLockStates; + } + + protected void handleException(Throwable ex) + { + try + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex); + } + + if (ex instanceof IRepository.WriteAccessHandler.TransactionValidationException) + { + rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_VALIDATION_ERROR; + rollback(ex.getLocalizedMessage()); + } + else + { + 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 + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex1); + } + } + catch (Exception ignore) + { + } + } + } + + protected void finishMonitor(OMMonitor monitor) + { + try + { + monitor.done(); + } + catch (Exception ex) + { + try + { + OM.LOG.warn(ex); + } + catch (Exception ignore) + { + } + } + } + + protected void setTimeStamp(OMMonitor monitor) + { + long[] times = createTimeStamp(monitor); // Could throw an exception + timeStamp = times[0]; + previousTimeStamp = times[1]; + CheckUtil.checkState(timeStamp != CDOBranchPoint.UNSPECIFIED_DATE, "Commit timestamp must not be 0"); + + transaction.setLastCommitAttempt(new CommitAttempt(commitNumber, timeStamp, previousTimeStamp)); + } + + protected long[] createTimeStamp(OMMonitor monitor) + { + return repository.createCommitTimeStamp(monitor); + } + + public 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) + { + try + { + if (packageRegistryLocked) + { + repository.getPackageRegistryCommitLock().release(); + } + } + catch (Throwable ex) + { + OM.LOG.warn("A problem occured while releasing the package registry commit lock", ex); + } + + try + { + // Send notifications (in particular FailureCommitInfos) only if timeStamp had been allocated + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + sendCommitNotifications(success); + } + } + catch (Throwable 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; + } + } + } + + protected void sendCommitNotifications(boolean success) + { + commitNotificationInfo.setSender(transaction.getSession()); + commitNotificationInfo.setRevisionProvider(this); + commitNotificationInfo.setLockChangeInfo(lockChangeInfo); + + if (success) + { + commitNotificationInfo.setCommitInfo(createCommitInfo()); + } + else + { + commitNotificationInfo.setCommitInfo(createFailureCommitInfo()); + } + + repository.sendCommitNotification(commitNotificationInfo); + } + + public CDOCommitInfo createCommitInfo() + { + String userID = transaction.getSession().getUserID(); + CDOCommitData commitData = createCommitData(); + + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, commitMergeSource, commitData); + } + + public CDOCommitInfo createFailureCommitInfo() + { + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + return new FailureCommitInfo(commitInfoManager, timeStamp, previousTimeStamp); + } + + protected CDOCommitData createCommitData() + { + List newPackageUnitsCollection = new IndexedList.ArrayBacked() + { + @Override + protected CDOPackageUnit[] getArray() + { + return newPackageUnits; + } + }; + + List newObjectsCollection = new IndexedList.ArrayBacked() + { + @Override + protected CDOIDAndVersion[] getArray() + { + return newObjects; + } + }; + + List changedObjectsCollection = new IndexedList.ArrayBacked() + { + @Override + protected CDORevisionKey[] getArray() + { + return dirtyObjectDeltas; + } + }; + + List detachedObjectsCollection = new IndexedList() + { + @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 CDOCommitInfoUtil.createCommitData(newPackageUnitsCollection, newObjectsCollection, changedObjectsCollection, detachedObjectsCollection); + } + + protected void adjustForCommit() + { + for (InternalCDOPackageUnit newPackageUnit : newPackageUnits) + { + newPackageUnit.setTimeStamp(timeStamp); + } + + for (InternalCDORevision newObject : newObjects) + { + newObject.adjustForCommit(branch, timeStamp); + } + } + + protected void lockObjects() throws InterruptedException + { + lockedObjects.clear(); + lockedTargets = null; + + try + { + CDOFeatureDeltaVisitor deltaTargetLocker = null; + if (ensuringReferentialIntegrity && !serializingCommits) + { + final Set newIDs = new HashSet(); + 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); + } + } + + final boolean supportingBranches = repository.isSupportingBranches(); + 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, branch); + lockedObjects.add(key); + } + + if (deltaTargetLocker != null) + { + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + InternalCDORevisionDelta delta = dirtyObjectDeltas[i]; + delta.accept(deltaTargetLocker); + } + } + + for (int i = 0; i < detachedObjects.length; i++) + { + CDOID id = detachedObjects[i]; + Object key = lockManager.getLockKey(id, branch); + lockedObjects.add(key); + } + + if (!lockedObjects.isEmpty()) + { + try + { + long timeout = repository.getOptimisticLockingTimeout(); + + // First lock all objects (incl. possible ref targets). + // This is a transient operation, it does not check for existance! + lockManager.lock2(LockType.WRITE, transaction, lockedObjects, timeout); + } + catch (Exception ex) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_OPTIMISTIC_LOCKING, ex); + } + + // If all locks could be acquired, check if locked targets do still exist + if (lockedTargets != null) + { + for (CDOID id : lockedTargets) + { + CDORevision revision = transaction.getRevision(id); + if (revision == null || revision instanceof DetachedCDORevision) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY, + "Attempt by " + transaction + " to introduce a stale reference"); + } + } + } + } + } + catch (RuntimeException ex) + { + lockedObjects.clear(); + lockedTargets = null; + throw ex; + } + } + + protected void lockTarget(Object value, Set 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, branch); + lockedObjects.add(key); + + // Let this object be checked for existance after it has been locked + if (lockedTargets == null) + { + lockedTargets = new ArrayList(); + } + + lockedTargets.add(id); + } + } + + protected 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$ + } + + if (!dirtyObjects[i].isWritable()) + { + throw new NoPermissionException(dirtyObjects[i]); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected InternalCDORevision computeDirtyObject(InternalCDORevisionDelta delta) + { + CDOID id = delta.getID(); + + InternalCDORevision oldRevision = null; + String rollbackMessage = null; + byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN; + + try + { + oldRevision = (InternalCDORevision)transaction.getRevision(id); + if (oldRevision != null) + { + if (oldRevision.getBranch() != delta.getBranch() || oldRevision.getVersion() != delta.getVersion()) + { + rollbackMessage = "Attempt by " + transaction + " to modify historical revision: " + delta; + rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_COMMIT_CONFLICT; + } + } + else + { + rollbackMessage = "Revision " + id + " not found by " + transaction; + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + + rollbackMessage = ex.getMessage(); + if (rollbackMessage == null) + { + rollbackMessage = ex.getClass().getName(); + } + } + + if (rollbackMessage != null) + { + // If the object is logically locked (see lockObjects) but has a wrong (newer) version, someone else modified it. + throw new RollbackException(rollbackReason, rollbackMessage); + } + + // Make sure all chunks are loaded + repository.ensureChunks(oldRevision, CDORevision.UNCHUNKED); + + oldRevisions.put(id, oldRevision); + + InternalCDORevision newRevision = oldRevision.copy(); + newRevision.adjustForCommit(branch, timeStamp); + + delta.applyTo(newRevision); + return newRevision; + } + + protected void checkContainmentCycles() + { + if (lastTreeRestructuringCommit == 0) + { + // If this was a tree-restructuring commit then lastTreeRestructuringCommit would be initialized. + return; + } + + if (lastUpdateTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + // Happens during replication (see CommitDelegationRequest). Commits are checked in the master repo. + return; + } + + if (lastTreeRestructuringCommit <= lastUpdateTime) + { + // If this client's original state includes the state of the last tree-restructuring commit there's no danger. + return; + } + + Set objectsThatReachTheRoot = new HashSet(); + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + InternalCDORevisionDelta revisionDelta = dirtyObjectDeltas[i]; + CDOFeatureDelta containerDelta = revisionDelta.getFeatureDelta(CDOContainerFeatureDelta.CONTAINER_FEATURE); + if (containerDelta != null) + { + InternalCDORevision revision = dirtyObjects[i]; + if (!isTheRootReachable(revision, objectsThatReachTheRoot, new HashSet())) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_CONTAINMENT_CYCLE, + "Attempt by " + transaction + " to introduce a containment cycle"); + } + } + } + } + + protected boolean isTheRootReachable(InternalCDORevision revision, Set objectsThatReachTheRoot, Set visited) + { + CDOID id = revision.getID(); + if (!visited.add(id)) + { + // Cycle detected on the way up to the root. + return false; + } + + if (!objectsThatReachTheRoot.add(id)) + { + // Has already been checked before. + return true; + } + + CDOID containerID = (CDOID)revision.getContainerID(); + if (CDOIDUtil.isNull(containerID)) + { + // The tree root has been reached. + return true; + } + + // Use this commit context as CDORevisionProvider for the container revisions. + // This is safe because Repository.commit() serializes all tree-restructuring commits. + InternalCDORevision containerRevision = getRevision(containerID); + + // Recurse Up + return isTheRootReachable(containerRevision, objectsThatReachTheRoot, visited); + } + + protected void checkXRefs() + { + if (ensuringReferentialIntegrity && detachedObjectTypes != null) + { + XRefContext context = new XRefContext(); + xRefs = context.getXRefs(accessor); + if (!xRefs.isEmpty()) + { + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY, + "Attempt by " + transaction + " to introduce a stale reference"); + } + } + } + + protected void checkUnitMoves() + { + if (repository.isSupportingUnits() && isTreeRestructuring()) + { + String checkUnitMoves = repository.getProperties().get(IRepository.Props.CHECK_UNIT_MOVES); + if ("true".equalsIgnoreCase(checkUnitMoves)) + { + InternalUnitManager unitManager = repository.getUnitManager(); + + List unitMoves = unitManager.getUnitMoves(dirtyObjectDeltas, transaction, this); + if (!unitMoves.isEmpty()) + { + StringBuilder builder = new StringBuilder("Attempt by " + transaction + " to move objects between units: "); + CDOIDUtil.write(builder, unitMoves); + + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_UNIT_INTEGRITY, builder.toString()); + } + } + } + } + + public synchronized void rollback(String message) + { + // Check if we already rolled back + if (rollbackMessage == null) + { + rollbackMessage = message; + + 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); + } + } + + releaseImplicitLocks(); + } + } + + public IStoreAccessor getAccessor() + { + return accessor; + } + + protected void updateInfraStructure(OMMonitor monitor) + { + try + { + monitor.begin(9); + addNewPackageUnits(monitor.fork()); + addRevisions(newObjects, monitor.fork()); + addRevisions(dirtyObjects, monitor.fork()); + reviseDetachedObjects(monitor.fork()); + + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + commitInfoManager.setLastCommitOfBranch(branch, timeStamp); + + releaseImplicitLocks(); + monitor.worked(); + + acquireLocksOnNewObjects(); + monitor.worked(); + + autoReleaseExplicitLocks(); + monitor.worked(); + + if (!postCommitLockStates.isEmpty()) + { + lockChangeInfo = createLockChangeInfo(postCommitLockStates); + } + + repository.notifyWriteAccessHandlers(transaction, this, false, monitor.fork()); + } + catch (Throwable t) + { + handleException(t); + } + finally + { + monitor.done(); + } + } + + protected synchronized void releaseImplicitLocks() + { + // Unlock objects locked during commit + if (!lockedObjects.isEmpty()) + { + lockManager.unlock2(LockType.WRITE, transaction, lockedObjects); + lockedObjects.clear(); + } + } + + protected void acquireLocksOnNewObjects() throws InterruptedException + { + final CDOLockOwner owner = CDOLockUtil.createLockOwner(transaction); + final boolean mapIDs = transaction.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; + + for (CDOLockState lockState : locksOnNewObjects) + { + Object target = lockState.getLockedObject(); + + if (mapIDs) + { + CDOIDAndBranch idAndBranch = target instanceof CDOIDAndBranch ? (CDOIDAndBranch)target : null; + CDOID id = idAndBranch != null ? ((CDOIDAndBranch)target).getID() : (CDOID)target; + CDOID newID = idMappings.get(id); + CheckUtil.checkNull(newID, "newID"); + + target = idAndBranch != null ? CDOIDUtil.createIDAndBranch(newID, idAndBranch.getBranch()) : newID; + } + + LockState postCommitLockState = null; + for (LockType type : LockType.values()) + { + if (lockState.isLocked(type, owner, false)) + { + List> lockStates = lockManager.lock2(type, transaction, Collections.singleton(target), 0); + postCommitLockState = lockStates.get(0); + } + } + + if (postCommitLockState != null) + { + postCommitLockStates.add(postCommitLockState); + } + } + } + + protected void autoReleaseExplicitLocks() throws InterruptedException + { + List targets = new ArrayList(); + + // Release locks that have been sent from the client. + for (CDOID id : idsToUnlock) + { + Object target = lockManager.getLockKey(id, branch); + targets.add(target); + } + + // Release durable locks that have been acquired on detached objects. + for (CDOID id : detachedObjects) + { + Object target = lockManager.getLockKey(id, branch); + if (lockManager.hasLock(LockType.WRITE, transaction, target)) + { + // We only need to consider detached objects that have been explicitly locked + targets.add(target); + } + } + + try + { + RWOLockManager.setUnlockAll(true); + + List> lockStates = lockManager.unlock2(true, LockType.WRITE, transaction, targets, false); + if (lockStates != null) + { + postCommitLockStates.addAll(lockStates); + } + } + finally + { + RWOLockManager.setUnlockAll(false); + } + } + + protected CDOLockChangeInfo createLockChangeInfo(List> newLockStates) + { + long timeStamp = getTimeStamp(); + CDOLockState[] newStates = Repository.toCDOLockStates(newLockStates); + + return CDOLockUtil.createLockChangeInfo(timeStamp, transaction, branch, Operation.UNLOCK, null, newStates); + } + + protected void addNewPackageUnits(OMMonitor monitor) + { + InternalCDOPackageRegistry repositoryPackageRegistry = repository.getPackageRegistry(false); + synchronized (repositoryPackageRegistry) + { + try + { + monitor.begin(newPackageUnits.length); + for (int i = 0; i < newPackageUnits.length; i++) + { + InternalCDOPackageUnit packageUnit = newPackageUnits[i]; + packageUnit.setState(CDOPackageUnit.State.LOADED); + packageUnit.setPackageRegistry(repositoryPackageRegistry); + repositoryPackageRegistry.putPackageUnit(packageUnit); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + } + + protected void addRevisions(CDORevision[] revisions, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + + for (CDORevision revision : revisions) + { + if (revision != null) + { + revisionManager.addRevision(revision); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected 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(); + } + } + + protected void detachObjects(OMMonitor monitor) + { + int size = detachedObjects.length; + cachedDetachedRevisions = new InternalCDORevision[size]; + + CDOID[] detachedObjects = getDetachedObjects(); + + try + { + monitor.begin(size); + InternalCDORevisionCache cache = repository.getRevisionManager().getCache(); + + 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)cache.getRevision(id, transaction); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected void writeAccessor(OMMonitor monitor) + { + accessor.write(this, monitor); + } + + @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 values() + { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() + { + return "TransactionPackageRegistry"; + } + } + + /** + * @author Eike Stepper + */ + protected static final class RollbackException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + private final byte rollbackReason; + + private final String rollbackMessage; + + public RollbackException(byte rollbackReason, String rollbackMessage) + { + this.rollbackReason = rollbackReason; + this.rollbackMessage = rollbackMessage; + } + + public RollbackException(byte rollbackReason, Throwable cause) + { + super(cause); + this.rollbackReason = rollbackReason; + rollbackMessage = cause.getMessage(); + } + + public byte getRollbackReason() + { + return rollbackReason; + } + + public String getRollbackMessage() + { + return rollbackMessage; + } + } + + /** + * @author Eike Stepper + */ + private final class XRefContext implements QueryXRefsContext + { + private Map> sourceCandidates = new HashMap>(); + + private Set detachedIDs = new HashSet(); + + private Set dirtyIDs = new HashSet(); + + private List result = new ArrayList(); + + 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 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 instanceof EReference && ((EReference)feature).isContainer())) + { + 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 branch; + } + + public Map getTargetObjects() + { + return detachedObjectTypes; + } + + public EReference[] getSourceReferences() + { + return new EReference[0]; + } + + public Map> 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java new file mode 100644 index 000000000..e9a0528fe --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.util.CDOException; +import org.eclipse.emf.cdo.server.IStoreAccessor.UnitSupport; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.server.IView; +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.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.ArrayList; +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; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * @author Eike Stepper + */ +public class UnitManager extends Container implements InternalUnitManager +{ + private final InternalRepository repository; + + private final Map units = CDOIDUtil.createMap(); + + private final Map unitInitializers = CDOIDUtil.createMap(); + + private final Set objectAttachers = new HashSet(); + + private final ReentrantReadWriteLock managerLock = new ReentrantReadWriteLock(); + + public UnitManager(InternalRepository repository) + { + this.repository = repository; + } + + public final InternalRepository getRepository() + { + return repository; + } + + public boolean isUnit(CDOID rootID) + { + checkActive(); + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.containsKey(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + checkActive(); + + WriteLock writeLock = managerLock.writeLock(); + UnitInitializer unitInitializer; + boolean hook = false; + + //////////////////////////////////// + // Phase 1: Register (short, locked) + //////////////////////////////////// + + writeLock.lock(); + + try + { + createUnitHook1(); + + // No need to synchronize on units because all other access holds the manager lock. + if (units.containsKey(rootID)) + { + return null; + } + + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializer = unitInitializers.get(rootID); + if (unitInitializer != null) + { + hook = true; + } + else + { + checkNotNested(rootID, view, units.keySet()); + checkNotNested(rootID, view, unitInitializers.keySet()); + + unitInitializer = createUnitInitializer(rootID, view, revisionHandler); + + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializers.put(rootID, unitInitializer); + + // Synchronize on objectAttachers because objectAttacherFinishedCommit() doesn't acquire the manager lock! + synchronized (objectAttachers) + { + for (ObjectAttacher objectAttacher : objectAttachers) + { + List ids = objectAttacher.removeUnmappedRevisionsFor(unitInitializer); + if (!ids.isEmpty()) + { + unitInitializer.addObjectAttacher(objectAttacher, ids); + } + } + } + } + } + finally + { + writeLock.unlock(); + } + + if (hook) + { + return unitInitializer.hook(rootID, view, revisionHandler, monitor); + } + + IUnit unit = null; + + try + { + ///////////////////////////////////////////////////// + // Phase 2: Initialize (potentially long, not locked) + ///////////////////////////////////////////////////// + + unit = unitInitializer.initialize(monitor); + } + finally + { + /////////////////////////////////// + // Phase 3: Publish (short, locked) + /////////////////////////////////// + + try + { + writeLock.lock(); + + try + { + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializers.remove(rootID); + + if (unit != null) + { + // No need to synchronize on units because all other access holds the manager lock. + units.put(rootID, unit); + } + } + finally + { + writeLock.unlock(); + } + } + finally + { + unitInitializer.notifyHookedInitializers(); + } + } + + fireElementAddedEvent(unit); + return unit; + } + + private void checkNotNested(CDOID rootID, IView view, Set unitIDs) + { + InternalCDORevision rootRevision = (InternalCDORevision)view.getRevision(rootID); + CDOID unitID = getUnitOf(rootRevision, view, unitIDs); + if (unitID != null) + { + throw new CDOException("Attempt to nest the new unit " + rootID + " in the existing unit " + unitID); + } + + Set set = Collections.singleton(rootID); + for (CDOID id : unitIDs) + { + InternalCDORevision revision = (InternalCDORevision)view.getRevision(id); + if (getUnitOf(revision, view, set) != null) + { + throw new CDOException("Attempt to nest the existing unit " + id + " in the new unit " + rootID); + } + } + } + + public IUnit getUnit(CDOID rootID) + { + checkActive(); + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.get(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit[] getUnits() + { + checkActive(); + return getElements(); + } + + public IUnit[] getElements() + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.values().toArray(new IUnit[units.size()]); + } + finally + { + readLock.unlock(); + } + } + + public Map getUnitsOf(Set ids, CDORevisionProvider revisionProvider) + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + Map units = new HashMap(); + + Set rootIDs = getRootIDs(); + if (!rootIDs.isEmpty()) + { + for (CDOID id : ids) + { + InternalCDORevision revision = (InternalCDORevision)revisionProvider.getRevision(id); + CDOID rootID = getUnitOf(revision, revisionProvider, rootIDs); + if (rootID != null) + { + units.put(id, rootID); + } + } + } + + return units; + } + finally + { + readLock.unlock(); + } + } + + public List getUnitMoves(InternalCDORevisionDelta[] deltas, CDORevisionProvider before, CDORevisionProvider after) + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + List unitMoves = new ArrayList(); + + Set rootIDs = getRootIDs(); + if (!rootIDs.isEmpty()) + { + for (InternalCDORevisionDelta delta : deltas) + { + CDOID id = delta.getID(); + + for (CDOFeatureDelta featureDelta : delta.getFeatureDeltas()) + { + EStructuralFeature feature = featureDelta.getFeature(); + if (feature == CDOContainerFeatureDelta.CONTAINER_FEATURE) + { + InternalCDORevision beforeRevision = (InternalCDORevision)before.getRevision(id); + CDOID beforeUnit = getUnitOf(beforeRevision, before, rootIDs); + if (beforeUnit != null) + { + InternalCDORevision afterRevision = (InternalCDORevision)after.getRevision(id); + CDOID afterUnit = getUnitOf(afterRevision, after, rootIDs); + if (afterUnit != beforeUnit) + { + unitMoves.add(delta); + } + } + } + } + } + } + + return unitMoves; + } + finally + { + readLock.unlock(); + } + } + + public InternalObjectAttacher attachObjects(InternalCommitContext commitContext) + { + checkActive(); + + long timeStamp = commitContext.getTimeStamp(); + + ObjectAttacher objectAttacher = null; + Map unitMappings = CDOIDUtil.createMap(); + + /////////////////////////////////////////////// + // Phase 1: Analyze new objects (short, locked) + /////////////////////////////////////////////// + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + attachObjectsHook1(); + + Set rootIDs = getRootIDs(); + boolean checkUnits = !rootIDs.isEmpty(); + + List unmappedRevisions = new ArrayList(); + for (InternalCDORevision revision : commitContext.getNewObjects()) + { + if (checkUnits) + { + CDOID rootID = getUnitOf(revision, commitContext, rootIDs); + if (rootID != null) + { + unitMappings.put(revision.getID(), rootID); + continue; + } + } + + unmappedRevisions.add(revision); + } + + if (!unmappedRevisions.isEmpty()) + { + objectAttacher = createObjectAttacher(commitContext, unmappedRevisions); + + // Read lock holders must synchronize modifications of the private collections. + synchronized (objectAttachers) + { + objectAttachers.add(objectAttacher); + } + } + } + finally + { + readLock.unlock(); + } + + ////////////////////////////////////////////////////////// + // Phase 2: Map objects to existing units (long, unlocked) + ////////////////////////////////////////////////////////// + + if (!unitMappings.isEmpty()) + { + mapAttachedObjectsToUnits(commitContext, timeStamp, unitMappings); + } + + return objectAttacher; + } + + /** + * Does not hold any manager lock when called. + */ + public void objectAttacherFinishedCommit(ObjectAttacher objectAttacher) + { + checkActive(); + + synchronized (objectAttachers) + { + objectAttachers.remove(objectAttacher); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getReader(null); + + try + { + List roots = storeAccessor.readUnitRoots(); + for (CDOID root : roots) + { + IUnit unit = createUnit(root); + + // No need to synchronize on units because all other access call checkActive() + units.put(root, unit); + } + } + finally + { + storeAccessor.release(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + // No need to synchronize on units because all other access call checkActive() + units.clear(); + + super.doDeactivate(); + } + + protected Unit createUnit(CDOID root) + { + return new Unit(root); + } + + protected UnitInitializer createUnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + return new UnitInitializer(rootID, view, revisionHandler); + } + + protected ObjectAttacher createObjectAttacher(InternalCommitContext commitContext, List unmappedRevisions) + { + return new ObjectAttacher(commitContext, unmappedRevisions); + } + + protected void mapAttachedObjectsToUnits(InternalCommitContext commitContext, long timeStamp, Map unitMappings) + { + UnitSupport storeAccessor = (UnitSupport)commitContext.getAccessor(); + storeAccessor.writeUnits(unitMappings, timeStamp); + } + + protected void createUnitHook1() + { + } + + protected void attachObjectsHook1() + { + } + + private Set getRootIDs() + { + Set rootIDs = new HashSet(); + + // No need to synchronize on units because all other modifiers hold the manager write lock. + rootIDs.addAll(units.keySet()); + + // No need to synchronize on unitInitializers because all other modifiers hold the manager write lock. + rootIDs.addAll(unitInitializers.keySet()); + + return rootIDs; + } + + private static CDOID getUnitOf(InternalCDORevision revision, CDORevisionProvider revisionProvider, Set rootIDs) + { + if (rootIDs.isEmpty()) + { + return null; + } + + CDOID id = revision.getID(); + if (rootIDs.contains(id)) + { + return id; + } + + CDORevision parentRevision = CDORevisionUtil.getParentRevision(revision, revisionProvider); + if (parentRevision != null) + { + return getUnitOf((InternalCDORevision)parentRevision, revisionProvider, rootIDs); + } + + return null; + } + + /** + * @author Eike Stepper + */ + protected class Unit implements IUnit + { + private final CDOID rootID; + + private final Set views = new HashSet(); + + public Unit(CDOID rootID) + { + this.rootID = rootID; + } + + public IUnitManager getManager() + { + return UnitManager.this; + } + + public CDOID getRootID() + { + return rootID; + } + + public boolean isOpen() + { + synchronized (views) + { + return !views.isEmpty(); + } + } + + public void open(IView view, final CDORevisionHandler revisionHandler, OMMonitor monitor) + { + synchronized (views) + { + views.add(view); + } + + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getReader(null); + + try + { + storeAccessor.readUnit(view, rootID, revisionHandler, monitor); + } + finally + { + storeAccessor.release(); + } + } + + public void close(IView view) + { + synchronized (views) + { + views.remove(view); + } + + ((InternalView)view).closeUnit(rootID); + } + + @Override + public String toString() + { + return "Unit[" + rootID + "]"; + } + + /** + * Does not hold any manager lock when called. + */ + public void initialize(IView view, long timeStamp, CDORevisionHandler revisionHandler, Map> objectAttachers, OMMonitor monitor) + { + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getWriter(null); + + try + { + Set initializedIDs = new HashSet(); + Object initResult = storeAccessor.initUnit(view, rootID, revisionHandler, initializedIDs, timeStamp, monitor); + + List ids = new ArrayList(); + for (Entry> entry : objectAttachers.entrySet()) + { + ObjectAttacher objectAttacher = entry.getKey(); + if (objectAttacher.awaitFinishedCommit()) + { + for (CDOID id : entry.getValue()) + { + if (!initializedIDs.contains(id)) + { + ids.add(id); + } + } + } + } + + storeAccessor.finishUnit(view, rootID, revisionHandler, timeStamp, initResult, ids); + } + finally + { + storeAccessor.release(); + } + } + } + + /** + * @author Eike Stepper + */ + protected class UnitInitializer implements CDORevisionHandler + { + private final long timeStamp = repository.getTimeStamp(); + + private final Map> concurrentObjectAttachers = new HashMap>(); + + private final CountDownLatch unitInitialized = new CountDownLatch(1); + + private final CDOID rootID; + + private final IView view; + + private final CDORevisionHandler revisionHandler; + + private final List hookedRevisionHandlers = new CopyOnWriteArrayList(); + + private volatile boolean hasHookedRevisionHandlers; + + private Unit unit; + + public UnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + this.rootID = rootID; + this.view = view; + this.revisionHandler = revisionHandler; + } + + public CDOID getRootID() + { + return rootID; + } + + /** + * Does not hold any manager lock when called. + */ + public IUnit initialize(OMMonitor monitor) + { + unit = new Unit(rootID); + unit.initialize(view, timeStamp, revisionHandler, concurrentObjectAttachers, monitor); + return unit; + } + + /** + * Does not hold any manager lock when called. + */ + public IUnit hook(CDOID rootID, IView view, final CDORevisionHandler revisionHandler, OMMonitor monitor) + { + final Set ids = new HashSet(); + hookedRevisionHandlers.add(new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + ids.add(revision.getID()); + return revisionHandler.handleRevision(revision); + } + }); + + // It's okay to do this unsynchronized. The worst thing that could happen is that the hooked revision handler is + // missed a few times during UnitInitializer.handleRevision(), but that's okay because it probably missed many + // revisions already and therefore performs an openUnit() subsequently, anyways. After all, hooked revision + // handlers, + // i.e., concurrent createUnit() calls for the same unit, are extremely rare. + hasHookedRevisionHandlers = true; + + monitor.begin(2); + + try + { + Async async = null; + + try + { + async = monitor.forkAsync(); + + // Now wait for the main revision handler to finish. + while (!unitInitialized.await(100, TimeUnit.MILLISECONDS)) + { + monitor.checkCanceled(); + } + } + catch (InterruptedException ex) + { + return null; + } + finally + { + if (async != null) + { + async.stop(); + } + } + + // Now send the missed revisions. + unit.open(view, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + if (ids.contains(revision.getID())) + { + // This revision has already been sent. Skip to the next one. + return true; + } + + return revisionHandler.handleRevision(revision); + } + }, monitor.fork()); + + return unit; + } + finally + { + monitor.done(); + } + } + + /** + * Does not hold any manager lock when called. + */ + public void notifyHookedInitializers() + { + unitInitialized.countDown(); + } + + public boolean handleRevision(CDORevision revision) + { + if (revisionHandler.handleRevision(revision)) + { + if (hasHookedRevisionHandlers) + { + for (CDORevisionHandler hookedRevisionHandler : hookedRevisionHandlers) + { + hookedRevisionHandler.handleRevision(revision); + } + } + + return true; + } + + return false; + } + + /** + * Holds the manager write lock when called. + */ + public void addObjectAttacher(ObjectAttacher objectAttacher, List ids) + { + concurrentObjectAttachers.put(objectAttacher, ids); + } + } + + /** + * @author Eike Stepper + */ + protected class ObjectAttacher implements InternalObjectAttacher + { + private final InternalCommitContext commitContext; + + private final List unmappedRevisions; + + private final CountDownLatch commitFinished = new CountDownLatch(1); + + private boolean commitSucceeded; + + public ObjectAttacher(InternalCommitContext commitContext, List unmappedRevisions) + { + this.commitContext = commitContext; + this.unmappedRevisions = unmappedRevisions; + } + + /** + * Does not hold any manager lock when called. + */ + public void finishedCommit(boolean success) + { + objectAttacherFinishedCommit(this); + + commitSucceeded = success; + commitFinished.countDown(); + } + + /** + * Holds the manager write lock when called. + */ + public List removeUnmappedRevisionsFor(UnitInitializer unitInitializer) + { + List ids = new ArrayList(); + + Set rootIDs = Collections.singleton(unitInitializer.getRootID()); + for (Iterator it = unmappedRevisions.iterator(); it.hasNext();) + { + InternalCDORevision revision = it.next(); + if (getUnitOf(revision, commitContext, rootIDs) != null) + { + ids.add(revision.getID()); + it.remove(); + } + } + + return ids; + } + + public boolean awaitFinishedCommit() + { + try + { + commitFinished.await(); + } + catch (InterruptedException ex) + { + return false; + } + + return commitSucceeded; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java new file mode 100644 index 000000000..6cf9482e1 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 Eike Stepper (Loehne, 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.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants.UnitOpcode; +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.CDORevisionDelta; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; +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.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.spi.server.InternalView; + +import org.eclipse.net4j.util.AdapterUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.options.IOptionsContainer; +import org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +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 CDOBranchPoint normalizedBranchPoint; + + private String durableLockingID; + + private final InternalRepository repository; + + private final Set changeSubscriptionIDs = new HashSet(); + + private final Set openUnitRoots = new HashSet(); + + private boolean lockNotificationsEnabled; + + private boolean closed; + + private final IRegistry properties = new HashMapRegistry() + { + @Override + public void setAutoCommit(boolean autoCommit) + { + throw new UnsupportedOperationException(); + } + }; + + /** + * @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 getSessionID() + { + return session.getSessionID(); + } + + public int getViewID() + { + return viewID; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public boolean isReadOnly() + { + return true; + } + + public boolean isHistorical() + { + return branchPoint.getTimeStamp() != CDOBranchPoint.UNSPECIFIED_DATE; + } + + public boolean isDurableView() + { + return durableLockingID != null; + } + + public String getDurableLockingID() + { + return durableLockingID; + } + + /** + * @since 2.0 + */ + public InternalRepository getRepository() + { + return repository; + } + + public InternalCDORevision getRevision(CDOID id) + { + CDORevisionManager revisionManager = repository.getRevisionManager(); + return (InternalCDORevision)revisionManager.getRevision(id, normalizedBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + } + + private List getRevisions(List ids) + { + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + return revisionManager.getRevisions(ids, normalizedBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + } + + public void changeTarget(CDOBranchPoint branchPoint, List invalidObjects, List allChangedObjects, List allDetachedObjects) + { + List oldRevisions = getRevisions(invalidObjects); + setBranchPoint(branchPoint); + List newRevisions = getRevisions(invalidObjects); + + Iterator it = newRevisions.iterator(); + for (CDORevision oldRevision : oldRevisions) + { + CDORevision newRevision = it.next(); + if (newRevision == null) + { + allDetachedObjects.add(oldRevision.getID()); + } + else if (newRevision != oldRevision) + { + // Fix for Bug 369646: ensure that revisions are fully loaded + repository.ensureChunks((InternalCDORevision)newRevision, CDORevision.UNCHUNKED); + repository.ensureChunks((InternalCDORevision)oldRevision, CDORevision.UNCHUNKED); + + CDORevisionDelta delta = newRevision.compare(oldRevision); + allChangedObjects.add(delta); + } + } + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + checkOpen(); + validateTimeStamp(branchPoint.getTimeStamp()); + + InternalCDOBranchManager branchManager = getSession().getManager().getRepository().getBranchManager(); + this.branchPoint = CDOBranchUtil.adjustBranchPoint(branchPoint, branchManager); + normalizedBranchPoint = CDOBranchUtil.normalizeBranchPoint(this.branchPoint); + } + + protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + if (timeStamp != UNSPECIFIED_DATE) + { + repository.validateTimeStamp(timeStamp); + } + } + + public void setDurableLockingID(String durableLockingID) + { + this.durableLockingID = durableLockingID; + } + + public boolean openUnit(CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + IUnitManager unitManager = repository.getUnitManager(); + IUnit unit = unitManager.getUnit(rootID); + + switch (opcode) + { + case CREATE: + case CREATE_AND_OPEN: + if (unit != null) + { + return false; + } + + unit = unitManager.createUnit(rootID, this, revisionHandler, monitor); + break; + + case OPEN: + case OPEN_DEMAND_CREATE: + if (unit == null) + { + if (opcode.isCreate()) + { + unit = unitManager.createUnit(rootID, this, revisionHandler, monitor); + break; + } + + return false; + } + + unit.open(this, revisionHandler, monitor); + break; + } + + if (opcode.isOpen()) + { + openUnitRoots.add(rootID); + } + + return true; + } + + public void closeUnit(CDOID rootID) + { + openUnitRoots.remove(rootID); + } + + public boolean isInOpenUnit(CDOID id) + { + if (openUnitRoots.isEmpty()) + { + return false; + } + + if (openUnitRoots.contains(id)) + { + return true; + } + + InternalCDORevision revision = getRevision(id); + if (revision != null) + { + CDOID parentID = revision.getResourceID(); + if (parentID == id) + { + // This must be the root resource; break the recursion. + return false; + } + + if (CDOIDUtil.isNull(parentID)) + { + parentID = (CDOID)revision.getContainerID(); + } + + return isInOpenUnit(parentID); + } + + return false; + } + + /** + * @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) + { + if (isClosed()) + { + return false; + } + + if (changeSubscriptionIDs.contains(id)) + { + return true; + } + + return false; + } + + /** + * @since 2.0 + */ + public synchronized void clearChangeSubscription() + { + checkOpen(); + changeSubscriptionIDs.clear(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) + { + return AdapterUtil.adapt(this, adapter, false); + } + + @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(); + openUnitRoots.clear(); + closed = true; + } + + /** + * @since 2.0 + */ + public boolean isClosed() + { + return closed; + } + + protected void checkOpen() + { + if (isClosed()) + { + throw new IllegalStateException("View closed"); //$NON-NLS-1$ + } + } + + public IOptionsContainer getContainer() + { + return this; + } + + public Options options() + { + return this; + } + + public final IRegistry properties() + { + return properties; + } + + public boolean isLockNotificationEnabled() + { + return lockNotificationsEnabled; + } + + public void setLockNotificationEnabled(boolean enable) + { + lockNotificationsEnabled = enable; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java new file mode 100644 index 000000000..4da2f6fd4 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2008-2012 Eike Stepper (Loehne, 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 state = new ConcurrentValue(CommitState.STARTING); + + public XATransactionCommitContext(InternalTransaction transaction) + { + super(transaction); + } + + public ConcurrentValue 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java new file mode 100644 index 000000000..1c0a14afc --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2010-2017 Eike Stepper (Loehne, 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.CDOModelUtil; +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.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +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.server.StoreThreadLocal.NoSessionRegisteredException; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision; +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.EcorePackage; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * @author Eike Stepper + */ +public class XRefsQueryHandler implements IQueryHandler +{ + public XRefsQueryHandler() + { + } + + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + try + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + + CDOBranchPoint branchPoint = context; + CDOBranch branch = branchPoint.getBranch(); + + if (branch.isMainBranch()) + { + QueryContext xrefsContext = new QueryContext(info, context); + accessor.queryXRefs(xrefsContext); + } + else + { + QueryContext xrefsContext = new QueryContextBranching(info, context); + accessor.queryXRefs(xrefsContext); + + int maxResults = info.getMaxResults(); + while (!branch.isMainBranch() && (maxResults == CDOQueryInfo.UNLIMITED_RESULTS || context.getResultCount() < maxResults)) + { + branchPoint = branch.getBase(); + branch = branchPoint.getBranch(); + + xrefsContext.setBranchPoint(branchPoint); + accessor.queryXRefs(xrefsContext); + } + } + } + catch (NoSessionRegisteredException ex) + { + // View has been closed - do nothing + } + } + + public static void collectSourceCandidates(IView view, Collection concreteTypes, Map> 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> 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 concreteTypes, Map> 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 concreteTypes, Map> sourceCandidates) + { + if (!eClass.isAbstract() && !eClass.isInterface()) + { + for (EReference eReference : CDOModelUtil.getClassInfo(eClass).getAllPersistentReferences()) + { + collectSourceCandidates(eClass, eReference, concreteTypes, sourceCandidates); + } + } + } + + public static void collectSourceCandidates(EReference eReference, Collection concreteTypes, Map> sourceCandidates, + CDOPackageRegistry packageRegistry) + { + EClass rootClass = eReference.getEContainingClass(); + collectSourceCandidates(rootClass, eReference, concreteTypes, sourceCandidates); + + Collection 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 concreteTypes, + Map> sourceCandidates) + { + if (eClass.isAbstract()) + { + return; + } + + if (eClass.isInterface()) + { + return; + } + + if (eReference.isContainer()) + { + return; + } + + if (eReference.isContainment() && !eReference.isResolveProxies()) + { + return; + } + + if (canReference(eReference.getEReferenceType(), concreteTypes)) + { + List list = sourceCandidates.get(eClass); + if (list == null) + { + list = new ArrayList(); + sourceCandidates.put(eClass, list); + } + + list.add(eReference); + } + } + + private static boolean canReference(EClass declaredType, Collection concreteTypes) + { + for (EClass concreteType : concreteTypes) + { + if (declaredType == EcorePackage.Literals.EOBJECT || declaredType.isSuperTypeOf(concreteType)) + { + return true; + } + } + + return false; + } + + /** + * @author Eike Stepper + * @since 3.0 + */ + private static class QueryContext implements IStoreAccessor.QueryXRefsContext + { + private CDOQueryInfo info; + + private IQueryContext context; + + private CDOBranchPoint branchPoint; + + private Map targetObjects; + + private Map> sourceCandidates; + + private EReference[] sourceReferences; + + public QueryContext(CDOQueryInfo info, IQueryContext context) + { + this.info = info; + this.context = context; + branchPoint = context; + } + + public final void setBranchPoint(CDOBranchPoint branchPoint) + { + this.branchPoint = branchPoint; + } + + public final CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public final long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public final Map getTargetObjects() + { + if (targetObjects == null) + { + IRepository repository = getRepository(); + IStore store = repository.getStore(); + CDOPackageRegistry packageRegistry = repository.getPackageRegistry(); + + targetObjects = CDOIDUtil.createMap(); + StringTokenizer tokenizer = new StringTokenizer(info.getQueryString(), "|"); + while (tokenizer.hasMoreTokens()) + { + String val = tokenizer.nextToken(); + + CDOID id; + if (val.startsWith("e")) + { + id = CDOIDUtil.createExternal(val.substring(1)); + } + else + { + id = store.createObjectID(val.substring(1)); + } + + 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 final EReference[] getSourceReferences() + { + if (sourceReferences == null) + { + sourceReferences = parseSourceReferences(); + } + + return sourceReferences; + } + + public final Map> getSourceCandidates() + { + if (sourceCandidates == null) + { + sourceCandidates = new HashMap>(); + Collection concreteTypes = getTargetObjects().values(); + EReference[] sourceReferences = getSourceReferences(); + + if (sourceReferences.length != 0) + { + InternalRepository repository = (InternalRepository)getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (EReference eReference : sourceReferences) + { + collectSourceCandidates(eReference, concreteTypes, sourceCandidates, packageRegistry); + } + } + else + { + collectSourceCandidates(context.getView(), concreteTypes, sourceCandidates); + } + } + + return sourceCandidates; + } + + public final int getMaxResults() + { + return info.getMaxResults(); + } + + public final IRepository getRepository() + { + return context.getView().getRepository(); + } + + public final boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex) + { + if (CDOIDUtil.isNull(targetID)) + { + return true; + } + + if (isIgnoredObject(sourceID)) + { + return true; + } + + return context.addResult(new CDOIDReference(targetID, sourceID, sourceReference, sourceIndex)); + } + + protected boolean isIgnoredObject(CDOID sourceID) + { + return false; + } + + private EReference[] parseSourceReferences() + { + List result = new ArrayList(); + CDOPackageRegistry packageRegistry = 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()]); + } + } + + /** + * @author Eike Stepper + */ + private static final class QueryContextBranching extends QueryContext + { + private final CDOBranchPoint originalBranchPoint; + + private final Set ignoredObjects = new HashSet(); + + public QueryContextBranching(CDOQueryInfo info, IQueryContext context) + { + super(info, context); + originalBranchPoint = CDOBranchUtil.copyBranchPoint(context); + } + + @Override + protected boolean isIgnoredObject(CDOID sourceID) + { + if (!ignoredObjects.add(sourceID)) + { + return true; + } + + if (isDetachedObject(sourceID)) + { + ignoredObjects.add(sourceID); + return true; + } + + return false; + } + + private boolean isDetachedObject(CDOID sourceID) + { + if (getBranch() == originalBranchPoint.getBranch()) + { + return false; + } + + SyntheticCDORevision[] synthetics = { null }; + + InternalCDORevisionManager revisionManager = (InternalCDORevisionManager)getRepository().getRevisionManager(); + revisionManager.getRevision(sourceID, originalBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true, synthetics); + + return synthetics[0] instanceof DetachedCDORevision; + } + } + + /** + * @author Eike Stepper + */ + public static class Factory extends QueryHandlerFactory + { + public Factory() + { + super(CDOProtocolConstants.QUERY_LANGUAGE_XREFS); + } + + @Override + public XRefsQueryHandler create(String description) throws ProductCreationException + { + return new XRefsQueryHandler(); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java new file mode 100644 index 000000000..84bbe345c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2010-2013, 2016, 2018 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 420540 + */ +package org.eclipse.emf.cdo.internal.server.bundle; + +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +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.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.CDOCommand; +import org.eclipse.emf.cdo.spi.server.CDOCommand.CommandException; +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.container.IPluginContainer; +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; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public class CDOCommandProvider implements CommandProvider +{ + private static final String NEW_LINE = "\r\n"; //$NON-NLS-1$ + + private static final CDOCommand list = new CDOCommand("list", "list all active repositories") + { + @Override + public void execute(String[] args) throws Exception + { + IManagedContainer container = CDOServerApplication.getContainer(); + for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP)) + { + if (element instanceof InternalRepository) + { + InternalRepository repository = (InternalRepository)element; + println(repository.getName()); + } + } + } + }; + + private static final CDOCommand start = new CDOCommand("start", "start repositories from a config file", CDOCommand.parameter("config-file")) + { + @Override + public void execute(String[] args) throws Exception + { + String configFile = args[0]; + + IManagedContainer container = CDOServerApplication.getContainer(); + RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container); + IRepository[] repositories = repositoryConfigurator.configure(new File(configFile)); + + println("Repositories started:"); + if (repositories != null) + { + for (IRepository repository : repositories) + { + println(repository.getName()); + } + } + } + }; + + private static final CDOCommand stop = new CDOCommand.WithRepository("stop", "stop a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + LifecycleUtil.deactivate(repository); + println("Repository stopped"); + } + }; + + private static final CDOCommand exportXML = new CDOCommand.WithRepository("export", "export the contents of a repository to an XML file", + CDOCommand.parameter("export-file")) + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + String exportFile = args[0]; + OutputStream out = null; + + try + { + out = new FileOutputStream(exportFile); + + CDOServerExporter.XML exporter = new CDOServerExporter.XML(repository); + exporter.exportRepository(out); + + if (args.length > 1) + { + if ("withSystemPackages".equalsIgnoreCase(args[1])) + { + exporter.setExportSystemPackages(true); + } + } + + println("Repository exported"); + } + finally + { + IOUtil.close(out); + } + } + }; + + private static final CDOCommand importXML = new CDOCommand.WithRepository("import", "import the contents of a repository from an XML file", + CDOCommand.parameter("import-file")) + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + String importFile = args[0]; + 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); + + println("Repository imported"); + } + finally + { + IOUtil.close(in); + } + } + }; + + private static final CDOCommand branches = new CDOCommand.WithRepository("branches", "dump the branches of a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + branches(repository.getBranchManager().getMainBranch(), ""); + } + + private void branches(InternalCDOBranch branch, String prefix) + { + println(prefix + branch); + prefix += CDOCommand.INDENT; + for (InternalCDOBranch child : branch.getBranches()) + { + branches(child, prefix); + } + } + }; + + private static final CDOCommand packages = new CDOCommand.WithRepository("packages", "dump the packages of a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (InternalCDOPackageUnit packageUnit : packageRegistry.getPackageUnits()) + { + println(packageUnit); + for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos()) + { + println(CDOCommand.INDENT + packageInfo); + } + } + } + }; + + private static final CDOCommand sessions = new CDOCommand.WithRepository("sessions", "dump the sessions of a repository") + { + @Override + public void execute(InternalRepository repository, String[] args) throws Exception + { + InternalSessionManager sessionManager = repository.getSessionManager(); + for (InternalSession session : sessionManager.getSessions()) + { + println(session); + for (InternalView view : session.getViews()) + { + println(INDENT + view); + } + } + } + }; + + private static final CDOCommand locks = new CDOCommand.WithAccessor("locks", "dump the locks of a repository", CDOCommand.optional("username-prefix")) + { + @Override + public void execute(InternalRepository repository, IStoreAccessor accessor, String[] args) throws Exception + { + String usernamePrefix = args[0]; + + repository.getLockingManager().getLockAreas(usernamePrefix, new IDurableLockingManager.LockArea.Handler() + { + public boolean handleLockArea(IDurableLockingManager.LockArea area) + { + println(area.getDurableLockingID()); + println(CDOCommand.INDENT + "userID = " + area.getUserID()); + println(CDOCommand.INDENT + "branch = " + area.getBranch()); + println(CDOCommand.INDENT + "timeStamp = " + CDOCommonUtil.formatTimeStamp(area.getTimeStamp())); + println(CDOCommand.INDENT + "readOnly = " + area.isReadOnly()); + println(CDOCommand.INDENT + "locks = " + area.getLocks()); + return true; + } + }); + } + }; + + private static final CDOCommand deletelocks = new CDOCommand.WithAccessor("deletelocks", "delete a durable locking area of a repository", + CDOCommand.parameter("area-id")) + { + @Override + public void execute(InternalRepository repository, IStoreAccessor accessor, String[] args) throws Exception + { + String areaID = args[0]; + repository.getLockingManager().deleteLockArea(areaID); + } + }; + + public CDOCommandProvider(BundleContext bundleContext) + { + bundleContext.registerService(CommandProvider.class.getName(), this, null); + } + + public Object _cdo(CommandInterpreter interpreter) + { + try + { + Map commands = getCommands(); + String cmd = interpreter.nextArgument(); + + CDOCommand command = commands.get(cmd); + if (command != null) + { + try + { + command.setInterpreter(interpreter); + command.execute(); + return null; + } + finally + { + command.setInterpreter(null); + } + } + + interpreter.println(getHelp()); + } + catch (CommandException ex) + { + interpreter.println(ex.getMessage()); + } + catch (Exception ex) + { + interpreter.printStackTrace(ex); + } + + return null; + } + + public String getHelp() + { + StringBuilder builder = new StringBuilder(); + + try + { + builder.append("---CDO commands---" + NEW_LINE); + + List commands = new ArrayList(getCommands().values()); + Collections.sort(commands, new Comparator() + { + public int compare(CDOCommand o1, CDOCommand o2) + { + return o1.getName().compareTo(o2.getName()); + } + }); + + for (CDOCommand command : commands) + { + try + { + builder.append(CDOCommand.INDENT); + builder.append(command.getSyntax()); + builder.append(" - "); + builder.append(command.getDescription()); + builder.append(NEW_LINE); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + + return builder.toString(); + } + + public synchronized Map getCommands() + { + Map commands = new HashMap(); + addCommand(commands, list); + addCommand(commands, start); + addCommand(commands, stop); + addCommand(commands, exportXML); + addCommand(commands, importXML); + addCommand(commands, branches); + addCommand(commands, deletelocks); + addCommand(commands, locks); + addCommand(commands, packages); + addCommand(commands, sessions); + + try + { + for (String name : IPluginContainer.INSTANCE.getFactoryTypes(CDOCommand.PRODUCT_GROUP)) + { + try + { + CDOCommand command = createCommand(name); + addCommand(commands, command); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + + return commands; + } + + protected CDOCommand createCommand(String name) + { + return (CDOCommand)IPluginContainer.INSTANCE.getElement(CDOCommand.PRODUCT_GROUP, name, null); + } + + private void addCommand(Map commands, CDOCommand command) + { + commands.put(command.getName(), command); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java new file mode 100644 index 000000000..df045415b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2007-2012, 2018 Eike Stepper (Loehne, 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_CONFIGURATOR_TYPE = "org.eclipse.emf.cdo.server.repositoryConfiguratorType"; + + public static final String PROP_CONFIGURATOR_DESCRIPTION = "org.eclipse.emf.cdo.server.repositoryConfiguratorDescription"; + + public static final String PROP_BROWSER_PORT = "org.eclipse.emf.cdo.server.browser.port"; //$NON-NLS-1$ + + private IRepository[] repositories; + + private List extensions = new ArrayList(); + + public CDOServerApplication() + { + super(ID); + } + + protected RepositoryConfigurator getConfigurator(IManagedContainer container) + { + String type = OMPlatform.INSTANCE.getProperty(PROP_CONFIGURATOR_TYPE, RepositoryConfigurator.Factory.Default.TYPE); + String description = OMPlatform.INSTANCE.getProperty(PROP_CONFIGURATOR_DESCRIPTION); + return (RepositoryConfigurator)container.getElement(RepositoryConfigurator.Factory.PRODUCT_GROUP, type, description); + } + + protected IManagedContainer getApplicationContainer() + { + return getContainer(); + } + + @Override + protected void doStart() throws Exception + { + super.doStart(); + IManagedContainer container = getApplicationContainer(); + + 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 = getConfigurator(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); + } + } + + IManagedContainer container = getApplicationContainer(); + container.deactivate(); + + 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java new file mode 100644 index 000000000..2858d7df9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007, 2008, 2010-2012, 2015, 2018 Eike Stepper (Loehne, 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 Operations & Maintenance 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_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 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java new file mode 100644 index 000000000..80f08e445 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java @@ -0,0 +1,1442 @@ +/* + * Copyright (c) 2009-2016 Eike Stepper (Loehne, 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.common.util.CDOTimeProvider; +import org.eclipse.emf.cdo.internal.common.revision.CDORevisionKeyImpl; +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.BranchLoader3; +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.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.emf.internal.cdo.transaction.CDOTransactionImpl; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.Predicate; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.collection.AbstractFilteredIterator; +import org.eclipse.net4j.util.collection.BidirectionalIterator; +import org.eclipse.net4j.util.collection.LimitedIterator; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.collection.PredicateIterator; +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.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader3, DurableLocking2 +{ + public static final String TYPE = "mem"; //$NON-NLS-1$ + + private long creationTime; + + private Map properties = new HashMap(); + + private Map branchInfos = new HashMap(); + + private int lastBranchID; + + private int lastLocalBranchID; + + private Map> revisions = new HashMap>(); + + private List commitInfos = new ArrayList(); + + private Map objectTypes = CDOIDUtil.createMap(); + + private Map lockAreas = new HashMap(); + + private Map lobs = new HashMap(); + + 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 + @Deprecated + public boolean isLocal(CDOID id) + { + throw new UnsupportedOperationException(); + } + + @Override + public void ensureLastObjectID(CDOID id) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + return; + } + + super.ensureLastObjectID(id); + } + + public synchronized Map getPersistentProperties(Set names) + { + if (names == null || names.isEmpty()) + { + return new HashMap(properties); + } + + Map result = new HashMap(); + for (String name : names) + { + String value = properties.get(name); + if (value != null) + { + result.put(name, value); + } + } + + return result; + } + + public synchronized void setPersistentProperties(Map properties) + { + this.properties.putAll(properties); + } + + public synchronized void removePersistentProperties(Set names) + { + for (String name : names) + { + properties.remove(name); + } + } + + public synchronized Pair createBranch(int branchID, BranchInfo branchInfo) + { + if (branchID == NEW_BRANCH) + { + branchID = ++lastBranchID; + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = --lastLocalBranchID; + } + + branchInfos.put(branchID, branchInfo); + return Pair.create(branchID, branchInfo.getBaseTimeStamp()); + } + + public synchronized BranchInfo loadBranch(int branchID) + { + return branchInfos.get(branchID); + } + + public synchronized SubBranchInfo[] loadSubBranches(int branchID) + { + List result = new ArrayList(); + for (Entry 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 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; + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + BranchInfo branchInfo = branchInfos.get(branchID); + if (branchInfo != null) + { + branchInfo.setName(newName); + } + } + + public synchronized void loadCommitInfos(final CDOBranch branch, long startTime, final long endTime, CDOCommitInfoHandler handler) + { + InternalCDOCommitInfoManager manager = getRepository().getCommitInfoManager(); + + // Optimize the getCommitInfo(timeStamp) case. + if (startTime == endTime && startTime > CDOBranchPoint.UNSPECIFIED_DATE) + { + int index = Collections.binarySearch(commitInfos, new CommitInfoKey(startTime)); + if (index >= 0) + { + // Found. + CommitInfo commitInfo = commitInfos.get(index); + commitInfo.handle(manager, handler); + } + + return; + } + + boolean counting = endTime < CDOBranchPoint.UNSPECIFIED_DATE; + int count = CDOCommitInfoUtil.decodeCount(endTime); + boolean forward = counting ? count > 0 : endTime > startTime; + + int startIndex; + if (startTime == CDOBranchPoint.UNSPECIFIED_DATE) + { + startIndex = commitInfos.size(); + } + else + { + startIndex = Collections.binarySearch(commitInfos, new CommitInfoKey(startTime)); + if (startIndex >= 0) + { + // Found. + if (!forward) + { + ++startIndex; + } + } + else + { + // Not found. + startIndex = -(startIndex + 1); // Insertion point. + if (forward) + { + --startIndex; + } + } + } + + ListIterator listIterator = commitInfos.listIterator(startIndex); + Iterator iterator = new BidirectionalIterator(listIterator, !forward); + + if (branch != null) + { + iterator = new AbstractFilteredIterator(iterator) + { + @Override + protected boolean isValid(CommitInfo element) + { + return element.getBranch() == branch; + } + }; + } + + if (counting) + { + iterator = new LimitedIterator(iterator, Math.abs(count)); + } + else if (startTime != CDOBranchPoint.UNSPECIFIED_DATE || endTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + Predicate predicate = forward ? new UpTo(endTime) : new DownTo(endTime); + iterator = new PredicateIterator(iterator, predicate); + } + + while (iterator.hasNext()) + { + CommitInfo commitInfo = iterator.next(); + commitInfo.handle(manager, handler); + } + } + + public synchronized Set readChangeSet(CDOChangeSetSegment[] segments) + { + Set ids = new HashSet(); + for (CDOChangeSetSegment segment : segments) + { + for (List list : revisions.values()) + { + readChangeSet(segment, list, ids); + } + } + + return ids; + } + + private void readChangeSet(CDOChangeSetSegment segment, List list, Set 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 (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 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 && 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 list : revisions.values()) + { + enforceListLimit(list); + } + } + + this.listLimit = listLimit; + } + + /** + * @since 2.0 + */ + public synchronized List getCurrentRevisions() + { + ArrayList simpleRevisions = new ArrayList(); + Iterator> itr = revisions.values().iterator(); + while (itr.hasNext()) + { + List 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 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 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 list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return getRevision(list, branchPoint); + } + + public synchronized void addRevision(InternalCDORevision revision, boolean raw) + { + InternalCDOBranch branch = revision.getBranch(); + if (branch.getBranchManager().getRepository() != getRepository()) + { + throw new IllegalArgumentException("Branch does not belong to this repository: " + branch); + } + + Object listKey = getListKey(revision.getID(), branch); + List list = revisions.get(listKey); + if (list == null) + { + list = new ArrayList(); + 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, CDOBranchPoint mergeSource) + { + 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, mergeSource); + 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 list = revisions.get(listKey); + if (list == null) + { + return false; + } + + for (Iterator 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 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(); + 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 (Map.Entry> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (branch != context.getBranch()) + { + continue; + } + + List 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); + if (CDOTransactionImpl.isResourceMatch(revisionName, name, exactMatch)) + { + if (!context.addResource(revision.getID())) + { + // No more results allowed + break; + } + } + } + } + + public synchronized void queryXRefs(QueryXRefsContext context) + { + Set targetIDs = context.getTargetObjects().keySet(); + Map> sourceCandidates = context.getSourceCandidates(); + + for (Entry> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (branch != context.getBranch()) + { + continue; + } + + List 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 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 ids = (List)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 targetIDs, CDOID targetID, CDOID sourceID, EReference sourceReference, int index) + { + for (CDOID id : targetIDs) + { + if (id == 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 list = revisions.get(listKey); + if (list != null) + { + for (Iterator 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 locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public synchronized LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 objectsToLock) + { + LockArea area = getLockArea(durableLockingID); + Map locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockingManager(); + 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 objectsToUnlock) + { + LockArea area = getLockArea(durableLockingID); + Map locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockingManager(); + 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 locks = area.getLocks(); + locks.clear(); + } + + public synchronized void queryLobs(List ids) + { + for (Iterator 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 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> getAllRevisions() + { + Map> result = new HashMap>(); + InternalCDOBranchManager branchManager = getRepository().getBranchManager(); + result.put(branchManager.getMainBranch(), new ArrayList()); + + for (Integer branchID : branchInfos.keySet()) + { + InternalCDOBranch branch = branchManager.getBranch(branchID); + result.put(branch, new ArrayList()); + } + + for (List list : revisions.values()) + { + for (InternalCDORevision revision : list) + { + CDOBranch branch = revision.getBranch(); + List 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 list) + { + int version = CDOBranchVersion.UNSPECIFIED_VERSION; + for (InternalCDORevision revision : list) + { + if (revision.getVersion() > version) + { + version = revision.getVersion(); + } + } + + return version; + } + + private InternalCDORevision getRevisionByVersion(List list, int version) + { + for (InternalCDORevision revision : list) + { + if (revision.getVersion() == version) + { + return revision; + } + } + + return null; + } + + private InternalCDORevision getRevision(List 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 list, InternalCDORevision revision, boolean raw) + { + boolean resource = !(revision instanceof SyntheticCDORevision) && revision.isResourceNode(); + 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) + { + throw new IllegalStateException( + "Concurrent modification of " + rev.getEClass().getName() + "@" + new CDORevisionKeyImpl(rev.getID(), rev.getBranch(), version)); + } + + // 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 list) + { + while (list.size() > listLimit) + { + list.remove(0); + } + } + + /** + * @author Eike Stepper + */ + private static final class UpTo implements Predicate + { + private final long timeStamp; + + public UpTo(long timeStamp) + { + this.timeStamp = timeStamp; + } + + public boolean apply(CDOTimeProvider commitInfo) + { + return commitInfo.getTimeStamp() <= timeStamp; + } + } + + /** + * @author Eike Stepper + */ + private static final class DownTo implements Predicate + { + private final long timeStamp; + + public DownTo(long timeStamp) + { + this.timeStamp = timeStamp; + } + + public boolean apply(CDOTimeProvider commitInfo) + { + return commitInfo.getTimeStamp() >= timeStamp; + } + } + + /** + * @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 id == that.getID() && branch == that.getBranch(); + } + + return false; + } + + @Override + public String toString() + { + return MessageFormat.format("{0}:{1}", id, branch.getID()); + } + } + + /** + * @author Eike Stepper + */ + private static class CommitInfoKey implements CDOTimeProvider, Comparable + { + private long timeStamp; + + public CommitInfoKey(long timeStamp) + { + this.timeStamp = timeStamp; + } + + public long getTimeStamp() + { + return timeStamp; + } + + public int compareTo(CommitInfoKey o) + { + return CDOCommonUtil.compareTimeStamps(timeStamp, o.timeStamp); + } + } + + /** + * @author Eike Stepper + */ + private static final class CommitInfo extends CommitInfoKey + { + private CDOBranch branch; + + private long previousTimeStamp; + + private String userID; + + private String comment; + + private CDOBranchPoint mergeSource; + + public CommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource) + { + super(timeStamp); + this.branch = branch; + this.previousTimeStamp = previousTimeStamp; + this.userID = userID; + this.comment = comment; + this.mergeSource = mergeSource; + } + + public CDOBranch getBranch() + { + return branch; + } + + public void handle(InternalCDOCommitInfoManager manager, CDOCommitInfoHandler handler) + { + CDOCommitInfo commitInfo = manager.createCommitInfo(branch, getTimeStamp(), previousTimeStamp, userID, comment, mergeSource, null); + handler.handleCommitInfo(commitInfo); + } + + @Override + public String toString() + { + return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}, {5}]", branch, getTimeStamp(), previousTimeStamp, userID, comment, mergeSource); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java new file mode 100644 index 000000000..21c816472 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2009-2016 Eike Stepper (Loehne, 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.Raw2; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader3; +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 Raw2, DurableLocking2, BranchLoader3 +{ + private final MEMStore store; + + private final IQueryHandler testQueryHandler = new IQueryHandler() + { + public void executeQuery(CDOQueryInfo info, IQueryContext queryContext) + { + List filters = new ArrayList(); + Object context = info.getParameters().get("context"); //$NON-NLS-1$ + Long sleep = (Long)info.getParameters().get("sleep"); //$NON-NLS-1$ + Integer integers = (Integer)info.getParameters().get("integers"); //$NON-NLS-1$ + Integer error = (Integer)info.getParameters().get("error"); //$NON-NLS-1$ + + if (integers != null) + { + executeQuery(integers, error, queryContext); + return; + } + + 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); + } + }); + } + } + + int i = 0; + for (InternalCDORevision revision : store.getCurrentRevisions()) + { + if (sleep != null) + { + try + { + Thread.sleep(sleep); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + if (isValid(revision, filters)) + { + throwExceptionAt(error, ++i); + + if (!queryContext.addResult(revision)) + { + // No more results allowed + break; + } + } + } + } + + private void throwExceptionAt(Integer error, int i) + { + if (error != null && i == error) + { + throw new RuntimeException("Simulated problem in query execution at result " + i); + } + } + + private void executeQuery(int integers, Integer error, IQueryContext queryContext) + { + for (int i = 1; i <= integers; ++i) + { + throwExceptionAt(error, i); + + if (!queryContext.addResult(i)) + { + // No more results allowed + break; + } + } + } + + private boolean isValid(InternalCDORevision revision, List filters) + { + for (Object filter : filters) + { + if (!filter.equals(revision)) + { + return false; + } + } + + return true; + } + }; + + private List newRevisions; + + public MEMStoreAccessor(MEMStore store, ISession session) + { + super(store, session); + this.store = store; + } + + /** + * @since 2.0 + */ + public MEMStoreAccessor(MEMStore store, ITransaction transaction) + { + super(store, transaction); + this.store = store; + } + + @Override + public MEMStore getStore() + { + return store; + } + + /** + * @since 2.0 + */ + public MEMStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + return new MEMStoreChunkReader(this, revision, feature); + } + + public Collection readPackageUnits() + { + return Collections.emptySet(); + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + throw new UnsupportedOperationException(); + } + + public Pair createBranch(int branchID, BranchInfo branchInfo) + { + return store.createBranch(branchID, branchInfo); + } + + public BranchInfo loadBranch(int branchID) + { + return store.loadBranch(branchID); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + return store.loadSubBranches(branchID); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + return store.loadBranches(startID, endID, branchHandler); + } + + @Deprecated + public void deleteBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public void renameBranch(int branchID, String newName) + { + throw new UnsupportedOperationException(); + } + + public void renameBranch(int branchID, String oldName, String newName) + { + store.renameBranch(branchID, oldName, newName); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + store.loadCommitInfos(branch, startTime, endTime, handler); + } + + public Set readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + return store.readChangeSet(segments); + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, CDORevisionCacheAdder cache) + { + return store.getRevision(id, branchPoint); + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, CDORevisionCacheAdder cache) + { + return store.getRevisionByVersion(id, branchVersion); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, CDORevisionHandler handler) + { + store.handleRevisions(eClass, branch, timeStamp, exactTime, handler); + } + + /** + * @since 2.0 + */ + @Override + protected void doCommit(OMMonitor monitor) + { + newRevisions = null; + } + + @Override + public void doWrite(InternalCommitContext context, OMMonitor monitor) + { + synchronized (store) + { + super.doWrite(context, monitor); + } + } + + @Deprecated + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor) + { + store.addCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource); + } + + @Override + protected void doRollback(CommitContext context) + { + if (newRevisions != null) + { + 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) + { + if (newRevisions == null) + { + newRevisions = new ArrayList(); + } + + newRevisions.add(revision); + store.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(); + InternalCDORevision revision = store.getRevisionByVersion(id, revisionDelta); + if (revision == null) + { + 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.applyTo(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) + { + store.detachObject(id, branch, timeStamp); + } + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context) + { + store.queryResources(context); + } + + public void queryXRefs(QueryXRefsContext context) + { + store.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 + { + store.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, OMMonitor monitor) throws IOException + { + store.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + store.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, null, monitor); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource, monitor); + } + + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + store.rawDelete(id, version, branch); + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + // Do nothing + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return store.createLockArea(userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map locks) + { + return store.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + } + + public void updateLockArea(LockArea lockArea) + { + store.updateLockArea(lockArea); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + return store.getLockArea(durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + store.getLockAreas(userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + store.deleteLockArea(durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection objectsToLock) + { + store.lock(durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection objectsToUnlock) + { + store.unlock(durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + store.unlock(durableLockingID); + } + + public void queryLobs(List ids) + { + store.queryLobs(ids); + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + store.handleLobs(fromTime, toTime, handler); + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + store.loadLob(id, out); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + store.writeBlob(id, size, inputStream); + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + store.writeClob(id, size, reader); + } + + @Override + protected void doActivate() throws Exception + { + // Do nothing + } + + @Override + protected void doDeactivate() throws Exception + { + if (newRevisions != null) + { + newRevisions.clear(); + newRevisions = null; + } + } + + @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java new file mode 100644 index 000000000..d2ad28aa5 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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 executeRead() + { + CDOID id = getRevision().getID(); + CDOBranchVersion branchVersion = getRevision(); + + MEMStore store = getAccessor().getStore(); + List 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java new file mode 100644 index 000000000..bedbfed8c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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 repositoryProperties, Element storeConfig) + { + return new MEMStore(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java new file mode 100644 index 000000000..21baa9b12 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties new file mode 100644 index 000000000..919570359 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties @@ -0,0 +1,16 @@ +# Copyright (c) 2009, 2012 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java new file mode 100644 index 000000000..ecbd05446 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2010-2012, 2016 Eike Stepper (Loehne, 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() + { + } + + 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(); + setState(ONLINE); + } + else + { + // Bug 312879 + setReplicationCountersToLatest(); + + // Switch on synchronizer + doStartSynchronization(); + } + } + + super.changingType(oldType, newType); + } + + 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java new file mode 100644 index 000000000..b15f8a70f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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.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.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * @author Eike Stepper + */ +public class OfflineClone extends SynchronizableRepository +{ + public OfflineClone() + { + } + + @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, //$NON-NLS-1$ + baseTimeStamp); + } + finally + { + StoreThreadLocal.release(); + } + } + + /** + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java new file mode 100644 index 000000000..f16131d76 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2010-2013, 2016 Eike Stepper (Loehne, 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.begin(); + + try + { + notifyBeforeCommitting(monitor); + } + finally + { + monitor.done(); + } + } + + @Override + protected void lockObjects() throws InterruptedException + { + // Do nothing + } + + @Override + protected void checkXRefs() + { + // Do nothing + } + + @Override + protected void checkContainmentCycles() + { + // Do nothing + } + + private static InternalCDOPackageUnit[] getNewPackageUnits(CDOCommitInfo commitInfo, InternalCDOPackageRegistry packageRegistry) + { + List 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 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 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 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java new file mode 100644 index 000000000..c6aa48e9b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2010-2015 Eike Stepper (Loehne, 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.CDOCommonRepository.State; +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.CDOBranchChangedEvent; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.CDOSession; +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.branch.CDOBranchAdjustable; +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.PriorityQueueRunnable; +import org.eclipse.net4j.util.concurrent.PriorityQueueRunner; +import org.eclipse.net4j.util.container.IContainerDelta; +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.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; + +/** + * @author Eike Stepper + * @since 3.0 + */ +public class RepositorySynchronizer extends PriorityQueueRunner implements InternalRepositorySynchronizer +{ + 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 = true; + + 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; + } + + public boolean isEmpty() + { + return remoteSession == null; + } + + public CDOSession[] getElements() + { + if (remoteSession == null) + { + return new CDOSession[0]; + } + + return new CDOSession[] { remoteSession }; + } + + @Override + protected String getThreadName() + { + return "CDORepositorySynchronizer"; //$NON-NLS-1$ + } + + @Override + protected void noWork(WorkContext context) + { + if (!localRepository.isActive()) + { + context.terminate(); + } + } + + @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) + { + closeRemoteSession(); + } + + super.doDeactivate(); + } + + protected void handleConnect() + { + scheduleReplicate(); + + remoteSession.addListener(remoteSessionListener); + remoteSession.getBranchManager().addListener(remoteSessionListener); + + fireEvent(new SingleDeltaContainerEvent(this, remoteSession, IContainerDelta.Kind.ADDED)); + } + + protected void handleDisconnect() + { + if (TRACER.isEnabled()) + { + TRACER.trace("Disconnected from master."); //$NON-NLS-1$ + } + + if (localRepository.hasBeenReplicated()) + { + localRepository.setState(CDOCommonRepository.State.OFFLINE); + } + else + { + localRepository.setState(CDOCommonRepository.State.INITIAL); + } + + if (remoteSession != null) + { + CDOSession element = remoteSession; + closeRemoteSession(); + fireEvent(new SingleDeltaContainerEvent(this, element, IContainerDelta.Kind.REMOVED)); + } + + reconnect(); + } + + private void closeRemoteSession() + { + remoteSession.removeListener(remoteSessionListener); + remoteSession.getBranchManager().removeListener(remoteSessionListener); + remoteSession.close(); + remoteSession = null; + } + + private void reconnect() + { + clearQueue(); + if (isActive()) + { + scheduleConnect(); + } + } + + private void scheduleConnect() + { + synchronized (connectLock) + { + State state = localRepository.getState(); + if (state.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 CDOBranchChangedEvent) + { + CDOBranchChangedEvent e = (CDOBranchChangedEvent)event; + if (e.getChangeKind() == ChangeKind.CREATED) + { + addWork(new BranchRunnable(e.getBranch())); + } + else + { + throw new UnsupportedOperationException("Branch renaming not supported: " + RepositorySynchronizer.this); + } + } + 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 final class ConnectRunnable extends PriorityQueueRunnable + { + 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(); + } + catch (Exception ex) + { + remoteSession = null; + + if (isActive()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Connection attempt failed. Retrying in {0} seconds...", retryInterval); //$NON-NLS-1$ + } + + fireThrowable(ex); + sleepRetryInterval(); + reconnect(); + } + + return; + } + + if (TRACER.isEnabled()) + { + TRACER.trace("Connected to master."); //$NON-NLS-1$ + } + + handleConnect(); + } + } + + @Override + protected Integer getPriority() + { + return CONNECT_PRIORITY; + } + + private void ensureNOOPRevisionCache() + { + // Ensure that incoming revisions are not cached! + InternalCDORevisionCache cache = remoteSession.getRevisionManager().getCache(); + if (!(cache instanceof NOOPRevisionCache)) + { + String message = "Master session does not use a NOOPRevisionCache: " + cache.getClass().getName(); + OM.LOG.error(message); + throw new Error(message); + } + } + } + + /** + * @author Eike Stepper + */ + private final class ReplicateRunnable extends PriorityQueueRunnable + { + public ReplicateRunnable() + { + } + + public void run() + { + try + { + checkActive(); + if (TRACER.isEnabled()) + { + TRACER.trace("Synchronizing with master..."); //$NON-NLS-1$ + } + + boolean firstSyncing = !localRepository.hasBeenReplicated(); + if (!firstSyncing) + { + 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); + } + + if (firstSyncing) + { + CDOID id = remoteSession.getRepositoryInfo().getRootResourceID(); + localRepository.setRootResourceID(id); + } + + localRepository.setState(CDOCommonRepository.State.ONLINE); + if (TRACER.isEnabled()) + { + TRACER.trace("Synchronized with master."); //$NON-NLS-1$ + } + } + catch (RuntimeException ex) + { + if (isActive()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Replication attempt failed. Retrying in {0} seconds...", ex, retryInterval); //$NON-NLS-1$ + } + + fireThrowable(ex); + sleepRetryInterval(); + handleDisconnect(); + } + } + } + + @Override + protected Integer getPriority() + { + return REPLICATE_PRIORITY; + } + } + + /** + * @author Eike Stepper + */ + private final class BranchRunnable extends PriorityQueueRunnable + { + private CDOBranch branch; + + public BranchRunnable(CDOBranch branch) + { + this.branch = branch; + } + + public void run() + { + try + { + localRepository.handleBranch(branch); + } + catch (Exception ex) + { + fireThrowable(ex); + } + } + + @Override + public int compareTo(PriorityQueueRunnable o) + { + int result = super.compareTo(o); + if (result == 0) + { + result = branch.compareTo(((BranchRunnable)o).branch); + } + + return result; + } + + @Override + protected Integer getPriority() + { + return BRANCH_PRIORITY; + } + } + + /** + * @author Eike Stepper + */ + private abstract class RetryingRunnable extends PriorityQueueRunnable + { + private List failedRuns; + + public RetryingRunnable() + { + } + + public void run() + { + try + { + doRun(); + } + catch (Exception ex) + { + fireThrowable(ex); + if (failedRuns == null) + { + failedRuns = new ArrayList(); + } + + failedRuns.add(ex); + if (failedRuns.size() <= maxRecommits) + { + if (TRACER.isEnabled()) + { + String simpleName = RetryingRunnable.this.getClass().getSimpleName(); + TRACER.format(simpleName + " failed. Trying again in {0} seconds...", recommitInterval); //$NON-NLS-1$ + } + + if (recommitTimer == null) + { + recommitTimer = new Timer("RetryTimer-" + RepositorySynchronizer.this); + } + + recommitTimer.schedule(new TimerTask() + { + @Override + public void run() + { + try + { + addWork(RetryingRunnable.this); + } + catch (Exception ex) + { + if (TRACER.isEnabled()) + { + TRACER.format("{0} failed. Exiting.", RetryingRunnable.this.getClass().getSimpleName()); //$NON-NLS-1$ + } + + fireThrowable(ex); + } + } + }, recommitInterval * 1000L); + } + else + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex); + } + + fireThrowable(ex); + } + } + } + + protected abstract void doRun(); + + protected abstract String getErrorMessage(); + } + + /** + * @author Eike Stepper + */ + 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(PriorityQueueRunnable o) + { + int result = super.compareTo(o); + if (result == 0) + { + Long timeStamp = commitInfo.getTimeStamp(); + + /** BEGIN SPECMATE PATCH */ + Long timeStamp2; + if(o instanceof CommitRunnable) { + timeStamp2 = ((CommitRunnable)o).commitInfo.getTimeStamp(); + } else { + return super.compareTo(o); + } + /** END SPECMATE PATCH */ + 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 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()); + + if (lockChangeInfo instanceof CDOBranchAdjustable) + { + ((CDOBranchAdjustable)lockChangeInfo).adjustBranches(localRepository.getBranchManager()); + } + + localRepository.handleLockChangeInfo(lockChangeInfo); + } + finally + { + StoreThreadLocal.release(); + } + } + + @Override + protected String getErrorMessage() + { + return "Replication of master lock changes failed:" + lockChangeInfo; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java new file mode 100644 index 000000000..eeed2e947 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2010-2017 Eike Stepper (Loehne, 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.CDOObject; +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.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.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.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.CDOLockUtil; +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.protocol.CDOProtocol.CommitNotificationInfo; +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.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOException; +import org.eclipse.emf.cdo.internal.common.commit.DelegatingCommitInfo; +import org.eclipse.emf.cdo.internal.common.revision.AbstractCDORevisionCache; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +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.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchAdjustable; +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.commit.CDOCommitInfoUtil; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +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.InternalCDORevisionCache; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +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.emf.ecore.EClass; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.collection.IndexedList; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; +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 org.eclipse.emf.spi.cdo.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext; + +import java.io.IOException; +import java.util.ArrayList; +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: + *
    + *
  • Handle new package units that had been committed during offline + * (testDisconnectAndCommitAndMergeWithNewPackages). + *
  • Make CDOIDs of new objects temporary when merging out of temp branch. + *
  • Provide custom branching strategies. + *
  • Consider non-auditing masters. + *
  • Test out-of-order commits. + *
  • Don't create branches table if branching not supported. + *
  • Implement raw replication for NUMERIC and DECIMAL. + *
  • Notify new branches during raw replication. + *
+ * + * @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 list = new ArrayList(Arrays.asList(super.getElements())); + list.add(synchronizer); + return list.toArray(); + } + + public boolean hasBeenReplicated() { + return getLastReplicatedCommitTime() != CDOBranchPoint.UNSPECIFIED_DATE; + } + + 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; + } + } + + @Override + public void setLastCommitTimeStamp(long lastCommitTimeStamp) { + super.setLastCommitTimeStamp(lastCommitTimeStamp); + + if (getType() == MASTER) { + // This MASTER might become a BACKUP, so don't replicate this commit in the + // future + setLastReplicatedCommitTime(lastCommitTimeStamp); + } + } + + public String[] getLockAreaIDs() { + try { + StoreThreadLocal.setSession(replicatorSession); + final List areaIDs = new LinkedList(); + getLockingManager().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(final CDOCommitInfo commitInfo) { + CDOBranch branch = commitInfo.getBranch(); + if (branch.isLocal()) { + return; + } + + // Convert branches from remoteSession to localRepository + InternalCDOBranchManager newBranchManager = getBranchManager(); + + for (CDOIDAndVersion key : commitInfo.getNewObjects()) { + if (key instanceof CDOBranchAdjustable) { + CDOBranchAdjustable branchAdjustable = (CDOBranchAdjustable) key; + branchAdjustable.adjustBranches(newBranchManager); + } + } + + for (CDORevisionKey key : commitInfo.getChangedObjects()) { + if (key instanceof CDOBranchAdjustable) { + CDOBranchAdjustable branchAdjustable = (CDOBranchAdjustable) key; + branchAdjustable.adjustBranches(newBranchManager); + } + } + + for (CDOIDAndVersion key : commitInfo.getDetachedObjects()) { + if (key instanceof CDOBranchAdjustable) { + CDOBranchAdjustable branchAdjustable = (CDOBranchAdjustable) key; + branchAdjustable.adjustBranches(newBranchManager); + } + } + + final InternalCDOBranch newBranch = newBranchManager.getBranch(branch.getID()); + CDOCommitInfo newCommitInfo = new DelegatingCommitInfo() { + @Override + protected CDOCommitInfo getDelegate() { + return commitInfo; + } + + @Override + public CDOBranch getBranch() { + return newBranch; + } + }; + + long timeStamp = newCommitInfo.getTimeStamp(); + CDOBranchPoint head = newBranch.getHead(); + + InternalTransaction transaction = replicatorSession.openTransaction(++lastTransactionID, head); + ReplicatorCommitContext commitContext = new ReplicatorCommitContext(transaction, newCommitInfo); + commitContext.preWrite(); + boolean success = false; + + try { + handleCommitInfoLock.lock(); + + commitContext.write(new Monitor()); + commitContext.commit(new Monitor()); + + setLastCommitTimeStamp(timeStamp); + setLastReplicatedCommitTime(timeStamp); + success = true; + } + /** BEGIN SPECMATE PATCH */ + catch (Exception e) { + throw e; + } + /** END SPECMATE PATCH */ + finally { + handleCommitInfoLock.unlock(); + commitContext.postCommit(success); + transaction.close(); + } + } + + public void handleLockChangeInfo(CDOLockChangeInfo lockChangeInfo) { + /** BEGIN SPECMATE PATCH */ + // FIXME: lockChangeInfo.getLockOwner can result in nullpointer exception, + // should be fixed in DefaultLocksChangedEvent + CDOLockOwner owner; + try { + owner = lockChangeInfo.getLockOwner(); + } catch (Exception e) { + return; + } + /** END SPECMATE PATCH */ + if (owner == null) { + return; + } + + String durableLockingID = owner.getDurableLockingID(); + CDOBranch viewedBranch = lockChangeInfo.getBranch(); + InternalLockManager lockManager = getLockingManager(); + LockType lockType = lockChangeInfo.getLockType(); + + InternalView view = null; + + try { + view = SyncingUtil.openViewWithLockArea(replicatorSession, lockManager, viewedBranch, durableLockingID); + List lockables = new LinkedList(); + + 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, false, timeout); + } else if (lockChangeInfo.getOperation() == Operation.UNLOCK) { + super.doUnlock(view, lockType, lockables, false); + } else { + throw new IllegalStateException("Unexpected: " + lockChangeInfo.getOperation()); + } + } finally { + LifecycleUtil.deactivate(view); + } + } + + public boolean handleLockArea(LockArea area) { + try { + StoreThreadLocal.setSession(replicatorSession); + getLockingManager().updateLockArea(area); + + getSessionManager().sendLockNotification(null, CDOLockUtil.createLockChangeInfo()); + return true; + } finally { + StoreThreadLocal.release(); + } + } + + /** + * Called by ReplicateRepositoryRawRequest.confirming(). + */ + public void replicateRaw(CDODataInput in, OMMonitor monitor) throws IOException { + try { + long previousCommitTime = getLastCommitTimeStamp(); + + int fromBranchID = lastReplicatedBranchID + 1; + int toBranchID = in.readXInt(); + long fromCommitTime = lastReplicatedCommitTime + 1L; + long toCommitTime = in.readXLong(); + + StoreThreadLocal.setSession(replicatorSession); + IStoreAccessor.Raw accessor = (IStoreAccessor.Raw) StoreThreadLocal.getAccessor(); + accessor.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + + replicateRawReviseRevisions(); + replicateRawReloadLocks(); + replicateRawNotifyClients(lastReplicatedCommitTime, toCommitTime, previousCommitTime); + + 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 replicateRawReloadLocks() { + getLockingManager().reloadLocks(); + } + + private void replicateRawNotifyClients(long fromCommitTime, long toCommitTime, long previousCommitTime) { + InternalCDOCommitInfoManager manager = getCommitInfoManager(); + InternalSessionManager sessionManager = getSessionManager(); + + Map branches = replicateRawGetBranches(fromCommitTime, toCommitTime); + for (Entry 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 newPackages = Collections.emptyList(); // TODO Notify about new packages + List newObjects = changeSet.getNewObjects(); + List changedObjects = changeSet.getChangedObjects(); + List detachedObjects = changeSet.getDetachedObjects(); + + CDOCommitData data = CDOCommitInfoUtil.createCommitData(newPackages, newObjects, changedObjects, + detachedObjects); + + String comment = ""; //$NON-NLS-1$ + final CDOCommitInfo commitInfo = manager.createCommitInfo( // + branch, toCommitTime, previousCommitTime, SYSTEM_USER_ID, comment, null, data); + + CommitNotificationInfo info = new CommitNotificationInfo(); + info.setSender(replicatorSession); + info.setCommitInfo(commitInfo); + info.setClearResourcePathCache(true); + + sessionManager.sendCommitNotification(info); + } + + CDOLockChangeInfo lockChangeInfo = CDOLockUtil.createLockChangeInfo(); + sessionManager.sendLockNotification(replicatorSession, lockChangeInfo); + } + + private Map replicateRawGetBranches(long fromCommitTime, long toCommitTime) { + final Map branches = new HashMap(); + 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 void notifyWriteAccessHandlers(ITransaction transaction, CommitContext commitContext, boolean beforeCommit, + OMMonitor monitor) { + if (beforeCommit && commitContext.getNewPackageUnits().length != 0) { + for (InternalCDOPackageUnit packageUnit : commitContext.getNewPackageUnits()) { + if (!packageUnit.isSystem()) { + throw new IllegalStateException( + "Synchronizable repositories don't support dynamic addition of new packages. Use IRepository.setInitialPackages() instead."); + } + } + } + + super.notifyWriteAccessHandlers(transaction, commitContext, beforeCommit, monitor); + } + + @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(); + + InternalCDORevisionCache cache = getRevisionManager().getCache(); + if (cache instanceof AbstractCDORevisionCache) { + // Enable branch checks to ensure that no branches from the replicator session + // are used + ((AbstractCDORevisionCache) cache).setBranchManager(getBranchManager()); + } + + InternalStore store = getStore(); + if (!store.isFirstStart()) { + Map map = store.getPersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN)); + if (!map.containsKey(PROP_GRACEFULLY_SHUT_DOWN)) { + setReplicationCountersToLatest(); + } else { + Set names = new HashSet(); + 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)); + + Type type = getType(); + if (type == MASTER) { + setState(ONLINE); + } else { + if (hasBeenReplicated()) { + setState(OFFLINE); + } else if (type == BACKUP) { + if (getLastReplicatedCommitTime() == CDOBranchPoint.UNSPECIFIED_DATE) { + boolean usedToBeMaster = getRootResourceID() != null; + if (usedToBeMaster) { + setLastReplicatedCommitTime(getLastCommitTimeStamp()); + } + } + } + + startSynchronization(); + } + } + + @Override + protected void doDeactivate() throws Exception { + stopSynchronization(); + + Map map = new HashMap(); + 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(); + } + } + + @Override + protected void setPostActivateState() { + // Do nothing (keep INITIAL) + } + + protected void setReplicationCountersToLatest() { + setLastReplicatedBranchID(getStore().getLastBranchID()); + setLastReplicatedCommitTime(getStore().getLastNonLocalCommitTime()); + } + + @Override + protected void initRootResource() { + // Non-MASTER repositories must wait for the first replication to receive their + // root resource ID + if (getType() == MASTER) { + super.initRootResource(); + } + } + + @Override + public LockObjectsResult lock(InternalView view, LockType lockType, List revisionKeys, + boolean recursive, long timeout) { + if (view.getBranch().isLocal()) { + return super.lock(view, lockType, revisionKeys, recursive, 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, false, timeout); + } + + private LockObjectsResult lockOnMaster(InternalView view, LockType type, List revKeys, + boolean recursive, 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 for view " + view); + } + + LockObjectsResult masterLockingResult = sessionProtocol.delegateLockObjects(areaID, revKeys, view.getBranch(), + type, recursive, 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(); + if (!remoteSession.waitForUpdate(requiredTimestamp, 10000)) { + throw new TimeoutRuntimeException(); + } + } + + return masterLockingResult; + } + + private LockObjectsResult lockThrough(InternalView view, LockType type, List keys, + boolean recursive, long timeout) { + try { + LockObjectsResult masterLockingResult = lockOnMaster(view, type, keys, recursive, timeout); + if (!masterLockingResult.isSuccessful()) { + return masterLockingResult; + } + + LockObjectsResult localLockingResult = super.lock(view, type, keys, recursive, timeout); + return localLockingResult; + } catch (InterruptedException ex) { + throw WrappedException.wrap(ex); + } + } + + @Override + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List objectIDs, boolean recursive) { + if (view.getBranch().isLocal()) { + super.unlock(view, lockType, objectIDs, recursive); + } + + 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, recursive); + } + + private void unlockOnMaster(InternalView view, LockType lockType, List objectIDs, boolean recursive) { + InternalCDOSession remoteSession = getSynchronizer().getRemoteSession(); + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + + String lockAreaID = view.getDurableLockingID(); + if (lockAreaID == null) { + throw new IllegalStateException("Durable locking is not enabled for view " + view); + } + + sessionProtocol.delegateUnlockObjects(lockAreaID, objectIDs, lockType, recursive); + } + + private UnlockObjectsResult unlockThrough(InternalView view, LockType lockType, List objectIDs, + boolean recursive) { + unlockOnMaster(view, lockType, objectIDs, recursive); + return super.unlock(view, lockType, objectIDs, recursive); + } + + /** + * @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 static final class CommitContextData implements CDOCommitData { + private InternalCommitContext commitContext; + + private CDOChangeKindCache changeKindCache; + + public CommitContextData(InternalCommitContext commitContext) { + this.commitContext = commitContext; + } + + /** BEGIN SPECMATE PATCH */ + public Map getDetachedTypes() { + return ((WriteThroughCommitContext) commitContext).getDetachedObjectTypes(); + } + /** END SPECMATE PATCH */ + + public boolean isEmpty() { + return false; + } + + public CDOChangeSetData copy() { + throw new UnsupportedOperationException(); + } + + public void merge(CDOChangeSetData changeSetData) { + throw new UnsupportedOperationException(); + } + + public List getNewPackageUnits() { + final InternalCDOPackageUnit[] newPackageUnits = commitContext.getNewPackageUnits(); + return new IndexedList() { + @Override + public CDOPackageUnit get(int index) { + return newPackageUnits[index]; + } + + @Override + public int size() { + return newPackageUnits.length; + } + }; + } + + public List getNewObjects() { + final InternalCDORevision[] newObjects = commitContext.getNewObjects(); + return new IndexedList() { + @Override + public CDOIDAndVersion get(int index) { + return newObjects[index]; + } + + @Override + public int size() { + return newObjects.length; + } + }; + } + + public List getChangedObjects() { + final InternalCDORevisionDelta[] changedObjects = commitContext.getDirtyObjectDeltas(); + return new IndexedList() { + @Override + public CDORevisionKey get(int index) { + return changedObjects[index]; + } + + @Override + public int size() { + return changedObjects.length; + } + }; + } + + public List getDetachedObjects() { + final CDOID[] detachedObjects = commitContext.getDetachedObjects(); + return new IndexedList() { + @Override + public CDOIDAndVersion get(int index) { + return CDOIDUtil.createIDAndVersion(detachedObjects[index], CDOBranchVersion.UNSPECIFIED_VERSION); + } + + @Override + public int size() { + return detachedObjects.length; + } + }; + } + + public synchronized Map 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 WriteThroughCommitContext extends TransactionCommitContext { + private static final int ARTIFICIAL_VIEW_ID = 0; + + private CommitTransactionResult result; + + 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) { + // Prepare commit to the master + final CDOCommitData commitData = new CommitContextData(this); + + InternalCDOCommitContext ctx = new InternalCDOCommitContext() { + public boolean isPartialCommit() { + return false; + } + + public Map getRevisionDeltas() { + throw new UnsupportedOperationException(); + } + + public List getNewPackageUnits() { + return commitData.getNewPackageUnits(); + } + + public Map getNewObjects() { + throw new UnsupportedOperationException(); + } + + public Collection> getLobs() { + return Collections.emptySet(); // TODO (CD) Did we forget to support this earlier? + } + + public Map getDirtyObjects() { + throw new UnsupportedOperationException(); + } + + public Map getDetachedObjects() { + throw new UnsupportedOperationException(); + } + + public void preCommit() { + throw new UnsupportedOperationException(); + } + + public void postCommit(CommitTransactionResult result) { + throw new UnsupportedOperationException(); + } + + public InternalCDOTransaction getTransaction() { + return null; + } + + public CDOCommitData getCommitData() { + return commitData; + } + + public int getViewID() { + return ARTIFICIAL_VIEW_ID; + } + + public String getUserID() { + return WriteThroughCommitContext.this.getUserID(); + } + + @Deprecated + public boolean isAutoReleaseLocks() { + return WriteThroughCommitContext.this.isAutoReleaseLocksEnabled(); + } + + public Collection getLocksOnNewObjects() { + CDOLockState[] locksOnNewObjectsArray = WriteThroughCommitContext.this.getLocksOnNewObjects(); + return Arrays.asList(locksOnNewObjectsArray); + } + + public Collection getIDsToUnlock() { + CDOID[] idsToUnlockArray = WriteThroughCommitContext.this.getIDsToUnlock(); + return Arrays.asList(idsToUnlockArray); + } + + public String getCommitComment() { + return WriteThroughCommitContext.this.getCommitComment(); + } + + public CDOBranchPoint getCommitMergeSource() { + return WriteThroughCommitContext.this.getCommitMergeSource(); + } + + public CDOBranch getBranch() { + return WriteThroughCommitContext.this.getTransaction().getBranch(); + } + }; + + // Delegate commit to the master + CDOSessionProtocol sessionProtocol = getSynchronizer().getRemoteSession().getSessionProtocol(); + result = sessionProtocol.commitDelegation(ctx, 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(); // result is set to null later! + 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) { + long timeStamp = result.getTimeStamp(); + long previousTimeStamp = result.getPreviousTimeStamp(); + result = null; + + InternalRepository repository = getTransaction().getSession().getManager().getRepository(); + repository.forceCommitTimeStamp(timeStamp, monitor); + + return new long[] { timeStamp, previousTimeStamp }; + } + + @Override + protected void lockObjects() throws InterruptedException { + // Do nothing + } + + private void addIDMappings(Map idMappings) { + for (Map.Entry idMapping : idMappings.entrySet()) { + CDOID oldID = idMapping.getKey(); + CDOID newID = idMapping.getValue(); + addIDMapping(oldID, newID); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java new file mode 100644 index 000000000..7acf211ff --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java @@ -0,0 +1,1448 @@ +/* + * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Loehne, 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.CDOCommonRepository.CommitInfoStorage; +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.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.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade; +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.InternalLockManager; +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.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.IManagedContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.factory.ProductCreationException; +import org.eclipse.net4j.util.io.IOUtil; + +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.Map.Entry; +import java.util.Set; + +/** + * A simple HTTP server that web browsers can connect to in order to render internal server data for debugging purposes. + *

+ * Actual content is contributed through pluggable {@link CDOServerBrowser.Page pages}. + *

+ * Note: Don't use this server in production, it's unsecure and does not perform or scale! + * + * @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> params = new InheritableThreadLocal>() + { + @Override + protected Map initialValue() + { + return new HashMap(); + } + }; + + private int port = 7777; + + private ServerSocket serverSocket; + + private Map repositories; + + private List pages = new ArrayList(); + + public CDOServerBrowser(Map repositories) + { + this.repositories = repositories; + setDaemon(true); + } + + public Map 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 map = this.params.get(); + for (String param : params.split("&")) + { + if (param.length() != 0) + { + String[] keyValue = param.split("="); + if (keyValue.length == 1) + { + map.put(keyValue[0], "true"); + } + else + { + map.put(keyValue[0], keyValue[1]); + } + } + } + } + + protected void clearParams() + { + Map map = params.get(); + map.clear(); + } + + public void removeParam(String key) + { + Map map = params.get(); + map.remove(key); + } + + public String getParam(String key) + { + Map map = params.get(); + return map.get(key); + } + + /** + * @since 4.5 + */ + public boolean isParam(String key) + { + Map map = params.get(); + return "true".equalsIgnoreCase(map.get(key)); + } + + public String href(String label, String resource, String... params) + { + Map map = new HashMap(this.params.get()); + for (int i = 0; i < params.length;) + { + map.put(params[i++], params[i++]); + } + + List list = new ArrayList(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 "" + escape(label) + ""; + } + + public String escape(String raw) + { + if (raw == null) + { + return "null"; + } + + return raw.replace("<", "<"); + } + + 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("

CDO Server Browser 4.0


\r\n"); + + for (Page page : pages) + { + pout.println("

" + href(page.getLabel(), page.getName()) + "

"); + } + } + + protected void showPage(PrintStream pout, Page page) + { + String repo = getParam("repo"); + + List repoNames = new ArrayList(getRepositoryNames()); + Collections.sort(repoNames); + + pout.print("

" + page.getLabel() + ":  "); + for (String repoName : repoNames) + { + InternalRepository repository = getRepository(repoName); + if (!page.canDisplay(repository)) + { + continue; + } + + if (repo == null) + { + repo = repoName; + } + + if (repoName.equals(repo)) + { + pout.print("" + escape(repoName) + "  "); + } + else + { + pout.print(href(repoName, page.getName(), "repo", repoName) + "  "); + } + } + + pout.print("

"); + + InternalRepository repository = getRepository(repo); + if (repository != null) + { + pout.print("

\r\n"); + + InternalSession session = repository.getSessionManager().openSession(null); + StoreThreadLocal.setSession(session); + + try + { + page.display(this, repository, pout); + } + finally + { + StoreThreadLocal.release(); + session.close(); + } + } + } + + protected Set getRepositoryNames() + { + return repositories.keySet(); + } + + protected InternalRepository getRepository(String name) + { + return repositories.get(name); + } + + @Override + protected String getThreadName() + { + return "CDOServerBrowser"; + } + + protected void initPages(List pages) + { + pages.add(new PackagesPage()); + pages.add(new LocksPage()); + pages.add(new RevisionsPage.FromCache()); + pages.add(new RevisionsPage.FromStore()); + pages.add(new LobsPage()); + pages.add(new HistoryPage()); + + IManagedContainer container = getPagesContainer(); + for (String factoryType : container.getFactoryTypes(Page.PRODUCT_GROUP)) + { + try + { + Page page = (Page)container.getElement(Page.PRODUCT_GROUP, factoryType, null); + pages.add(page); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + + /** + * @since 4.1 + */ + protected IManagedContainer getPagesContainer() + { + return IPluginContainer.INSTANCE; + } + + @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(); + } + + /** + * @since 4.5 + */ + public static String formatTimeStamp(long timeStamp) + { + String str = CDOCommonUtil.formatTimeStamp(timeStamp); + if (!CDOCommonUtil.UNSPECIFIED_DATE_STRING.equals(str)) + { + str += " - " + timeStamp; + str = str.replaceAll(" ", " "); + } + + return str; + } + + /** + * A {@link CDOServerBrowser server browser} for the repositories in a {@link IManagedContainer managed container}. + * + * @author Eike Stepper + */ + public static class ContainerBased extends CDOServerBrowser + { + private IContainer container; + + private IListener containerListener = new ContainerEventAdapter() + { + @Override + protected void onAdded(IContainer container, Object element) + { + addElement(element); + } + + @Override + protected void onRemoved(IContainer container, Object element) + { + removeElement(element); + } + }; + + public ContainerBased(IContainer container) + { + super(new HashMap()); + this.container = container; + } + + public ContainerBased() + { + this(IPluginContainer.INSTANCE); + } + + public IContainer getContainer() + { + return container; + } + + @Override + protected IManagedContainer getPagesContainer() + { + if (container instanceof IManagedContainer) + { + return (IManagedContainer)container; + } + + return IPluginContainer.INSTANCE; + } + + @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()); + } + } + + /** + * Creates {@link CDOServerBrowser server browsers} for the repositories in a {@link IManagedContainer managed + * container}. + * + * @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 + { + int port = 0; + + if (!StringUtil.isEmpty(description)) + { + int digits = 0; + for (int i = 0; i < description.length(); i++) + { + if (Character.isDigit(description.charAt(i))) + { + ++digits; + } + else + { + break; + } + } + + if (digits != 0) + { + port = Integer.parseInt(description.substring(0, digits)); + } + } + + if (port == 0) + { + port = IOUtil.getFreePort(); + } + + browser.setPort(port); + } + catch (Exception ex) + { + OM.LOG.warn(ex); + } + + return browser; + } + } + } + + /** + * Represents pluggable content for a {@link CDOServerBrowser server 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); + } + + /** + * An abstract base implementation of a {@link Page server browser page}. + * + * @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; + } + } + + /** + * A {@link Page server browser page} that renders the package registry contents of a repository. + * + * @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, "  "); + } + } + + protected String showPackage(InternalCDOPackageInfo info, InternalCDOPackageRegistry packageRegistry, CDOServerBrowser browser, String param, + PrintStream out, String prefix) + { + EPackage ePackage = info.getEPackage(); + out.println("

" + prefix + ePackage.getName() + "  [" + ePackage.getNsURI() + "]

"); + + 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 + "  " + label); + + if (classifier instanceof EEnum) + { + EEnum eenum = (EEnum)classifier; + out.print("  " + eenum.getELiterals()); + } + else if (classifier instanceof EDataType) + { + EDataType eDataType = (EDataType)classifier; + out.print("  " + eDataType.getInstanceClassName()); + } + + out.println("
"); + } + + for (EPackage sub : ePackage.getESubpackages()) + { + InternalCDOPackageInfo subInfo = packageRegistry.getPackageInfo(sub); + param = showPackage(subInfo, packageRegistry, browser, param, out, prefix + "  "); + } + + return param; + } + } + + /** + * A {@link Page server browser page} that renders the locking manager contents of a repository. + * + * @author Eike Stepper + * @since 4.2 + */ + public static class LocksPage extends AbstractPage + { + public static final String NAME = "locks"; + + public LocksPage() + { + super(NAME, "Locks"); + } + + public boolean canDisplay(InternalRepository repository) + { + return true; + } + + public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out) + { + InternalLockManager lockingManager = repository.getLockingManager(); + for (InternalSession session : repository.getSessionManager().getSessions()) + { + boolean sessionRendered = false; + for (InternalView view : session.getViews()) + { + Map locks = lockingManager.getLocks(view); + if (locks != null && !locks.isEmpty()) + { + if (!sessionRendered) + { + int sessionID = session.getSessionID(); + String userID = session.getUserID(); + out.println("

Session " + sessionID + "  [" + userID + "]

"); + out.println("
    "); + sessionRendered = true; + } + + out.println("
  • " + view + "
  • "); + out.println("
      "); + + for (Entry entry : locks.entrySet()) + { + out.println("
    • " + entry.getKey() + " = " + entry.getValue() + "
    • "); + } + + out.println("
    "); + } + + if (sessionRendered) + { + out.println("
"); + } + } + } + } + } + + /** + * A {@link Page server browser page} that renders {@link CDORevision revisions}. + * + * @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> allRevisions = getAllRevisions(repository); + Map> ids = getAllIDs(allRevisions); + + out.print("\r\n"); + out.print("\r\n"); + + out.print("\r\n"); + out.print("\r\n"); + + if (revision[0] != null) + { + out.print("\r\n"); + } + + out.print("\r\n"); + out.print("
\r\n"); + out.print(""); + out.println(""); + + out.println(""); + out.println(""); + + lastRevision = null; + versionsBuilder = null; + } + } + }.dump(); + + out.print("
\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 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(" "); + } + + 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("" + version + ""); + } + else + { + versionsBuilder.append(browser.href(version, getName(), "revision", key)); + } + + lastRevision = rev; + } + + protected void dumpLastRevision() + { + if (versionsBuilder != null) + { + PrintStream out = out(); + + out.println("
    "); + out.println(getCDOIDLabel(lastRevision)); + out.println("    "); + out.println(versionsBuilder.toString()); + out.println("
   \r\n"); + showRevision(out, browser, allRevisions, ids, revision[0], repository); + out.print("
\r\n"); + } + + /** + * @since 4.0 + */ + protected void showRevision(PrintStream pout, CDOServerBrowser browser, Map> allRevisions, Map> 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> ids, InternalCDORevision revision) + { + String className = revision.getEClass().toString(); + className = className.substring(className.indexOf(' ')); + className = StringUtil.replace(className, new String[] { "(", ")", "," }, new String[] { "
", "", "
" }); + className = className.substring("
".length() + 1); + + String created = formatTimeStamp(revision.getTimeStamp()); + String commitInfo = browser.href(created, HistoryPage.NAME, "time", String.valueOf(revision.getTimeStamp())); + + pout.print("\r\n"); + showKeyValue(pout, true, "type", "" + revision.getClass().getSimpleName() + ""); + 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", formatTimeStamp(revision.getRevised())); + if (revision instanceof SyntheticCDORevision) + { + if (revision instanceof PointerCDORevision) + { + PointerCDORevision pointer = (PointerCDORevision)revision; + CDOBranchVersion target = pointer.getTarget(); + CDOBranch branch = target.getBranch(); + int version = target.getVersion(); + + String label = getVersionLabel("v", version, branch); + CDORevisionKey targetKey = CDORevisionUtil.createRevisionKey(pointer.getID(), branch, version); + String value = CDORevisionUtil.formatRevisionKey(targetKey); + showKeyValue(pout, true, "target", browser.href(label, getName(), "revision", value)); + } + } + else + { + 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("
\r\n"); + } + + /** + * @since 4.0 + */ + protected Object getRevisionValue(Object value, CDOServerBrowser browser, Map> ids, InternalCDORevision context) + { + if (value instanceof CDOID) + { + List revisions = ids.get(value); + if (revisions != null) + { + StringBuilder builder = new StringBuilder(); + builder.append(getCDOIDLabel(revisions.get(0))); + + if (browser != null) + { + builder.append("  "); + for (CDORevision revision : revisions) + { + String versionPrefix = getVersionPrefix(revision); + int version = revision.getVersion(); + CDOBranch branch = revision.getBranch(); + String label = getVersionLabel(versionPrefix, version, branch); + + 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 ? "" : "
"); + builder.append(getRevisionValue(element, browser, ids, context)); + } + + return builder.toString(); + } + + return value; + } + + private String getVersionLabel(String versionPrefix, int version, CDOBranch branch) + { + String label = versionPrefix + version; + String branchName = branch.getName(); + if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName)) + { + label += "[" + branchName + "]"; + } + return label; + } + + 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("\r\n"); + pout.print("" + key + "\r\n"); + pout.print(""); + pout.print(value); + pout.print("\r\n"); + pout.print("\r\n"); + } + + protected abstract Map> getAllRevisions(InternalRepository repository); + + private Map> getAllIDs(Map> allRevisions) + { + Map> ids = CDOIDUtil.createMap(); + for (List list : allRevisions.values()) + { + for (CDORevision revision : list) + { + CDOID id = revision.getID(); + List revisions = ids.get(id); + if (revisions == null) + { + revisions = new ArrayList(); + ids.put(id, revisions); + } + + revisions.add(revision); + } + } + + return ids; + } + + protected String getCDOIDLabel(CDORevision revision) + { + String label = revision.toString(); + return label.substring(0, label.indexOf(':')); + } + + /** + * A {@link Page server browser page} that renders the {@link CDORevision revisions} in a revision cache. + * + * @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> getAllRevisions(InternalRepository repository) + { + return repository.getRevisionManager().getCache().getAllRevisions(); + } + } + + /** + * A {@link Page server browser page} that renders the {@link CDORevision revisions} in a {@link IStore store}. + * + * @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> getAllRevisions(InternalRepository repository) + { + return ((CDOAllRevisionsProvider)repository.getStore()).getAllRevisions(); + } + } + } + + /** + * A {@link Page server browser page} that renders {@link CDOLobInfo large object infos}. + * + * @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("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + + if (details[0] != null) + { + out.print("\r\n"); + out.print("\r\n"); + } + + out.print("\r\n"); + out.print("
\r\n"); + + 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); + } + + out.print("   \r\n"); + if (details[0] instanceof ByteArrayOutputStream) + { + ByteArrayOutputStream baos = (ByteArrayOutputStream)details[0]; + String hex = HexUtil.bytesToHex(baos.toByteArray()); + + out.println("

Blob " + details[1] + " (" + details[2] + ")

"); + out.println("
\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("
\r\n"); + } + else + { + CharArrayWriter caw = (CharArrayWriter)details[0]; + out.println("

Clob " + details[1] + " (" + details[2] + ")

"); + out.println("
" + caw + "
"); + } + + out.print("
\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; + } + } + + /** + * A {@link Page server browser page} that renders {@link CDOCommitInfo commit infos}. + * + * @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("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("
\r\n"); + + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + final String param = browser.getParam("time"); + + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + out.print("\r\n"); + + if (repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE) + { + out.print("\r\n"); + } + + out.print("\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("
TimeBranchUserCommentMerge
\r\n"); + out.print("
   \r\n"); + + if (auditing) + { + CDOCommitInfo commitInfo = details[0]; + if (commitInfo != null) + { + out.print("

Commit Info " + commitInfo.getTimeStamp() + "

\r\n"); + showCommitData(out, commitInfo, browser); + } + } + else + { + out.print("

No audit data available in this repository.

\r\n"); + } + + out.print("
\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 = formatTimeStamp(commitInfo.getTimeStamp()); + String label = formatted; + if (!selected && auditing) + { + label = browser.href(formatted, getName(), "time", timeStamp); + } + + out.print("\r\n"); + out.print("\r\n"); + out.print(label); + out.print("\r\n"); + + CDOBranch branch = commitInfo.getBranch(); + out.print("\r\n"); + out.print(branch.getName() + "[" + branch.getID() + "]"); + out.print("\r\n"); + + String userID = commitInfo.getUserID(); + out.print("\r\n"); + out.print(StringUtil.isEmpty(userID) ? " " : browser.escape(userID)); + out.print("\r\n"); + + String comment = commitInfo.getComment(); + out.print("\r\n"); + out.print(StringUtil.isEmpty(comment) ? " " : browser.escape(comment)); + out.print("\r\n"); + + CDOCommonRepository repository = commitInfo.getCommitInfoManager().getRepository(); + if (repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE) + { + out.print("\r\n"); + + CDOBranchPoint mergeSource = commitInfo.getMergeSource(); + if (mergeSource == null) + { + out.print(" "); + } + else + { + String mergeSourceLabel = browser.escape(mergeSource.getBranch().getPathName()) + " - " + formatTimeStamp(mergeSource.getTimeStamp()); + out.print(browser.href(mergeSourceLabel, getName(), "time", String.valueOf(mergeSource.getTimeStamp()))); + } + + out.print("\r\n"); + } + + out.print("\r\n"); + return selected; + } + + protected void showCommitData(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser) + { + out.print("

New Objects:

\r\n"); + out.print("
    \r\n"); + for (CDOIDAndVersion key : commitInfo.getNewObjects()) + { + CDORevision newObject = (CDORevision)key; + out.print( + "
  • " + browser.href(newObject.toString(), RevisionsPage.FromStore.NAME, "revision", CDORevisionUtil.formatRevisionKey(newObject)) + "
    \r\n"); + } + + out.print("
\r\n"); + out.print("

Changed Objects:

\r\n"); + out.print("
    \r\n"); + for (CDORevisionKey key : commitInfo.getChangedObjects()) + { + CDORevisionDelta changedObject = (CDORevisionDelta)key; + out.print("
  • " + changedObject.toString() + "
    \r\n"); + } + + out.print("
\r\n"); + out.print("

Detached Objects:

\r\n"); + out.print("
    \r\n"); + for (CDOIDAndVersion key : commitInfo.getDetachedObjects()) + { + out.print("
  • " + key.toString() + "
    \r\n"); + } + + out.print("
\r\n"); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java new file mode 100644 index 000000000..19dd39177 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java @@ -0,0 +1,804 @@ +/* + * Copyright (c) 2010-2016, 2018 Eike Stepper (Loehne, 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.CDORevisionData; +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.DetachedCDORevision; +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.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; + +/** + * Exports the complete contents of a {@link IRepository repository} in a format suitable for {@link CDOServerImporter + * imports} into new repositories. + *

+ * Subtypes specifiy the actual exchange format. + * + * @author Eike Stepper + * @since 4.0 + */ +public abstract class CDOServerExporter +{ + private InternalRepository repository; + + private boolean exportSystemPackages; + + public CDOServerExporter(IRepository repository) + { + this.repository = (InternalRepository)repository; + } + + public final IRepository getRepository() + { + return repository; + } + + /** + * @since 4.7 + */ + public boolean isExportSystemPackages() + { + return exportSystemPackages; + } + + /** + * @since 4.7 + */ + public void setExportSystemPackages(boolean exportSystemPackages) + { + this.exportSystemPackages = exportSystemPackages; + } + + 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(true); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + if (packageUnit.isSystem() && !exportSystemPackages) + { + continue; + } + + 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() + { + 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; + + /** + * XML constants being used by both {@link CDOServerExporter exporters} and {@link CDOServerImporter importers}. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @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"; + + /** + * @since 4.7 + */ + public static final String REVISION_DETACHED = "detached"; + + 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"; + + /** + * @since 4.7 + */ + public static final String FEATURE_ISSET = "isset"; + + /** + * @since 4.7 + */ + public static final String FEATURE_ISNULL = "isnull"; + + 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"; + + /** + * @since 4.7 + */ + public static final String TYPE_BYTE_ARRAY = "ByteArray"; + + 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"; + + /** + * @since 4.6 + */ + public static final String MERGE_SOURCE_BRANCH = "mergeSourceBranch"; + + /** + * @since 4.6 + */ + public static final String MERGE_SOURCE_TIME = "mergeSourceTime"; + } + + /** + * An {@link CDOServerExporter exporter} that creates XML output suitable to be interpreted by an + * {@link CDOServerImporter.XML XML importer}. + * + * @author Eike Stepper + */ + public static class XML extends CDOServerExporter 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; + boolean detached = rev instanceof DetachedCDORevision; + + 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); + } + + if (detached) + { + out.attribute(REVISION_DETACHED, true); + return; + } + + 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(); + + InternalRepository repository = (InternalRepository)getRepository(); + repository.ensureChunks(rev, CDORevision.UNCHUNKED); + + CDOClassInfo classInfo = rev.getClassInfo(); + for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) + { + if (feature.isMany()) + { + @SuppressWarnings("unchecked") + List list = (List)rev.getValue(feature); + if (list != null && !list.isEmpty()) + { + for (Object value : list) + { + exportFeature(out, feature, value); + } + } + else if (feature.isUnsettable()) + { + out.element(FEATURE); + out.attribute(FEATURE_NAME, feature.getName()); + out.attribute(FEATURE_ISSET, list != null); + } + } + 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 byte[]) + { + byte[] array = (byte[])value; + out.attribute(featureType, TYPE_BYTE_ARRAY); + out.attribute(FEATURE_VALUE, HexUtil.bytesToHex(array)); + } + else if (value == CDORevisionData.NIL) + { + out.attribute(FEATURE_ISNULL, true); + } + 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()); + + CDOBranchPoint mergeSource = commitInfo.getMergeSource(); + if (mergeSource != null) + { + out.attribute(MERGE_SOURCE_BRANCH, mergeSource.getBranch().getID()); + out.attribute(MERGE_SOURCE_TIME, mergeSource.getTimeStamp()); + } + } + + 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(); + } + + if (value instanceof BigDecimal) + { + return BigDecimal.class.getSimpleName(); + } + + if (value instanceof BigInteger) + { + return BigInteger.class.getSimpleName(); + } + + throw new IllegalArgumentException("Invalid type: " + value.getClass().getName()); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java new file mode 100644 index 000000000..883950cb8 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java @@ -0,0 +1,795 @@ +/* + * Copyright (c) 2010-2012, 2014, 2016, 2018 Eike Stepper (Loehne, 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.CDORevisionData; +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.server.IStoreAccessor.Raw2; +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.DetachedCDORevision; +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.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Imports the complete contents of a {@link IRepository repository} from the output created by a + * {@link CDOServerExporter exporter} into a new repository. + *

+ * Subtypes specifiy the actual exchange format. + * + * @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; + + /** + * Persists the data that has been read by a {@link CDOServerImporter importer} into a new {@link IRepository + * repository}. + * + * @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(); + } + + /** + * Persists the data that has been read by a {@link CDOServerImporter importer} into a new {@link IRepository + * repository}. + * + * @author Eike Stepper + * @since 4.6 + */ + public static interface Handler2 extends Handler + { + public void handleCommitInfo(long time, long previous, int branch, String user, String comment, int mergeSourceBranchID, long mergeSourceTime); + } + + /** + * @author Eike Stepper + */ + private final class FlushHandler implements Handler2 + { + private OMMonitor monitor = new Monitor(); + + private IStoreAccessor.Raw accessor; + + private Map models = new HashMap(); + + private LinkedList packageUnits = new LinkedList(); + + private List 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(true); + 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(); + 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) + { + handleCommitInfo(time, previous, branchID, user, comment, 0, CDOBranchPoint.UNSPECIFIED_DATE); + } + + public void handleCommitInfo(long time, long previous, int branchID, String user, String comment, int mergeSourceBranchID, long mergeSourceTime) + { + CDOBranch branch = repository.getBranchManager().getBranch(branchID); + if (mergeSourceBranchID != 0 && accessor instanceof Raw2) + { + CDOBranch mergeSourceBranch = repository.getBranchManager().getBranch(mergeSourceBranchID); + CDOBranchPoint mergeSource = mergeSourceBranch.getPoint(mergeSourceTime); + + Raw2 raw2 = (Raw2)accessor; + raw2.rawStore(branch, time, previous, user, comment, mergeSource, monitor); + } + else + { + 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; + } + } + } + + /** + * An {@link CDOServerImporter importer} that reads and interprets XML output created by an + * {@link CDOServerExporter.XML XML exporter}. + * + * @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); + if (!CDOModelUtil.isSystemPackageURI(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); + if (!CDOModelUtil.isSystemPackageURI(packageURI)) + { + 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); + + CDOID id = id(attributes.getValue(REVISION_ID)); + int version = Integer.parseInt(attributes.getValue(REVISION_VERSION)); + long created = timeStamp(attributes.getValue(REVISION_TIME)); + long revised = timeStamp(attributes.getValue(REVISION_REVISED)); + boolean detached = "true".equals(attributes.getValue(REVISION_DETACHED)); + if (detached) + { + revision = new DetachedCDORevision(eClass, id, branch, version, created, revised); + } + else + { + revision = (InternalCDORevision)CDORevisionFactory.DEFAULT.createRevision(eClass); + revision.setID(id); + revision.setBranchPoint(branch.getPoint(created)); + revision.setVersion(version); + revision.setRevised(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); + EClass eClass = revision.getEClass(); + EStructuralFeature feature = eClass.getEStructuralFeature(name); + if (feature == null) + { + throw new IllegalStateException("Feature " + name + " not found in class " + eClass.getName()); + } + + String isSetString = attributes.getValue(FEATURE_ISSET); + if (isSetString != null) + { + // This must be an empty or an unset list. + boolean isSet = Boolean.parseBoolean(isSetString); + if (isSet) + { + // Create an empty list. + revision.getOrCreateList(feature); + } + else + { + // Leave the list unset. + } + } + else + { + Object value = value(attributes); + + if (feature.isMany()) + { + CDOList list = revision.getOrCreateList(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); + + if (handler instanceof Handler2) + { + Handler2 handler2 = (Handler2)handler; + + value = attributes.getValue(MERGE_SOURCE_BRANCH); + if (value != null) + { + int mergeSourceBranch = Integer.parseInt(value); + long mergeSourceTime = Long.parseLong(attributes.getValue(MERGE_SOURCE_TIME)); + + handler2.handleCommitInfo(time, previous, branch, user, comment, mergeSourceBranch, mergeSourceTime); + return; + } + } + + 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 final long timeStamp(String str) + { + if (str == null) + { + return CDOBranchPoint.UNSPECIFIED_DATE; + } + + return Long.parseLong(str); + } + + protected Object value(Attributes attributes) + { + String type = attributes.getValue(FEATURE_TYPE); + + if (type == null) + { + String isNullString = attributes.getValue(FEATURE_ISNULL); + if (isNullString != null) + { + // This must be an explicit single-valued null. + boolean isNull = Boolean.parseBoolean(isNullString); + if (isNull) + { + return CDORevisionData.NIL; + } + + throw new IllegalArgumentException("Invalid attribute: isnull=false"); + } + } + + 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)); + } + + if (BigDecimal.class.getSimpleName().equals(type)) + { + return new BigDecimal(str); + } + + if (BigInteger.class.getSimpleName().equals(type)) + { + return new BigInteger(str); + } + + if (TYPE_BYTE_ARRAY.equals(type)) + { + return HexUtil.hexToBytes(str); + } + + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java new file mode 100644 index 000000000..deaa1457a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2007-2013, 2015-2017 Eike Stepper (Loehne, 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.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.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.InternalRepository; +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.container.IPluginContainer; +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; + +/** + * Various static methods that may help with CDO {@link IRepository repositories} and server-side {@link CDOView views}. + * + * @author Eike Stepper + * @apiviz.exclude + */ +public final class CDOServerUtil +{ + private CDOServerUtil() + { + } + + // /** + // * @since 4.2 + // */ + // public static CDOView openView(ISession session, CDOBranchPoint branchPoint, CDORevisionProvider revisionProvider) + // { + // return new ServerCDOView((InternalSession)session, branchPoint, revisionProvider); + // } + // + // /** + // * @since 4.2 + // */ + // public static CDOView openView(ISession session, CDOBranchPoint branchPoint) + // { + // CDORevisionManager revisionManager = session.getManager().getRepository().getRevisionManager(); + // CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branchPoint); + // return new ServerCDOView((InternalSession)session, branchPoint, revisionProvider); + // } + // + // /** + // * @since 4.2 + // */ + // public static CDOView openView(IView view) + // { + // ISession session = view.getSession(); + // CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); + // return openView(session, branchPoint, view); + // } + // + // /** + // * @since 4.2 + // */ + // public static CDOView openView(IStoreAccessor.CommitContext commitContext) + // { + // ISession session = commitContext.getTransaction().getSession(); + // CDOBranchPoint branchPoint = commitContext.getBranchPoint(); + // return openView(session, branchPoint, commitContext); + // } + + /** + * @since 4.2 + */ + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, CDORevisionProvider revisionProvider) + { + return new ServerCDOView((InternalSession)session, branchPoint, revisionProvider); + } + + /** + * @since 4.2 + */ + public static CDOView openView(ISession session, CDOBranchPoint branchPoint) + { + CDORevisionManager revisionManager = session.getManager().getRepository().getRevisionManager(); + CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branchPoint); + return openView(session, branchPoint, revisionProvider); + } + + /** + * @since 4.2 + */ + public static CDOView openView(IView view) + { + ISession session = view.getSession(); + CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); + return openView(session, branchPoint, view); + } + + /** + * @since 4.2 + */ + public static CDOView openView(IStoreAccessor.CommitContext commitContext) + { + ISession session = commitContext.getTransaction().getSession(); + CDOBranchPoint branchPoint = commitContext.getBranchPoint(); + return openView(session, branchPoint, commitContext); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(ISession, CDOBranchPoint, CDORevisionProvider)}. + */ + @Deprecated + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled, CDORevisionProvider revisionProvider) + { + return openView(session, branchPoint, revisionProvider); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(ISession, CDOBranchPoint)}. + */ + @Deprecated + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled) + { + return openView(session, branchPoint); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(IView)}. + */ + @Deprecated + public static CDOView openView(IView view, boolean legacyModeEnabled) + { + return openView(view); + } + + /** + * @since 4.0 + * @deprecated As of 4.2 the legacy mode is always enabled, use {@link #openView(IStoreAccessor.CommitContext)}. + */ + @Deprecated + public static CDOView openView(IStoreAccessor.CommitContext commitContext, boolean legacyModeEnabled) + { + return openView(commitContext); + } + + /** + * @since 3.0 + */ + public static ISessionManager createSessionManager() + { + return new SessionManager(); + } + + public static IRepository createRepository(String name, IStore store, Map 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 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 props, IRepositorySynchronizer synchronizer, + boolean master, boolean allowBackupCommits) + { + FailoverParticipant repository = new FailoverParticipant(); + initRepository(repository, name, store, props); + + if (synchronizer != null) + { + 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 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 props, IRepositorySynchronizer synchronizer) + { + return createFailoverParticipant(name, store, props, synchronizer, false); + } + + /** + * @since 4.0 + */ + public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, Map props) + { + return createFailoverParticipant(name, store, props, null); + } + + private static void initRepository(Repository repository, String name, IStore store, Map props) + { + repository.setName(name); + repository.setStore((InternalStore)store); + repository.setProperties(props); + } + + public static void addRepository(IManagedContainer container, IRepository repository) + { + InternalRepository internal = (InternalRepository)repository; + if (internal.getContainer() == null && container != IPluginContainer.INSTANCE) + { + internal.setContainer(container); + } + + String productGroup = RepositoryFactory.PRODUCT_GROUP; + String type = RepositoryFactory.TYPE; + String name = repository.getName(); + + container.putElement(productGroup, type, name, 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$ + } + + /** + * An abstract {@link IRepository.ReadAccessHandler read-access handler} that grants or denies access to single + * {@link CDORevision revisions}. + * + * @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 additionalRevisions) throws RuntimeException + { + List violations = new ArrayList(); + for (CDORevision revision : revisions) + { + String violation = validate(session, revision); + if (violation != null) + { + violations.add(violation); + } + } + + if (!violations.isEmpty()) + { + throwException(session, violations); + } + + for (Iterator 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 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java new file mode 100644 index 000000000..33dcf1163 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2013 Eike Stepper (Loehne, 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.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.ContainmentCycleException; + +/** + * An unchecked exception that can thrown from a commit operation that is based on stale information + * about the tree structure of the model and would introduce a containment cycle. + *

+ * This situation results from a network race condition and can not be prevented by write locks on + * the changed objects. The committing client must {@link CDOTransaction#rollback() rollback} the transaction + * , replay the original changes and try to {@link CDOTransaction#commit() commit} again. + * + * @author Eike Stepper + * @since 4.0 + * @deprecated As of 4.2 no longer used in the server; replaced by {@link ContainmentCycleException} in the client. + */ +@Deprecated +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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ILockingManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ILockingManager.java new file mode 100644 index 000000000..6b9cb2ef2 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ILockingManager.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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.server; + +import org.eclipse.emf.cdo.common.CDOCommonSession; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; + +/** + * Manages all persistent aspects of durable CDO views and provides for vetoable + * {@link #addDurableViewHandler(ILockingManager.DurableViewHandler) interception} of the durable view resumption + * process. + * + * @author Caspar De Groot + * @since 4.1 + */ +public interface ILockingManager extends IDurableLockingManager +{ + public void addDurableViewHandler(DurableViewHandler handler); + + public void removeDurableViewHandler(DurableViewHandler handler); + + public DurableViewHandler[] getDurableViewHandlers(); + + /** + * A call-back interface primarily intended to allow implementers to prevent the view from being opened by throwing an + * exception. See {@link ILockingManager#addDurableViewHandler(DurableViewHandler)}. + * + * @author Caspar De Groot + * @since 4.1 + */ + public interface DurableViewHandler + { + /** + * A call-back method primarily intended to allow implementers to prevent the view from being opened by throwing an + * exception. See {@link ILockingManager#addDurableViewHandler(DurableViewHandler)}. + */ + public void openingView(CDOCommonSession session, int viewID, boolean readOnly, LockArea area) throws Exception; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java new file mode 100644 index 000000000..42daf80b1 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, 2010-2012, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IPermissionManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IPermissionManager.java new file mode 100644 index 000000000..d0d9a3ac3 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IPermissionManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012, 2013 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.security.CDOPermission; + +import java.util.Set; + +/** + * Provides the protection level of {@link CDORevision revisions} in the context of a specific user. + * + * @author Eike Stepper + * @since 4.1 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IPermissionManager +{ + /** + * @deprecated As of 4.2 call {@link #getPermission(CDORevision, CDOBranchPoint, ISession)}. + */ + @Deprecated + public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, String userID); + + /** + * @since 4.2 + */ + public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, ISession session); + + /** + * @since 4.3 + */ + public boolean hasAnyRule(ISession session, Set rules); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java new file mode 100644 index 000000000..1cb060838 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-2012, 2015 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.view.CDOQuery; + +/** + * Represents the execution state of a {@link CDOQuery query} in the server towards a {@link IQueryHandler query + * handler}. + * + * @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 true to indicate that more results can be passed subsequently, false otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResult(Object object); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java new file mode 100644 index 000000000..4c9ec3c8b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008-2012, 2015 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.view.CDOQuery; + +/** + * A query language handler that is capable of executing a {@link CDOQuery query}. + * + * @author Eike Stepper + * @since 2.0 + */ +public interface IQueryHandler +{ + /** + * Executes the {@link CDOQuery query} represented by the specified {@link CDOQueryInfo query info} by + * {@link IQueryContext#addResult(Object) passing} the query results to the query execution engine represented by the + * specified {@link IQueryContext execution context}. + * + * @since 3.0 + */ + public void executeQuery(CDOQueryInfo info, IQueryContext context); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java new file mode 100644 index 000000000..bde36110b --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2008-2012, 2015 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.view.CDOQuery; + +/** + * Provides the consumer with {@link IQueryHandler query handlers} that are capable of executing {@link CDOQuery + * queries} represented by specific {@link CDOQueryInfo query infos}. + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java new file mode 100644 index 000000000..6869fbe86 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399487 + */ +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.commit.CDOCommitInfoManager; +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.emf.cdo.common.util.CDOCommonUtil; + +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.lifecycle.ILifecycle; +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; +import java.util.Set; + +/** + * A CDO repository. + * + * @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, ILifecycle +{ + /** + * @since 3.0 + */ + public static final String SYSTEM_USER_ID = CDOCommonUtil.SYSTEM_USER_ID; + + public IStore getStore(); + + public Map 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(); + + /** + * @since 4.2 + */ + public CDOCommitInfoManager getCommitInfoManager(); + + public ISessionManager getSessionManager(); + + /** + * @since 4.5 + */ + public IUnitManager getUnitManager(); + + /** + * @since 4.0 + * @deprecated As of 4.1 use {@link #getLockingManager()}. + */ + @Deprecated + public IDurableLockingManager getLockManager(); + + /** + * @since 4.1 + */ + public ILockingManager getLockingManager(); + + /** + * @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.1 + * @deprecated As of 4.2 call {@link CDOCommitInfoManager#getCommitInfoHandlers()} + */ + @Deprecated + public CDOCommitInfoHandler[] getCommitInfoHandlers(); + + /** + * @since 4.0 + * @deprecated As of 4.2 call {@link CDOCommitInfoManager#addCommitInfoHandler(CDOCommitInfoHandler)} + */ + @Deprecated + public void addCommitInfoHandler(CDOCommitInfoHandler handler); + + /** + * @since 4.0 + * @deprecated As of 4.2 call {@link CDOCommitInfoManager#removeCommitInfoHandler(CDOCommitInfoHandler)} + */ + @Deprecated + public void removeCommitInfoHandler(CDOCommitInfoHandler handler); + + /** + * @since 4.1 + */ + public Set getHandlers(); + + /** + * @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 additionalRevisions) throws RuntimeException; + } + + /** + * Provides a way to handle commits that are received from a client. + * + * @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. Note that you must not alter the + * internal state of the commit context in any way! + * @param monitor + * A monitor that should be used by the implementor to avoid timeouts. + * @throws TransactionValidationException + * to indicate that the commit operation must not be executed against the backend store because some + * semantic validation checks failed. The message should describe the validation failure and will be + * passed through to the client + * @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. Note that you must not alter the internal + * state of the commit context in any way! + * @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); + + /** + * An exception that a {@link WriteAccessHandler} may throw to indicate that a + * {@linkplain WriteAccessHandler#handleTransactionBeforeCommitting(ITransaction, org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext, OMMonitor) transaction commit} + * was rejected because one or more semantic validation checks reported errors. + * + * @author Christian W. Damus (CEA LIST) + * + * @since 4.3 + */ + public static final class TransactionValidationException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + public TransactionValidationException() + { + } + + public TransactionValidationException(String message, Throwable cause) + { + super(message, cause); + } + + public TransactionValidationException(String message) + { + super(message); + } + + public TransactionValidationException(Throwable cause) + { + super(cause); + } + + } + } + + /** + * Contains symbolic constants that specifiy valid keys of {@link IRepository#getProperties() repository properties}. + * + * @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.5 + */ + public static final String SUPPORTING_UNITS = "supportingUnits"; //$NON-NLS-1$ + + /** + * @since 4.5 + */ + public static final String CHECK_UNIT_MOVES = "checkUnitMoves"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String SERIALIZE_COMMITS = "serializeCommits"; //$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$ + + /** + * Possible values: NO | YES | WITH_MERGE_SOURCE. + * + * @since 4.6 + */ + public static final String COMMIT_INFO_STORAGE = "commitInfoStorage"; //$NON-NLS-1$ + + /** + * @since 4.2 + */ + public static final String OPTIMISTIC_LOCKING_TIMEOUT = "optimisticLockingTimeout"; //$NON-NLS-1$ + + /** + * @since 4.0 + * @deprecated As of 4.2 instances of Ecore are always supported (on demand). + */ + @Deprecated + public static final String SUPPORTING_ECORE = "supportingEcore"; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java new file mode 100644 index 000000000..e2173b753 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2007, 2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * Creates CDO {@link IRepository repositories}. + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java new file mode 100644 index 000000000..de97a54e5 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2007, 2011, 2012, 2015 Eike Stepper (Loehne, 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; + +/** + * Provides the consumer with CDO {@link IRepository repositories} specified by their name. + * + * @author Eike Stepper + * @apiviz.uses {@link IRepository} - - provides + */ +public interface IRepositoryProvider +{ + public IRepository getRepository(String name); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java new file mode 100644 index 000000000..30c9c28ed --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2012, 2015 Eike Stepper (Loehne, 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.container.IContainer; + +/** + * Synchronizes a {@link ISynchronizableRepository synchronizable repository} with a master 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 org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory} oneway - - remote + */ +public interface IRepositorySynchronizer extends IContainer +{ + /** + * @since 4.4 + */ + public static final int DEFAULT_RETRY_INTERVAL = 3; + + /** + * @since 4.4 + */ + public static final int DEFAULT_MAX_RECOMMITS = 10; + + /** + * @since 4.4 + */ + public static final int DEFAULT_RECOMMIT_INTERVAL = 1; + + 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java new file mode 100644 index 000000000..141c7d738 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007-2013 Eike Stepper (Loehne, 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.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.ISessionProtocol; + +import org.eclipse.net4j.util.container.IContainer; + +/** + * The server-side representation of a client {@link CDOSession session}. + * + * @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 +{ + /** + * @since 3.0 + */ + public ISessionManager getManager(); + + /** + * @since 3.0 + */ + public ISessionProtocol getProtocol(); + + /** + * @since 4.0 + * @deprecated The return value of this method can not be relied upon to be strictly ordered! + */ + @Deprecated + 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java new file mode 100644 index 000000000..e379186e1 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012 Eike Stepper (Loehne, 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; +import org.eclipse.net4j.util.security.IAuthenticator; + +/** + * Manages the user {@link ISession sessions} of a {@link IRepository repository}. + * + * @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 +{ + /** + * @since 2.0 + */ + public IRepository getRepository(); + + public ISession[] getSessions(); + + /** + * @since 2.0 + */ + public ISession getSession(int sessionID); + + /** + * @since 4.2 + */ + public IAuthenticator getAuthenticator(); + + /** + * @since 4.2 + */ + public void setAuthenticator(IAuthenticator authenticator); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java new file mode 100644 index 000000000..a032d1951 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2007-2012, 2015 Eike Stepper (Loehne, 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.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; + +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Represents the physical data storage back-end of a CDO {@link IRepository repository}, such as a database or a file + * system folder. + * + * @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 getObjectIDTypes(); + + /** + * @since 4.0 + */ + public CDOID createObjectID(String val); + + /** + * @since 2.0 + */ + public Set getSupportedChangeFormats(); + + /** + * @since 2.0 + */ + public Set getSupportedRevisionTemporalities(); + + /** + * @since 2.0 + */ + public Set getSupportedRevisionParallelisms(); + + /** + * @since 2.0 + */ + public RevisionTemporality getRevisionTemporality(); + + /** + * @since 2.0 + */ + public RevisionParallelism getRevisionParallelism(); + + /** + * Returns trueif this store was activated for the first time, false 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 names if names is not + * null and not {@link Collection#isEmpty() empty}, all existing property entries otherwise. + * + * @since 4.0 + */ + public Map getPersistentProperties(Set names); + + /** + * @since 4.0 + */ + public void setPersistentProperties(Map properties); + + /** + * @since 4.0 + */ + public void removePersistentProperties(Set 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 null. 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 null, 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 + * null. + * @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 null. + * @since 2.0 + */ + public IStoreAccessor getWriter(ITransaction transaction); + + /** + * @since 2.0 + */ + public ProgressDistributor getIndicatingCommitDistributor(); + + /** + * Enumerates the possible data formats a {@link IStore store} can accept for commit operations. + * + * @author Eike Stepper + * @since 2.0 + */ + public enum ChangeFormat + { + /** + * An indication that the store accepts full {@link CDORevision revisions} for dirty objects. + */ + REVISION, + + /** + * An indication that the store accepts incremental {@link CDORevisionDelta revision deltas} for dirty objects. + */ + DELTA + } + + /** + * Enumerates the possible history recording options a {@link IStore store} can accept. + * + * @author Eike Stepper + * @since 2.0 + */ + public enum RevisionTemporality + { + /** + * An indication that the store can work without auditing. + */ + NONE, + + /** + * An indication that the store can work with auditing. + */ + AUDITING + } + + /** + * Enumerates the possible branching options a {@link IStore store} can accept. + * + * @author Eike Stepper + * @since 2.0 + */ + public enum RevisionParallelism + { + /** + * An indication that the store can work without branching. + */ + NONE, + + /** + * An indication that the store can work with branching. + */ + 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java new file mode 100644 index 000000000..e0b8bf7a7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java @@ -0,0 +1,879 @@ +/* + * Copyright (c) 2007-2013, 2016, 2017 Eike Stepper (Loehne, 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.CDOLockState; +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; + +/** + * Represents a connection to a physical data storage back-end. + * + * @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 false, + * null otherwise. + * + * @since 2.0 + */ + public ITransaction getTransaction(); + + /** + * Returns true if this accessor has been configured for read-only access to the back-end, + * false otherwise. + * + * @since 2.0 + */ + public boolean isReader(); + + /** + * @since 2.0 + */ + public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature); + + /** + * @since 2.0 + */ + public Collection readPackageUnits(); + + /** + * Demand loads a given package proxy that has been created on startup of the repository. + *

+ * This method must only load the given package, not 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 all of the following + * conditions are met: + *

    + *
  • The eClass parameter is null or equal to revision.getEClass(). + *
  • The branch parameter is null or equal to revision.getBranch(). + *
  • One of the following conditions is met: + *
      + *
    • The timeStamp parameter is {@link CDOBranchPoint#INVALID_DATE INVALID}. + *
    • The exactTime parameter is true and the timeStamp parameter is + * {@link CDOBranchPoint#UNSPECIFIED_DATE UNSPECIFIED} or equal to revision.getTimeStamp(). + *
    • The exactTime parameter is false and the timeStamp parameter is between + * revision.getTimeStamp() and revision.getRevised(). + *
    + *
+ * + * @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 readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments); + + /** + * Returns the CDOID of the resource node with the given folderID and name if a resource with this + * folderID and name exists in the store, null 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. + *

+ * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object. + *

+ * Usage context: 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 ids); + + /** + * Serializes the content of the large object identified by the given {@link CDOLob#getID() ID} to the given + * stream. + *

+ * 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 stream. + * @throws IOException + * if the stream 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)}. + *

+ * Note: {@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. + *

+ * Note: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and + * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads. + *

+ * Note: Implementors should detect if dirty write occurred. In this case it should throw an exception. + * + *

+   * if (revision.getVersion() != revisionDelta.getOriginVersion())
+   * {
+   *   throw new ConcurrentModificationException("Trying to update object " + revisionDelta.getID()
+   *       + " that was already modified");
+   * }
+   * 
+ * + * @since 2.0 + */ + public void commit(OMMonitor monitor); + + /** + * Note: {@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 CommitContext 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. + * @noextend This interface is not intended to be extended by clients. + * @apiviz.exclude + */ + public interface CommitContext extends CDORevisionProvider + { + /** + * Returns the transactional view (ITransaction) which is the scope of the commit operation represented + * by this CommitContext. + * + * @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 4.6 + */ + public CDOBranchPoint getCommitMergeSource(); + + /** + * @since 4.2 + */ + public long getLastUpdateTime(); + + /** + * Returns the temporary, transactional package manager associated with the commit operation represented by this + * CommitContext. 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(); + + /** + * @since 4.2 + */ + public boolean isClearResourcePathCache(); + + /** + * @since 4.3 + */ + public byte getSecurityImpact(); + + /** + * @since 4.2 + */ + public boolean isUsingEcore(); + + /** + * @since 4.2 + */ + public boolean isUsingEtypes(); + + /** + * Returns an array of the new package units that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDOPackageUnit[] getNewPackageUnits(); + + /** + * Returns an array of the new objects that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDORevision[] getNewObjects(); + + /** + * Returns an array of the dirty objects that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDORevision[] getDirtyObjects(); + + /** + * Returns an array of the dirty object deltas that are part of the commit operation represented by this + * CommitContext. + */ + public InternalCDORevisionDelta[] getDirtyObjectDeltas(); + + /** + * Returns an array of the removed object that are part of the commit operation represented by this + * CommitContext. + * + * @since 2.0 + */ + public CDOID[] getDetachedObjects(); + + /** + * Returns a map with an {@link EClass} value per {@link CDOID} type. + * + * @since 4.0 + */ + public Map getDetachedObjectTypes(); + + /** + * @since 4.2 + */ + public CDOBranchVersion[] getDetachedObjectVersions(); + + /** + * @since 4.6 + */ + public Map getOldRevisions(); + + /** + * @since 4.6 + */ + public Map getNewRevisions(); + + /** + * Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the + * stream is: + *

+ *

    + *
  1. {@link ExtendedDataInputStream#readInt() int}: the number of lobs to be read from the stream. + *
  2. The following data can be read from the stream in a loop with one iteration per lob in the stream: + *
      + *
    1. {@link ExtendedDataInputStream#readByteArray() int + byte[]}: the id of the lob (prepended by the size of the + * id). + *
    2. {@link ExtendedDataInputStream#readLong() long}: the size of the lob. The following interpretation applies: + *
        + *
      • 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}. + *
      • 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}. + *
      + *
    + *
+ * + * @since 4.0 + */ + public ExtendedDataInputStream getLobs(); + + /** + * + * @since 3.0 + * @deprecated As of 4.5 no longer supported. See {@link #getIDsToUnlock()}. + */ + @Deprecated + public boolean isAutoReleaseLocksEnabled(); + + /** + * Returns an array of the locks on the new objects that are part of the commit operation represented by this + * CommitContext. + * + * @since 4.1 + */ + public CDOLockState[] getLocksOnNewObjects(); + + /** + * @since 4.6 + */ + public CDOID[] getIDsToUnlock(); + + /** + * Returns an unmodifiable map from all temporary IDs to their persistent counter parts. + */ + public Map getIDMappings(); + + /** + * @since 4.0 + */ + public CDOCommitInfo createCommitInfo(); + + /** + * @see CDOProtocolConstants + * @since 4.2 + */ + public byte getRollbackReason(); + + /** + * @since 3.0 + */ + public String getRollbackMessage(); + + /** + * @since 4.0 + */ + public List getXRefs(); + + /** + * @since 4.1 + */ + public List> getPostCommmitLockStates(); + + /** + * @since 4.3 + */ + public T getData(Object key); + + /** + * @since 4.3 + */ + public T setData(Object key, T data); + } + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources + * query}. + * + * @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 true to indicate that more results can be passed subsequently, false otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResource(CDOID resourceID); + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources + * query} that is supposed to deliver one exact resource, or null. + * + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ + public interface ExactMatch extends QueryResourcesContext + { + public CDOID getResourceID(); + } + } + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryXRefs(QueryXRefsContext) XRefs query}. + * + * @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 getTargetObjects(); + + public EReference[] getSourceReferences(); + + /** + * @since 4.0 + */ + public Map> 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 true to indicate that more results can be passed subsequently, false 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); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support raw data access as needed by + * {@link IRepositorySynchronizer repository synchronizers} or {@link CDOServerImporter server importers}. + * + * @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}. + *

+ * Implementation note: 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}. + *

+ * Usage context: 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 stream 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 stream 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}. + *

+ * Implementation note: 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}. + *

+ * Usage context: 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 stream 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 stream 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. + *

+ * Implementation note: 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 may be used to report proper progress of this operation to the caller and + * may be used to react to cancelation requests of the caller and must be 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. + *

+ * Implementation note: 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 may be used to report proper progress of this operation to the caller and + * may be used to react to cancelation requests of the caller and must be 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}. + *

+ * Implementation note: 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}. + *

+ * Implementation note: 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}. + *

+ * Implementation note: 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}. + *

+ * Implementation note: 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) + */ + 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 may be used to report proper progress of this operation to the caller and + * may be used to react to cancelation requests of the caller and must be 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); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support raw data access as needed by + * {@link IRepositorySynchronizer repository synchronizers} or {@link CDOServerImporter server importers}. + * + * @author Eike Stepper + * @since 4.6 + * @apiviz.exclude + */ + public interface Raw2 extends Raw + { + /** + * 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}. + *

+ * Implementation note: 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}. + * @param mergeSource + * the {@link CDOCommitInfo#getMergeSource() merge source} of the commit info to be stored in the backend + * represented by this {@link IStoreAccessor.Raw2 raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support durable locking. + * + * @see DurableLocking2 + * @author Eike Stepper + * @since 4.0 + * @apiviz.exclude + */ + public interface DurableLocking extends IDurableLockingManager + { + public void lock(String durableLockingID, LockType type, Collection objectsToLock); + + public void unlock(String durableLockingID, LockType type, Collection objectsToUnlock); + + public void unlock(String durableLockingID); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support durable locking. + * + * @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 locks); + + public void updateLockArea(LockArea lockArea); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support units. + * + * @author Eike Stepper + * @since 4.5 + * @apiviz.exclude + */ + public interface UnitSupport extends IStoreAccessor + { + public List readUnitRoots(); + + public void readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, OMMonitor monitor); + + public Object initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, Set initializedIDs, long timeStamp, OMMonitor monitor); + + public void finishUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, long timeStamp, Object initResult, List ids); + + public void writeUnits(Map unitMappings, long timeStamp); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java new file mode 100644 index 000000000..f3c685255 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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.cdo.session.CDOCollectionLoadingPolicy; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.List; + +/** + * Reads {@link Chunk chunks} of + * {@link org.eclipse.emf.cdo.session.CDOSession.Options#setCollectionLoadingPolicy(CDOCollectionLoadingPolicy) + * partially loaded} lists from a physical data storage backend. + * + * @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 executeRead(); + + /** + * Represents a {@link List#subList(int, int) sublist} of consecutive elements that are subject to partial + * collection loading. + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java new file mode 100644 index 000000000..7832b63c6 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007, 2011, 2012, 2015 Eike Stepper (Loehne, 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; + +/** + * Creates {@link IStore stores}. + * + * @author Eike Stepper + * @apiviz.uses {@link IStore} - - creates + */ +public interface IStoreFactory +{ + public String getStoreType(); + + /** + * @since 4.0 + */ + public IStore createStore(String repositoryName, Map repositoryProperties, Element storeConfig); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java new file mode 100644 index 000000000..56082e37f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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.2 + */ + public boolean hasBeenReplicated(); + + /** + * @since 4.1 + */ + public void goOnline(); + + /** + * @since 4.1 + */ + public void goOffline(); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java new file mode 100644 index 000000000..78769d123 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2007-2012, 2015 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.transaction.CDOTransaction; + +/** + * The server-side representation of a client {@link CDOTransaction transaction}. + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.java new file mode 100644 index 000000000..f9f6c12d4 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Eike Stepper + * @since 4.5 + */ +public interface IUnit +{ + public IUnitManager getManager(); + + public CDOID getRootID(); + + public boolean isOpen(); + + public void open(IView view, CDORevisionHandler revisionHandler, OMMonitor monitor); + + public void close(IView view); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java new file mode 100644 index 000000000..e83dbdf35 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; + +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.Map; +import java.util.Set; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Eike Stepper + * @since 4.5 + */ +public interface IUnitManager extends IContainer +{ + public IRepository getRepository(); + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler, OMMonitor monitor); + + public boolean isUnit(CDOID rootID); + + public IUnit getUnit(CDOID rootID); + + public IUnit[] getUnits(); + + public Map getUnitsOf(Set ids, CDORevisionProvider revisionProvider); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java new file mode 100644 index 000000000..9e06de4d9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.view.CDOView; + +/** + * The server-side representation of a client {@link CDOView view}. + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java new file mode 100644 index 000000000..6e0653667 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2007-2012, 2015 Eike Stepper (Loehne, 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; +import org.eclipse.emf.cdo.session.CDOSession; + +/** + * An unchecked exception being thrown when opening a {@link CDOSession session} to a named {@link IRepository + * repository} that cannot be found. + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java new file mode 100644 index 000000000..2c6db4295 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2008-2013, 2016-2018 Eike Stepper (Loehne, 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.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +/** + * Provides server-side consumers with the {@link IStoreAccessor store accessor} that is valid in the context of a + * specific {@link ISession session} during read operations or a specific {@link CommitContext commit context} during + * write operations. + * + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ +public final class StoreThreadLocal +{ + private static final ThreadLocal SESSION = new InheritableThreadLocal(); + + private static final ThreadLocal ACCESSOR = new InheritableThreadLocal(); + + private static final ThreadLocal COMMIT_CONTEXT = new InheritableThreadLocal(); + + private StoreThreadLocal() + { + } + + /** + * @since 3.0 + */ + public static void setSession(InternalSession session) + { + if (session == null) + { + SESSION.remove(); + } + else + { + SESSION.set(session); + } + + ACCESSOR.remove(); + COMMIT_CONTEXT.remove(); + } + + /** + * Returns the session associated with the current thread. + * + * @return Never null. + * @throws IllegalStateException + * if no session is associated with the current thread. + * @since 3.0 + */ + public static InternalSession getSession() throws NoSessionRegisteredException + { + InternalSession session = SESSION.get(); + if (session == null) + { + throw new NoSessionRegisteredException(); + } + + return session; + } + + /** + * @since 4.2 + */ + public static boolean hasSession() + { + return SESSION.get() != null; + } + + public static void setAccessor(IStoreAccessor accessor) + { + if (accessor == null) + { + ACCESSOR.remove(); + SESSION.remove(); + } + else + { + ACCESSOR.set(accessor); + SESSION.set(accessor.getSession()); + } + } + + public static IStoreAccessor getAccessor() throws NoSessionRegisteredException + { + 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; + } + + /** + * @since 4.7 + */ + public static boolean hasAccessor() + { + return ACCESSOR.get() != null; + } + + public static void setCommitContext(IStoreAccessor.CommitContext commitContext) + { + if (commitContext == null) + { + COMMIT_CONTEXT.remove(); + } + else + { + COMMIT_CONTEXT.set(commitContext); + } + } + + public static IStoreAccessor.CommitContext getCommitContext() + { + return COMMIT_CONTEXT.get(); + } + + /** + * @since 4.7 + */ + public static boolean hasCommitContext() + { + return COMMIT_CONTEXT.get() != null; + } + + /** + * @since 4.2 + */ + public static void release() + { + try + { + IStoreAccessor accessor = ACCESSOR.get(); + if (accessor != null) + { + if (LifecycleUtil.isActive(accessor)) + { + accessor.release(); + } + } + } + finally + { + remove(); + } + } + + /** + * @since 4.5 + */ + public static void remove() + { + ACCESSOR.remove(); + SESSION.remove(); + COMMIT_CONTEXT.remove(); + } + + /** + * An {@link IllegalStateException} that can be thrown from {@link StoreThreadLocal#getSession()}. + * + * @author Eike Stepper + * @since 4.2 + */ + public static final class NoSessionRegisteredException extends IllegalStateException + { + private static final long serialVersionUID = 1L; + + public NoSessionRegisteredException() + { + super("session == null"); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java new file mode 100644 index 000000000..9827a9d70 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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 {@link IStore 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. + *

+ * 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. + *

+ * 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java new file mode 100644 index 000000000..69706f2b4 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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; + +/** + * Creates {@link IMEMStore} instances. + * + * @author Eike Stepper + * @since 2.0 + */ +public final class MEMStoreUtil +{ + private MEMStoreUtil() + { + } + + /** + * Creates a {@link IMEMStore} instance. + * + * @since 4.0 + */ + public static IMEMStore createMEMStore() + { + return new MEMStore(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java new file mode 100644 index 000000000..d13ec31bd --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java new file mode 100644 index 000000000..6f34433bb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/AuthenticationUtil.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/AuthenticationUtil.java new file mode 100644 index 000000000..0bee6e31f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/AuthenticationUtil.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013-2016 Eike Stepper (Loehne, 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: + * Christian W. Damus (CEA LIST) - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import java.util.concurrent.Callable; + +/** + * Static utility methods for binding {@link IAuthenticationProtocol authentication protocols} to the current thread. + * + * @author Christian W. Damus (CEA LIST) + * @since 4.3 + */ +public final class AuthenticationUtil +{ + private static final ThreadLocal AUTHENTICATION_PROTOCOL = new ThreadLocal(); + + // Not instantiable by clients. + private AuthenticationUtil() + { + } + + /** + * Obtains the authentication protocol, if any, on which the current thread should + * authenticate administrative operations in handling incoming signals. + * + * @return the authentication protocol to use, or {@code null} if authentication is not required + */ + public static IAuthenticationProtocol getAuthenticationProtocol() + { + return AUTHENTICATION_PROTOCOL.get(); + } + + /** + * Wrap an {@code operation} to make an authentication protocol {@linkplain #getAuthenticationProtocol() available} + * to the thread that invokes it, for the duration of the {@code operation}'s execution. + */ + public static Callable authenticatingOperation(IAuthenticationProtocol authenticationProtocol, final Callable operation) + { + return new AuthenticatingOperation(authenticationProtocol) + { + @Override + protected V doCall() throws Exception + { + return operation.call(); + } + }; + } + + /** + * Encapsulation of an administrative operation requiring (potentially) client + * authentication to authorize the operation. + * + * @author Christian W. Damus (CEA LIST) + * + * @since 4.3 + */ + public static abstract class AuthenticatingOperation implements Callable + { + private final IAuthenticationProtocol authenticationProtocol; + + public AuthenticatingOperation(IAuthenticationProtocol authenticationProtocol) + { + this.authenticationProtocol = authenticationProtocol; + } + + public final V call() throws Exception + { + V result; + + try + { + AuthenticationUtil.AUTHENTICATION_PROTOCOL.set(authenticationProtocol); + result = doCall(); + } + finally + { + AuthenticationUtil.AUTHENTICATION_PROTOCOL.remove(); + } + + return result; + } + + protected abstract V doCall() throws Exception; + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/CDOCommand.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/CDOCommand.java new file mode 100644 index 000000000..f64116ca7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/CDOCommand.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2013, 2015, 2016 Eike Stepper (Loehne, 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.CDOServerApplication; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.StoreThreadLocal; + +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.factory.ProductCreationException; + +import org.eclipse.osgi.framework.console.CommandInterpreter; + +import org.osgi.framework.Bundle; + +import java.util.Dictionary; + +/** + * @author Eike Stepper + * @since 4.3 + */ +public abstract class CDOCommand extends org.eclipse.net4j.util.factory.Factory +{ + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.commands"; + + public static final String INDENT = " "; //$NON-NLS-1$ + + private static final CommandParameter[] NO_PARAMETERS = new CommandParameter[0]; + + private final String description; + + private final CommandParameter[] parameters; + + private CommandInterpreter interpreter; + + public CDOCommand(String name, String description, CommandParameter... parameters) + { + super(PRODUCT_GROUP, name); + this.description = description; + this.parameters = parameters == null ? NO_PARAMETERS : parameters; + } + + public CDOCommand(String name, String description) + { + this(name, description, NO_PARAMETERS); + } + + public final CDOCommand create(String description) throws ProductCreationException + { + return this; + } + + public final CommandInterpreter getInterpreter() + { + return interpreter; + } + + public final void setInterpreter(CommandInterpreter interpreter) + { + this.interpreter = interpreter; + } + + public final String getName() + { + return getType(); + } + + public final String getDescription() + { + return description; + } + + public final CommandParameter[] getParameters() + { + return parameters; + } + + public final String getSyntax() + { + StringBuilder builder = new StringBuilder(); + builder.append("cdo "); + builder.append(getName()); + + for (CommandParameter parameter : parameters) + { + builder.append(" "); + if (parameter.isOptional()) + { + builder.append("["); + } + + builder.append("<"); + builder.append(parameter.getName()); + builder.append(">"); + + if (parameter.isOptional()) + { + builder.append("]"); + } + } + + return builder.toString(); + } + + public final Object executeCommand(String cmd) + { + return interpreter.execute(cmd); + } + + public final void print(Object o) + { + interpreter.print(o); + } + + public final void println() + { + interpreter.println(); + } + + public final void println(Object o) + { + interpreter.println(o); + } + + public final void printStackTrace(Throwable t) + { + interpreter.printStackTrace(t); + } + + public final void printDictionary(Dictionary dic, String title) + { + interpreter.printDictionary(dic, title); + } + + public final void printBundleResource(Bundle bundle, String resource) + { + interpreter.printBundleResource(bundle, resource); + } + + public final void execute() throws Exception + { + int length = parameters.length; + String[] args = new String[length]; + for (int i = 0; i < parameters.length; i++) + { + String arg; + + CommandParameter parameter = parameters[i]; + if (parameter.isOptional()) + { + arg = nextArgumentOptional(); + } + else + { + arg = nextArgument(); + } + + args[i] = arg; + } + + execute(args); + } + + public abstract void execute(String[] args) throws Exception; + + private String nextArgument() + { + String argument = interpreter.nextArgument(); + if (argument == null && parameters != null) + { + throw new CommandException("Syntax: " + getSyntax()); + } + + return argument; + } + + private String nextArgumentOptional() + { + return interpreter.nextArgument(); + } + + public static CommandParameter[] parameters(CommandParameter parameter, CommandParameter[] parameters) + { + if (parameters == null || parameters.length == 0) + { + return new CommandParameter[] { parameter }; + } + + CommandParameter[] result = new CommandParameter[1 + parameters.length]; + result[0] = parameter; + System.arraycopy(parameters, 0, result, 1, parameters.length); + return result; + } + + public static CommandParameter parameter(String name, boolean optional) + { + return new CommandParameter(name, optional); + } + + public static CommandParameter parameter(String name) + { + return parameter(name, false); + } + + public static CommandParameter optional(String name) + { + return parameter(name, true); + } + + protected static String[] trimFirstArgument(String[] args) + { + int length = args.length - 1; + String[] result = new String[length]; + System.arraycopy(args, 1, result, 0, length); + return result; + } + + /** + * @author Eike Stepper + */ + public static abstract class WithRepository extends CDOCommand + { + public WithRepository(String name, String description, CommandParameter... parameters) + { + super(name, description, parameters(parameter("repository-name"), parameters)); + } + + public WithRepository(String name, String description) + { + this(name, description, NO_PARAMETERS); + } + + @Override + public final void execute(String[] args) throws Exception + { + String repositoryName = args[0]; + InternalRepository repository = getRepository(repositoryName); + if (repository == null) + { + throw new CommandException("Repository not found: " + repositoryName); + } + + execute(repository, trimFirstArgument(args)); + } + + public abstract void execute(InternalRepository repository, String[] args) throws Exception; + + 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 + */ + public static abstract class WithAccessor extends CDOCommand.WithRepository + { + public WithAccessor(String name, String description, CommandParameter... parameters) + { + super(name, description, parameters); + } + + public WithAccessor(String name, String description) + { + this(name, description, NO_PARAMETERS); + } + + @Override + public final void execute(InternalRepository repository, String[] args) throws Exception + { + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + try + { + execute(repository, accessor, args); + } + finally + { + StoreThreadLocal.release(); + } + } + + public abstract void execute(InternalRepository repository, IStoreAccessor accessor, String[] args) throws Exception; + } + + /** + * @author Eike Stepper + */ + public static final class CommandParameter + { + private final String name; + + private final boolean optional; + + public CommandParameter(String name, boolean optional) + { + this.name = name; + this.optional = optional; + } + + public String getName() + { + return name; + } + + public boolean isOptional() + { + return optional; + } + } + + /** + * @author Eike Stepper + */ + public static final class CommandException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + public CommandException(String message) + { + super(message); + } + } + + // /** + // * @author Eike Stepper + // */ + // public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory + // { + // public Factory(String type) + // { + // super(CDOCommand.PRODUCT_GROUP, type); + // } + // + // public abstract CDOCommand create(String description) throws ProductCreationException; + // } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java new file mode 100644 index 000000000..abfe0a349 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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; + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @since 3.0 + */ + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + return (IQueryHandler)container.getElement(QueryHandlerFactory.PRODUCT_GROUP, info.getQueryLanguage(), null); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java new file mode 100644 index 000000000..3f624ee92 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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.IRepository; +import org.eclipse.emf.cdo.server.IRepositoryProvider; + +import org.eclipse.net4j.util.container.IManagedContainer; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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) + { + return null; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DelegatingQueryResourcesContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DelegatingQueryResourcesContext.java new file mode 100644 index 000000000..eb0db848d --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DelegatingQueryResourcesContext.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 Eike Stepper (Loehne, 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.server.IStoreAccessor.QueryResourcesContext; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ +public abstract class DelegatingQueryResourcesContext implements QueryResourcesContext +{ + public CDOBranch getBranch() + { + return getDelegate().getBranch(); + } + + public long getTimeStamp() + { + return getDelegate().getTimeStamp(); + } + + public CDOID getFolderID() + { + return getDelegate().getFolderID(); + } + + public String getName() + { + return getDelegate().getName(); + } + + public boolean exactMatch() + { + return getDelegate().exactMatch(); + } + + public int getMaxResults() + { + return getDelegate().getMaxResults(); + } + + public boolean addResource(CDOID resourceID) + { + return getDelegate().addResource(resourceID); + } + + protected abstract QueryResourcesContext getDelegate(); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java new file mode 100644 index 000000000..35276b587 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011, 2012, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 locks; + + public DurableLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, Map 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 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java new file mode 100644 index 000000000..bbf98cb8f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public class FactoriesQueryHandlerProvider implements IQueryHandlerProvider +{ + private IRegistry registry; + + public FactoriesQueryHandlerProvider() + { + } + + public FactoriesQueryHandlerProvider(IRegistry registry) + { + setRegistry(registry); + } + + public FactoriesQueryHandlerProvider(IFactory factory) + { + addFactory(factory); + } + + public IRegistry getRegistry() + { + if (registry == null) + { + registry = new HashMapRegistry(); + } + + return registry; + } + + public void setRegistry(IRegistry 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java new file mode 100644 index 000000000..1612c25d7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension2.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension2.java new file mode 100644 index 000000000..912cb219a --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension2.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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: + * Christian W. Damus - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import java.io.Reader; + +/** + * An optional extension of the {@link IAppExtension} interface for app extensions that support invocation + * on the XML configurations of dynamically-managed repositories. These may be instantiated multiple + * times, will only be given repository configurations (not Net4j acceptors etc.) and are stopped if and + * when their associated repositories are deleted. + * + * @author Christian W. Damus (CEA LIST) + * @since 4.3 + */ +public interface IAppExtension2 extends IAppExtension +{ + public void startDynamic(Reader xmlConfigReader) throws Exception; +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAuthenticationProtocol.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAuthenticationProtocol.java new file mode 100644 index 000000000..7596ce7a9 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAuthenticationProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 418454: factored out authentication from ISessionProtocol + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.net4j.util.security.CredentialsUpdateOperation; +import org.eclipse.net4j.util.security.DiffieHellman.Client.Response; +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; + +/** + * @author Eike Stepper + * + * @since 4.3 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IAuthenticationProtocol +{ + /** + * Sends a challenge to the client to authenticate the user attempting to + * or already connected. + * + * @since 4.2 + */ + public Response sendAuthenticationChallenge(Challenge challenge) throws Exception; + + /** + * Sends a challenge to the client to change the authenticated user's credentials. + * This is an optional operation; implementators may simply throw + * {@link UnsupportedOperationException}. + * + * @since 4.3 + * + * @throws UnsupportedOperationException if credentials change is not supported + */ + public Response sendCredentialsChallenge(Challenge challenge, String userID, CredentialsUpdateOperation operation) throws Exception; +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java new file mode 100644 index 000000000..3fd57bb7f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009-2013, 2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399306 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.branch.InternalCDOBranch; + +import org.eclipse.net4j.util.security.DiffieHellman.Server.Challenge; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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, IAuthenticationProtocol +{ + /** + * @since 4.0 + * @deprecated As of 4.2 {@link #sendAuthenticationChallenge(Challenge)} is called. + */ + @Deprecated + public org.eclipse.emf.cdo.spi.common.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; + + /** + * @deprecated As of 4.3 use {@link #sendBranchNotification(InternalCDOBranch, ChangeKind)}. + */ + @Deprecated + public void sendBranchNotification(InternalCDOBranch branch) throws Exception; + + /** + * @since 4.3 + */ + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception; + + /** + * @deprecated As of 4.2 use {@link #sendCommitNotification(CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception; + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception; + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info) 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java new file mode 100644 index 000000000..44c00598e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.util.CDOTimeProvider; +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; +import java.util.Set; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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, CDOTimeProvider +{ + @SuppressWarnings("unchecked") + public static final ProgressDistributable[] OPS = ProgressDistributor.array( // + new ProgressDistributable.Default() + { + public void runLoop(int index, InternalCommitContext commitContext, OMMonitor monitor) throws Exception + { + commitContext.write(monitor.fork()); + } + }, // + + new ProgressDistributable.Default() + { + 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(); + + /** + * @since 4.5 + */ + public IStoreAccessor getAccessor(); + + /** + * @since 4.2 + */ + public long getTimeStamp(); + + /** + * @since 4.5 + */ + public boolean isTreeRestructuring(); + + /** + * @since 4.2 + */ + public void setLastTreeRestructuringCommit(long lastTreeRestructuringCommit); + + 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(); + + /** + * @since 4.6 + */ + public InternalCDORevision[] getDetachedRevisions(boolean check); + + /** + * @since 4.2 + */ + public void setClearResourcePathCache(boolean clearResourcePathCache); + + /** + * @since 4.2 + */ + public void setUsingEcore(boolean usingEcore); + + /** + * @since 4.2 + */ + public void setUsingEtypes(boolean usingEtypes); + + 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 detachedObjectTypes); + + /** + * @since 4.2 + */ + public void setDetachedObjectVersions(CDOBranchVersion[] detachedObjectVersions); + + /** + * @since 4.2 + */ + public void setLastUpdateTime(long lastUpdateTime); + + /** + * @deprecated As of 4.5 no longer supported. See {@link #setIDsToUnlock(CDOID[])}. + */ + @Deprecated + public void setAutoReleaseLocksEnabled(boolean on); + + /** + * @since 4.1 + */ + public void setLocksOnNewObjects(CDOLockState[] locksOnNewObjects); + + /** + * @since 4.6 + */ + public void setIDsToUnlock(CDOID[] idsToUnlock); + + /** + * @since 4.5 + */ + public void setCommitNumber(int commitNumber); + + public void setCommitComment(String comment); + + /** + * @since 4.6 + */ + public void setCommitMergeSource(CDOBranchPoint mergeSource); + + /** + * @since 4.0 + */ + public void setLobs(ExtendedDataInputStream in); + + public void addIDMapping(CDOID oldID, CDOID newID); + + public void applyIDMappings(OMMonitor monitor); + + /** + * @since 4.3 + */ + public void setSecurityImpact(byte securityImpact, Set impactedRules); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java new file mode 100644 index 000000000..d11d2f683 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2015 Eike Stepper (Loehne, 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.protocol.CDODataInput; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.concurrent.ExecutionException; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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. + * + * @deprecated As of 4.5 use {@link #preCommit(InternalCommitContext, CDODataInput, OMMonitor)}. + */ + @Deprecated + public void preCommit(InternalCommitContext commitContext, OMMonitor monitor); + + /** + * Create a future to execute commitContext in a different thread. + * @since 4.5 + */ + public void preCommit(InternalCommitContext commitContext, CDODataInput in, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java new file mode 100644 index 000000000..056d38daa --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java new file mode 100644 index 000000000..86d6e9c36 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009-2012, 2014-2016 Eike Stepper (Loehne, 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.revision.CDOIDAndBranch; +import org.eclipse.emf.cdo.server.ILockingManager; +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, ILockingManager +{ + 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 getLocks(IView view); + + /** + * @since 4.0 + */ + @Deprecated + public void lock(boolean explicit, LockType type, IView context, Collection objects, long timeout) throws InterruptedException; + + /** + * @since 4.1 + */ + public List> lock2(boolean explicit, LockType type, IView context, Collection objects, boolean recursive, + 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 objects); + + /** + * @since 4.1 + */ + public List> unlock2(boolean explicit, LockType type, IView context, Collection objects, boolean recursive); + + /** + * 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> 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 getLockState(Object key); + + /** + * @since 4.4 + */ + public List> getLockStates(); + + /** + * @since 4.1 + */ + public void setLockState(Object key, LockState lockState); + + /** + * @since 4.1 + */ + public void reloadLocks(); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java new file mode 100644 index 000000000..eef57c7ce --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java new file mode 100644 index 000000000..a562fa112 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 +{ + public int getQueryID(); + + public CDOQueryInfo getQueryInfo(); + + public InternalView getView(); + + public CDOQueryQueue getQueue(); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java new file mode 100644 index 000000000..2e3d168e1 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2009-2016, 2018 Eike Stepper (Loehne, 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.protocol.CDOProtocol.CommitNotificationInfo; +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.util.CDOTimeProvider; +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.BranchLoader3; +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.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.CDORevisionUnchunker; +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.RevisionLoader2; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.event.IEvent; +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.MergeDataResult; +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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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, BranchLoader3, RevisionLoader2, CommitInfoLoader, CDORevisionUnchunker +{ + 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 properties); + + public InternalCDOBranchManager getBranchManager(); + + public void setBranchManager(InternalCDOBranchManager branchManager); + + /** + * @since 4.6 + */ + public CDOTimeProvider getTimeProvider(); + + /** + * @since 4.6 + */ + public void setTimeProvider(CDOTimeProvider timeProvider); + + /** + * @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); + + /** + * @deprecated As of 4.1 use {@link #getLockingManager()}. + */ + @Deprecated + public InternalLockManager getLockManager(); + + /** + * @since 4.1 + */ + public InternalLockManager getLockingManager(); + + /** + * @since 4.5 + */ + public InternalUnitManager getUnitManager(); + + /** + * @since 4.5 + */ + public void setUnitManager(InternalUnitManager unitManager); + + public InternalQueryManager getQueryManager(); + + public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider); + + /** + * @since 4.3 + */ + public IManagedContainer getContainer(); + + /** + * @since 4.3 + */ + public void setContainer(IManagedContainer container); + + 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 + * long 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.5 + */ + public void executeOutsideStartCommit(Runnable runnable); + + /** + * @since 4.2 + */ + public void commit(InternalCommitContext commitContext, OMMonitor monitor); + + /** + * @since 4.0 + * @deprecated As of 4.2 use {@link #sendCommitNotification(InternalSession, CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(ISessionProtocol.CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache); + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info); + + public void setRootResourceID(CDOID rootResourceID); + + /** + * @since 4.0 + */ + public void setLastCommitTimeStamp(long commitTimeStamp); + + /** + * @since 4.1 + */ + public void ensureChunks(InternalCDORevision revision); + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, int chunkEnd); + + public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions, List 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 + * @deprecated as of 4.6 use {@link #getMergeData2(CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, CDORevisionAvailabilityInfo, OMMonitor)}. + */ + @Deprecated + public Set getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, CDORevisionAvailabilityInfo targetBaseInfo, + CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor); + + /** + * @since 4.6 + */ + public MergeDataResult getMergeData2(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor); + + /** + * @since 4.0 + */ + public void queryLobs(List 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 + * @deprecated As of 4.3 use {@link #initSystemPackages()}. + */ + @Deprecated + public void initSystemPackages(); + + /** + * @since 4.3 + */ + public void initSystemPackages(boolean firstStart); + + /** + * @since 4.0 + */ + public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp); + + /** + * @since 4.1 + */ + public LockObjectsResult lock(InternalView view, LockType type, List keys, boolean recursive, long timeout); + + /** + * @since 4.1 + */ + public UnlockObjectsResult unlock(InternalView view, LockType type, List ids, boolean recursive); + + /** + * @since 4.2 + */ + public long getOptimisticLockingTimeout(); + + /** + * @since 4.3 + */ + public void setOptimisticLockingTimeout(long optimisticLockingTimeout); + + /** + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @author Eike Stepper + * @since 4.6 + */ + public interface PackagesInitializedEvent extends IEvent + { + public InternalRepository getSource(); + + public boolean isFirstStart(); + + /** + * @since 4.7 + */ + public List getPackageUnits(); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java new file mode 100644 index 000000000..7c981f625 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java new file mode 100644 index 000000000..c70289b6e --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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.CDOBranchChangedEvent.ChangeKind; +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.protocol.CDOProtocol.CommitNotificationInfo; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.security.CDOPermissionProvider; +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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.3 + * @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, CDOPermissionProvider, CDOCommonSession.Options +{ + public static final int TEMP_VIEW_ID = 0; + + public InternalSessionManager getManager(); + + /** + * @since 4.2 + */ + public void setUserID(String userID); + + /** + * @since 4.5 + */ + public long getFirstUpdateTime(); + + /** + * @since 4.5 + */ + public void setFirstUpdateTime(long firstUpdateTime); + + /** + * @since 4.5 + */ + public boolean isOpenOnClientSide(); + + /** + * @since 4.5 + */ + public void setOpenOnClientSide(); + + 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 revisions, + List 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; + + /** + * @deprecated As of 4.3 use {@link #sendBranchNotification(InternalCDOBranch, ChangeKind)}. + */ + @Deprecated + public void sendBranchNotification(InternalCDOBranch branch) throws Exception; + + /** + * @since 4.3 + */ + public void sendBranchNotification(InternalCDOBranch branch, ChangeKind changeKind) throws Exception; + + /** + * @deprecated As of 4.2 use {@link #sendCommitNotification(CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception; + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(CDOCommitInfo commitInfo, boolean clearResourcePathCache) throws Exception; + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info) 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java new file mode 100644 index 000000000..6665b4aa7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 399306 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchChangedEvent.ChangeKind; +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.CommitNotificationInfo; +import org.eclipse.emf.cdo.server.IPermissionManager; +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.DiffieHellman; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IUserManager; + +import java.util.List; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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); + + /** + * @since 4.1 + * @deprecated As of 4.2 use {@link #getAuthenticator()} + */ + @Deprecated + public IUserManager getUserManager(); + + /** + * @deprecated As of 4.2 use {@link #setAuthenticator(IAuthenticator)} + */ + @Deprecated + public void setUserManager(IUserManager userManager); + + /** + * @since 4.2 + */ + public DiffieHellman.Server getAuthenticationServer(); + + /** + * @since 4.2 + */ + public void setAuthenticationServer(DiffieHellman.Server authenticationServer); + + /** + * Initiates the change-credentials protocol with the client and processes the + * client response to update the user's credentials. + * + * @since 4.3 + */ + public void changeUserCredentials(IAuthenticationProtocol sessionProtocol, String userID); + + /** + * Initiates the administrative reset-credentials protocol with the client and + * processes the client response to reset the specified {@code userID}'s credentials. + * + * @since 4.3 + */ + public void resetUserCredentials(IAuthenticationProtocol sessionProtocol, String userID); + + /** + * Challenges the connected user to authenticate the connection. + * + * @param sessionProtocol the authenticatable session protocol + * @return the user ID with which the user authenticated herself, or {@code null} + * if the server does not require authentication for this connection + * + * @throws SecurityException on failure to authenticate + * + * @since 4.3 + */ + public String authenticateUser(IAuthenticationProtocol sessionProtocol) throws SecurityException; + + /** + * @since 4.1 + */ + public IPermissionManager getPermissionManager(); + + /** + * @since 4.1 + */ + public void setPermissionManager(IPermissionManager permissionManager); + + public InternalSession[] getSessions(); + + public InternalSession getSession(int sessionID); + + /** + * @return Never null + */ + public InternalSession openSession(ISessionProtocol sessionProtocol); + + public void sessionClosed(InternalSession session); + + /** + * @since 4.5 + */ + public void openedOnClientSide(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); + + /** + * @deprecated As of 4.3 use {@link #sendBranchNotification(InternalSession, InternalCDOBranch, ChangeKind)}. + */ + @Deprecated + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch); + + /** + * @since 4.3 + */ + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch, ChangeKind changeKind); + + /** + * @deprecated As of 4.2 use {@link #sendCommitNotification(InternalSession, CDOCommitInfo, boolean)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + + /** + * @since 4.2 + * @deprecated As of 4.3 use {@link #sendCommitNotification(ISessionProtocol.CommitNotificationInfo)}. + */ + @Deprecated + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache); + + /** + * @since 4.3 + */ + public void sendCommitNotification(CommitNotificationInfo info); + + /** + * @since 4.1 + */ + public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo); + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode); + + public List sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, int[] recipients); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java new file mode 100644 index 000000000..85a713515 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010-2012, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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); + + /** + * @deprecated Not used anymore. + */ + @Deprecated + 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); + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + * @deprecated As of 4.6 use IRepositoryConfig.CAPABILITY_EXTERNAL_REFS. + */ + @Deprecated + public interface NoExternalReferences + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoQueryXRefs + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoLargeObjects + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoFeatureMaps + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoHandleRevisions + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ + public interface NoRawAccess + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public interface NoChangeSets + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public interface NoCommitInfos + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public interface NoDurableLocking + { + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java new file mode 100644 index 000000000..19d6b7b4c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2012, 2015, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java new file mode 100644 index 000000000..f6f0b1da2 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2009, 2011, 2012, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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(); + + /** + * @since 4.5 + */ + public CommitAttempt getLastCommitAttempt(); + + /** + * @since 4.5 + */ + public void setLastCommitAttempt(CommitAttempt lastCommitAttempt); + + /** + * @author Eike Stepper + * @since 4.5 + */ + public static final class CommitAttempt + { + private final int commitNumber; + + private final long timeStamp; + + private final long previousTimeStamp; + + public CommitAttempt(int commitNumber, long timeStamp, long previousTimeStamp) + { + this.commitNumber = commitNumber; + this.timeStamp = timeStamp; + this.previousTimeStamp = previousTimeStamp; + } + + public int getCommitNumber() + { + return commitNumber; + } + + public long getTimeStamp() + { + return timeStamp; + } + + public long getPreviousTimeStamp() + { + return previousTimeStamp; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java new file mode 100644 index 000000000..f3a9a0e2f --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.CDORevisionProvider; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import java.util.List; + +/** + * @author Eike Stepper + * @since 4.5 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. +*/ +public interface InternalUnitManager extends IUnitManager +{ + public InternalRepository getRepository(); + + public List getUnitMoves(InternalCDORevisionDelta[] deltas, CDORevisionProvider before, CDORevisionProvider after); + + public InternalObjectAttacher attachObjects(InternalCommitContext commitContext); + + /** + * @author Eike Stepper + */ + public interface InternalObjectAttacher + { + public void finishedCommit(boolean success); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java new file mode 100644 index 000000000..530afa4bb --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2012, 2016 Eike Stepper (Loehne, 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.protocol.CDOProtocolConstants.UnitOpcode; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.lifecycle.ILifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.List; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 invalidObjects, List allChangedObjects, List allDetachedObjects); + + public void subscribe(CDOID id); + + public void unsubscribe(CDOID id); + + public boolean hasSubscription(CDOID id); + + public void clearChangeSubscription(); + + public void doClose(); + + /** + * @since 4.5 + */ + public boolean openUnit(CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor); + + /** + * @since 4.5 + */ + public void closeUnit(CDOID rootID); + + /** + * @since 4.5 + */ + public boolean isInOpenUnit(CDOID id); +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java new file mode 100644 index 000000000..64f5dd462 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2009-2012, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class LongIDStore extends Store +{ + /** + * @since 3.0 + */ + public static final Set 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 supportedChangeFormats, Set supportedRevisionTemporalities, + Set 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()); + } + + @Deprecated + public boolean isLocal(CDOID id) + { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.0 + */ + public void ensureLastObjectID(CDOID id) + { + long addedID = CDOIDUtil.getLong(id); + if (addedID > getLastObjectID()) + { + setLastObjectID(addedID); + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java new file mode 100644 index 000000000..fb1e05123 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java new file mode 100644 index 000000000..edafd3bd7 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010-2013, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ +public class ObjectWriteAccessHandler implements WriteAccessHandler +{ + private IStoreAccessor.CommitContext commitContext; + + private CDOView view; + + private EObject[] newObjects; + + private EObject[] dirtyObjects; + + public ObjectWriteAccessHandler() + { + } + + /** + * @deprecated As of 4.2 the legacy mode is always enabled. + */ + @Deprecated + public ObjectWriteAccessHandler(boolean legacyModeEnabled) + { + } + + /** + * @deprecated As of 4.2 the legacy mode is always enabled. + */ + @Deprecated + public final boolean isLegacyModeEnabled() + { + return true; + } + + 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); + } + + 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java new file mode 100644 index 000000000..39ee87448 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java new file mode 100644 index 000000000..43a069756 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryActivityLog.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryActivityLog.java new file mode 100644 index 000000000..315c80b56 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryActivityLog.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2004-2018 Eike Stepper (Loehne, 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.IRepository.WriteAccessHandler; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.StringUtil; +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.factory.ProductCreationException; +import org.eclipse.net4j.util.lifecycle.LifecycleHook; +import org.eclipse.net4j.util.om.log.Log; +import org.eclipse.net4j.util.om.log.RollingLog; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + * @since 4.7 + */ +public abstract class RepositoryActivityLog extends LifecycleHook implements Log +{ + private final IListener sessionManagerListener = new ContainerEventAdapter() + { + @Override + protected void onAdded(IContainer container, ISession session) + { + sessionOpened(session, concurrentSessions.incrementAndGet(), sessions.incrementAndGet()); + session.addListener(sessionListener); + } + + @Override + protected void onRemoved(IContainer container, ISession session) + { + session.removeListener(sessionListener); + sessionClosed(session, concurrentSessions.decrementAndGet()); + } + }; + + private final IListener sessionListener = new ContainerEventAdapter() + { + @Override + protected void onAdded(IContainer container, IView view) + { + if (view instanceof ITransaction) + { + transactionOpened((ITransaction)view, concurrentTransactions.incrementAndGet(), transactions.incrementAndGet()); + } + else + { + viewOpened(view, concurrentViews.incrementAndGet(), views.incrementAndGet()); + } + } + + @Override + protected void onRemoved(IContainer container, IView view) + { + if (view instanceof ITransaction) + { + transactionClosed((ITransaction)view, concurrentTransactions.decrementAndGet()); + } + else + { + viewClosed(view, concurrentViews.decrementAndGet()); + } + } + }; + + private final WriteAccessHandler writeAccessHandler = new WriteAccessHandler() + { + public void handleTransactionBeforeCommitting(ITransaction transaction, CommitContext commitContext, OMMonitor monitor) throws RuntimeException + { + commitStarted(commitContext, concurrentCommits.incrementAndGet(), commits.incrementAndGet()); + } + + public void handleTransactionAfterCommitted(ITransaction transaction, CommitContext commitContext, OMMonitor monitor) + { + commitFinished(commitContext, concurrentCommits.decrementAndGet()); + } + }; + + private final AtomicInteger sessions = new AtomicInteger(); + + private final AtomicInteger views = new AtomicInteger(); + + private final AtomicInteger transactions = new AtomicInteger(); + + private final AtomicInteger commits = new AtomicInteger(); + + private final AtomicInteger concurrentSessions = new AtomicInteger(); + + private final AtomicInteger concurrentViews = new AtomicInteger(); + + private final AtomicInteger concurrentTransactions = new AtomicInteger(); + + private final AtomicInteger concurrentCommits = new AtomicInteger(); + + public RepositoryActivityLog() + { + } + + public IRepository getRepository() + { + return getDelegate(); + } + + public void setRepository(IRepository repository) + { + setDelegate(repository); + } + + protected void sessionOpened(ISession session, int concurrentSessions, int sessions) + { + log(formatSession(session) + " opened" + formatUser(session) + " (" + concurrentSessions + "/" + sessions + ")"); + } + + protected void sessionClosed(ISession session, int concurrentSessions) + { + log(formatSession(session) + " closed" + formatUser(session) + " (" + concurrentSessions + ")"); + } + + protected void viewOpened(IView view, int concurrentViews, int views) + { + log(formatView(view) + " opened" + formatUser(view.getSession()) + " (" + concurrentViews + "/" + views + ")"); + } + + protected void viewClosed(IView view, int concurrentViews) + { + log(formatView(view) + " closed" + formatUser(view.getSession()) + " (" + concurrentViews + ")"); + } + + protected void transactionOpened(ITransaction transaction, int concurrentTransactions, int transactions) + { + log(formatView(transaction) + " opened" + formatUser(transaction.getSession()) + " (" + concurrentTransactions + "/" + transactions + ")"); + } + + protected void transactionClosed(ITransaction transaction, int concurrentTransactions) + { + log(formatView(transaction) + " closed" + formatUser(transaction.getSession()) + " (" + concurrentTransactions + ")"); + } + + protected void commitStarted(CommitContext commitContext, int concurrentCommits, int commits) + { + ITransaction transaction = commitContext.getTransaction(); + log(formatView(transaction) + " committing " + commitContext.getBranchPoint().getTimeStamp() + formatUser(transaction.getSession()) + " (" + + concurrentCommits + "/" + commits + ")"); + } + + protected void commitFinished(CommitContext commitContext, int concurrentCommits) + { + ITransaction transaction = commitContext.getTransaction(); + log(formatView(transaction) + (commitContext.getRollbackMessage() != null ? " committed " : " rolled back ") + commitContext.getBranchPoint().getTimeStamp() + + formatUser(transaction.getSession()) + " (" + concurrentCommits + ")"); + } + + protected String formatSession(ISession session) + { + return "Session " + session.getSessionID(); + } + + protected String formatUser(ISession session) + { + String userID = session.getUserID(); + return StringUtil.isEmpty(userID) ? "" : " by user " + userID; + } + + protected String formatView(IView view) + { + return (view instanceof ITransaction ? "Transaction " : "View ") + view.getSessionID() + ":" + view.getViewID(); + } + + @Override + protected void hookDelegate(IRepository repository) + { + repository.getSessionManager().addListener(sessionManagerListener); + repository.addHandler(writeAccessHandler); + } + + @Override + protected void unhookDelegate(IRepository repository) + { + repository.removeHandler(writeAccessHandler); + repository.getSessionManager().removeListener(sessionManagerListener); + } + + /** + * @author Eike Stepper + * @since 4.7 + */ + public static abstract class Factory extends org.eclipse.net4j.util.factory.PropertiesFactory + { + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.repositoryActivityLogs"; //$NON-NLS-1$ + + public Factory(String type) + { + super(PRODUCT_GROUP, type); + } + + @Override + protected abstract RepositoryActivityLog create(Map properties) throws ProductCreationException; + } + + /** + * @author Eike Stepper + * @since 4.7 + */ + public static class Rolling extends RepositoryActivityLog + { + private final RollingLog rollingLog; + + public Rolling(String logFile, long logSize, boolean append) + { + rollingLog = new RollingLog(logFile, logSize, append); + } + + public void log(String message) + { + rollingLog.log(message); + } + + @Override + protected void delegateChanged(IRepository oldRepository, IRepository newRepository) + { + if (newRepository != null) + { + OM.LOG.info("Logging activities of repository " + newRepository.getName() + " to " + rollingLog.getLogFile()); + } + + } + + @Override + protected void doActivate() throws Exception + { + rollingLog.activate(); + super.doActivate(); + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + rollingLog.deactivate(); + } + + /** + * @author Eike Stepper + */ + public static final class Factory extends RepositoryActivityLog.Factory + { + public static final String TYPE = "rolling"; //$NON-NLS-1$ + + public Factory() + { + super(TYPE); + } + + @Override + protected RepositoryActivityLog create(Map properties) throws ProductCreationException + { + String file = properties.get("file"); //$NON-NLS-1$ + if (file == null) + { + file = "activities"; + } + + String size = properties.get("size"); //$NON-NLS-1$ + if (StringUtil.isEmpty(size)) + { + size = "100000000"; + } + + String append = properties.get("append"); //$NON-NLS-1$ + if (StringUtil.isEmpty(append)) + { + append = Boolean.TRUE.toString(); + } + + return new RepositoryActivityLog.Rolling(file, Long.parseLong(size), Boolean.parseBoolean(append)); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java new file mode 100644 index 000000000..381f2bea0 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2011-2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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 + * Christian W. Damus (CEA LIST) - bug 418454 + */ +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.container.IManagedContainer.ContainerAware; +import org.eclipse.net4j.util.factory.ProductCreationException; +import org.eclipse.net4j.util.factory.PropertiesFactory; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.net4j.util.security.AuthenticatorFactory; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.UserManagerFactory; + +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.InputSource; +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.io.Reader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 repositoryFactories = new HashMap(); + + private Map storeFactories = new HashMap(); + + public RepositoryConfigurator() + { + this(null); + } + + public RepositoryConfigurator(IManagedContainer container) + { + this.container = container; + } + + public IManagedContainer getContainer() + { + return container; + } + + public Map getRepositoryFactories() + { + return repositoryFactories; + } + + public Map 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$ + } + + return configure(getDocument(configFile)); + } + + /** + * @since 4.3 + */ + public IRepository[] configure(Reader configReader) throws ParserConfigurationException, SAXException, IOException, CoreException + { + if (TRACER.isEnabled()) + { + TRACER.trace("Configuring CDO server from dynamic configuration"); //$NON-NLS-1$ + } + + return configure(getDocument(configReader)); + } + + /** + * @since 4.3 + */ + protected IRepository[] configure(Document document) throws ParserConfigurationException, SAXException, IOException, CoreException + { + List repositories = new ArrayList(); + 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); + OM.LOG.info("CDO repository " + repository.getName() + " started"); + } + else + { + OM.LOG.info("CDO repository " + repository.getName() + " added"); + } + } + + 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); + } + + /** + * @since 4.3 + */ + protected Document getDocument(Reader configReader) throws ParserConfigurationException, SAXException, IOException + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(new InputSource(configReader)); + } + + 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 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); + + setUserManager(repository, repositoryConfig); + setAuthenticator(repository, repositoryConfig); + setActivityLog(repository, repositoryConfig); + + 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(UserManagerFactory.PRODUCT_GROUP, type, description); + if (userManager == null) + { + throw new IllegalStateException("UserManager factory not found: " + type); //$NON-NLS-1$ + } + + return userManager; + } + + /** + * @since 4.2 + */ + @SuppressWarnings("deprecation") + protected void setUserManager(InternalRepository repository, Element repositoryConfig) throws CoreException + { + 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); + } + } + } + + /** + * @since 4.2 + */ + protected Element getAuthenticatorConfig(Element repositoryConfig) + { + NodeList authenticatorConfig = repositoryConfig.getElementsByTagName("authenticator"); //$NON-NLS-1$ + if (authenticatorConfig.getLength() > 1) + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + throw new IllegalStateException("At most one authenticator must be configured for repository " + repositoryName); //$NON-NLS-1$ + } + + return (Element)(authenticatorConfig.getLength() > 0 ? authenticatorConfig.item(0) : null); + } + + /** + * @since 4.2 + */ + protected IAuthenticator getAuthenticator(Element authenticatorConfig) throws CoreException + { + String type = authenticatorConfig.getAttribute("type"); //$NON-NLS-1$ + String description = authenticatorConfig.getAttribute("description"); //$NON-NLS-1$ + return getAuthenticator(type, description); + } + + /** + * @since 4.2 + */ + protected IAuthenticator getAuthenticator(String type, String description) throws CoreException + { + IAuthenticator authenticator = (IAuthenticator)container.getElement(AuthenticatorFactory.PRODUCT_GROUP, type, description); + if (authenticator == null) + { + throw new IllegalStateException("Authenticator factory not found: " + type); //$NON-NLS-1$ + } + + return authenticator; + } + + /** + * @since 4.2 + */ + protected void setAuthenticator(InternalRepository repository, Element repositoryConfig) throws CoreException + { + Element authenticatorConfig = getAuthenticatorConfig(repositoryConfig); + if (authenticatorConfig != null) + { + IAuthenticator authenticator = getAuthenticator(authenticatorConfig); + if (authenticator != null) + { + InternalSessionManager sessionManager = repository.getSessionManager(); + if (sessionManager == null) + { + sessionManager = new SessionManager(); + repository.setSessionManager(sessionManager); + } + + sessionManager.setAuthenticator(authenticator); + } + } + } + + /** + * @since 4.7 + */ + protected void setActivityLog(InternalRepository repository, Element repositoryConfig) + { + NodeList activityLogConfig = repositoryConfig.getElementsByTagName("activityLog"); //$NON-NLS-1$ + if (activityLogConfig.getLength() > 1) + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + throw new IllegalStateException("At most one activity log must be configured for repository " + repositoryName); //$NON-NLS-1$ + } + + if (activityLogConfig.getLength() > 0) + { + Element activityLogElement = (Element)activityLogConfig.item(0); + + RepositoryActivityLog activityLog = getContainerElement(activityLogElement, RepositoryActivityLog.Rolling.Factory.TYPE); + activityLog.setRepository(repository); + } + } + + protected EPackage[] getInitialPackages(Element repositoryConfig) + { + List result = new ArrayList(); + + 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 repositoryProperties, Element storeConfig) throws CoreException + { + String type = storeConfig.getAttribute("type"); //$NON-NLS-1$ + IStoreFactory storeFactory = getStoreFactory(type); + return storeFactory.createStore(repositoryName, repositoryProperties, storeConfig); + } + + /** + * @since 4.7 + */ + protected T getContainerElement(Element element, String defaultType) + { + String type = element.getAttribute("type"); //$NON-NLS-1$ + if (StringUtil.isEmpty(type)) + { + type = defaultType; + } + + String description = element.getAttribute("description"); //$NON-NLS-1$ + if (StringUtil.isEmpty(description)) + { + Map properties = getProperties(element, 1); + description = PropertiesFactory.createDescription(properties); + } + + @SuppressWarnings("unchecked") + T containerElement = (T)container.getElement(RepositoryActivityLog.Factory.PRODUCT_GROUP, type, description); + + return containerElement; + } + + public static Map getProperties(Element element, int levels) + { + Map properties = new HashMap(); + collectProperties(element, "", properties, levels); //$NON-NLS-1$ + return properties; + } + + private static void collectProperties(Element element, String prefix, Map 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 createExecutableExtension(String extPointName, String elementName, String attributeName, String type) throws CoreException + { + if (OMPlatform.INSTANCE.isExtensionRegistryAvailable()) + { + 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; + } + + /** + * @author Eike Stepper + * @since 4.7 + */ + public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory implements ContainerAware + { + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.repositoryConfigurators"; //$NON-NLS-1$ + + private IManagedContainer container; + + public Factory(String type) + { + super(PRODUCT_GROUP, type); + } + + public void setManagedContainer(IManagedContainer container) + { + this.container = container; + } + + public final RepositoryConfigurator create(String description) throws ProductCreationException + { + return create(container, description); + } + + public abstract RepositoryConfigurator create(IManagedContainer container, String description) throws ProductCreationException; + + /** + * @author Eike Stepper + */ + public static final class Default extends Factory + { + public static final String TYPE = "default"; //$NON-NLS-1$ + + public Default() + { + super(TYPE); + } + + @Override + public RepositoryConfigurator create(IManagedContainer container, String description) throws ProductCreationException + { + return new RepositoryConfigurator(container); + } + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java new file mode 100644 index 000000000..33e690048 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java new file mode 100644 index 000000000..eaa5d7baf --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2011, 2012, 2016 Eike Stepper (Loehne, 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.AuthenticatorFactory; +import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.SecurityUtil; +import org.eclipse.net4j.util.security.UserManagerFactory; + +import java.util.Arrays; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.0 + */ +public abstract class RepositoryUserManager extends Lifecycle implements IUserManager, IAuthenticator +{ + 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 + { + try + { + char[] password = getPassword(userID); + return SecurityUtil.encrypt(data, password, algorithmName, salt, count); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new SecurityException(ex); + } + } + + /** + * @since 4.2 + */ + public void authenticate(String userID, char[] password) throws SecurityException + { + char[] userPassword = getPassword(userID); + if (!Arrays.equals(password, userPassword)) + { + throw new SecurityException("Access denied"); //$NON-NLS-1$ + } + } + + protected IRepository getRepository(IManagedContainer container, String repositoryName) + { + return CDOServerUtil.getRepository(container, repositoryName); + } + + /** + * @since 4.2 + */ + protected char[] getPassword(String userID) + { + 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$ + } + return password; + } + + protected abstract char[] getPassword(IRepository repository, String userID); + + public static void prepareContainer(IManagedContainer container, RepositoryUserManagerFactory factory) + { + container.registerFactory(factory); + container.addPostProcessor(new RepositoryInjector()); + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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; + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 4.2 + */ + public static abstract class RepositoryAuthenticatorFactory extends AuthenticatorFactory + { + protected RepositoryAuthenticatorFactory(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; + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java new file mode 100644 index 000000000..2319ce4f1 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2009-2012, 2016 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 objectIDTypes; + + private Set supportedChangeFormats; + + private Set supportedRevisionTemporalities; + + private Set 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 objectIDTypes, Set supportedChangeFormats, + Set supportedRevisionTemporalities, Set 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; + } + + /** + * @since 4.2 + */ + public Store(String type) + { + this.type = type; + } + + public final String getType() + { + return type; + } + + /** + * @since 3.0 + */ + public Set getObjectIDTypes() + { + return objectIDTypes; + } + + /** + * @since 4.0 + */ + protected void setObjectIDTypes(Set objectIDTypes) + { + this.objectIDTypes = objectIDTypes; + } + + public Set getSupportedChangeFormats() + { + return supportedChangeFormats; + } + + public Set getSupportedRevisionTemporalities() + { + return supportedRevisionTemporalities; + } + + public final Set 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); + + if (id != null) + { + 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 null 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. + *

+ * 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). Note: Closing a session implies 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 null. + * @param forReleasing + * Enables lazy pool creation. The implementor is not supposed to create a new pool if false is + * passed. If true 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 null 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. + *

+ * 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). Note: Closing a session implies 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 null. + * @param forReleasing + * Enables lazy pool creation. The implementor is not supposed to create a new pool if false is + * passed. If true 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 new {@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 new {@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 Set set(T... elements) + { + return Collections.unmodifiableSet(new HashSet(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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java new file mode 100644 index 000000000..4caec9134 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2009-2013, 2015, 2016 Eike Stepper (Loehne, 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.branch.CDOBranchPoint; +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 org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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(); + CDOBranchPoint mergeSource = context.getCommitMergeSource(); + + Store store = getStore(); + boolean deltas = store.getSupportedChangeFormats().contains(IStore.ChangeFormat.DELTA); + + InternalCDOPackageUnit[] newPackageUnits = context.getNewPackageUnits(); + InternalCDORevision[] newObjects = context.getNewObjects(); + CDOID[] detachedObjects = context.getDetachedObjects(); + InternalCDORevisionDelta[] dirtyObjectDeltas = context.getDirtyObjectDeltas(); + InternalCDORevision[] dirtyObjects = context.getDirtyObjects(); + + int dirtyCount = deltas ? dirtyObjectDeltas.length : dirtyObjects.length; + int postProcessCount = needsRevisionPostProcessing() ? newObjects.length + detachedObjects.length + dirtyCount : 0; + + try + { + monitor.begin(1 + newPackageUnits.length + 2 + newObjects.length + detachedObjects.length + dirtyCount + postProcessCount + 1); + + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, mergeSource, monitor.fork()); + writePackageUnits(newPackageUnits, monitor.fork(newPackageUnits.length)); + + IDGenerationLocation idGenerationLocation = store.getRepository().getIDGenerationLocation(); + if (idGenerationLocation == 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) + { + writeNewObjectRevisions(context, newObjects, branch, monitor.fork(newObjects.length)); + } + + if (dirtyCount != 0) + { + if (deltas) + { + writeRevisionDeltas(dirtyObjectDeltas, branch, timeStamp, monitor.fork(dirtyCount)); + } + else + { + writeDirtyObjectRevisions(context, dirtyObjects, branch, monitor.fork(dirtyCount)); + } + } + + if (needsRevisionPostProcessing()) + { + postProcessRevisions(context, monitor.fork(postProcessCount)); + } + + ExtendedDataInputStream in = context.getLobs(); + if (in != null) + { + Async async = monitor.forkAsync(); + + 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 + { + async.stop(); + } + } + else + { + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + /** + * @since 4.6 + */ + protected boolean needsRevisionPostProcessing() + { + return false; + } + + /** + * @since 4.6 + */ + protected void postProcessRevisions(InternalCommitContext context, OMMonitor monitor) + { + } + + /** + * @since 3.0 + */ + protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor) + { + context.applyIDMappings(monitor.fork()); + } + + /** + * @since 4.0 + * @deprecated As of 4.6 override {@link #writeCommitInfo(CDOBranch, long, long, String, String, CDOBranchPoint, OMMonitor)}. + */ + @Deprecated + protected abstract void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, OMMonitor monitor); + + /** + * @since 4.6 + */ + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, CDOBranchPoint mergeSource, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + /** + * @since 4.5 + */ + protected void writeNewObjectRevisions(InternalCommitContext context, InternalCDORevision[] newObjects, CDOBranch branch, OMMonitor monitor) + { + writeRevisions(newObjects, branch, monitor); + } + + /** + * @since 4.5 + */ + protected void writeDirtyObjectRevisions(InternalCommitContext context, InternalCDORevision[] dirtyObjects, CDOBranch branch, OMMonitor monitor) + { + writeRevisions(dirtyObjects, branch, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java new file mode 100644 index 000000000..708f80e21 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2011-2013, 2015, 2016 Eike Stepper (Loehne, 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.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.common.revision.CDOIDAndVersionImpl; +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.commit.CDOCommitInfoUtil; +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.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.util.ArrayList; +import java.util.List; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @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 commitContexts = new ArrayList(); + + 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; + } + } + } + + store.setLastCommitTime(latest); + store.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 + { + } + + /** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 3.0 + */ + public static class CommitDataRevisionHandler implements CDORevisionHandler + { + private IStoreAccessor storeAccessor; + + private long timeStamp; + + private InternalCDORevisionManager revisionManager; + + private List newPackageUnits = new ArrayList(); + + private List newObjects = new ArrayList(); + + private List changedObjects = new ArrayList(); + + private List detachedObjects = new ArrayList(); + + 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, this); + return CDOCommitInfoUtil.createCommitData(newPackageUnits, newObjects, changedObjects, detachedObjects); + } + + /** + * @since 4.0 + */ + public boolean handleRevision(CDORevision rev) + { + if (rev instanceof DetachedCDORevision) + { + CDOIDAndVersion copy = new CDOIDAndVersionImpl(rev.getID(), rev.getVersion()); + detachedObjects.add(copy); + return true; + } + + if (rev.getTimeStamp() != timeStamp) + { + throw new IllegalArgumentException("Invalid revision time stamp: " + CDOCommonUtil.formatTimeStamp(rev.getTimeStamp())); + } + + 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); + } + 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; + } + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java new file mode 100644 index 000000000..d9f7a4748 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2009-2012 Eike Stepper (Loehne, 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.LinkedList; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public class StoreAccessorPool +{ + /** + * @since 4.2 + */ + public static final int DEFAULT_CAPACITY = 15; + + /** + * 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 null if + * this pool is not contextual. + */ + private Object context; + + private int capacity = DEFAULT_CAPACITY; + + private LinkedList accessors = new LinkedList(); + + public StoreAccessorPool(IStore store, Object context) + { + this.store = store; + this.context = context; + } + + public IStore getStore() + { + return store; + } + + public Object getContext() + { + return context; + } + + /** + * @since 4.2 + */ + public int getCapacity() + { + return capacity; + } + + /** + * @since 4.2 + */ + public void setCapacity(int capacity) + { + this.capacity = capacity; + retainStoreAccessors(capacity); + } + + /** + * Passivates the given {@link StoreAccessor store accessor} and adds it to this pool if the pool size is smaller than the {@link #getCapacity() capacity}, + * or disposes of the store accessor otherwise. + * + * @since 4.0 + */ + public void addStoreAccessor(StoreAccessorBase storeAccessor) + { + try + { + storeAccessor.doPassivate(); + + boolean full = false; + synchronized (accessors) + { + if (accessors.size() >= capacity) + { + full = true; + } + else + { + accessors.addFirst(storeAccessor); + } + } + + if (full) + { + disposeStoreAccessor(storeAccessor); + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + + /** + * Returns a {@link StoreAccessor store accessor} from this pool if one is available, or null 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; + synchronized (accessors) + { + 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() + { + retainStoreAccessors(0); + + context = null; + store = null; + } + + /** + * @since 4.2 + */ + protected void retainStoreAccessors(int targetSize) + { + synchronized (accessors) + { + while (accessors.size() > targetSize) + { + StoreAccessorBase accessor = accessors.removeLast(); + disposeStoreAccessor(accessor); + } + } + } + + /** + * @since 4.2 + */ + protected void disposeStoreAccessor(StoreAccessorBase accessor) + { + LifecycleUtil.deactivate(accessor, OMLogger.Level.WARN); + } +} diff --git a/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java new file mode 100644 index 000000000..acdc8a4bc --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2009, 2011, 2012 Eike Stepper (Loehne, 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; + +/** + * If the meaning of this type isn't clear, there really should be more of a description here... + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class StoreChunkReader implements IStoreChunkReader +{ + private IStoreAccessor accessor; + + private CDORevision revision; + + private EStructuralFeature feature; + + private List chunks = new ArrayList(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 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java new file mode 100644 index 000000000..f775e7a9c --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011, 2012, 2015, 2016 Eike Stepper (Loehne, 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/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java new file mode 100644 index 000000000..2944b6603 --- /dev/null +++ b/bundles/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2011, 2012, 2015 Eike Stepper (Loehne, 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; diff --git a/bundles/org.eclipse.net4j.db.oracle/.classpath b/bundles/org.eclipse.net4j.db.oracle/.classpath new file mode 100644 index 000000000..2e55b8d6a --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.eclipse.net4j.db.oracle/.gitignore b/bundles/org.eclipse.net4j.db.oracle/.gitignore new file mode 100644 index 000000000..00f4ffad8 --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/generated/ +/jar/ \ No newline at end of file diff --git a/bundles/org.eclipse.net4j.db.oracle/.project b/bundles/org.eclipse.net4j.db.oracle/.project new file mode 100644 index 000000000..87400913d --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/.project @@ -0,0 +1,23 @@ + + + org.eclipse.net4j.db.oracle + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/bundles/org.eclipse.net4j.db.oracle/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.net4j.db.oracle/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..2c715ef3b --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/bundles/org.eclipse.net4j.db.oracle/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.net4j.db.oracle/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..bb35fa0a8 --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.eclipse.net4j.db.oracle/bnd.bnd b/bundles/org.eclipse.net4j.db.oracle/bnd.bnd new file mode 100644 index 000000000..9a939cb71 --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/bnd.bnd @@ -0,0 +1,22 @@ +Bundle-Version: 1.1.300.v20161018-1819 +-buildpath: \ + jar/oracle-driver-ojdbc-12.1.0.2.jar;version=file,\ + org.eclipse.net4j.util,\ + osgi.core,\ + org.eclipse.net4j.db;version=latest +Private-Package: \ + oracle.*,\ + org.eclipse.net4j.db.oracle.internal.bundle +Export-Package: org.eclipse.net4j.db.oracle +Import-Package: \ + com.oracle.common.internal.net.ipclw.mql.*;resolution:=optional, \ + com.oracle.common.io.*;resolution:=optional, \ + com.oracle.common.net.*;resolution:=optional, \ + com.sun.security.auth.module*;resolution:=optional, \ + oracle.i18n.text.*;resolution:=optional, \ + oracle.ons.*;resolution:=optional, \ + oracle.security.pki.*;resolution:=optional, \ + javax.resource*;resolution:=optional, \ + sun.*;resolution:=optional, \ + oracle.*;resolution:=optional,\ + *;resolution:=optional diff --git a/bundles/org.eclipse.net4j.db.oracle/src/org/eclipse/net4j/db/oracle/OracleAdapter.java b/bundles/org.eclipse.net4j.db.oracle/src/org/eclipse/net4j/db/oracle/OracleAdapter.java new file mode 100644 index 000000000..5083a7ae8 --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/src/org/eclipse/net4j/db/oracle/OracleAdapter.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2012, 2013, 2015, 2016 Eike Stepper (Loehne, 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 + * Erdal Karaca - copied from mysql impl to adapt to oracle db + */ +package org.eclipse.net4j.db.oracle; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.spi.db.DBAdapter; +import org.eclipse.net4j.util.security.IUserAware; + +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import oracle.jdbc.pool.OracleDataSource; + +public class OracleAdapter extends DBAdapter +{ + public static final String NAME = "oracle"; //$NON-NLS-1$ + + public static final String VERSION = "11.1.0.7"; //$NON-NLS-1$ + + public OracleAdapter() + { + super(NAME, VERSION); + } + + @Override + public IDBConnectionProvider createConnectionProvider(DataSource dataSource) + { + if (dataSource instanceof OracleDataSource) + { + return DBUtil.createConnectionProvider(dataSource, ((OracleDataSource)dataSource).getUser()); + } + + return super.createConnectionProvider(dataSource); + } + + public String[] getReservedWords() + { + List list = new ArrayList(Arrays.asList(getSQL92ReservedWords())); + list.add("INDEX"); + list.add("COMMENT"); + list.add("ACCESS"); + return list.toArray(new String[list.size()]); + } + + @Override + public boolean isTypeIndexable(DBType type) + { + switch (type) + { + case VARCHAR: + return false; + + default: + return super.isTypeIndexable(type); + } + } + + @Override + protected String getTypeName(IDBField field) + { + DBType type = field.getType(); + switch (type) + { + case NUMERIC: + case DECIMAL: + case FLOAT: + case REAL: + case DOUBLE: + case BIGINT: + return "NUMBER"; + case TINYINT: + return "NUMBER(5)"; + case SMALLINT: + case BOOLEAN: + case BIT: + return "NUMBER(7)"; + case INTEGER: + return "NUMBER(12)"; + case DATE: + case TIME: + return "DATE"; + case CHAR: + case VARCHAR: + return "VARCHAR2(" + field.getPrecision() + " CHAR)"; + case LONGVARCHAR: + return "LONG"; + case BINARY: + case VARBINARY: + case LONGVARBINARY: + return "LONG RAW"; + default: + return super.getTypeName(field); + } + } + + @Override + public int getFieldLength(DBType type) + { + if (type == DBType.VARCHAR) + { + return 4000; // Oracle only supports 4000 for VARCHAR + } + + return super.getFieldLength(type); + } + + @Override + public DBType adaptType(DBType type) + { + if (type == DBType.BOOLEAN) + { + return DBType.SMALLINT; + } + + return super.adaptType(type); + } + + @Override + public int getMaxTableNameLength() + { + return 30; + } + + @Override + public int getMaxFieldNameLength() + { + return 30; + } + + @Override + public boolean isTableNotFoundException(SQLException ex) + { + String message = ex.getMessage(); + return message != null && message.toLowerCase().contains("ora-00942") && "42000".equals(ex.getSQLState()); + } + + @Override + public boolean isColumnNotFoundException(SQLException ex) + { + String message = ex.getMessage(); + return message != null && message.toLowerCase().contains("ora-00904") && "42000".equals(ex.getSQLState()); + } + + @Override + public boolean isDuplicateKeyException(SQLException ex) + { + String message = ex.getMessage(); + return message != null && message.toLowerCase().contains("ora-00001") && "23000".equals(ex.getSQLState()); + } + + @Override + protected String sqlModifyField(String tableName, String fieldName, String definition) + { + return "ALTER TABLE " + tableName + " MODIFY " + fieldName + " " + definition; + } + + @Override + protected ResultSet readTables(Connection connection, DatabaseMetaData metaData, String schemaName) throws SQLException + { + if (schemaName == null && connection instanceof IUserAware) + { + schemaName = ((IUserAware)connection).getUserID(); + } + + return metaData.getTables(null, schemaName, null, new String[] { "TABLE" }); + } + + @Override + public String convertString(PreparedStatement preparedStatement, int parameterIndex, String value) + { + if (value != null && value.length() == 0) + { + String replacement = getEmptyStringReplacement(); + if (replacement != null) + { + value = replacement; + } + } + + return super.convertString(preparedStatement, parameterIndex, value); + } + + @Override + public String convertString(ResultSet resultSet, int columnIndex, String value) + { + value = super.convertString(resultSet, columnIndex, value); + + if (value != null) + { + String replacement = getEmptyStringReplacement(); + if (replacement != null && replacement.equals(value)) + { + value = ""; + } + } + + return value; + } + + @Override + public String convertString(ResultSet resultSet, String columnLabel, String value) + { + value = super.convertString(resultSet, columnLabel, value); + + if (value != null) + { + String replacement = getEmptyStringReplacement(); + if (replacement != null && replacement.equals(value)) + { + value = null; + } + } + + return value; + } + + /** + * @since 1.1 + */ + protected String getEmptyStringReplacement() + { + return ""; + } +} diff --git a/bundles/org.eclipse.net4j.db.oracle/src/org/eclipse/net4j/db/oracle/internal/bundle/OM.java b/bundles/org.eclipse.net4j.db.oracle/src/org/eclipse/net4j/db/oracle/internal/bundle/OM.java new file mode 100644 index 000000000..cede91f9e --- /dev/null +++ b/bundles/org.eclipse.net4j.db.oracle/src/org/eclipse/net4j/db/oracle/internal/bundle/OM.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 Eike Stepper (Loehne, 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.net4j.db.oracle.internal.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 Operations & Maintenance class of this bundle. + * + * @author Eike Stepper + */ +public abstract class OM +{ + public static final String BUNDLE_ID = "org.eclipse.net4j.db.oracle"; //$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_SQL = DEBUG.tracer("sql"); //$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/bundles/org.eclipse.net4j.db/.classpath b/bundles/org.eclipse.net4j.db/.classpath new file mode 100644 index 000000000..b263de401 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.eclipse.net4j.db/.gitignore b/bundles/org.eclipse.net4j.db/.gitignore new file mode 100644 index 000000000..57b341172 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/generated/ diff --git a/bundles/org.eclipse.net4j.db/.project b/bundles/org.eclipse.net4j.db/.project new file mode 100644 index 000000000..bcf3fb34f --- /dev/null +++ b/bundles/org.eclipse.net4j.db/.project @@ -0,0 +1,23 @@ + + + org.eclipse.net4j.db + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/bundles/org.eclipse.net4j.db/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.net4j.db/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..2c715ef3b --- /dev/null +++ b/bundles/org.eclipse.net4j.db/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/bundles/org.eclipse.net4j.db/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.net4j.db/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..bb35fa0a8 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.eclipse.net4j.db/bnd.bnd b/bundles/org.eclipse.net4j.db/bnd.bnd new file mode 100644 index 000000000..a9317099f --- /dev/null +++ b/bundles/org.eclipse.net4j.db/bnd.bnd @@ -0,0 +1,23 @@ +Require-Bundle: org.eclipse.net4j.util;visibility:=reexport + +Bundle-Version: 4.6.0 +-buildpath: \ + org.eclipse.net4j.util,\ + org.eclipse.equinox.common,\ + osgi.core,\ + org.eclipse.net4j.db.jdbc,\ + org.eclipse.core.runtime,\ + org.eclipse.equinox.registry +Bundle-Name: org.eclipse.net4j.db +Export-Package: \ + org.eclipse.net4j.db,\ + org.eclipse.net4j.internal.db,\ + org.eclipse.net4j.internal.db.bundle,\ + org.eclipse.net4j.internal.db.ddl,\ + org.eclipse.net4j.internal.db.ddl.delta,\ + org.eclipse.net4j.internal.db.dml,\ + org.eclipse.net4j.spi.db,\ + org.eclipse.net4j.spi.db.ddl,\ + org.eclipse.net4j.db.ddl,\ + org.eclipse.net4j.db.ddl.delta,\ + org.eclipse.net4j.db.dml \ No newline at end of file diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/BatchedStatement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/BatchedStatement.java new file mode 100644 index 000000000..b099e730a --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/BatchedStatement.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.db; + +import java.sql.PreparedStatement; + +/** + * @since 4.5 + * @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 BatchedStatement extends PreparedStatement +{ + public int getBatchSize(); + + public int getBatchCount(); + + public int getTotalResult(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBException.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBException.java new file mode 100644 index 000000000..e0fc2301f --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBException.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007, 2008, 2010-2012, 2015 Eike Stepper (Loehne, 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.net4j.db; + +import org.eclipse.net4j.util.StringUtil; + +import java.sql.SQLException; + +/** + * A {@link RuntimeException runtime exception} thrown to indicate problems with a database, frequently used to wrap + * checked {@link SQLException SQL exceptions}. + * + * @author Eike Stepper + */ +public class DBException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DBException() + { + } + + public DBException(String message) + { + super(message); + } + + public DBException(Throwable cause) + { + super(cause); + } + + public DBException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * @since 4.0 + */ + public DBException(Throwable cause, String sql) + { + super(format(null, sql), cause); + } + + /** + * @since 4.0 + */ + public DBException(String message, Throwable cause, String sql) + { + super(format(message, sql), cause); + } + + private static String format(String message, String sql) + { + if (StringUtil.isEmpty(message)) + { + if (StringUtil.isEmpty(sql)) + { + return ""; + } + + return sql; + } + + if (StringUtil.isEmpty(sql)) + { + return message; + } + + return message + ": " + sql; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java new file mode 100644 index 000000000..471ef62a9 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBType.java @@ -0,0 +1,1209 @@ +/* + * Copyright (c) 2007-2013, 2015, 2016 Eike Stepper (Loehne, 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 + * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations + */ +package org.eclipse.net4j.db; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.CharBuffer; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.eclipse.net4j.util.io.ExtendedDataInput; +import org.eclipse.net4j.util.io.ExtendedDataOutput; +import org.eclipse.net4j.util.io.ExtendedIOUtil; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.io.TMPUtil; + +/** + * Enumerates the SQL data types that are compatible with the DB framework. + * + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + */ +public enum DBType +{ + BOOLEAN(16) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + return writeValueBoolean(out, resultSet, column, canBeNull); + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + return readValueBoolean(in, statement, column, canBeNull, getCode()); + } + }, + + BIT(-7) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + return writeValueBoolean(out, resultSet, column, canBeNull); + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + return readValueBoolean(in, statement, column, canBeNull, getCode()); + } + }, + + TINYINT(-6) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + byte value = resultSet.getByte(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeByte(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + byte value = in.readByte(); + statement.setByte(column, value); + return value; + } + }, + + SMALLINT(5) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + short value = resultSet.getShort(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeShort(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + short value = in.readShort(); + statement.setShort(column, value); + return value; + } + }, + + INTEGER(4) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + int value = resultSet.getInt(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeInt(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + int value = in.readInt(); + statement.setInt(column, value); + return value; + } + }, + + BIGINT(-5) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + long value = resultSet.getLong(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeLong(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + long value = in.readLong(); + statement.setLong(column, value); + return value; + } + }, + + FLOAT(6) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + float value = resultSet.getFloat(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeFloat(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + float value = in.readFloat(); + statement.setFloat(column, value); + return value; + } + }, + + REAL(7) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + float value = resultSet.getFloat(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeFloat(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + float value = in.readFloat(); + statement.setFloat(column, value); + return value; + } + }, + + DOUBLE(8) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + double value = resultSet.getDouble(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeDouble(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + double value = in.readDouble(); + statement.setDouble(column, value); + return value; + } + }, + + NUMERIC(2) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + /** BEGIN SPECMATE PATCH */ + //throw new UnsupportedOperationException("SQL NULL has to be considered"); + BigDecimal value = resultSet.getBigDecimal(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + BigInteger valueUnscaled = value.unscaledValue(); + + byte[] byteArray = valueUnscaled.toByteArray(); + out.writeInt(byteArray.length); + out.write(byteArray); + out.writeInt(value.scale()); + return value; + /** END SPECMATE PATCH */ + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + /** BEGIN SPECMATE PATCH */ + //throw new UnsupportedOperationException("SQL NULL has to be considered"); + if (canBeNull && !in.readBoolean()) { + statement.setNull(column, getCode()); + return null; + } + byte[] bytes = in.readByteArray(); + + int scale = in.readInt(); + BigInteger valueUnscaled = new BigInteger(bytes); + BigDecimal value = new BigDecimal(valueUnscaled, scale); + + // TODO: Read out the precision, scale information and bring the big decimal to the correct form. + statement.setBigDecimal(column, value); + return value; + /** END SPECMATE PATCH */ + } + }, + + DECIMAL(3) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + throw new UnsupportedOperationException("SQL NULL has to be considered"); + // BigDecimal value = resultSet.getBigDecimal(column); + // BigInteger valueUnscaled = value.unscaledValue(); + // + // byte[] byteArray = valueUnscaled.toByteArray(); + // out.writeInt(byteArray.length); + // out.write(byteArray); + // out.writeInt(value.scale()); + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + throw new UnsupportedOperationException("SQL NULL has to be considered"); + // byte[] bytes = in.readByteArray(); + // int scale = in.readInt(); + // + // BigInteger valueUnscaled = new BigInteger(bytes); + // BigDecimal value = new BigDecimal(valueUnscaled, scale); + // statement.setBigDecimal(column, value); + } + }, + + CHAR(1) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + String value = resultSet.getString(column); + out.writeString(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + String value = in.readString(); + statement.setString(column, value); + return value; + } + }, + + VARCHAR(12) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + String value = resultSet.getString(column); + out.writeString(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + String value = in.readString(); + statement.setString(column, value); + return value; + } + }, + + LONGVARCHAR(-1, "LONG VARCHAR") //$NON-NLS-1$ + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + String value = resultSet.getString(column); + out.writeString(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + String value = in.readString(); + statement.setString(column, value); + return value; + } + }, + + CLOB(2005) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + Reader value = null; + + try + { + value = resultSet.getCharacterStream(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + return ExtendedIOUtil.writeCharacterStream(out, value); + } + finally + { + IOUtil.close(value); + } + } + + @Override + public Object readValueWithResult(final ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + ReaderWithLength value = ReaderWithLength.create(in); + long length = value.getLength(); + + statement.setCharacterStream(column, value, (int)length); + return length; + } + }, + + DATE(91) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + java.sql.Date value = resultSet.getDate(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + long longValue = value.getTime(); + out.writeLong(longValue); + return longValue; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + long value = in.readLong(); + statement.setDate(column, new java.sql.Date(value)); + return value; + } + }, + + TIME(92) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + java.sql.Time value = resultSet.getTime(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + long longValue = value.getTime(); + out.writeLong(longValue); + return longValue; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + long value = in.readLong(); + statement.setTime(column, new java.sql.Time(value)); + return value; + } + }, + + TIMESTAMP(93) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + java.sql.Timestamp value = resultSet.getTimestamp(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeLong(value.getTime()); + out.writeInt(value.getNanos()); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + long value = in.readLong(); + int nanos = in.readInt(); + java.sql.Timestamp timeStamp = new java.sql.Timestamp(value); + timeStamp.setNanos(nanos); + statement.setTimestamp(column, timeStamp); + return timeStamp; + } + }, + + BINARY(-2) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + byte[] value = resultSet.getBytes(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeInt(value.length); + out.write(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + byte[] value = in.readByteArray(); + statement.setBytes(column, value); + return value; + } + }, + + VARBINARY(-3) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + byte[] value = resultSet.getBytes(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeInt(value.length); + out.write(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + byte[] value = in.readByteArray(); + statement.setBytes(column, value); + return value; + } + }, + + LONGVARBINARY(-4, "LONG VARBINARY") //$NON-NLS-1$ + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + byte[] value = resultSet.getBytes(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + out.writeInt(value.length); + out.write(value); + return value; + } + + @Override + public Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + byte[] value = in.readByteArray(); + statement.setBytes(column, value); + return value; + } + }, + + BLOB(2004) + { + @Override + public Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + InputStream value = null; + + try + { + value = resultSet.getBinaryStream(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeBoolean(false); + return null; + } + + out.writeBoolean(true); + } + + return ExtendedIOUtil.writeBinaryStream(out, value); + } + finally + { + IOUtil.close(value); + } + } + + @Override + public Object readValueWithResult(final ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + if (canBeNull && !in.readBoolean()) + { + statement.setNull(column, getCode()); + return null; + } + + InputStreamWithLength value = InputStreamWithLength.create(in); + long length = value.getLength(); + + statement.setBinaryStream(column, value, (int)length); + return length; + } + }; + + private static final int BOOLEAN_NULL = -1; + + private static final int BOOLEAN_FALSE = 0; + + private static final int BOOLEAN_TRUE = 1; + + private int code; + + private String keyword; + + private DBType(int code, String keyword) + { + this.code = code; + this.keyword = keyword; + } + + private DBType(int code) + { + this(code, null); + } + + public int getCode() + { + return code; + } + + public String getKeyword() + { + return keyword == null ? super.toString() : keyword; + } + + @Override + public String toString() + { + return getKeyword(); + } + + /** + * @since 3.0 + */ + public void writeValue(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + writeValueWithResult(out, resultSet, column, canBeNull); + } + + /** + * @since 4.1 + */ + public abstract Object writeValueWithResult(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException; + + /** + * @since 3.0 + */ + public void readValue(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException + { + readValueWithResult(in, statement, column, canBeNull); + } + + /** + * @since 4.1 + */ + public abstract Object readValueWithResult(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull) throws SQLException, IOException; + + private static Boolean writeValueBoolean(ExtendedDataOutput out, ResultSet resultSet, int column, boolean canBeNull) throws SQLException, IOException + { + boolean value = resultSet.getBoolean(column); + if (canBeNull) + { + if (resultSet.wasNull()) + { + out.writeByte(BOOLEAN_NULL); + return null; + } + + out.writeByte(value ? BOOLEAN_TRUE : BOOLEAN_FALSE); + return value; + } + + out.writeBoolean(value); + return value; + } + + private static Boolean readValueBoolean(ExtendedDataInput in, PreparedStatement statement, int column, boolean canBeNull, int sqlType) + throws IOException, SQLException + { + if (canBeNull) + { + byte opcode = in.readByte(); + switch (opcode) + { + case BOOLEAN_NULL: + statement.setNull(column, sqlType); + return null; + + case BOOLEAN_FALSE: + statement.setBoolean(column, false); + return false; + + case BOOLEAN_TRUE: + statement.setBoolean(column, true); + return true; + + default: + throw new IOException("Invalid boolean opcode: " + opcode); + } + } + + boolean value = in.readBoolean(); + statement.setBoolean(column, value); + return value; + } + + /** + * @since 3.0 + */ + public static DBType getTypeByKeyword(String keyword) + { + DBType[] values = DBType.values(); + for (int i = 0; i < values.length; i++) + { + DBType dbType = values[i]; + if (dbType.getKeyword().equalsIgnoreCase(keyword)) + { + return dbType; + } + } + + return null; + } + + /** + * @since 4.2 + */ + public static DBType getTypeByCode(int code) + { + DBType[] values = DBType.values(); + for (int i = 0; i < values.length; i++) + { + DBType dbType = values[i]; + if (dbType.getCode() == code) + { + return dbType; + } + } + + return null; + } + + /** + * @author Eike Stepper + */ + private static final class InputStreamWithLength extends FileInputStream + { + private final File file; + + private final long length; + + private long pos; + + private InputStreamWithLength(File file, long length) throws FileNotFoundException + { + super(file); + this.file = file; + this.length = length; + } + + private void incrementPos(long n) throws IOException + { + pos += n; + if (pos >= length) + { + close(); + } + } + + public long getLength() + { + return length; + } + + @Override + public int read() throws IOException + { + try + { + return super.read(); + } + finally + { + incrementPos(1); + } + } + + @Override + public int read(byte[] b) throws IOException + { + try + { + return super.read(b); + } + finally + { + incrementPos(b.length); + } + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + try + { + return super.read(b, off, len); + } + finally + { + incrementPos(len); + } + } + + @Override + public long skip(long n) throws IOException + { + try + { + return super.skip(n); + } + finally + { + incrementPos(n); + } + } + + @Override + public void close() throws IOException + { + super.close(); + file.delete(); + } + + public static InputStreamWithLength create(final ExtendedDataInput in) throws IOException + { + // new Reader() + // { + // @Override + // public int read(char[] cbuf, int off, int len) throws IOException + // { + // return IOUtil.EOF; + // } + // + // @Override + // public void close() throws IOException + // { + // // Do nothing + // } + // }; + + FileOutputStream fileOutputStream = null; + + try + { + File tempFile = TMPUtil.createTempFile("lob_", ".tmp"); + fileOutputStream = new FileOutputStream(tempFile); + + long length = ExtendedIOUtil.readBinaryStream(in, fileOutputStream); + + return new InputStreamWithLength(tempFile, length); + } + finally + { + IOUtil.close(fileOutputStream); + } + } + } + + /** + * @author Eike Stepper + */ + private static final class ReaderWithLength extends FileReader + { + private final File file; + + private final long length; + + private long pos; + + private ReaderWithLength(File file, long length) throws FileNotFoundException + { + super(file); + this.file = file; + this.length = length; + } + + private void incrementPos(long n) throws IOException + { + pos += n; + if (pos >= length) + { + close(); + } + } + + public long getLength() + { + return length; + } + + @Override + public int read() throws IOException + { + try + { + return super.read(); + } + finally + { + incrementPos(1); + } + } + + @Override + public int read(char[] cbuf, int offset, int length) throws IOException + { + try + { + return super.read(cbuf, offset, length); + } + finally + { + incrementPos(length); + } + } + + @Override + public int read(CharBuffer target) throws IOException + { + try + { + return super.read(target); + } + finally + { + incrementPos(target.length()); + } + } + + @Override + public int read(char[] cbuf) throws IOException + { + try + { + return super.read(cbuf); + } + finally + { + incrementPos(cbuf.length); + } + } + + @Override + public long skip(long n) throws IOException + { + try + { + return super.skip(n); + } + finally + { + incrementPos(n); + } + } + + @Override + public boolean markSupported() + { + return false; + } + + @Override + public void mark(int readAheadLimit) throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public void reset() throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException + { + super.close(); + file.delete(); + } + + public static ReaderWithLength create(final ExtendedDataInput in) throws IOException + { + // new Reader() + // { + // @Override + // public int read(char[] cbuf, int off, int len) throws IOException + // { + // return IOUtil.EOF; + // } + // + // @Override + // public void close() throws IOException + // { + // // Do nothing + // } + // }; + + FileWriter fileWriter = null; + + try + { + File tempFile = TMPUtil.createTempFile("lob_", ".tmp"); + fileWriter = new FileWriter(tempFile); + + long length = ExtendedIOUtil.readCharacterStream(in, fileWriter); + + return new ReaderWithLength(tempFile, length); + } + finally + { + IOUtil.close(fileWriter); + } + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java new file mode 100644 index 000000000..487e35479 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java @@ -0,0 +1,1527 @@ +/* + * Copyright (c) 2007-2016, 2018 Eike Stepper (Loehne, 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.net4j.db; + +import org.eclipse.net4j.db.ddl.IDBElement; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBNamedElement; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.internal.db.BatchedStatementImpl; +import org.eclipse.net4j.internal.db.DBConnection; +import org.eclipse.net4j.internal.db.DBDatabase; +import org.eclipse.net4j.internal.db.DataSourceConnectionProvider; +import org.eclipse.net4j.internal.db.bundle.OM; +import org.eclipse.net4j.internal.db.ddl.DBIndex; +import org.eclipse.net4j.internal.db.ddl.DBNamedElement; +import org.eclipse.net4j.spi.db.DBAdapter; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.util.ReflectUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.io.ExtendedDataInput; +import org.eclipse.net4j.util.io.ExtendedDataOutput; +import org.eclipse.net4j.util.om.OMPlatform; +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.net4j.util.security.IUserAware; + +import javax.sql.DataSource; + +import java.io.IOException; +import java.io.Writer; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A utility class with various static factory and convenience methods. + * + * @author Eike Stepper + */ +@SuppressWarnings("resource") +public final class DBUtil +{ + /** + * @since 4.2 + */ + public static final int MAX_BATCH_SIZE = OMPlatform.INSTANCE.getProperty("org.eclipse.net4j.db.MAX_BATCH_SIZE", 2000); + + /** + * A system property to enable noisy close, i.e. exception catch in close methods are thrown as {@link DBException} exception. + * + * @since 4.4 + */ + public static final String PROP_ENABLE_NOISY_CLOSE = "org.eclipse.net4j.db.close.noisy"; + + private static final boolean IS_NOISY_CLOSE_ENABLED = OMPlatform.INSTANCE.isProperty(PROP_ENABLE_NOISY_CLOSE); + + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SQL, DBUtil.class); + + private DBUtil() + { + } + + /** + * @deprecated This method exists only to create a "resource leak" warning, so that DBUtil can safely SuppressWarnings("resource"). + */ + @Deprecated + @SuppressWarnings("unused") + private static void avoidResourceLeakWarning() throws SQLException + { + Connection connection = new DBConnection(null, null); + Statement statement = connection.createStatement(); + close(statement); + } + + /** + * For debugging purposes ONLY! + * + * @deprecated Should only be used when debugging. + * @since 3.0 + */ + @Deprecated + public static void sqlDump(Connection conn, String sql) + { + ResultSet rs = null; + + try + { + TRACER.format("Dumping output of {0}", sql); //$NON-NLS-1$ + rs = conn.createStatement().executeQuery(sql); + int numCol = rs.getMetaData().getColumnCount(); + + StringBuilder row = new StringBuilder(); + for (int c = 1; c <= numCol; c++) + { + row.append(String.format("%10s | ", rs.getMetaData().getColumnLabel(c))); //$NON-NLS-1$ + } + + TRACER.trace(row.toString()); + + row = new StringBuilder(); + for (int c = 1; c <= numCol; c++) + { + row.append("-----------+--"); //$NON-NLS-1$ + } + + TRACER.trace(row.toString()); + + while (rs.next()) + { + row = new StringBuilder(); + for (int c = 1; c <= numCol; c++) + { + row.append(String.format("%10s | ", rs.getString(c))); //$NON-NLS-1$ + } + + TRACER.trace(row.toString()); + } + + row = new StringBuilder(); + for (int c = 1; c <= numCol; c++) + { + row.append("-----------+-"); //$NON-NLS-1$ + } + + TRACER.trace(row.toString()); + } + catch (SQLException ex) + { + // Do nothing + } + finally + { + close(rs); + } + } + + /** + * For debugging purposes ONLY! + * + * @deprecated Should only be used when debugging. + * @since 3.0 + */ + @Deprecated + public static void sqlDump(IDBConnectionProvider connectionProvider, String sql) + { + Connection connection = connectionProvider.getConnection(); + + try + { + sqlDump(connection, sql); + } + finally + { + close(connection); + } + } + + /** + * @since 4.2 + */ + public static String dumpToString(IDBNamedElement namedElement) + { + return ((DBNamedElement)namedElement).dumpToString(); + } + + /** + * @since 4.2 + */ + public static void dump(IDBNamedElement namedElement) + { + ((DBNamedElement)namedElement).dump(); + } + + /** + * @since 4.2 + */ + public static void dump(IDBNamedElement namedElement, Writer writer) throws IOException + { + ((DBNamedElement)namedElement).dump(writer); + } + + /** + * @since 4.2 + */ + public static IDBDatabase openDatabase(IDBAdapter adapter, IDBConnectionProvider connectionProvider, String schemaName) + { + return openDatabase(adapter, connectionProvider, schemaName, false); + } + + /** + * @since 4.2 + */ + public static IDBDatabase openDatabase(IDBAdapter adapter, IDBConnectionProvider connectionProvider, String schemaName, boolean fixNullableIndexColumns) + { + return new DBDatabase((DBAdapter)adapter, connectionProvider, schemaName, fixNullableIndexColumns); + } + + public static IDBSchema createSchema(String name) + { + return new org.eclipse.net4j.internal.db.ddl.DBSchema(name); + } + + /** + * @since 4.2 + */ + public static void readSchema(IDBAdapter adapter, Connection connection, IDBSchema schema) + { + adapter.readSchema(connection, schema); + } + + /** + * @since 4.2 + */ + public static IDBSchema readSchema(IDBAdapter adapter, Connection connection, String name) + { + return readSchema(adapter, connection, name, false); + } + + /** + * @since 4.2 + */ + public static IDBSchema readSchema(IDBAdapter adapter, Connection connection, String name, boolean fixNullableIndexColumns) + { + IDBSchema schema = new org.eclipse.net4j.internal.db.ddl.DBSchema(name); + + if (fixNullableIndexColumns) + { + DBIndex.FIX_NULLABLE_INDEX_COLUMNS.set(true); + } + + try + { + readSchema(adapter, connection, schema); + } + finally + { + if (fixNullableIndexColumns) + { + try + { + Set nullableIndexFields = DBIndex.NULLABLE_INDEX_FIELDS.get(); + if (nullableIndexFields != null && !nullableIndexFields.isEmpty()) + { + fixNullableIndexFields(adapter, connection, nullableIndexFields); + } + } + finally + { + DBIndex.NULLABLE_INDEX_FIELDS.remove(); + DBIndex.FIX_NULLABLE_INDEX_COLUMNS.remove(); + } + } + } + + return schema; + } + + private static void fixNullableIndexFields(IDBAdapter adapter, Connection connection, Set nullableIndexFields) + { + StringBuilder builder = new StringBuilder(); + builder.append("The internal schema migration has fixed the following nullable index columns:"); + builder.append(StringUtil.NL); + + boolean autoCommit = false; + Statement statement = null; + + try + { + autoCommit = setAutoCommit(connection, false); + statement = connection.createStatement(); + + for (IDBField field : nullableIndexFields) + { + field.setNotNull(true); + + String sql = adapter.sqlModifyField(field); + statement.execute(sql); + + builder.append("- "); + builder.append(sql); + builder.append(StringUtil.NL); + } + + connection.commit(); + OM.LOG.info(builder.toString()); + } + catch (SQLException ex) + { + OM.LOG.error(ex); + rollbackSilently(connection); + throw new DBException(ex); + } + finally + { + close(statement); + setAutoCommit(connection, autoCommit); + } + } + + /** + * @since 4.2 + */ + public static IDBSchema copySchema(IDBSchema source) + { + return new org.eclipse.net4j.internal.db.ddl.DBSchema(source); + } + + public static DataSource createDataSource(Map properties) + { + return createDataSource(properties, null); + } + + public static DataSource createDataSource(Map properties, String namespace) + { + return createDataSource(properties, namespace, "class"); //$NON-NLS-1$ + } + + public static DataSource createDataSource(Map properties, String namespace, String driverClassKey) + { + try + { + return (DataSource)ReflectUtil.instantiate(properties, namespace, driverClassKey, OM.class.getClassLoader()); + } + catch (Exception ex) + { + throw new DBException(ex); + } + } + + public static IDBConnectionProvider createConnectionProvider(DataSource dataSource) + { + return createConnectionProvider(dataSource, null); + } + + /** + * @since 4.3 + */ + public static IDBConnectionProvider2 createConnectionProvider(DataSource dataSource, String user) + { + return new DataSourceConnectionProvider(dataSource, user); + } + + /** + * Retrieves an {@link IDBAdapter adapter} from the {@link IDBAdapter#REGISTRY adapter registry}. + *

+ * If Eclipse is running adapters are automatically created from descriptors that are contributed to the extension point org.eclipse.net4j.db.dbAdapters. + *

+ * In standalone scenarios the needed adapter instances must be registered with the {@link IDBAdapter#REGISTRY adapter registry} manually. + */ + public static IDBAdapter getDBAdapter(String adapterName) + { + return IDBAdapter.REGISTRY.get(adapterName); + } + + /** + * @since 4.5 + */ + public static BatchedStatement batched(PreparedStatement delegate, int batchSize) throws DBException + { + return new BatchedStatementImpl(delegate, batchSize); + } + + public static Exception close(ResultSet resultSet) + { + if (resultSet != null) + { + try + { + Statement statement = resultSet.getStatement(); + if (statement != null && statement.getMaxRows() != 0) + { + statement.setMaxRows(0); + } + } + catch (Exception ignore) + { + //$FALL-THROUGH$ + } + + try + { + resultSet.close(); + } + catch (Exception ex) + { + if (IS_NOISY_CLOSE_ENABLED) + { + throw new DBException(ex); + } + + OM.LOG.error(ex); + return ex; + } + } + + return null; + } + + public static Exception close(Statement statement) + { + if (statement != null) + { + try + { + statement.close(); + } + catch (Exception ex) + { + if (IS_NOISY_CLOSE_ENABLED) + { + throw new DBException(ex); + } + + OM.LOG.error(ex); + return ex; + } + } + + return null; + } + + public static Exception close(Connection connection) + { + if (connection != null) + { + try + { + // Only for connections with autoCommit = false, we try a rollback + // first to clear any open transactions. + if (!connection.getAutoCommit()) + { + rollbackSilently(connection); + } + + connection.close(); + } + catch (Exception ex) + { + if (IS_NOISY_CLOSE_ENABLED) + { + throw new DBException(ex); + } + + OM.LOG.error(ex); + return ex; + } + } + + return null; + } + + /** + * @since 4.6 + */ + public static boolean isOptional(IDBElement element) + { + if (element instanceof InternalDBIndex) + { + InternalDBIndex index = (InternalDBIndex)element; + return index.isOptional(); + } + + return false; + } + + /** + * @since 4.6 + */ + public static boolean setOptional(IDBElement element, boolean optional) + { + if (element instanceof InternalDBIndex) + { + InternalDBIndex index = (InternalDBIndex)element; + index.setOptional(optional); + return true; + } + + return false; + } + + /** + * @since 4.2 + */ + public static boolean setAutoCommit(Connection connection, boolean autoCommit) + { + try + { + if (connection.getAutoCommit() != autoCommit) + { + connection.setAutoCommit(autoCommit); + return !autoCommit; + } + + return autoCommit; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + /** + * @since 4.2 + */ + public static Exception rollbackSilently(Connection connection) + { + try + { + if (!connection.getAutoCommit()) + { + connection.rollback(); + } + + return null; + } + catch (Exception ex) + { + OM.LOG.error(ex); + return ex; + } + } + + /** + * @since 3.0 + * @deprecated As of 4.2 use {@link #getAllSchemaNames(Connection)}. + */ + @Deprecated + public static List getAllSchemaTableNames(Connection connection) + { + return getAllSchemaNames(connection); + } + + /** + * @since 3.0 + * @deprecated As of 4.2 use {@link #getAllSchemaNames(DatabaseMetaData)}. + */ + @Deprecated + public static List getAllSchemaTableNames(DatabaseMetaData metaData) + { + return new ArrayList(getAllSchemaNames(metaData)); + } + + /** + * @since 4.2 + */ + public static List getAllSchemaNames(Connection connection) + { + try + { + DatabaseMetaData metaData = connection.getMetaData(); + return new ArrayList(getAllSchemaNames(metaData)); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + /** + * @since 4.2 + */ + public static Set getAllSchemaNames(DatabaseMetaData metaData) + { + ResultSet schemas = null; + + try + { + Set names = new HashSet(); + schemas = metaData.getSchemas(); + while (schemas.next()) + { + String name = schemas.getString(1); + names.add(name); + } + + return names; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(schemas); + } + } + + public static List getAllTableNames(Connection connection, String dbName) + { + ResultSet tables = null; + + try + { + List names = new ArrayList(); + DatabaseMetaData metaData = connection.getMetaData(); + if (dbName != null) + { + dbName = dbName.toUpperCase(); + Set schemaNames = getAllSchemaNames(metaData); + if (!schemaNames.contains(dbName)) + { + dbName = null; + } + } + + if (dbName == null && connection instanceof IUserAware) + { + dbName = ((IUserAware)connection).getUserID(); + } + + tables = metaData.getTables(null, dbName, null, new String[] { "TABLE" }); //$NON-NLS-1$ + while (tables.next()) + { + String name = tables.getString(3); + names.add(name); + } + + return names; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(tables); + } + } + + /** + * @since 4.0 + */ + public static List dropAllTables(Connection connection, String dbName) + { + Statement statement = null; + + try + { + statement = connection.createStatement(); + List exceptions = new ArrayList(); + + for (String tableName : getAllTableNames(connection, dbName)) + { + String sql = "DROP TABLE " + tableName; //$NON-NLS-1$ + trace(sql); + + try + { + statement.execute(sql); + } + catch (SQLException ex) + { + exceptions.add(ex); + } + } + + return exceptions; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + } + } + + /** + * @since 4.2 + */ + public static int asInt(Object value) + { + if (value instanceof Number) + { + return ((Number)value).intValue(); + } + + return 0; + } + + /** + * @since 4.2 + */ + public static long asLong(Object value) + { + if (value instanceof Number) + { + return ((Number)value).longValue(); + } + + return 0L; + } + + /** + * @since 3.0 + */ + public static int selectMinimumInt(Connection connection, IDBField field, String... where) throws DBException + { + Number number = getFunctionResult(connection, field, "MIN", where); //$NON-NLS-1$ + return asInt(number); + } + + /** + * @since 3.0 + */ + public static long selectMinimumLong(Connection connection, IDBField field, String... where) throws DBException + { + Number number = getFunctionResult(connection, field, "MIN", where); //$NON-NLS-1$ + return asLong(number); + } + + /** + * @since 3.0 + */ + public static int selectMaximumInt(Connection connection, IDBField field, String... where) throws DBException + { + Number number = getFunctionResult(connection, field, "MAX", where); //$NON-NLS-1$ + return asInt(number); + } + + /** + * @since 3.0 + */ + public static long selectMaximumLong(Connection connection, IDBField field, String... where) throws DBException + { + Number number = getFunctionResult(connection, field, "MAX", where); //$NON-NLS-1$ + return asLong(number); + } + + private static Number getFunctionResult(Connection connection, IDBField field, String function, String... where) throws DBException + { + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(function); + builder.append("("); //$NON-NLS-1$ + builder.append(field); + builder.append(") FROM "); //$NON-NLS-1$ + builder.append(field.getTable()); + + for (int i = 0; i < where.length; i++) + { + if (i == 0) + { + builder.append(" WHERE "); //$NON-NLS-1$ + } + else + { + builder.append(" AND "); //$NON-NLS-1$ + } + + builder.append("("); //$NON-NLS-1$ + builder.append(where[i]); + builder.append(")"); //$NON-NLS-1$ + } + + String sql = trace(builder.toString()); + Statement statement = null; + ResultSet resultSet = null; + + try + { + statement = connection.createStatement(); + + try + { + resultSet = statement.executeQuery(sql); + if (!resultSet.next()) + { + return null; + } + + return (Number)resultSet.getObject(1); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + } + } + + /** + * @since 4.2 + */ + public static T execute(IDBConnectionProvider connectionProvider, RunnableWithConnection runnable) + { + Connection connection = null; + + try + { + connection = connectionProvider.getConnection(); + + T result = runnable.run(connection); + connection.commit(); + return result; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(connection); + } + } + + /** + * @since 4.2 + */ + public static void execute(Connection connection, CharSequence sql) + { + String string = sql.toString(); + if (TRACER.isEnabled()) + { + TRACER.trace(string); + } + + Statement statement = null; + + try + { + statement = connection.createStatement(); + statement.execute(string); + } + catch (SQLException ex) + { + throw new DBException(ex.getMessage() + " --> " + sql, ex); + } + finally + { + close(statement); + } + } + + /** + * @since 4.1 + */ + public static void executeBatch(PreparedStatement stmt, int counter) + { + executeBatch(stmt, counter, true); + } + + /** + * @since 4.1 + */ + public static void executeBatch(PreparedStatement stmt, int counter, boolean checkExactlyOne) + { + try + { + int[] results = stmt.executeBatch(); + if (results.length != counter) + { + throw new DBException("Statement has " + results.length + " results (expected: " + counter + ")"); + } + + for (int i = 0; i < results.length; i++) + { + int result = results[i]; + if (result != Statement.SUCCESS_NO_INFO) + { + if (result < 0) + { + throw new DBException("Result " + i + " is not successful: " + result); + } + + if (checkExactlyOne && result != 1) + { + throw new DBException("Result " + i + " did not affect exactly one row: " + result); + } + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + try + { + stmt.clearBatch(); + } + catch (SQLException ex) + { + OM.LOG.warn(ex); + } + } + } + + public static int update(Connection connection, String sql) + { + trace(sql); + Statement statement = null; + + try + { + statement = connection.createStatement(); + return statement.executeUpdate(sql); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + } + } + + /** + * Execute update on the given prepared statement and handle common cases of return values. + * + * @param stmt + * the prepared statement + * @param exactlyOne + * if true, the update count is checked to be 1. Else the update result is only + * checked so that the update was successful (i.e. result code != Statement.EXECUTE_FAILED). + * @return the update count / execution result as returned by {@link PreparedStatement#executeUpdate()}. Can be used + * by the caller to perform more advanced checks. + * @throws SQLException + * if {@link PreparedStatement#executeUpdate()} throws it. + * @throws IllegalStateException + * if the check indicated by excatlyOne indicates an error. + * @since 4.0 + */ + public static int update(PreparedStatement stmt, boolean exactlyOne) throws SQLException + { + if (isTracerEnabled()) + { + trace(stmt.toString()); + } + + int result = stmt.executeUpdate(); + + // basic check of update result + if (exactlyOne && result != 1) + { + throw new IllegalStateException(stmt.toString() + " returned Update count " + result + " (expected: 1)"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (result == Statement.EXECUTE_FAILED) + { + throw new IllegalStateException(stmt.toString() + " returned EXECUTE_FAILED"); //$NON-NLS-1$ + } + + return result; + } + + /** + * @since 4.1 + */ + public static int clearTable(Connection connection, IDBTable table) + { + return clearTable(connection, table.getName()); + } + + /** + * @since 4.1 + */ + public static int clearTable(Connection connection, String tableName) + { + String sql = "DELETE FROM " + tableName; + return update(connection, sql); + } + + public static int select(Connection connection, IDBRowHandler rowHandler, String where, IDBField... fields) throws DBException + { + IDBTable table = fields[0].getTable(); + for (int i = 1; i < fields.length; i++) + { + if (fields[i].getTable() != table) + { + throw new IllegalArgumentException("Multiple tables not allowed: " + Arrays.asList(fields)); //$NON-NLS-1$ + } + } + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + for (int i = 0; i < fields.length; i++) + { + if (i > 0) + { + builder.append(", "); //$NON-NLS-1$ + } + + builder.append(fields[i]); + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + if (where != null) + { + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(where); + } + + String sql = trace(builder.toString()); + Statement statement = null; + ResultSet resultSet = null; + + try + { + statement = connection.createStatement(); + + try + { + int rows = 0; + boolean proceed = true; + Object[] values = new Object[fields.length]; + resultSet = statement.executeQuery(sql); + while (proceed && resultSet.next()) + { + for (int i = 0; i < fields.length; i++) + { + values[i] = resultSet.getObject(i + 1); + if (values[i] instanceof Blob) + { + Blob blob = (Blob)values[i]; + long length = blob.length(); + if (length > Integer.MAX_VALUE) + { + throw new IllegalStateException("byte[] too long: " + length); //$NON-NLS-1$ + } + + values[i] = blob.getBytes(1, (int)length); + } + else if (values[i] instanceof Clob) + { + Clob clob = (Clob)values[i]; + long length = clob.length(); + if (length > Integer.MAX_VALUE) + { + throw new IllegalStateException("String too long: " + length); //$NON-NLS-1$ + } + + values[i] = clob.getSubString(1, (int)length); + } + } + + proceed = rowHandler.handle(rows++, values); + } + + return rows; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + } + } + + public static int select(Connection connection, IDBRowHandler rowHandler, IDBField... fields) throws DBException + { + return select(connection, rowHandler, null, fields); + } + + public static Object[] select(Connection connection, String where, IDBField... fields) throws DBException + { + final Object[][] result = new Object[1][]; + IDBRowHandler rowHandler = new IDBRowHandler() + { + public boolean handle(int row, Object... values) + { + result[0] = values; + return false; + } + }; + + select(connection, rowHandler, where, fields); + return result[0]; + } + + /** + * Returns the number of rows contained in the given result set. + *

+ * The {@link ResultSet#getStatement() statement} of the result set must have been created with + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE TYPE_SCROLL_INSENSITIVE}. + * + * @since 4.0 + */ + public static int getRowCount(ResultSet resultSet) throws DBException + { + reset(resultSet); + + try + { + resultSet.last(); + return resultSet.getRow(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + reset(resultSet); + } + } + + /** + * Returns the number of rows contained in the given table. + * + * @since 4.5 + */ + public static int getRowCount(Connection connection, String tableName) throws DBException + { + String sql = trace("SELECT COUNT(*) FROM " + tableName); + Statement statement = null; + ResultSet resultSet = null; + + try + { + statement = connection.createStatement(); + + try + { + resultSet = statement.executeQuery(sql); + if (!resultSet.next()) + { + return -1; + } + + return resultSet.getInt(1); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + } + } + + private static void reset(ResultSet resultSet) throws DBException + { + try + { + resultSet.beforeFirst(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + /** + * @since 3.0 + */ + public static void serializeTable(ExtendedDataOutput out, Connection connection, IDBTable table, String tableAlias, String sqlSuffix) + throws DBException, IOException + { + serializeTable(out, connection, table, tableAlias, sqlSuffix, null); + } + + /** + * @since 4.1 + */ + public static void serializeTable(ExtendedDataOutput out, Connection connection, IDBTable table, String tableAlias, String sqlSuffix, + SerializeRowHandler handler) throws DBException, IOException + { + IDBField[] fields = table.getFields(); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + for (int i = 0; i < fields.length; i++) + { + if (i > 0) + { + builder.append(", "); //$NON-NLS-1$ + } + + if (tableAlias != null) + { + builder.append(tableAlias); + builder.append("."); //$NON-NLS-1$ + } + + builder.append(fields[i]); + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + if (tableAlias != null) + { + builder.append(" "); //$NON-NLS-1$ + builder.append(tableAlias); + } + + if (sqlSuffix != null) + { + builder.append(sqlSuffix); + } + + String sql = trace(builder.toString()); + Statement statement = null; + ResultSet resultSet = null; + boolean successful = false; + + try + { + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + + try + { + resultSet = statement.executeQuery(sql); + + // Write resultSet size for progress monitoring + int size = getRowCount(resultSet); + out.writeInt(size); + if (size == 0) + { + return; + } + + Object[] values = handler != null ? new Object[fields.length] : null; + + while (resultSet.next()) + { + for (int i = 0; i < fields.length; i++) + { + IDBField field = fields[i]; + DBType type = field.getType(); + boolean canBeNull = !field.isNotNull(); + + Object value = type.writeValueWithResult(out, resultSet, i + 1, canBeNull); + if (values != null) + { + values[i] = value; + } + } + + if (handler != null) + { + handler.handleRow(out, connection, fields, values); + } + } + + successful = true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + try + { + if (handler != null) + { + handler.done(successful); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + } + } + } + + /** + * @since 4.0 + */ + public static void deserializeTable(ExtendedDataInput in, Connection connection, IDBTable table, OMMonitor monitor) throws IOException + { + deserializeTable(in, connection, table, monitor, null); + } + + /** + * @since 4.1 + */ + public static void deserializeTable(ExtendedDataInput in, Connection connection, IDBTable table, OMMonitor monitor, DeserializeRowHandler handler) + throws IOException + { + int size = in.readInt(); + if (size == 0) + { + return; + } + + IDBField[] fields = table.getFields(); + + StringBuilder builder = new StringBuilder(); + StringBuilder params = new StringBuilder(); + + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(table); + builder.append("("); //$NON-NLS-1$ + + for (int i = 0; i < fields.length; i++) + { + if (i > 0) + { + builder.append(", "); //$NON-NLS-1$ + params.append(", "); //$NON-NLS-1$ + } + + builder.append(fields[i]); + params.append("?"); //$NON-NLS-1$ + } + + builder.append(") VALUES ("); //$NON-NLS-1$ + builder.append(params.toString()); + builder.append(")"); //$NON-NLS-1$ + + String sql = trace(builder.toString()); + PreparedStatement statement = null; + boolean successful = false; + + monitor.begin(1 + 2 * size); + + try + { + statement = connection.prepareStatement(sql); + monitor.worked(); + + Object[] values = handler != null ? new Object[fields.length] : null; + + int batchSize = 0; + for (int row = 0; row < size; row++) + { + for (int i = 0; i < fields.length; i++) + { + IDBField field = fields[i]; + DBType type = field.getType(); + boolean canBeNull = !field.isNotNull(); + Object value = type.readValueWithResult(in, statement, i + 1, canBeNull); + if (values != null) + { + values[i] = value; + } + } + + statement.addBatch(); + if (handler != null) + { + handler.handleRow(in, connection, fields, values); + } + + monitor.worked(); + + if (++batchSize == MAX_BATCH_SIZE) + { + executeBatch(statement, batchSize, monitor); + batchSize = 0; + } + } + + if (batchSize != 0) + { + executeBatch(statement, batchSize, monitor); + } + + successful = true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + try + { + if (handler != null) + { + handler.done(successful); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + close(statement); + monitor.done(); + } + } + } + + private static void executeBatch(PreparedStatement statement, int batchSize, OMMonitor monitor) throws SQLException + { + Async async = monitor.forkAsync(batchSize); + + try + { + statement.executeBatch(); + } + finally + { + async.stop(); + } + } + + /** + * @since 3.0 + */ + public static String trace(String sql) + { + if (isTracerEnabled()) + { + TRACER.trace(sql); + } + + return sql; + } + + /** + * @since 4.2 + */ + public static boolean isTracerEnabled() + { + return TRACER.isEnabled(); + } + + /** + * @since 4.2 + * @author Eike Stepper + */ + public interface RunnableWithConnection + { + public T run(Connection connection) throws SQLException; + } + + /** + * Call-back interface with a {@link #done(boolean) method} that is called after + * a number of table rows have been handled by one of the subtypes of this interface. + * + * @since 4.1 + * @author Eike Stepper + */ + public interface RowHandler + { + public void done(boolean successful) throws SQLException, IOException; + } + + /** + * A {@link RowHandler row handler} with a {@link #handleRow(ExtendedDataOutput, Connection, IDBField[], Object[]) method} + * that is called once per row serialized within {@link DBUtil#serializeTable(ExtendedDataOutput, Connection, IDBTable, String, String, SerializeRowHandler) DBUtil.serializeTable()}. + * + * @since 4.1 + * @author Eike Stepper + */ + public interface SerializeRowHandler extends RowHandler + { + public void handleRow(ExtendedDataOutput out, Connection connection, IDBField[] fields, Object[] values) throws SQLException, IOException; + } + + /** + * A {@link RowHandler row handler} with a {@link #handleRow(ExtendedDataInput, Connection, IDBField[], Object[]) method} + * that is called once per row deserialized within {@link DBUtil#deserializeTable(ExtendedDataInput, Connection, IDBTable, OMMonitor, DeserializeRowHandler) DBUtil.deserializeTable()}. + * + * @since 4.1 + * @author Eike Stepper + */ + public interface DeserializeRowHandler extends RowHandler + { + public void handleRow(ExtendedDataInput in, Connection connection, IDBField[] fields, Object[] values) throws SQLException, IOException; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java new file mode 100644 index 000000000..428c03d42 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2007-2013, 2016 Eike Stepper (Loehne, 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 289445 + */ +package org.eclipse.net4j.db; + +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.internal.db.DBAdapterRegistry; +import org.eclipse.net4j.spi.db.DBAdapter; +import org.eclipse.net4j.util.registry.IRegistry; + +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collection; +import java.util.Set; + +/** + * Abstracts all aspects of a database that are vendor-specific. + * + * @author Eike Stepper + * @noimplement This interface is not intended to be implemented by clients. Subclass {@link DBAdapter} instead. + */ +public interface IDBAdapter +{ + public static final IRegistry REGISTRY = DBAdapterRegistry.INSTANCE; + + public String getName(); + + public String getVersion(); + + /** + * @deprecated As of 4.2 no longer supported because of IP issues for external build dependencies (the vendor driver libs). + */ + @Deprecated + public Driver getJDBCDriver(); + + /** + * @deprecated As of 4.2 no longer supported because of IP issues for external build dependencies (the vendor driver libs). + */ + @Deprecated + public DataSource createJDBCDataSource(); + + /** + * @since 4.3 + */ + public IDBConnectionProvider createConnectionProvider(DataSource dataSource); + + /** + * @since 4.5 + */ + public Connection modifyConnection(Connection connection); + + /** + * @since 4.2 + */ + public IDBSchema readSchema(Connection connection, String name); + + /** + * @since 4.2 + */ + public void readSchema(Connection connection, IDBSchema schema); + + /** + * @since 4.2 + */ + public void updateSchema(Connection connection, IDBSchema schema, IDBSchemaDelta delta) throws DBException; + + public Set createTables(Iterable tables, Connection connection) throws DBException; + + public boolean createTable(IDBTable table, Statement statement) throws DBException; + + public Collection dropTables(Iterable tables, Connection connection) throws DBException; + + public boolean dropTable(IDBTable table, Statement statement); + + public String[] getReservedWords(); + + public boolean isReservedWord(String word); + + /** + * @since 2.0 + */ + public int getMaxTableNameLength(); + + /** + * @since 2.0 + */ + public int getMaxFieldNameLength(); + + /** + * Returns the column length for the given database type. + * + * @param type the {@link DBType} to check. + * @return the supported column length for the type. + * @since 4.2 + */ + public int getFieldLength(DBType type); + + public boolean isTypeIndexable(DBType type); + + /** + * Provide a way for the DBAdapter to override unsupported DB types with replacements. The default implementation just + * returns the given type. Subclasses may override single types with replacements. + * + * @since 3.0 + */ + public DBType adaptType(DBType type); + + /** + * Check if a character is valid as first character. (e.g., underscores are forbidden as first character in Derby + * elements. + * + * @since 4.0 + */ + public boolean isValidFirstChar(char ch); + + /** + * Check if an exception indicates a constraint violation (duplicate key) + * + * @since 4.0 + */ + public boolean isDuplicateKeyException(SQLException ex); + + /** + * @since 4.2 + */ + public boolean isTableNotFoundException(SQLException ex); + + /** + * @since 4.2 + */ + public boolean isColumnNotFoundException(SQLException ex); + + /** + * @since 4.2 + */ + public String sqlRenameField(IDBField field, String oldName); + + /** + * @since 4.2 + */ + public String sqlModifyField(IDBField field); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnection.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnection.java new file mode 100644 index 000000000..23a8ba5fc --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnection.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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.net4j.db; + +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.util.security.IUserAware; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * @since 4.2 + * @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. + */ +public interface IDBConnection extends Connection, IUserAware +{ + public IDBDatabase getDatabase(); + + public IDBSchemaTransaction openSchemaTransaction(); + + public IDBPreparedStatement prepareStatement(String sql, ReuseProbability reuseProbability); + + public IDBPreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, ReuseProbability reuseProbability); + + /** + * @deprecated Not supported. + */ + @Deprecated + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException; + + /** + * @deprecated Not supported. + */ + @Deprecated + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * @deprecated Not supported. + */ + @Deprecated + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException; + + /** + * @deprecated Not supported. + */ + @Deprecated + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException; +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnectionProvider.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnectionProvider.java new file mode 100644 index 000000000..916926e9b --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnectionProvider.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.db; + +import javax.sql.DataSource; + +import java.sql.Connection; + +/** + * Provides a database {@link Connection connection}, roughly comparable with a {@link DataSource data source}. + * + * @author Eike Stepper + */ +public interface IDBConnectionProvider +{ + /** + * Returns a connection. + */ + public Connection getConnection() throws DBException; +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnectionProvider2.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnectionProvider2.java new file mode 100644 index 000000000..87f9a9ae8 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBConnectionProvider2.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.db; + +import org.eclipse.net4j.util.security.IUserAware; + +import javax.sql.DataSource; + +import java.sql.Connection; + +/** + * Provides a database {@link Connection connection}, roughly comparable with a {@link DataSource data source}. + * + * @author Eike Stepper + * @since 4.3 + */ +public interface IDBConnectionProvider2 extends IDBConnectionProvider, IUserAware +{ +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java new file mode 100644 index 000000000..e924788c0 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013, 2018 Eike Stepper (Loehne, 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.net4j.db; + +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.util.collection.Closeable; +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.event.IEvent; + +/** + * @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. + * @since 4.2 + */ +public interface IDBDatabase extends IContainer, IDBConnectionProvider2, Closeable +{ + public static final int DEFAULT_STATEMENT_CACHE_CAPACITY = 200; + + public IDBAdapter getAdapter(); + + public IDBSchema getSchema(); + + public IDBSchemaTransaction openSchemaTransaction(); + + /** + * @since 4.7 + */ + public IDBSchemaTransaction openSchemaTransaction(IDBConnection connection); + + /** + * @deprecated As of 4.7 no longer supported in favor of support for multiple schema transactions. + */ + @Deprecated + public IDBSchemaTransaction getSchemaTransaction(); + + public void updateSchema(RunnableWithSchema runnable); + + public IDBConnection getConnection(); + + public IDBConnection[] getConnections(); + + public int getStatementCacheCapacity(); + + public void setStatementCacheCapacity(int statementCacheCapacity); + + /** + * @author Eike Stepper + */ + public interface SchemaChangedEvent extends IEvent + { + public IDBDatabase getSource(); + + public IDBSchemaDelta getSchemaDelta(); + } + + /** + * @author Eike Stepper + */ + public interface RunnableWithSchema + { + public void run(IDBSchema schema); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBPreparedStatement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBPreparedStatement.java new file mode 100644 index 000000000..b8d78dcaf --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBPreparedStatement.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.db; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @since 4.2 + * @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 IDBPreparedStatement extends Comparable, PreparedStatement +{ + /** + * @since 4.3 + */ + public IDBConnection getConnection() throws SQLException; + + /** + * @deprecated As of 4.3 use {@link #getConnection()}. + */ + @Deprecated + public IDBConnection getTransaction(); + + public String getSQL(); + + public ReuseProbability getReuseProbability(); + + public IDBResultSet getGeneratedKeys() throws SQLException; + + public IDBResultSet getResultSet() throws SQLException; + + public IDBResultSet executeQuery() throws SQLException; + + /** + * @deprecated Not supported. + */ + @Deprecated + public ResultSet executeQuery(String sql) throws SQLException; + + /** + * 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: + *

    + *
  • For global statements which are used regularly use {@link ReuseProbability#MAX MAX}. + *
  • For constant object-specific statements which are used regularly use {@link ReuseProbability#HIGH HIGH}. + *
  • For object-specific statements which are assembled from constants which are used regularly use {@link ReuseProbability#MEDIUM MEDIUM}. + *
  • For all other dynamic statements, like queries, use {@link ReuseProbability#LOW LOW} + *
+ * + * @author Stefan Winkler + */ + public static enum ReuseProbability + { + MAX, HIGH, MEDIUM, LOW; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBResultSet.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBResultSet.java new file mode 100644 index 000000000..16c99e7a3 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBResultSet.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @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.2 + */ +public interface IDBResultSet extends ResultSet +{ + public IDBPreparedStatement getStatement() throws SQLException; +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBRowHandler.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBRowHandler.java new file mode 100644 index 000000000..4bfd25d21 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBRowHandler.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2007, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.db; + +/** + * Call-back that handles the values of, for example, a row in a database table. + * + * @see DBUtil#select(java.sql.Connection, IDBRowHandler, String, org.eclipse.net4j.db.ddl.IDBField...) + * @author Eike Stepper + */ +public interface IDBRowHandler +{ + public boolean handle(int row, Object... values); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBSchemaTransaction.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBSchemaTransaction.java new file mode 100644 index 000000000..1afb8fb66 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBSchemaTransaction.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.db; + +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.delta.IDBDelta.ChangeKind; +import org.eclipse.net4j.db.ddl.delta.IDBDelta.DeltaType; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor.Filter.Policy; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.util.collection.Closeable; + +/** + * @since 4.2 + * @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 IDBSchemaTransaction extends Closeable +{ + public static final Policy DEFAULT_ENSURE_SCHEMA_POLICY = // + new IDBDeltaVisitor.Filter.Policy().allow(DeltaType.SCHEMA, ChangeKind.CHANGE).allow(ChangeKind.ADD).freeze(); + + public IDBDatabase getDatabase(); + + public IDBConnection getConnection(); + + public IDBSchema getWorkingCopy(); + + public IDBSchemaDelta ensureSchema(IDBSchema schema, IDBDeltaVisitor.Filter.Policy policy); + + public IDBSchemaDelta ensureSchema(IDBSchema schema); + + public IDBSchemaDelta getSchemaDelta(); + + public IDBSchemaDelta commit(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBElement.java new file mode 100644 index 000000000..de64f7a9e --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBElement.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.util.event.INotifier; + +import java.util.Properties; + +/** + * @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. + * @since 4.2 + */ +public interface IDBElement extends INotifier +{ + public Properties getProperties(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java new file mode 100644 index 000000000..a91a10c8e --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBField.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2008, 2010-2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.util.collection.PositionProvider; + +/** + * A field (column) specification in a {@link IDBTable DB table}. + * + * @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. + */ +public interface IDBField extends IDBSchemaElement, PositionProvider +{ + public static final int DEFAULT = -1; + + /** + * @since 4.2 + */ + public IDBTable getParent(); + + public IDBTable getTable(); + + public DBType getType(); + + public void setType(DBType type); + + public int getPrecision(); + + public void setPrecision(int precision); + + public int getScale(); + + public void setScale(int scale); + + public boolean isNotNull(); + + public void setNotNull(boolean notNull); + + public String getFullName(); + + public String formatPrecision(); + + public String formatPrecisionAndScale(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBIndex.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBIndex.java new file mode 100644 index 000000000..788cf1134 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBIndex.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2008, 2011-2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +/** + * An index specification in a {@link IDBTable DB table}. + * + * @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. + */ +public interface IDBIndex extends IDBSchemaElement +{ + /** + * @since 4.2 + */ + public IDBTable getParent(); + + public IDBTable getTable(); + + public Type getType(); + + /** + * @since 4.2 + */ + public void setType(Type type); + + @Deprecated + public int getPosition(); + + /** + * @since 4.2 + */ + public IDBIndexField addIndexField(IDBField field); + + /** + * @since 4.2 + */ + public IDBIndexField addIndexField(String name) throws SchemaElementNotFoundException; + + /** + * @since 4.2 + */ + public IDBIndexField getIndexFieldSafe(String name) throws SchemaElementNotFoundException; + + /** + * @since 4.2 + */ + public IDBIndexField getIndexField(String name); + + /** + * @since 4.2 + */ + public IDBIndexField getIndexField(int position); + + /** + * @since 4.2 + */ + public IDBField getFieldSafe(String name) throws SchemaElementNotFoundException; + + /** + * @since 4.2 + */ + public IDBField getField(String name); + + public IDBField getField(int position); + + public int getFieldCount(); + + /** + * @since 4.2 + */ + public IDBIndexField[] getIndexFields(); + + public IDBField[] getFields(); + + /** + * The type of an {@link IDBIndex index} specification in a {@link IDBTable DB table}. + * + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + */ + public enum Type + { + PRIMARY_KEY, UNIQUE, NON_UNIQUE + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBIndexField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBIndexField.java new file mode 100644 index 000000000..97df3d39a --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBIndexField.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.util.collection.PositionProvider; + +/** + * An index field specification in a {@link IDBIndex DB index}. + * + * @since 4.2 + * @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. + */ +public interface IDBIndexField extends IDBSchemaElement, PositionProvider +{ + /** + * @since 4.2 + */ + public IDBIndex getParent(); + + public IDBIndex getIndex(); + + public IDBField getField(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBNamedElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBNamedElement.java new file mode 100644 index 000000000..465c210b6 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBNamedElement.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +/** + * @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. + * @since 4.2 + */ +public interface IDBNamedElement extends IDBElement +{ + public String getName(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchema.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchema.java new file mode 100644 index 000000000..24dbcfb09 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchema.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008, 2010-2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; + +import javax.sql.DataSource; + +import java.io.PrintStream; +import java.sql.Connection; +import java.util.Set; + +/** + * Specifies a number of {@link IDBTable DB tables} that can be created in or dropped from a database through a + * {@link IDBAdapter DB adapter}. + * + * @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. + */ +public interface IDBSchema extends IDBSchemaElement +{ + /** + * @since 4.2 + */ + public boolean isLocked(); + + /** + * @since 4.2 + */ + public T findElement(IDBSchemaElement prototype); + + public IDBTable addTable(String name) throws DBException; + + /** + * @since 4.0 + */ + public IDBTable removeTable(String name) throws DBException; + + /** + * @since 4.2 + */ + public IDBTable getTableSafe(String name) throws SchemaElementNotFoundException; + + public IDBTable getTable(String name); + + public IDBTable[] getTables(); + + public Set create(IDBAdapter dbAdapter, Connection connection) throws DBException; + + public Set create(IDBAdapter dbAdapter, DataSource dataSource) throws DBException; + + public Set create(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException; + + public void drop(IDBAdapter dbAdapter, Connection connection) throws DBException; + + public void drop(IDBAdapter dbAdapter, DataSource dataSource) throws DBException; + + public void drop(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException; + + public void export(Connection connection, PrintStream out) throws DBException; + + public void export(DataSource dataSource, PrintStream out) throws DBException; + + public void export(IDBConnectionProvider connectionProvider, PrintStream out) throws DBException; + + /** + * @since 4.2 + */ + public IDBSchemaDelta compare(IDBSchema oldSchema); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchemaElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchemaElement.java new file mode 100644 index 000000000..26b6a3100 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchemaElement.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2008, 2011-2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.util.container.IContainer; + +/** + * Specifies a hierachical namespace for elements in a {@link IDBSchema DB schema}. + * + * @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. + */ +public interface IDBSchemaElement extends IDBNamedElement, IContainer, Comparable +{ + /** + * @since 4.2 + */ + public SchemaElementType getSchemaElementType(); + + public IDBSchema getSchema(); + + /** + * @since 4.2 + */ + public IDBSchemaElement getParent(); + + /** + * @since 4.2 + */ + @Deprecated + public void setName(String name); + + public String getFullName(); + + /** + * @since 4.2 + */ + public T getElement(Class type, String name); + + /** + * @since 4.2 + */ + public void accept(IDBSchemaVisitor visitor); + + /** + * @since 4.2 + */ + public void remove(); + + /** + * @since 4.2 + * @author Eike Stepper + */ + public enum SchemaElementType + { + SCHEMA(0), TABLE(1), FIELD(2), INDEX(2), INDEX_FIELD(3); + + private final int level; + + private SchemaElementType(int level) + { + this.level = level; + } + + public int getLevel() + { + return level; + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchemaVisitor.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchemaVisitor.java new file mode 100644 index 000000000..63b405471 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBSchemaVisitor.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl; + +/** + * @since 4.2 + * @author Eike Stepper + */ +public interface IDBSchemaVisitor +{ + public void visit(IDBSchema schema); + + public void visit(IDBTable table); + + public void visit(IDBField field); + + public void visit(IDBIndex index); + + public void visit(IDBIndexField indexField); + + /** + * @author Eike Stepper + */ + public static final class StopRecursion extends RuntimeException + { + private static final long serialVersionUID = 1L; + } + + /** + * @author Eike Stepper + */ + public static class Default implements IDBSchemaVisitor + { + public void visit(IDBSchema element) + { + visitDefault(element); + } + + public void visit(IDBTable element) + { + visitDefault(element); + } + + public void visit(IDBField element) + { + visitDefault(element); + } + + public void visit(IDBIndex element) + { + visitDefault(element); + } + + public void visit(IDBIndexField element) + { + visitDefault(element); + } + + protected void visitDefault(IDBSchemaElement element) + { + visitDefault(element); + } + + protected final void stopRecursion() + { + throw new StopRecursion(); + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBTable.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBTable.java new file mode 100644 index 000000000..67543a536 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/IDBTable.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2008, 2011-2013, 2015 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.db.DBType; + +/** + * A table specification in a {@link IDBSchema DB schema}. + * + * @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. + */ +public interface IDBTable extends IDBSchemaElement +{ + /** + * @since 4.2 + */ + public IDBSchema getParent(); + + public IDBField addField(String name, DBType type); + + public IDBField addField(String name, DBType type, boolean notNull); + + public IDBField addField(String name, DBType type, int precision); + + public IDBField addField(String name, DBType type, int precision, boolean notNull); + + public IDBField addField(String name, DBType type, int precision, int scale); + + public IDBField addField(String name, DBType type, int precision, int scale, boolean notNull); + + /** + * @since 4.2 + */ + public IDBField getFieldSafe(String name) throws SchemaElementNotFoundException; + + public IDBField getField(String name); + + public IDBField getField(int position); + + public int getFieldCount(); + + public IDBField[] getFields(); + + /** + * @since 4.2 + */ + public IDBField[] getFields(String... fieldNames) throws SchemaElementNotFoundException; + + /** + * @since 4.5 + */ + public boolean hasIndexFor(IDBField... fields); + + /** + * @since 4.2 + */ + public IDBIndex addIndex(String name, IDBIndex.Type type, IDBField... fields); + + /** + * @since 4.2 + */ + public IDBIndex addIndex(String name, IDBIndex.Type type, String... fieldNames) throws SchemaElementNotFoundException; + + /** + * @since 4.2 + */ + public IDBIndex addIndexEmpty(String name, IDBIndex.Type type); + + public IDBIndex addIndex(IDBIndex.Type type, IDBField... fields); + + /** + * @since 4.2 + */ + public IDBIndex addIndex(IDBIndex.Type type, String... fieldNames) throws SchemaElementNotFoundException; + + /** + * @since 4.2 + */ + public IDBIndex addIndexEmpty(IDBIndex.Type type); + + /** + * @since 4.2 + */ + public IDBIndex getIndexSafe(String name) throws SchemaElementNotFoundException; + + /** + * @since 4.2 + */ + public IDBIndex getIndex(String name); + + /** + * @since 4.2 + */ + public IDBIndex getIndex(int position); + + public int getIndexCount(); + + public IDBIndex[] getIndices(); + + public IDBIndex getPrimaryKeyIndex(); + + public String sqlInsert(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/SchemaElementNotFoundException.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/SchemaElementNotFoundException.java new file mode 100644 index 000000000..7c3e976b5 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/SchemaElementNotFoundException.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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.net4j.db.ddl; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.internal.db.ddl.DBSchemaElement; + +/** + * @since 4.2 + * @author Eike Stepper + */ +public class SchemaElementNotFoundException extends DBException +{ + private static final long serialVersionUID = 1L; + + private final IDBSchemaElement parent; + + private final IDBSchemaElement.SchemaElementType type; + + private final String name; + + public SchemaElementNotFoundException(IDBSchemaElement parent, IDBSchemaElement.SchemaElementType type, String name) + { + super(type.toString() + " " + DBSchemaElement.name(name) + " not found in " + parent.getSchemaElementType() + " " + parent); + this.parent = parent; + this.type = type; + this.name = DBSchemaElement.name(name); + } + + public IDBSchemaElement getParent() + { + return parent; + } + + public IDBSchemaElement.SchemaElementType getType() + { + return type; + } + + public String getName() + { + return name; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDelta.java new file mode 100644 index 000000000..0c99d1b20 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDelta.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBNamedElement; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.util.container.IContainer; + +/** + * @since 4.2 + * @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 IDBDelta extends IDBNamedElement, IContainer, Comparable +{ + public DeltaType getDeltaType(); + + public IDBDelta getParent(); + + public ChangeKind getChangeKind(); + + public void accept(IDBDeltaVisitor visitor); + + public IDBSchemaElement getSchemaElement(IDBSchema schema); + + /** + * @author Eike Stepper + */ + public enum DeltaType + { + SCHEMA, TABLE, FIELD, INDEX, INDEX_FIELD, PROPERTY + } + + /** + * @author Eike Stepper + */ + public enum ChangeKind + { + ADD, REMOVE, CHANGE + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java new file mode 100644 index 000000000..1dc20b00b --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java @@ -0,0 +1,862 @@ +/* + * Copyright (c) 2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.delta.IDBDelta.ChangeKind; +import org.eclipse.net4j.db.ddl.delta.IDBDelta.DeltaType; +import org.eclipse.net4j.internal.db.ddl.delta.DBDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBDeltaWithProperties; +import org.eclipse.net4j.internal.db.ddl.delta.DBFieldDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBIndexDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBIndexFieldDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBPropertyDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBTableDelta; +import org.eclipse.net4j.util.collection.Pair; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * @since 4.2 + * @author Eike Stepper + */ +public interface IDBDeltaVisitor +{ + public void visit(IDBSchemaDelta delta); + + public void visit(IDBTableDelta delta); + + public void visit(IDBFieldDelta delta); + + public void visit(IDBIndexDelta delta); + + public void visit(IDBIndexFieldDelta delta); + + public void visit(IDBPropertyDelta delta); + + /** + * @author Eike Stepper + */ + public static final class StopRecursion extends RuntimeException + { + private static final long serialVersionUID = 1L; + } + + /** + * @author Eike Stepper + */ + public static class Default implements IDBDeltaVisitor + { + public void visit(IDBSchemaDelta delta) + { + if (handle(delta)) + { + ChangeKind changeKind = delta.getChangeKind(); + switch (changeKind) + { + case ADD: + added(delta); + break; + + case REMOVE: + removed(delta); + break; + + case CHANGE: + changed(delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + else + { + stopRecursion(); + } + } + + protected void added(IDBSchemaDelta delta) + { + visitDefault(delta); + } + + protected void removed(IDBSchemaDelta delta) + { + visitDefault(delta); + } + + protected void changed(IDBSchemaDelta delta) + { + visitDefault(delta); + } + + public void visit(IDBTableDelta delta) + { + if (handle(delta)) + { + ChangeKind changeKind = delta.getChangeKind(); + switch (changeKind) + { + case ADD: + added(delta); + break; + + case REMOVE: + removed(delta); + break; + + case CHANGE: + changed(delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + else + { + stopRecursion(); + } + } + + protected void added(IDBTableDelta delta) + { + visitDefault(delta); + } + + protected void removed(IDBTableDelta delta) + { + visitDefault(delta); + } + + protected void changed(IDBTableDelta delta) + { + visitDefault(delta); + } + + public void visit(IDBFieldDelta delta) + { + if (handle(delta)) + { + ChangeKind changeKind = delta.getChangeKind(); + switch (changeKind) + { + case ADD: + added(delta); + break; + + case REMOVE: + removed(delta); + break; + + case CHANGE: + changed(delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + else + { + stopRecursion(); + } + } + + protected void added(IDBFieldDelta delta) + { + visitDefault(delta); + } + + protected void removed(IDBFieldDelta delta) + { + visitDefault(delta); + } + + protected void changed(IDBFieldDelta delta) + { + visitDefault(delta); + } + + public void visit(IDBIndexDelta delta) + { + if (handle(delta)) + { + ChangeKind changeKind = delta.getChangeKind(); + switch (changeKind) + { + case ADD: + added(delta); + break; + + case REMOVE: + removed(delta); + break; + + case CHANGE: + changed(delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + else + { + stopRecursion(); + } + } + + protected void added(IDBIndexDelta delta) + { + visitDefault(delta); + } + + protected void removed(IDBIndexDelta delta) + { + visitDefault(delta); + } + + protected void changed(IDBIndexDelta delta) + { + visitDefault(delta); + } + + public void visit(IDBIndexFieldDelta delta) + { + if (handle(delta)) + { + ChangeKind changeKind = delta.getChangeKind(); + switch (changeKind) + { + case ADD: + added(delta); + break; + + case REMOVE: + removed(delta); + break; + + case CHANGE: + changed(delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + else + { + stopRecursion(); + } + } + + protected void added(IDBIndexFieldDelta delta) + { + visitDefault(delta); + } + + protected void removed(IDBIndexFieldDelta delta) + { + visitDefault(delta); + } + + protected void changed(IDBIndexFieldDelta delta) + { + visitDefault(delta); + } + + public void visit(IDBPropertyDelta delta) + { + if (handle(delta)) + { + ChangeKind changeKind = delta.getChangeKind(); + switch (changeKind) + { + case ADD: + added(delta); + break; + + case REMOVE: + removed(delta); + break; + + case CHANGE: + changed(delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + else + { + stopRecursion(); + } + } + + protected void added(IDBPropertyDelta delta) + { + visitDefault(delta); + + } + + protected void removed(IDBPropertyDelta delta) + { + visitDefault(delta); + } + + protected void changed(IDBPropertyDelta delta) + { + visitDefault(delta); + } + + protected void visitDefault(IDBDelta delta) + { + } + + protected boolean handle(IDBDelta delta) + { + return true; + } + + protected final void stopRecursion() + { + throw new StopRecursion(); + } + + /** + * @since 4.6 + */ + public static RuntimeException illegalChangeKind(ChangeKind changeKind) + { + return new IllegalStateException("Illegal change kind: " + changeKind); + } + } + + /** + * @author Eike Stepper + */ + public static class Filter extends Default + { + public static final Policy DEFAULT_POLICY = new Policy().allowAll().freeze(); + + private Policy policy; + + public Filter() + { + this(null); + } + + public Filter(Policy policy) + { + this.policy = policy == null ? DEFAULT_POLICY : policy; + } + + public final Policy getPolicy() + { + return policy; + } + + @Override + protected void added(IDBSchemaDelta delta) + { + doVisit(delta); + } + + @Override + protected void added(IDBTableDelta delta) + { + doVisit(delta); + } + + @Override + protected void added(IDBFieldDelta delta) + { + doVisit(delta); + } + + @Override + protected void added(IDBIndexDelta delta) + { + doVisit(delta); + } + + @Override + protected void added(IDBIndexFieldDelta delta) + { + doVisit(delta); + } + + @Override + protected void added(IDBPropertyDelta delta) + { + doVisit(delta); + } + + @Override + protected void removed(IDBSchemaDelta delta) + { + doVisit(delta); + } + + @Override + protected void removed(IDBTableDelta delta) + { + doVisit(delta); + } + + @Override + protected void removed(IDBFieldDelta delta) + { + doVisit(delta); + } + + @Override + protected void removed(IDBIndexDelta delta) + { + doVisit(delta); + } + + @Override + protected void removed(IDBIndexFieldDelta delta) + { + doVisit(delta); + } + + @Override + protected void removed(IDBPropertyDelta delta) + { + doVisit(delta); + } + + @Override + protected void changed(IDBSchemaDelta delta) + { + doVisit(delta); + } + + @Override + protected void changed(IDBTableDelta delta) + { + doVisit(delta); + } + + @Override + protected void changed(IDBFieldDelta delta) + { + doVisit(delta); + } + + @Override + protected void changed(IDBIndexDelta delta) + { + doVisit(delta); + } + + @Override + protected void changed(IDBIndexFieldDelta delta) + { + doVisit(delta); + } + + @Override + protected void changed(IDBPropertyDelta delta) + { + doVisit(delta); + } + + protected void doVisit(IDBSchemaDelta delta) + { + visitDefault(delta); + } + + protected void doVisit(IDBTableDelta delta) + { + visitDefault(delta); + } + + protected void doVisit(IDBFieldDelta delta) + { + visitDefault(delta); + } + + protected void doVisit(IDBIndexDelta delta) + { + visitDefault(delta); + } + + protected void doVisit(IDBIndexFieldDelta delta) + { + visitDefault(delta); + } + + protected void doVisit(IDBPropertyDelta delta) + { + visitDefault(delta); + } + + @Override + protected final boolean handle(IDBDelta delta) + { + if (policy.isForbidden(delta)) + { + throw new ForbiddenChangeException(delta); + } + + if (policy.isAllowed(delta)) + { + return true; + } + + return false; + } + + /** + * @author Eike Stepper + */ + public static final class Policy implements Serializable + { + public static final Object ALLOWED = "ALLOWED"; + + public static final Object FORBIDDEN = "FORBIDDEN"; + + public static final Object IGNORED = "IGNORED"; + + private static final long serialVersionUID = 1L; + + private Map clauses = new HashMap(); + + private transient boolean frozen; + + public Policy() + { + } + + public boolean isAllowed(DeltaType deltaType) + { + Object value = clauses.get(deltaType); + return value == ALLOWED; + } + + public boolean isAllowed(ChangeKind changeKind) + { + Object value = clauses.get(changeKind); + return value == ALLOWED; + } + + public boolean isAllowed(DeltaType deltaType, ChangeKind changeKind) + { + Object value = clauses.get(Pair.create(deltaType, changeKind)); + if (value == null) + { + value = clauses.get(deltaType); + if (value == null) + { + value = clauses.get(changeKind); + } + } + + return value == ALLOWED; + } + + public boolean isAllowed(IDBDelta delta) + { + return isAllowed(delta.getDeltaType(), delta.getChangeKind()); + } + + public boolean isForbidden(DeltaType deltaType) + { + Object value = clauses.get(deltaType); + return value == FORBIDDEN; + } + + public boolean isForbidden(ChangeKind changeKind) + { + Object value = clauses.get(changeKind); + return value == FORBIDDEN; + } + + public boolean isForbidden(DeltaType deltaType, ChangeKind changeKind) + { + Object value = clauses.get(Pair.create(deltaType, changeKind)); + if (value == null) + { + value = clauses.get(deltaType); + if (value == null) + { + value = clauses.get(changeKind); + } + } + + return value == FORBIDDEN; + } + + public boolean isForbidden(IDBDelta delta) + { + return isForbidden(delta.getDeltaType(), delta.getChangeKind()); + } + + public boolean isIgnored(DeltaType deltaType) + { + Object value = clauses.get(deltaType); + return value == null || value == IGNORED; + } + + public boolean isIgnored(ChangeKind changeKind) + { + Object value = clauses.get(changeKind); + return value == null || value == IGNORED; + } + + public boolean isIgnored(DeltaType deltaType, ChangeKind changeKind) + { + Object value = clauses.get(Pair.create(deltaType, changeKind)); + if (value == null) + { + value = clauses.get(deltaType); + if (value == null) + { + value = clauses.get(changeKind); + } + } + + return value == null || value == IGNORED; + } + + public boolean isIgnored(IDBDelta delta) + { + return isIgnored(delta.getDeltaType(), delta.getChangeKind()); + } + + public Policy allow(DeltaType deltaType) + { + return addClause(deltaType, ALLOWED); + } + + public Policy allow(ChangeKind changeKind) + { + return addClause(changeKind, ALLOWED); + } + + public Policy allow(DeltaType deltaType, ChangeKind changeKind) + { + return addClause(Pair.create(deltaType, changeKind), ALLOWED); + } + + public Policy allowAll() + { + return ignoreAll().allow(ChangeKind.ADD).allow(ChangeKind.REMOVE).allow(ChangeKind.CHANGE); + } + + public Policy forbid(DeltaType deltaType) + { + return addClause(deltaType, FORBIDDEN); + } + + public Policy forbid(ChangeKind changeKind) + { + return addClause(changeKind, FORBIDDEN); + } + + public Policy forbid(DeltaType deltaType, ChangeKind changeKind) + { + return addClause(Pair.create(deltaType, changeKind), FORBIDDEN); + } + + public Policy forbidAll() + { + return ignoreAll().forbid(ChangeKind.ADD).forbid(ChangeKind.REMOVE).forbid(ChangeKind.CHANGE); + } + + public Policy ignore(DeltaType deltaType) + { + return removeClause(deltaType); + } + + public Policy ignore(ChangeKind changeKind) + { + return removeClause(changeKind); + } + + public Policy ignore(DeltaType deltaType, ChangeKind changeKind) + { + return removeClause(Pair.create(deltaType, changeKind)); + } + + public Policy ignoreAll() + { + checkFrozen(); + clauses.clear(); + return this; + } + + @Override + public String toString() + { + return "Policy" + clauses; + } + + public Policy freeze() + { + frozen = true; + return this; + } + + private void checkFrozen() + { + if (frozen) + { + throw new IllegalStateException("Policy is frozen: " + this); + } + } + + private Policy addClause(Object key, Object value) + { + checkFrozen(); + clauses.put(key, value); + return this; + } + + private Policy removeClause(Object key) + { + checkFrozen(); + clauses.remove(key); + return this; + } + } + + /** + * @author Eike Stepper + */ + public static final class ForbiddenChangeException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + private final IDBDelta delta; + + public ForbiddenChangeException(IDBDelta delta) + { + super("Forbidden change: " + delta); + this.delta = delta; + } + + public IDBDelta getDelta() + { + return delta; + } + } + } + + /** + * @author Eike Stepper + */ + public static class Copier extends Filter + { + private DBSchemaDelta result; + + public Copier() + { + } + + public Copier(Policy policy) + { + super(policy); + } + + public final IDBSchemaDelta getResult() + { + return result; + } + + @Override + protected void doVisit(IDBSchemaDelta delta) + { + result = new DBSchemaDelta(delta.getName(), delta.getChangeKind()); + } + + @Override + protected void doVisit(IDBTableDelta delta) + { + DBTableDelta copy = new DBTableDelta(result, delta.getName(), delta.getChangeKind()); + result.addTableDelta(copy); + } + + @Override + protected void doVisit(IDBFieldDelta delta) + { + DBTableDelta parentCopy = getParentCopy(delta); + DBFieldDelta copy = new DBFieldDelta(parentCopy, delta.getName(), delta.getChangeKind()); + parentCopy.addFieldDelta(copy); + } + + @Override + protected void doVisit(IDBIndexDelta delta) + { + DBTableDelta parentCopy = getParentCopy(delta); + DBIndexDelta copy = new DBIndexDelta(parentCopy, delta.getName(), delta.getChangeKind()); + parentCopy.addIndexDelta(copy); + } + + @Override + protected void doVisit(IDBIndexFieldDelta delta) + { + DBIndexDelta parentCopy = getParentCopy(delta); + DBIndexFieldDelta copy = new DBIndexFieldDelta(parentCopy, delta.getName(), delta.getChangeKind()); + parentCopy.addIndexFieldDelta(copy); + } + + @Override + protected void doVisit(IDBPropertyDelta delta) + { + DBDeltaWithProperties parentCopy = getParentCopy(delta); + @SuppressWarnings({ "rawtypes", "unchecked" }) + DBPropertyDelta copy = new DBPropertyDelta(parentCopy, delta.getName(), delta.getType(), delta.getValue(), delta.getOldValue()); + parentCopy.addPropertyDelta(copy); + } + + @SuppressWarnings("unchecked") + private T getParentCopy(IDBDelta delta) + { + if (result == null) + { + throw new IllegalStateException("Copier can only be accepted by schema deltas"); + } + + DBDelta parent = (DBDelta)delta.getParent(); + DeltaType deltaType = parent.getDeltaType(); + switch (deltaType) + { + case SCHEMA: + return (T)result; + + case TABLE: + return (T)result.getTableDelta(parent.getName()); + + case FIELD: + return (T)result.getTableDelta(parent.getParent().getName()).getFieldDelta(parent.getName()); + + case INDEX: + return (T)result.getTableDelta(parent.getParent().getName()).getIndexDelta(parent.getName()); + + case INDEX_FIELD: + return (T)result.getTableDelta(parent.getParent().getParent().getName()).getIndexDelta(parent.getParent().getName()) + .getIndexFieldDelta(parent.getName()); + + default: + throw new IllegalStateException("Illegal delta type: " + deltaType); + } + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaWithPosition.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaWithPosition.java new file mode 100644 index 000000000..532975479 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaWithPosition.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.util.collection.PositionProvider; + +/** + * @since 4.2 + * @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 IDBDeltaWithPosition extends IDBDeltaWithProperties, PositionProvider +{ + public static final String POSITION_PROPERTY = "position"; +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaWithProperties.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaWithProperties.java new file mode 100644 index 000000000..8e5b1fe4d --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaWithProperties.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import java.util.Map; + +/** + * @since 4.2 + * @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 IDBDeltaWithProperties extends IDBDelta +{ + public IDBPropertyDelta getPropertyDelta(String name); + + public T getPropertyValue(String name); + + public T getPropertyValue(String name, boolean old); + + public Map> getPropertyDeltas(); + + public IDBPropertyDelta[] getPropertyDeltasSortedByName(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBFieldDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBFieldDelta.java new file mode 100644 index 000000000..85a848215 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBFieldDelta.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchema; + +/** + * @since 4.2 + * @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 IDBFieldDelta extends IDBDeltaWithPosition +{ + public static final String TYPE_PROPERTY = "type"; + + public static final String PRECISION_PROPERTY = "precision"; + + public static final String SCALE_PROPERTY = "scale"; + + public static final String NOT_NULL_PROPERTY = "notNull"; + + public IDBTableDelta getParent(); + + public IDBField getSchemaElement(IDBSchema schema); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBIndexDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBIndexDelta.java new file mode 100644 index 000000000..af707cf3e --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBIndexDelta.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBSchema; + +import java.util.Map; + +/** + * @since 4.2 + * @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 IDBIndexDelta extends IDBDeltaWithProperties +{ + public static final String TYPE_PROPERTY = "type"; + + /** + * @since 4.5 + */ + public static final String OPTIONAL_PROPERTY = "optional"; + + public IDBTableDelta getParent(); + + public int getIndexFieldDeltaCount(); + + public IDBIndexFieldDelta getIndexFieldDelta(int position); + + public IDBIndexFieldDelta getIndexFieldDelta(String name); + + public Map getIndexFieldDeltas(); + + public IDBIndexFieldDelta[] getIndexFieldDeltasSortedByPosition(); + + public IDBIndex getSchemaElement(IDBSchema schema); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBIndexFieldDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBIndexFieldDelta.java new file mode 100644 index 000000000..7c5e65c49 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBIndexFieldDelta.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; + +/** + * @since 4.2 + * @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 IDBIndexFieldDelta extends IDBDeltaWithPosition +{ + public IDBIndexDelta getParent(); + + public IDBIndexField getSchemaElement(IDBSchema schema); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBPropertyDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBPropertyDelta.java new file mode 100644 index 000000000..232cba68c --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBPropertyDelta.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +/** + * @since 4.2 + * @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 IDBPropertyDelta extends IDBDelta +{ + public IDBPropertyDelta.Type getType(); + + public T getValue(); + + public T getOldValue(); + + /** + * @author Eike Stepper + */ + public enum Type + { + BOOLEAN, INTEGER, STRING, FIELD_TYPE, INDEX_TYPE + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBSchemaDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBSchemaDelta.java new file mode 100644 index 000000000..8ea369388 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBSchemaDelta.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBSchema; + +import java.util.Map; + +/** + * @since 4.2 + * @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 IDBSchemaDelta extends IDBDelta +{ + public int getTableDeltaCount(); + + public IDBTableDelta getTableDelta(String name); + + public Map getTableDeltas(); + + public IDBTableDelta[] getTableDeltasSortedByName(); + + public IDBSchema getSchemaElement(IDBSchema schema); + + public void applyTo(IDBSchema schema); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBTableDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBTableDelta.java new file mode 100644 index 000000000..0a4bb605c --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBTableDelta.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; + +import java.util.Map; + +/** + * @since 4.2 + * @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 IDBTableDelta extends IDBDelta +{ + public IDBSchemaDelta getParent(); + + public int getFieldDeltaCount(); + + public int getIndexDeltaCount(); + + public IDBFieldDelta getFieldDelta(int position); + + public IDBFieldDelta getFieldDelta(String name); + + public IDBIndexDelta getIndexDelta(String name); + + public Map getFieldDeltas(); + + public Map getIndexDeltas(); + + public IDBFieldDelta[] getFieldDeltasSortedByPosition(); + + public IDBIndexDelta[] getIndexDeltasSortedByName(); + + public IDBTable getSchemaElement(IDBSchema schema); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/package-info.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/package-info.java new file mode 100644 index 000000000..68f6b7442 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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 + */ + +/** + * The Net4j DB framework concepts for the abstraction of schema deltas. + */ +package org.eclipse.net4j.db.ddl.delta; diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/package-info.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/package-info.java new file mode 100644 index 000000000..5fbbbe2c3 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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 + */ + +/** + * The Net4j DB framework concepts for the abstraction of the SQL data definition language. + */ +package org.eclipse.net4j.db.ddl; diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/IDBParameter.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/IDBParameter.java new file mode 100644 index 000000000..fe34f2910 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/IDBParameter.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008, 2011-2013 Eike Stepper (Loehne, 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.net4j.db.dml; + +import org.eclipse.net4j.db.DBType; + +/** + * A parameter specification in a {@link IDBStatement DB statement}. + * + * @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. + * @deprecated + */ +@Deprecated +public interface IDBParameter +{ + public IDBStatement getStatement(); + + /** + * Returns the zero based position of this parameter within the {@link IDBStatement#getParameters() parameters} list + * of the containing {@link #getStatement() statement}. + */ + public int getPosition(); + + public DBType getType(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/IDBStatement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/IDBStatement.java new file mode 100644 index 000000000..267d30158 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/IDBStatement.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008, 2011-2013 Eike Stepper (Loehne, 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.net4j.db.dml; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; + +/** + * Specifies an SQL statement with zero or more {@link IDBParameter parameters}. + * + * @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. + * @deprecated + */ +@Deprecated +public interface IDBStatement +{ + public IDBParameter addParameter(DBType type); + + public IDBParameter addParameter(IDBField field); + + public IDBParameter[] getParameters(); + + public void addSQL(String literal); + + public void addSQL(IDBParameter parameter); + + public void addSQL(IDBSchemaElement schemaElement); + + public String getSQL(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/package-info.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/package-info.java new file mode 100644 index 000000000..c3148ba26 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/dml/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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 + */ + +/** + * The Net4j DB framework concepts for the abstraction of the SQL data manipulation language. + */ +package org.eclipse.net4j.db.dml; diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/package-info.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/package-info.java new file mode 100644 index 000000000..4ac73fc1a --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/db/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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 + */ + +/** + * The Net4j DB framework. + */ +package org.eclipse.net4j.db; diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/BatchedStatementImpl.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/BatchedStatementImpl.java new file mode 100644 index 000000000..1e36554b1 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/BatchedStatementImpl.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.BatchedStatement; +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.jdbc.DelegatingPreparedStatement; + +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.5 + */ +public final class BatchedStatementImpl extends DelegatingPreparedStatement implements BatchedStatement +{ + private final int batchSize; + + private int batchCount; + + private int totalResult; + + public BatchedStatementImpl(PreparedStatement delegate, int batchSize) throws DBException + { + super(delegate, getConnection(delegate)); + this.batchSize = batchSize; + } + + public int getBatchSize() + { + return batchSize; + } + + public int getBatchCount() + { + return batchCount; + } + + public int getTotalResult() + { + return totalResult; + } + + @Override + public int executeUpdate() throws SQLException + { + PreparedStatement delegate = getDelegate(); + delegate.addBatch(); + + if (++batchCount >= batchSize) + { + return doExecuteBatch(); + } + + return 0; + } + + @Override + public void close() throws SQLException + { + if (batchCount != 0) + { + doExecuteBatch(); + } + + super.close(); + } + + @Override + public ResultSet getResultSet() throws SQLException + { + throw new UnsupportedOperationException("Only updates are supported"); + } + + @Override + public ResultSet executeQuery() throws SQLException + { + throw new UnsupportedOperationException("Only updates are supported"); + } + + @Deprecated + @Override + public ResultSet executeQuery(String sql) throws SQLException + { + throw new UnsupportedOperationException("Only updates are supported"); + } + + private int doExecuteBatch() throws SQLException + { + int sum = 0; + + int[] results = getDelegate().executeBatch(); + for (int i = 0; i < results.length; i++) + { + int result = results[i]; + if (result != Statement.SUCCESS_NO_INFO) + { + if (result < 0) + { + throw new DBException("Result " + i + " is not successful: " + result); + } + + sum += result; + } + } + + totalResult += sum; + return sum; + } + + private static Connection getConnection(PreparedStatement delegate) throws DBException + { + try + { + return delegate.getConnection(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBAdapterDescriptor.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBAdapterDescriptor.java new file mode 100644 index 000000000..dc4fd1498 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBAdapterDescriptor.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.IDBAdapter; + +/** + * @author Eike Stepper + */ +public abstract class DBAdapterDescriptor +{ + private String name; + + public DBAdapterDescriptor(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + + public abstract IDBAdapter createDBAdapter(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBAdapterRegistry.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBAdapterRegistry.java new file mode 100644 index 000000000..314d265ed --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBAdapterRegistry.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2007, 2008, 2011, 2012, 2015 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.util.registry.HashMapRegistry; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public class DBAdapterRegistry extends HashMapRegistry +{ + public static final DBAdapterRegistry INSTANCE = new DBAdapterRegistry(); + + private Map descriptors = new HashMap(); + + public DBAdapterRegistry() + { + } + + public DBAdapterRegistry(int initialCapacity) + { + super(initialCapacity); + } + + public DBAdapterRegistry(int initialCapacity, float loadFactor) + { + super(initialCapacity, loadFactor); + } + + public DBAdapterRegistry(Map m) + { + super(m); + } + + @Override + public IDBAdapter get(Object key) + { + IDBAdapter adapter = super.get(key); + if (adapter == null) + { + if (key instanceof String) + { + DBAdapterDescriptor descriptor = descriptors.get(key); + if (descriptor != null) + { + adapter = descriptor.createDBAdapter(); + if (adapter != null) + { + put((String)key, adapter); + } + } + } + } + + return adapter; + } + + public DBAdapterDescriptor addDescriptor(DBAdapterDescriptor descriptor) + { + return descriptors.put(descriptor.getName(), descriptor); + } + + public DBAdapterDescriptor removeDescriptor(String name) + { + return descriptors.remove(name); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java new file mode 100644 index 000000000..c69d82209 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.jdbc.DelegatingConnection; +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.collection.HashBag; +import org.eclipse.net4j.util.om.OMPlatform; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.TreeMap; + +/** + * @author Eike Stepper + */ +public final class DBConnection extends DelegatingConnection implements IDBConnection +{ + private static final boolean VALIDATE_CHECKOUTS = OMPlatform.INSTANCE.isProperty("org.eclipse.net4j.internal.db.DBConnection.VALIDATE_CHECKOUTS"); + + private final TreeMap cache = new TreeMap(); + + private HashBag checkOuts; + + private final DBDatabase database; + + private int cacheSize; + + private int lastTouch; + + private boolean closed; + + public DBConnection(DBDatabase database, Connection delegate) + { + super(delegate); + this.database = database; + + if (VALIDATE_CHECKOUTS) + { + checkOuts = new HashBag(); + } + + try + { + delegate.setAutoCommit(false); + } + catch (SQLException ex) + { + throw new DBException(ex, "SET AUTO COMMIT = false"); + } + } + + public DBDatabase getDatabase() + { + return database; + } + + public String getUserID() + { + return database.getUserID(); + } + + @Override + public void close() + { + DBUtil.close(getDelegate()); + // System.out.println("-- Open connections: " + --COUNT); + closed = true; + database.closeConnection(this); + } + + @Override + public boolean isClosed() + { + return closed; + } + + public IDBSchemaTransaction openSchemaTransaction() + { + DBSchemaTransaction schemaTransaction = database.openSchemaTransaction(this); + return schemaTransaction; + } + + @Override + @Deprecated + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException + { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException + { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException + { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException + { + throw new UnsupportedOperationException(); + } + + public IDBPreparedStatement prepareStatement(String sql, ReuseProbability reuseProbability) + { + return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, reuseProbability); + } + + public IDBPreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, ReuseProbability reuseProbability) + { + database.beginSchemaAccess(false); + + DBPreparedStatement preparedStatement; + synchronized (this) + { + preparedStatement = cache.remove(sql); + if (preparedStatement == null) + { + try + { + PreparedStatement delegate = getDelegate().prepareStatement(sql, resultSetType, resultSetConcurrency); + preparedStatement = new DBPreparedStatement(this, sql, reuseProbability, delegate); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + else + { + --cacheSize; + + DBPreparedStatement nextCached = preparedStatement.getNextCached(); + if (nextCached != null) + { + cache.put(sql, nextCached); + preparedStatement.setNextCached(null); + } + } + + if (VALIDATE_CHECKOUTS) + { + checkOuts.add(preparedStatement); + } + } + + return preparedStatement; + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException + { + return prepareStatement(sql, ReuseProbability.LOW); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException + { + return prepareStatement(sql, resultSetType, resultSetConcurrency, ReuseProbability.LOW); + } + + public void releasePreparedStatement(DBPreparedStatement preparedStatement) + { + try + { + if (preparedStatement == null) + { + // Bug 276926: Silently accept preparedStatement == null and do nothing. + return; + } + + synchronized (this) + { + if (VALIDATE_CHECKOUTS) + { + checkOuts.remove(preparedStatement); + } + + preparedStatement.setTouch(++lastTouch); + + String sql = preparedStatement.getSQL(); + DBPreparedStatement cached = cache.put(sql, preparedStatement); + if (cached != null) + { + preparedStatement.setNextCached(cached); + } + + if (++cacheSize > database.getStatementCacheCapacity()) + { + String firstKey = cache.firstKey(); + DBPreparedStatement old = cache.remove(firstKey); + DBPreparedStatement nextCached = old.getNextCached(); + + DBUtil.close(old.getDelegate()); + --cacheSize; + + if (nextCached != null) + { + cache.put(firstKey, nextCached); + } + } + } + } + finally + { + database.endSchemaAccess(); + } + } + + public void invalidateStatementCache() + { + synchronized (this) + { + if (VALIDATE_CHECKOUTS) + { + CheckUtil.checkState(checkOuts.isEmpty(), "Statements are checked out: " + checkOuts); + } + + // Close all statements in the cache, then clear the cache. + for (DBPreparedStatement preparedStatement : cache.values()) + { + while (preparedStatement != null) + { + PreparedStatement delegate = preparedStatement.getDelegate(); + DBUtil.close(delegate); + + preparedStatement = preparedStatement.getNextCached(); + } + } + + cache.clear(); + cacheSize = 0; + } + } + + public String convertString(DBPreparedStatement preparedStatement, int parameterIndex, String value) + { + return getDatabase().convertString(preparedStatement, parameterIndex, value); + } + + public String convertString(DBResultSet resultSet, int columnIndex, String value) + { + return getDatabase().convertString(resultSet, columnIndex, value); + } + + public String convertString(DBResultSet resultSet, String columnLabel, String value) + { + return getDatabase().convertString(resultSet, columnLabel, value); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java new file mode 100644 index 000000000..9d77344f4 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2013, 2015, 2016, 2018 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.DBUtil.RunnableWithConnection; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta; +import org.eclipse.net4j.spi.db.DBAdapter; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; +import org.eclipse.net4j.util.container.SetContainer; +import org.eclipse.net4j.util.event.Event; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.security.IUserAware; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.LinkedList; + +/** + * @author Eike Stepper + */ +public final class DBDatabase extends SetContainer implements IDBDatabase +{ + private static final long TIMEOUT_SCHEMA_ACCESS = OMPlatform.INSTANCE.getProperty("org.eclipse.net4j.internal.db.DBDatabase.TIMEOUT_SCHEMA_ACCESS", 15000L); + + private static final boolean DEBUG_SCHEMA_ACCESS = OMPlatform.INSTANCE.isProperty("org.eclipse.net4j.internal.db.DBDatabase.DEBUG_SCHEMA_ACCESS"); + + private DBAdapter adapter; + + private IDBConnectionProvider connectionProvider; + + private int statementCacheCapacity = DEFAULT_STATEMENT_CACHE_CAPACITY; + + private IDBSchema schema; + + private final LinkedList schemaAccessQueue = new LinkedList(); + + private int schemaWriters; + + public DBDatabase(final DBAdapter adapter, IDBConnectionProvider connectionProvider, final String schemaName, final boolean fixNullableIndexColumns) + { + super(IDBConnection.class); + this.adapter = adapter; + this.connectionProvider = connectionProvider; + + schema = DBUtil.execute(DBDatabase.this, new RunnableWithConnection() + { + public IDBSchema run(Connection connection) throws SQLException + { + return DBUtil.readSchema(adapter, connection, schemaName, fixNullableIndexColumns); + } + }); + + ((InternalDBSchema)schema).lock(); + activate(); + } + + public String getUserID() + { + if (connectionProvider instanceof IUserAware) + { + return ((IUserAware)connectionProvider).getUserID(); + } + + return null; + } + + public DBAdapter getAdapter() + { + return adapter; + } + + public IDBSchema getSchema() + { + return schema; + } + + public DBSchemaTransaction openSchemaTransaction() + { + return openSchemaTransaction(null); + } + + public DBSchemaTransaction openSchemaTransaction(IDBConnection connection) + { + DBSchemaTransaction schemaTransaction = new DBSchemaTransaction(this); + schemaTransaction.setConnection((DBConnection)connection); + return schemaTransaction; + } + + public void closeSchemaTransaction(DBSchemaDelta delta) + { + if (delta == null || delta.isEmpty()) + { + return; + } + + try + { + beginSchemaAccess(true); + + for (IDBConnection transaction : getConnections()) + { + ((DBConnection)transaction).invalidateStatementCache(); + } + + fireEvent(new SchemaChangedEventImpl(this, delta)); + } + finally + { + endSchemaAccess(); + } + } + + @Deprecated + public DBSchemaTransaction getSchemaTransaction() + { + throw new UnsupportedOperationException(); + } + + public void updateSchema(RunnableWithSchema runnable) + { + DBSchemaTransaction schemaTransaction = openSchemaTransaction(); + + try + { + IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); + runnable.run(workingCopy); + schemaTransaction.commit(); + } + finally + { + schemaTransaction.close(); + } + } + + public DBConnection getConnection() + { + Connection delegate = connectionProvider.getConnection(); + if (delegate == null) + { + throw new DBException("No connection from connection provider: " + connectionProvider); + } + + delegate = adapter.modifyConnection(delegate); + + DBConnection connection = new DBConnection(this, delegate); + addElement(connection); + return connection; + } + + public void closeConnection(DBConnection connection) + { + removeElement(connection); + } + + public IDBConnection[] getConnections() + { + return getElements(); + } + + public int getStatementCacheCapacity() + { + return statementCacheCapacity; + } + + public void setStatementCacheCapacity(int statementCacheCapacity) + { + this.statementCacheCapacity = statementCacheCapacity; + } + + public boolean isClosed() + { + return !isActive(); + } + + public void close() + { + deactivate(); + } + + @Override + protected void doDeactivate() throws Exception + { + for (IDBConnection connection : getConnections()) + { + connection.close(); + } + + super.doDeactivate(); + } + + public void beginSchemaAccess(boolean write) + { + if (DEBUG_SCHEMA_ACCESS) + { + try + { + throw new Exception("Begin " + (write ? "write" : "read") + " schema access: " + schema.getName()); + } + catch (Exception ex) + { + ex.printStackTrace(IOUtil.OUT()); + } + } + + SchemaAccess schemaAccess = null; + synchronized (schemaAccessQueue) + { + if (write) + { + schemaAccess = new WriteSchemaAccess(); + schemaAccessQueue.addLast(schemaAccess); + ++schemaWriters; + } + else + { + if (schemaWriters == 0 && !schemaAccessQueue.isEmpty()) + { + schemaAccess = schemaAccessQueue.getFirst(); + if (schemaAccess instanceof ReadSchemaAccess) + { + ReadSchemaAccess readSchemaAccess = (ReadSchemaAccess)schemaAccess; + readSchemaAccess.incrementReaders(); + } + else + { + schemaAccess = null; + } + } + + if (schemaAccess == null) + { + schemaAccess = new ReadSchemaAccess(); + schemaAccessQueue.addLast(schemaAccess); + } + } + } + + long end = System.currentTimeMillis() + TIMEOUT_SCHEMA_ACCESS; + + do + { + synchronized (schemaAccessQueue) + { + if (schemaAccessQueue.getFirst() == schemaAccess) + { + if (write) + { + --schemaWriters; + } + + return; + } + + try + { + schemaAccessQueue.wait(1000L); + } + catch (InterruptedException ex) + { + Thread.currentThread().interrupt(); + throw WrappedException.wrap(ex); + } + } + } while (System.currentTimeMillis() < end); + + throw new TimeoutRuntimeException( + "Schema " + schema.getName() + " could not be locked for " + (write ? "write" : "read") + " access within " + TIMEOUT_SCHEMA_ACCESS + " milliseconds"); + } + + public void endSchemaAccess() + { + if (DEBUG_SCHEMA_ACCESS) + { + try + { + throw new Exception("End schema access: " + schema.getName()); + } + catch (Exception ex) + { + ex.printStackTrace(IOUtil.OUT()); + } + } + + synchronized (schemaAccessQueue) + { + SchemaAccess schemaAccess = schemaAccessQueue.getFirst(); + if (schemaAccess instanceof ReadSchemaAccess) + { + ReadSchemaAccess readSchemaAccess = (ReadSchemaAccess)schemaAccess; + if (readSchemaAccess.decrementReaders()) + { + return; + } + } + + schemaAccessQueue.removeFirst(); + schemaAccessQueue.notifyAll(); + } + } + + public String convertString(DBPreparedStatement preparedStatement, int parameterIndex, String value) + { + return adapter.convertString(preparedStatement, parameterIndex, value); + } + + public String convertString(DBResultSet resultSet, int columnIndex, String value) + { + return adapter.convertString(resultSet, columnIndex, value); + } + + public String convertString(DBResultSet resultSet, String columnLabel, String value) + { + return adapter.convertString(resultSet, columnLabel, value); + } + + /** + * @author Eike Stepper + */ + private interface SchemaAccess + { + } + + /** + * @author Eike Stepper + */ + private static final class ReadSchemaAccess implements SchemaAccess + { + private int readers = 1; + + public void incrementReaders() + { + ++readers; + } + + public boolean decrementReaders() + { + return --readers > 0; + } + + @Override + public String toString() + { + return "READERS[" + readers + "]"; + } + } + + /** + * @author Eike Stepper + */ + private static final class WriteSchemaAccess implements SchemaAccess + { + @Override + public String toString() + { + return "WRITER"; + } + } + + /** + * @author Eike Stepper + */ + private static final class SchemaChangedEventImpl extends Event implements SchemaChangedEvent + { + private static final long serialVersionUID = 1L; + + private final IDBSchemaDelta schemaDelta; + + public SchemaChangedEventImpl(DBDatabase database, IDBSchemaDelta schemaDelta) + { + super(database); + this.schemaDelta = schemaDelta; + } + + @Override + public IDBDatabase getSource() + { + return (IDBDatabase)super.getSource(); + } + + public IDBSchemaDelta getSchemaDelta() + { + return schemaDelta; + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java new file mode 100644 index 000000000..a68340249 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2013, 2016, 2018 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBResultSet; +import org.eclipse.net4j.db.jdbc.DelegatingPreparedStatement; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @author Eike Stepper + */ +public class DBPreparedStatement extends DelegatingPreparedStatement implements IDBPreparedStatement +{ + private final String sql; + + private final ReuseProbability reuseProbability; + + private int touch; + + private DBPreparedStatement nextCached; + + public DBPreparedStatement(DBConnection transaction, String sql, ReuseProbability reuseProbability, PreparedStatement delegate) + { + super(delegate, transaction); + this.sql = sql; + this.reuseProbability = reuseProbability; + } + + @Override + public DBConnection getConnection() throws SQLException + { + return (DBConnection)super.getConnection(); + } + + @Deprecated + public DBConnection getTransaction() + { + try + { + return getConnection(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + public String getSQL() + { + return sql; + } + + public ReuseProbability getReuseProbability() + { + return reuseProbability; + } + + public void setTouch(int touch) + { + this.touch = touch; + } + + public DBPreparedStatement getNextCached() + { + return nextCached; + } + + public void setNextCached(DBPreparedStatement nextCached) + { + this.nextCached = nextCached; + } + + public int compareTo(IDBPreparedStatement o) + { + int result = reuseProbability.compareTo(o.getReuseProbability()); + if (result == 0) + { + result = ((DBPreparedStatement)o).touch - touch; + } + + return result; + } + + @Override + public String toString() + { + return "PreparedStatement[sql=" + sql + ", probability=" + reuseProbability + ", touch=" + touch + "]"; + } + + @Override + public void close() throws SQLException + { + getConnection().releasePreparedStatement(this); + } + + @Override + public IDBResultSet getGeneratedKeys() throws SQLException + { + return new DBResultSet(getDelegate().getGeneratedKeys(), this); + } + + @Override + public IDBResultSet getResultSet() throws SQLException + { + return new DBResultSet(getDelegate().getResultSet(), this); + } + + @Override + public IDBResultSet executeQuery() throws SQLException + { + return new DBResultSet(getDelegate().executeQuery(), this); + } + + @Override + @Deprecated + public ResultSet executeQuery(String sql) throws SQLException + { + throw new UnsupportedOperationException(); + } + + @Override + public void setString(int parameterIndex, String value) throws SQLException + { + value = getConnection().convertString(this, parameterIndex, value); + super.setString(parameterIndex, value); + } + + public String convertString(DBResultSet resultSet, int columnIndex, String value) throws SQLException + { + return getConnection().convertString(resultSet, columnIndex, value); + } + + public String convertString(DBResultSet resultSet, String columnLabel, String value) throws SQLException + { + return getConnection().convertString(resultSet, columnLabel, value); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBResultSet.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBResultSet.java new file mode 100644 index 000000000..1bc5cf902 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBResultSet.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBResultSet; +import org.eclipse.net4j.db.jdbc.DelegatingResultSet; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @author Eike Stepper + */ +public final class DBResultSet extends DelegatingResultSet implements IDBResultSet +{ + public DBResultSet(ResultSet delegate, IDBPreparedStatement preparedStatement) + { + super(delegate, preparedStatement); + } + + @Override + public DBPreparedStatement getStatement() throws SQLException + { + return (DBPreparedStatement)super.getStatement(); + } + + @Override + public String getString(int columnIndex) throws SQLException + { + String value = super.getString(columnIndex); + return getStatement().convertString(this, columnIndex, value); + } + + @Override + public String getString(String columnLabel) throws SQLException + { + String value = super.getString(columnLabel); + return getStatement().convertString(this, columnLabel, value); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java new file mode 100644 index 000000000..10724041b --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013, 2018 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.DBUtil.RunnableWithConnection; +import org.eclipse.net4j.db.IDBSchemaTransaction; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.internal.db.ddl.DelegatingDBSchema; +import org.eclipse.net4j.internal.db.ddl.DelegatingDBSchemaElement; +import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta; +import org.eclipse.net4j.spi.db.DBAdapter; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author Eike Stepper + */ +public final class DBSchemaTransaction implements IDBSchemaTransaction, RunnableWithConnection +{ + private DBDatabase database; + + private DBConnection connection; + + private IDBSchema oldSchema; + + private IDBSchema oldSchemaCopy; + + private IDBSchema workingCopy; + + public DBSchemaTransaction(DBDatabase database) + { + this.database = database; + + oldSchema = database.getSchema(); + oldSchemaCopy = DBUtil.copySchema(oldSchema); + + IDBSchema copy = DBUtil.copySchema(oldSchema); + workingCopy = DelegatingDBSchemaElement.wrap(copy); + } + + public DBDatabase getDatabase() + { + return database; + } + + public DBConnection getConnection() + { + return connection; + } + + public void setConnection(DBConnection connection) + { + this.connection = connection; + } + + public IDBSchema getWorkingCopy() + { + return workingCopy; + } + + public DBSchemaDelta ensureSchema(IDBSchema schema, IDBDeltaVisitor.Filter.Policy policy) + { + IDBSchema workingCopy = getWorkingCopy(); + + IDBDeltaVisitor.Copier copier = new IDBDeltaVisitor.Copier(policy); + IDBSchemaDelta delta = schema.compare(workingCopy); + delta.accept(copier); + + DBSchemaDelta result = (DBSchemaDelta)copier.getResult(); + result.setName(workingCopy.getName()); + result.applyTo(workingCopy); + return result; + } + + public DBSchemaDelta ensureSchema(IDBSchema schema) + { + return ensureSchema(schema, DEFAULT_ENSURE_SCHEMA_POLICY); + } + + public DBSchemaDelta getSchemaDelta() + { + return (DBSchemaDelta)workingCopy.compare(oldSchemaCopy); + } + + public DBSchemaDelta commit() + { + if (connection == null) + { + return DBUtil.execute(database, this); + } + + // Remember connection locally because the instance field will be reset in run/doClose. + DBConnection connection = this.connection; + + try + { + DBSchemaDelta result = run(connection); + connection.commit(); + return result; + } + catch (SQLException ex) + { + DBUtil.rollbackSilently(connection); + throw new DBException(ex); + } + } + + public DBSchemaDelta run(Connection connection) throws SQLException + { + DBSchemaDelta delta = getSchemaDelta(); + + synchronized (oldSchema) + { + try + { + ((InternalDBSchema)oldSchema).unlock(); + + DBAdapter adapter = database.getAdapter(); + adapter.updateSchema(connection, oldSchema, delta); + + ((DelegatingDBSchema)workingCopy).setDelegate(oldSchema); + } + finally + { + ((InternalDBSchema)oldSchema).lock(); + doClose(delta); + } + } + + return delta; + } + + public void close() + { + doClose(null); + } + + private void doClose(DBSchemaDelta delta) + { + if (!isClosed()) + { + database.closeSchemaTransaction(delta); + connection = null; + oldSchema = null; + oldSchemaCopy = null; + workingCopy = null; + } + } + + public boolean isClosed() + { + return workingCopy == null; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DataSourceConnectionProvider.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DataSourceConnectionProvider.java new file mode 100644 index 000000000..522d15064 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DataSourceConnectionProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2007, 2008, 2011-2013, 2015 Eike Stepper (Loehne, 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.net4j.internal.db; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.IDBConnectionProvider2; + +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author Eike Stepper + */ +public class DataSourceConnectionProvider implements IDBConnectionProvider2 +{ + private final DataSource dataSource; + + private final String user; + + public DataSourceConnectionProvider(DataSource dataSource, String user) + { + this.dataSource = dataSource; + this.user = user; + } + + public DataSource getDataSource() + { + return dataSource; + } + + public String getUserID() + { + return user; + } + + public Connection getConnection() + { + try + { + return dataSource.getConnection(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + @Override + public String toString() + { + return dataSource.toString(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/bundle/OM.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/bundle/OM.java new file mode 100644 index 000000000..ef3fa1ce2 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/bundle/OM.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007-2009, 2011, 2012, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.bundle; + +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.internal.db.DBAdapterDescriptor; +import org.eclipse.net4j.internal.db.DBAdapterRegistry; +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; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +/** + * The Operations & Maintenance class of this bundle. + * + * @author Eike Stepper + */ +public abstract class OM +{ + public static final String BUNDLE_ID = "org.eclipse.net4j.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 OMTracer DEBUG_SQL = DEBUG.tracer("sql"); //$NON-NLS-1$ + + public static final OMLogger LOG = BUNDLE.logger(); + + public static final String EXT_POINT = "dbAdapters"; //$NON-NLS-1$ + + /** + * @author Eike Stepper + */ + public static final class Activator extends OSGiActivator + { + public Activator() + { + super(BUNDLE); + } + + @Override + protected void doStart() throws Exception + { + initDBAdapterRegistry(); + } + + private void initDBAdapterRegistry() + { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] elements = registry.getConfigurationElementsFor(BUNDLE_ID, EXT_POINT); + for (final IConfigurationElement element : elements) + { + if ("dbAdapter".equals(element.getName())) //$NON-NLS-1$ + { + DBAdapterDescriptor descriptor = new DBAdapterDescriptor(element.getAttribute("name")) //$NON-NLS-1$ + { + @Override + public IDBAdapter createDBAdapter() + { + try + { + return (IDBAdapter)element.createExecutableExtension("class"); //$NON-NLS-1$ + } + catch (CoreException ex) + { + OM.LOG.error(ex); + return null; + } + } + }; + + DBAdapterRegistry.INSTANCE.addDescriptor(descriptor); + } + } + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBElement.java new file mode 100644 index 000000000..53c870745 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBElement.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.spi.db.ddl.InternalDBElement; +import org.eclipse.net4j.util.event.Notifier; + +import java.io.Serializable; +import java.util.Properties; + +/** + * @since 4.2 + * @author Eike Stepper + */ +public abstract class DBElement extends Notifier implements InternalDBElement, Serializable +{ + private static final long serialVersionUID = 1L; + + private Properties properties; + + public DBElement() + { + } + + public synchronized final Properties getProperties() + { + if (properties == null) + { + properties = new Properties(); + } + + return properties; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java new file mode 100644 index 000000000..5d494256a --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBField.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2008-2015 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.spi.db.ddl.InternalDBField; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; +import org.eclipse.net4j.spi.db.ddl.InternalDBTable; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class DBField extends DBSchemaElement implements InternalDBField +{ + public static final int DEFAULT_BOOLEAN_PRECISION = 1; + + public static final int DEFAULT_INTEGER_PRECISION = 10; + + public static final int DEFAULT_DECIMAL_PRECISION = 5; + + public static final int DEFAULT_PRECISION = 0; + + public static final int DEFAULT_SCALE = 0; + + public static final int DEFAULT_CHAR_LENGTH = 1; + + public static final int DEFAULT_VARCHAR_LENGTH = 255; + + private static final ThreadLocal TRACK_CONSTRUCTION = new InheritableThreadLocal() + { + @Override + protected Boolean initialValue() + { + return false; + } + }; + + private static final long serialVersionUID = 1L; + + private IDBTable table; + + private DBType type; + + private int precision; + + private int scale; + + private boolean notNull; + + private int position; + + /** + * Tracks the construction stack trace to provide better debug infos in IDBTable.addIndex(). + */ + private transient Exception constructionStackTrace; + + public DBField(IDBTable table, String name, DBType type, int precision, int scale, boolean notNull, int position) + { + super(name); + this.table = table; + this.type = type; + this.precision = precision; + this.scale = scale; + this.notNull = notNull; + this.position = position; + + if (TRACK_CONSTRUCTION.get() == Boolean.TRUE) + { + try + { + throw new Exception("The field " + this + " has been constructed here:"); + } + catch (Exception ex) + { + constructionStackTrace = ex; + } + } + } + + /** + * Constructor for deserialization. + */ + protected DBField() + { + } + + @Override + public IDBField getWrapper() + { + return (IDBField)super.getWrapper(); + } + + public SchemaElementType getSchemaElementType() + { + return SchemaElementType.FIELD; + } + + public IDBSchema getSchema() + { + return table.getSchema(); + } + + public IDBTable getTable() + { + return table; + } + + public IDBTable getParent() + { + return getTable(); + } + + public DBType getType() + { + return type; + } + + public void setType(DBType type) + { + assertUnlocked(); + this.type = type; + } + + public int getPrecision() + { + if (precision == DEFAULT) + { + switch (type) + { + case BOOLEAN: + return DEFAULT_BOOLEAN_PRECISION; + + case INTEGER: + return DEFAULT_INTEGER_PRECISION; + + case CHAR: + return DEFAULT_CHAR_LENGTH; + + case VARCHAR: + case VARBINARY: + return DEFAULT_VARCHAR_LENGTH; + + case DECIMAL: + case NUMERIC: + return DEFAULT_DECIMAL_PRECISION; + + default: + return DEFAULT_PRECISION; + } + } + + return precision; + } + + public void setPrecision(int precision) + { + assertUnlocked(); + this.precision = precision; + } + + public int getScale() + { + if (scale == DEFAULT) + { + return DEFAULT_SCALE; + } + + return scale; + } + + public void setScale(int scale) + { + assertUnlocked(); + this.scale = scale; + } + + public boolean isNotNull() + { + return notNull; + } + + public void setNotNull(boolean notNull) + { + if (DBIndex.FIX_NULLABLE_INDEX_COLUMNS.get() != Boolean.TRUE) + { + assertUnlocked(); + } + + this.notNull = notNull; + } + + public int getPosition() + { + return position; + } + + public void setPosition(int position) + { + assertUnlocked(); + this.position = position; + } + + public String getFullName() + { + return table.getName() + "." + getName(); //$NON-NLS-1$ + } + + public void remove() + { + ((InternalDBTable)table).removeField(this); + } + + public String formatPrecision() + { + int precision = getPrecision(); + if (precision > 0) + { + return "(" + precision + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + return ""; //$NON-NLS-1$ + } + + public String formatPrecisionAndScale() + { + if (scale == DEFAULT) + { + return "(" + getPrecision() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + return "(" + getPrecision() + ", " + getScale() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public Exception getConstructionStackTrace() + { + return constructionStackTrace; + } + + @Override + protected void collectElements(List elements) + { + // Do nothing + } + + @Override + protected void doAccept(IDBSchemaVisitor visitor) + { + visitor.visit(this); + } + + @Override + protected void dumpAdditionalProperties(Writer writer) throws IOException + { + writer.append(", type="); + writer.append(getType().toString()); + writer.append(", precision="); + writer.append(String.valueOf(getPrecision())); + writer.append(", scale="); + writer.append(String.valueOf(getScale())); + writer.append(", notNull="); + writer.append(String.valueOf(isNotNull())); + } + + private void assertUnlocked() + { + ((InternalDBSchema)table.getSchema()).assertUnlocked(); + } + + public static void trackConstruction(boolean on) + { + if (on) + { + TRACK_CONSTRUCTION.set(true); + } + else + { + TRACK_CONSTRUCTION.remove(); + } + } + + public static boolean isTrackConstruction() + { + return TRACK_CONSTRUCTION.get(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBIndex.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBIndex.java new file mode 100644 index 000000000..54da8c565 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBIndex.java @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2008, 2009, 2011-2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.SchemaElementNotFoundException; +import org.eclipse.net4j.spi.db.ddl.InternalDBField; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; +import org.eclipse.net4j.spi.db.ddl.InternalDBTable; +import org.eclipse.net4j.util.om.OMPlatform; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class DBIndex extends DBSchemaElement implements InternalDBIndex +{ + public static final ThreadLocal FIX_NULLABLE_INDEX_COLUMNS = new InheritableThreadLocal(); + + public static final ThreadLocal> NULLABLE_INDEX_FIELDS = new InheritableThreadLocal>(); + + private static final boolean DISABLE_NULLABLE_CHECK = OMPlatform.INSTANCE.isProperty("org.eclipse.net4j.db.DisableNullableCheck", true); + + private static final long serialVersionUID = 1L; + + private IDBTable table; + + private Type type; + + private List indexFields = new ArrayList(); + + private boolean optional; + + public DBIndex(IDBTable table, String name, Type type, IDBField[] fields) + { + super(name); + this.table = table; + this.type = type; + + for (int i = 0; i < fields.length; i++) + { + IDBField field = fields[i]; + addIndexField(field); + } + } + + /** + * Constructor for deserialization. + */ + protected DBIndex() + { + } + + @Override + public IDBIndex getWrapper() + { + return (IDBIndex)super.getWrapper(); + } + + public SchemaElementType getSchemaElementType() + { + return SchemaElementType.INDEX; + } + + public IDBSchema getSchema() + { + return table.getSchema(); + } + + public IDBTable getTable() + { + return table; + } + + public IDBTable getParent() + { + return getTable(); + } + + public Type getType() + { + return type; + } + + public void setType(Type type) + { + assertUnlocked(); + this.type = type; + } + + public boolean isOptional() + { + return optional; + } + + public void setOptional(boolean optional) + { + this.optional = optional; + } + + @Deprecated + public int getPosition() + { + IDBIndex[] indices = table.getIndices(); + return Arrays.asList(indices).indexOf(this); + } + + public IDBIndexField addIndexField(IDBField field) + { + assertUnlocked(); + + if (type != Type.NON_UNIQUE && !field.isNotNull()) + { + if (!DISABLE_NULLABLE_CHECK && FIX_NULLABLE_INDEX_COLUMNS.get() != Boolean.TRUE) + { + Exception constructionStackTrace = ((InternalDBField)field).getConstructionStackTrace(); + throw new DBException("Index field is nullable: " + field //$NON-NLS-1$ + + " (to disable this check run with '-Dorg.eclipse.net4j.db.DisableNullableCheck=true')", constructionStackTrace); + } + + Set nullableIndexFields = NULLABLE_INDEX_FIELDS.get(); + if (nullableIndexFields == null) + { + nullableIndexFields = new HashSet(); + NULLABLE_INDEX_FIELDS.set(nullableIndexFields); + } + + nullableIndexFields.add(field); + } + + if (field.getTable() != table) + { + throw new DBException("Index field is from different table: " + field); //$NON-NLS-1$ + } + + String name = field.getName(); + if (getIndexField(name) != null) + { + throw new DBException("Index field exists: " + name); //$NON-NLS-1$ + } + + int position = indexFields.size(); + IDBIndexField indexField = new DBIndexField(this, field, position); + indexFields.add(indexField); + resetElements(); + return indexField; + } + + public IDBIndexField addIndexField(String name) throws SchemaElementNotFoundException + { + IDBField field = table.getFieldSafe(name); + return addIndexField(field); + } + + public void removeIndexField(IDBIndexField indexFieldToRemove) + { + assertUnlocked(); + + boolean found = false; + for (Iterator it = indexFields.iterator(); it.hasNext();) + { + IDBIndexField indexField = it.next(); + if (found) + { + ((InternalDBField)indexField).setPosition(indexField.getPosition() - 1); + } + else if (indexField == indexFieldToRemove) + { + it.remove(); + found = true; + } + } + + resetElements(); + } + + public IDBIndexField getIndexFieldSafe(String name) throws SchemaElementNotFoundException + { + IDBIndexField indexField = getIndexField(name); + if (indexField == null) + { + throw new SchemaElementNotFoundException(this, SchemaElementType.INDEX_FIELD, name); + } + + return indexField; + } + + public IDBIndexField getIndexField(String name) + { + return findElement(getIndexFields(), name); + } + + public IDBIndexField getIndexField(int position) + { + return indexFields.get(position); + } + + public IDBField getFieldSafe(String name) throws SchemaElementNotFoundException + { + IDBIndexField indexField = getIndexFieldSafe(name); + return indexField.getField(); + } + + public IDBField getField(String name) + { + name = name(name); + for (IDBIndexField indexField : indexFields) + { + if (indexField.getName() == name) + { + return indexField.getField(); + } + } + + return null; + } + + public IDBField getField(int position) + { + return indexFields.get(position).getField(); + } + + public int getFieldCount() + { + return indexFields.size(); + } + + public IDBIndexField[] getIndexFields() + { + return indexFields.toArray(new IDBIndexField[indexFields.size()]); + } + + public IDBField[] getFields() + { + IDBField[] fields = new IDBField[indexFields.size()]; + for (int i = 0; i < fields.length; i++) + { + fields[i] = getField(i); + } + + return fields; + } + + public String getFullName() + { + return getName(); + } + + public void remove() + { + ((InternalDBTable)table).removeIndex(this); + } + + @Override + protected void collectElements(List elements) + { + elements.addAll(indexFields); + } + + @Override + protected void doAccept(IDBSchemaVisitor visitor) + { + visitor.visit(this); + } + + @Override + protected void dumpAdditionalProperties(Writer writer) throws IOException + { + writer.append(", type="); + writer.append(String.valueOf(getType())); + } + + private void assertUnlocked() + { + ((InternalDBSchema)table.getSchema()).assertUnlocked(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBIndexField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBIndexField.java new file mode 100644 index 000000000..756b5d469 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBIndexField.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndexField; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; + +import java.util.List; + +/** + * @author Eike Stepper + */ +public class DBIndexField extends DBSchemaElement implements InternalDBIndexField +{ + private static final long serialVersionUID = 1L; + + private IDBIndex index; + + private IDBField field; + + private int position; + + public DBIndexField(IDBIndex index, IDBField field, int position) + { + super(field.getName()); + this.index = index; + this.field = field; + this.position = position; + } + + /** + * Constructor for deserialization. + */ + protected DBIndexField() + { + } + + @Override + public IDBIndexField getWrapper() + { + return (IDBIndexField)super.getWrapper(); + } + + public SchemaElementType getSchemaElementType() + { + return SchemaElementType.INDEX_FIELD; + } + + public IDBIndex getIndex() + { + return index; + } + + public IDBIndex getParent() + { + return getIndex(); + } + + public IDBField getField() + { + return field; + } + + public int getPosition() + { + return position; + } + + public void setPosition(int position) + { + assertUnlocked(); + this.position = position; + } + + public IDBSchema getSchema() + { + return field.getSchema(); + } + + public String getFullName() + { + return field.getFullName(); + } + + public void remove() + { + ((InternalDBIndex)index).removeIndexField(this); + } + + @Override + protected void collectElements(List elements) + { + // Do nothing + } + + @Override + protected void doAccept(IDBSchemaVisitor visitor) + { + visitor.visit(this); + } + + private void assertUnlocked() + { + ((InternalDBSchema)index.getTable().getSchema()).assertUnlocked(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBNamedElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBNamedElement.java new file mode 100644 index 000000000..c38a035b3 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBNamedElement.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBNamedElement; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.spi.db.ddl.InternalDBNamedElement; +import org.eclipse.net4j.util.io.IORuntimeException; + +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; + +/** + * @since 4.2 + * @author Eike Stepper + */ +public abstract class DBNamedElement extends DBElement implements InternalDBNamedElement +{ + private static final long serialVersionUID = 1L; + + private String name; + + public DBNamedElement(String name) + { + setName(name); + } + + /** + * Constructor for deserialization. + */ + protected DBNamedElement() + { + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name(name); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + + if (obj instanceof IDBNamedElement) + { + IDBNamedElement that = (IDBNamedElement)obj; + return name == that.getName(); + } + + return false; + } + + @Override + public int hashCode() + { + return name.hashCode(); + } + + @Override + public String toString() + { + return name; + } + + public String dumpToString() + { + try + { + CharArrayWriter writer = new CharArrayWriter(); + dump(writer); + return writer.toString(); + } + catch (IOException ex) + { + throw new IORuntimeException(ex); + } + } + + public final void dump() + { + try + { + OutputStreamWriter writer = new OutputStreamWriter(System.out); + dump(writer); + writer.flush(); + } + catch (IOException ex) + { + throw new IORuntimeException(ex); + } + } + + public static String name(String name) + { + return name.toUpperCase().intern(); + } + + public static T findElement(T[] elements, String name) + { + name = name(name); + for (int i = 0; i < elements.length; i++) + { + T element = elements[i]; + if (element.getName() == name) + { + return element; + } + } + + return null; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBSchema.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBSchema.java new file mode 100644 index 000000000..b312dfbbb --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBSchema.java @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2008, 2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +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.IDBRowHandler; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.SchemaElementNotFoundException; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; + +import javax.sql.DataSource; + +import java.io.PrintStream; +import java.sql.Connection; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class DBSchema extends DBSchemaElement implements InternalDBSchema +{ + private static final long serialVersionUID = 1L; + + private static int indexCounter; + + private Map tables = new HashMap(); + + private transient boolean locked; + + public DBSchema(String name) + { + super(name); + } + + /** + * @since 4.2 + */ + public DBSchema(IDBSchema source) + { + super(source.getName()); + + for (IDBTable sourceTable : source.getTables()) + { + IDBTable table = addTable(sourceTable.getName()); + + for (IDBField sourceField : sourceTable.getFields()) + { + table.addField(sourceField.getName(), sourceField.getType(), sourceField.getPrecision(), sourceField.getScale(), sourceField.isNotNull()); + } + + for (IDBIndex sourceIndex : sourceTable.getIndices()) + { + IDBIndex index = table.addIndexEmpty(sourceIndex.getName(), sourceIndex.getType()); + DBUtil.setOptional(index, DBUtil.isOptional(sourceIndex)); + + for (IDBField sourceField : sourceIndex.getFields()) + { + IDBField field = table.getField(sourceField.getPosition()); + index.addIndexField(field); + } + } + } + } + + /** + * Constructor for deserialization. + * + * @since 4.2 + */ + protected DBSchema() + { + } + + @Override + public IDBSchema getWrapper() + { + return (IDBSchema)super.getWrapper(); + } + + /** + * @since 4.2 + */ + public SchemaElementType getSchemaElementType() + { + return SchemaElementType.SCHEMA; + } + + public IDBSchema getSchema() + { + return this; + } + + /** + * @since 4.2 + */ + public final IDBSchemaElement getParent() + { + return null; + } + + public String getFullName() + { + return getName(); + } + + /** + * @since 4.2 + */ + @SuppressWarnings("unchecked") + public final T findElement(IDBSchemaElement prototype) + { + SchemaElementType schemaElementType = prototype.getSchemaElementType(); + switch (schemaElementType) + { + case SCHEMA: + return (T)(prototype.equals(this) ? this : null); + + case TABLE: + return (T)getElement(IDBTable.class, prototype.getName()); + + case FIELD: + { + IDBTable table = getElement(IDBTable.class, prototype.getParent().getName()); + if (table == null) + { + return null; + } + + return (T)table.getElement(IDBField.class, prototype.getName()); + } + + case INDEX: + { + IDBTable table = getElement(IDBTable.class, prototype.getParent().getName()); + if (table == null) + { + return null; + } + + return (T)table.getElement(IDBIndex.class, prototype.getName()); + } + + case INDEX_FIELD: + { + IDBTable table = getElement(IDBTable.class, prototype.getParent().getParent().getName()); + if (table == null) + { + return null; + } + + IDBIndex index = table.getElement(IDBIndex.class, prototype.getParent().getName()); + if (index == null) + { + return null; + } + + return (T)index.getElement(IDBIndexField.class, prototype.getName()); + } + + default: + throw new IllegalStateException("Illegal schema element type: " + schemaElementType); + } + } + + /** + * @since 2.0 + */ + public IDBTable addTable(String name) throws DBException + { + assertUnlocked(); + if (tables.containsKey(name)) + { + throw new DBException("IDBTable exists: " + name); //$NON-NLS-1$ + } + + IDBTable table = new DBTable(this, name); + tables.put(table.getName(), table); + resetElements(); + return table; + } + + /** + * @since 4.0 + */ + public IDBTable removeTable(String name) + { + assertUnlocked(); + name = name(name); + IDBTable table = tables.remove(name); + resetElements(); + return table; + } + + /** + * @since 4.2 + */ + public final IDBTable getTableSafe(String name) throws SchemaElementNotFoundException + { + IDBTable table = getTable(name); + if (table == null) + { + throw new SchemaElementNotFoundException(this, SchemaElementType.TABLE, name); + } + + return table; + } + + /** + * @since 2.0 + */ + public IDBTable getTable(String name) + { + name = name(name); + return tables.get(name); + } + + /** + * @since 2.0 + */ + public IDBTable[] getTables() + { + return tables.values().toArray(new IDBTable[tables.size()]); + } + + /** + * @since 4.2 + */ + public void remove() + { + assertUnlocked(); + tables.clear(); + } + + public boolean isLocked() + { + return locked; + } + + public boolean lock() + { + return locked = true; + } + + /** + * @since 4.2 + */ + public boolean unlock() + { + return locked = false; + } + + public void assertUnlocked() throws DBException + { + if (locked) + { + throw new DBException("Schema locked: " + this); //$NON-NLS-1$ + } + } + + public Set create(IDBAdapter dbAdapter, Connection connection) throws DBException + { + return dbAdapter.createTables(tables.values(), connection); + } + + public Set create(IDBAdapter dbAdapter, DataSource dataSource) throws DBException + { + return create(dbAdapter, dbAdapter.createConnectionProvider(dataSource)); + } + + public Set create(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException + { + Connection connection = null; + + try + { + connection = connectionProvider.getConnection(); + if (connection == null) + { + throw new DBException("No connection available from " + connectionProvider); //$NON-NLS-1$ + } + + return create(dbAdapter, connection); + } + finally + { + DBUtil.close(connection); + } + } + + public void drop(IDBAdapter dbAdapter, Connection connection) throws DBException + { + dbAdapter.dropTables(tables.values(), connection); + } + + public void drop(IDBAdapter dbAdapter, DataSource dataSource) throws DBException + { + drop(dbAdapter, dbAdapter.createConnectionProvider(dataSource)); + } + + public void drop(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException + { + Connection connection = null; + + try + { + connection = connectionProvider.getConnection(); + drop(dbAdapter, connection); + } + finally + { + DBUtil.close(connection); + } + } + + public void export(Connection connection, PrintStream out) throws DBException + { + for (IDBTable table : getTables()) + { + export(table, connection, out); + } + } + + private void export(final IDBTable table, Connection connection, final PrintStream out) + { + if (DBUtil.select(connection, new IDBRowHandler() + { + public boolean handle(int row, Object... values) + { + if (row == 0) + { + String tableName = table.getName(); + out.println(tableName); + for (int i = 0; i < tableName.length(); i++) + { + out.print("="); //$NON-NLS-1$ + } + + out.println(); + } + + out.println(Arrays.asList(values)); + return true; + } + }, table.getFields()) > 0) + + { + out.println(); + } + } + + public void export(DataSource dataSource, PrintStream out) throws DBException + { + export(DBUtil.createConnectionProvider(dataSource), out); + } + + public void export(IDBConnectionProvider connectionProvider, PrintStream out) throws DBException + { + Connection connection = null; + + try + { + connection = connectionProvider.getConnection(); + export(connection, out); + } + finally + { + DBUtil.close(connection); + } + } + + /** + * @since 4.2 + */ + public IDBSchemaDelta compare(IDBSchema oldSchema) + { + return new DBSchemaDelta(this, oldSchema); + } + + /** + * @since 4.2 + */ + public String createIndexName(IDBTable table, IDBIndex.Type type, IDBField[] fields, int position) + { + return "I" + System.currentTimeMillis() + "_" + ++indexCounter; + } + + /** + * @since 4.2 + */ + @Override + protected void collectElements(List elements) + { + elements.addAll(tables.values()); + } + + /** + * @since 4.2 + */ + @Override + protected void doAccept(IDBSchemaVisitor visitor) + { + visitor.visit(this); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBSchemaElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBSchemaElement.java new file mode 100644 index 000000000..722c71d57 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBSchemaElement.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2008, 2011-2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor.StopRecursion; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchemaElement; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.PositionProvider; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @since 4.2 + * @author Eike Stepper + */ +public abstract class DBSchemaElement extends DBNamedElement implements InternalDBSchemaElement +{ + private static final long serialVersionUID = 1L; + + private static final IDBSchemaElement[] NO_ELEMENTS = {}; + + private transient IDBSchemaElement[] elements; + + private transient IDBSchemaElement wrapper; + + public DBSchemaElement(String name) + { + super(name); + } + + /** + * Constructor for deserialization. + */ + protected DBSchemaElement() + { + } + + public IDBSchemaElement getWrapper() + { + return wrapper; + } + + public final void setWrapper(IDBSchemaElement wrapper) + { + this.wrapper = wrapper; + } + + @Override + public final boolean equals(Object obj) + { + if (super.equals(obj)) + { + return getSchemaElementType() == ((IDBSchemaElement)obj).getSchemaElementType(); + } + + return false; + } + + @Override + public final int hashCode() + { + return super.hashCode() ^ getSchemaElementType().hashCode(); + } + + public final int compareTo(IDBSchemaElement element2) + { + int result = getSchemaElementType().compareTo(element2.getSchemaElementType()); + if (result == 0) + { + if (this instanceof PositionProvider && element2 instanceof PositionProvider) + { + PositionProvider withPosition1 = (PositionProvider)this; + PositionProvider withPosition2 = (PositionProvider)element2; + return withPosition1.getPosition() - withPosition2.getPosition(); + } + + result = getName().compareTo(element2.getName()); + } + + return result; + } + + public final boolean isEmpty() + { + return getElements().length == 0; + } + + public final IDBSchemaElement[] getElements() + { + if (elements == null) + { + List result = new ArrayList(); + collectElements(result); + + if (result.isEmpty()) + { + elements = NO_ELEMENTS; + } + else + { + elements = result.toArray(new IDBSchemaElement[result.size()]); + Arrays.sort(elements); + } + } + + return elements; + } + + protected final void resetElements() + { + elements = null; + } + + protected abstract void collectElements(List elements); + + public final T getElement(Class type, String name) + { + name = name(name); + for (IDBSchemaElement element : getElements()) + { + if (element.getName() == name && type.isAssignableFrom(element.getClass())) + { + @SuppressWarnings("unchecked") + T result = (T)element; + return result; + } + } + + return null; + } + + public final void accept(IDBSchemaVisitor visitor) + { + try + { + doAccept(visitor); + } + catch (StopRecursion ex) + { + return; + } + + for (IDBSchemaElement element : getElements()) + { + element.accept(visitor); + } + } + + protected abstract void doAccept(IDBSchemaVisitor visitor); + + public void dump(Writer writer) throws IOException + { + SchemaElementType schemaElementType = getSchemaElementType(); + int level = schemaElementType.getLevel(); + for (int i = 0; i < level; i++) + { + writer.append(" "); + } + + writer.append(schemaElementType.toString()); + writer.append(" "); + writer.append(getName()); + dumpAdditionalProperties(writer); + writer.append(StringUtil.NL); + + for (IDBSchemaElement element : getElements()) + { + ((InternalDBSchemaElement)element).dump(writer); + } + } + + protected void dumpAdditionalProperties(Writer writer) throws IOException + { + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBTable.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBTable.java new file mode 100644 index 000000000..576132c64 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DBTable.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2008, 2009, 2011-2013, 2015 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.DBException; +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.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.SchemaElementNotFoundException; +import org.eclipse.net4j.spi.db.ddl.InternalDBField; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; +import org.eclipse.net4j.spi.db.ddl.InternalDBTable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class DBTable extends DBSchemaElement implements InternalDBTable +{ + private static final long serialVersionUID = 1L; + + private IDBSchema schema; + + private List fields = new ArrayList(); + + private List indices = new ArrayList(); + + public DBTable(IDBSchema schema, String name) + { + super(name); + this.schema = schema; + } + + /** + * Constructor for deserialization. + */ + protected DBTable() + { + } + + @Override + public IDBTable getWrapper() + { + return (IDBTable)super.getWrapper(); + } + + public SchemaElementType getSchemaElementType() + { + return SchemaElementType.TABLE; + } + + public IDBSchema getSchema() + { + return schema; + } + + public IDBSchema getParent() + { + return getSchema(); + } + + public IDBField addField(String name, DBType type) + { + return addField(name, type, IDBField.DEFAULT, IDBField.DEFAULT, false); + } + + public IDBField addField(String name, DBType type, boolean notNull) + { + return addField(name, type, IDBField.DEFAULT, IDBField.DEFAULT, notNull); + } + + public IDBField addField(String name, DBType type, int precision) + { + return addField(name, type, precision, IDBField.DEFAULT, false); + } + + public IDBField addField(String name, DBType type, int precision, boolean notNull) + { + return addField(name, type, precision, IDBField.DEFAULT, notNull); + } + + public IDBField addField(String name, DBType type, int precision, int scale) + { + return addField(name, type, precision, scale, false); + } + + public IDBField addField(String name, DBType type, int precision, int scale, boolean notNull) + { + assertUnlocked(); + + if (getField(name) != null) + { + throw new DBException("Field exists: " + name); //$NON-NLS-1$ + } + + int position = fields.size(); + IDBField field = new DBField(this, name, type, precision, scale, notNull, position); + fields.add(field); + resetElements(); + return field; + } + + public void removeField(IDBField fieldToRemove) + { + assertUnlocked(); + + boolean found = false; + for (Iterator it = fields.iterator(); it.hasNext();) + { + IDBField field = it.next(); + if (found) + { + ((InternalDBField)field).setPosition(field.getPosition() - 1); + } + else if (field == fieldToRemove) + { + it.remove(); + found = true; + } + } + + resetElements(); + } + + public IDBField getFieldSafe(String name) throws SchemaElementNotFoundException + { + IDBField field = getField(name); + if (field == null) + { + throw new SchemaElementNotFoundException(this, SchemaElementType.FIELD, name); + } + + return field; + } + + public IDBField getField(String name) + { + return findElement(getFields(), name); + } + + public IDBField getField(int position) + { + return fields.get(position); + } + + public int getFieldCount() + { + return fields.size(); + } + + public IDBField[] getFields() + { + return fields.toArray(new IDBField[fields.size()]); + } + + public IDBField[] getFields(String... fieldNames) throws SchemaElementNotFoundException + { + List result = new ArrayList(); + for (String fieldName : fieldNames) + { + IDBField field = getFieldSafe(fieldName); + result.add(field); + } + + return result.toArray(new IDBField[result.size()]); + } + + public boolean hasIndexFor(IDBField... fields) + { + for (IDBIndex index : indices) + { + IDBField[] indexFields = index.getFields(); + if (startsWith(indexFields, fields)) + { + return true; + } + } + + return false; + } + + public IDBIndex addIndex(String name, IDBIndex.Type type, IDBField... fields) + { + assertUnlocked(); + + if (name == null) + { + int position = indices.size(); + name = ((InternalDBSchema)schema).createIndexName(this, type, fields, position); + } + + if (getIndex(name) != null) + { + throw new DBException("Index exists: " + name); //$NON-NLS-1$ + } + + if (type == IDBIndex.Type.PRIMARY_KEY) + { + for (IDBIndex index : getIndices()) + { + if (index.getType() == IDBIndex.Type.PRIMARY_KEY) + { + throw new DBException("Primary key exists: " + index); //$NON-NLS-1$ + } + } + } + + IDBIndex index = new DBIndex(this, name, type, fields); + indices.add(index); + resetElements(); + return index; + } + + public IDBIndex addIndex(String name, IDBIndex.Type type, String... fieldNames) + { + return addIndex(name, type, getFields(fieldNames)); + } + + public IDBIndex addIndexEmpty(String name, IDBIndex.Type type) + { + return addIndex(name, type, NO_FIELDS); + } + + public IDBIndex addIndex(IDBIndex.Type type, IDBField... fields) + { + return addIndex(null, type, fields); + } + + public IDBIndex addIndex(IDBIndex.Type type, String... fieldNames) + { + IDBField[] fields = getFields(fieldNames); + return addIndex(type, fields); + } + + public IDBIndex addIndexEmpty(IDBIndex.Type type) + { + return addIndex(type, NO_FIELDS); + } + + public void removeIndex(IDBIndex indexToRemove) + { + assertUnlocked(); + if (indices.remove(indexToRemove)) + { + resetElements(); + } + } + + public IDBIndex getIndexSafe(String name) throws SchemaElementNotFoundException + { + IDBIndex index = getIndex(name); + if (index == null) + { + throw new SchemaElementNotFoundException(this, SchemaElementType.INDEX, name); + } + + return index; + } + + public IDBIndex getIndex(String name) + { + return findElement(getIndices(), name); + } + + public IDBIndex getIndex(int position) + { + return indices.get(position); + } + + public int getIndexCount() + { + return indices.size(); + } + + public IDBIndex[] getIndices() + { + return indices.toArray(new IDBIndex[indices.size()]); + } + + public IDBIndex getPrimaryKeyIndex() + { + for (IDBIndex index : indices) + { + if (index.getType() == IDBIndex.Type.PRIMARY_KEY) + { + return index; + } + } + + return null; + } + + public String getFullName() + { + return getName(); + } + + public void remove() + { + schema.removeTable(getName()); + } + + public String sqlInsert() + { + StringBuilder builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getName()); + builder.append(" VALUES ("); //$NON-NLS-1$ + + for (int i = 0; i < fields.size(); i++) + { + if (i > 0) + { + builder.append(", "); //$NON-NLS-1$ + } + + builder.append("?"); //$NON-NLS-1$ + } + + builder.append(")"); //$NON-NLS-1$ + return builder.toString(); + } + + @Override + protected void collectElements(List elements) + { + elements.addAll(fields); + elements.addAll(indices); + } + + @Override + protected void doAccept(IDBSchemaVisitor visitor) + { + visitor.visit(this); + } + + private void assertUnlocked() + { + ((InternalDBSchema)schema).assertUnlocked(); + } + + private static boolean startsWith(IDBField[] indexFields, IDBField[] fields) + { + int length = fields.length; + if (length <= indexFields.length) + { + for (int i = 0; i < length; i++) + { + IDBField field = fields[i]; + if (field != indexFields[i]) + { + return false; + } + } + + return true; + } + + return false; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java new file mode 100644 index 000000000..519e03554 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBField.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.spi.db.ddl.InternalDBField; + +/** + * @author Eike Stepper + */ +public final class DelegatingDBField extends DelegatingDBSchemaElement implements InternalDBField +{ + DelegatingDBField(InternalDBField delegate) + { + super(delegate); + } + + @Override + public InternalDBField getDelegate() + { + return (InternalDBField)super.getDelegate(); + } + + public IDBField getWrapper() + { + return this; + } + + public int getPosition() + { + return getDelegate().getPosition(); + } + + public void setPosition(int position) + { + getDelegate().setPosition(position); + } + + public Exception getConstructionStackTrace() + { + return getDelegate().getConstructionStackTrace(); + } + + @Override + public IDBTable getParent() + { + return wrap(getDelegate().getParent()); + } + + public IDBTable getTable() + { + return wrap(getDelegate().getTable()); + } + + public DBType getType() + { + return getDelegate().getType(); + } + + public void setType(DBType type) + { + getDelegate().setType(type); + } + + public int getPrecision() + { + return getDelegate().getPrecision(); + } + + public void setPrecision(int precision) + { + getDelegate().setPrecision(precision); + } + + public int getScale() + { + return getDelegate().getScale(); + } + + public void setScale(int scale) + { + getDelegate().setScale(scale); + } + + public boolean isNotNull() + { + return getDelegate().isNotNull(); + } + + public void setNotNull(boolean notNull) + { + getDelegate().setNotNull(notNull); + } + + public String formatPrecision() + { + return getDelegate().formatPrecision(); + } + + public String formatPrecisionAndScale() + { + return getDelegate().formatPrecisionAndScale(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBIndex.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBIndex.java new file mode 100644 index 000000000..d571e208d --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBIndex.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.SchemaElementNotFoundException; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; + +/** + * @author Eike Stepper + */ +public final class DelegatingDBIndex extends DelegatingDBSchemaElement implements InternalDBIndex +{ + DelegatingDBIndex(InternalDBIndex delegate) + { + super(delegate); + } + + @Override + public InternalDBIndex getDelegate() + { + return (InternalDBIndex)super.getDelegate(); + } + + @Override + public void setDelegate(IDBSchemaElement delegate) + { + IDBIndexField[] wrapperIndexFields = getIndexFields(); + + IDBIndex delegateIndex = (IDBIndex)delegate; + super.setDelegate(delegateIndex); + + for (IDBIndexField wrapperIndexField : wrapperIndexFields) + { + IDBIndexField delegateIndexField = delegateIndex.getIndexField(wrapperIndexField.getName()); + ((DelegatingDBSchemaElement)wrapperIndexField).setDelegate(delegateIndexField); + } + } + + public IDBIndex getWrapper() + { + return this; + } + + @Override + public IDBTable getParent() + { + return wrap(getDelegate().getParent()); + } + + public IDBTable getTable() + { + return wrap(getDelegate().getTable()); + } + + public Type getType() + { + return getDelegate().getType(); + } + + public void setType(Type type) + { + getDelegate().setType(type); + } + + public void removeIndexField(IDBIndexField indexFieldToRemove) + { + getDelegate().removeIndexField(unwrap(indexFieldToRemove)); + } + + public boolean isOptional() + { + return getDelegate().isOptional(); + } + + public void setOptional(boolean optional) + { + getDelegate().setOptional(optional); + } + + @Deprecated + public int getPosition() + { + return getDelegate().getPosition(); + } + + public IDBIndexField addIndexField(IDBField field) + { + return wrap(getDelegate().addIndexField(unwrap(field))); + } + + public IDBIndexField addIndexField(String name) throws SchemaElementNotFoundException + { + return wrap(getDelegate().addIndexField(name)); + } + + public IDBIndexField getIndexFieldSafe(String name) throws SchemaElementNotFoundException + { + return wrap(getDelegate().getIndexFieldSafe(name)); + } + + public IDBIndexField getIndexField(String name) + { + return wrap(getDelegate().getIndexField(name)); + } + + public IDBIndexField getIndexField(int position) + { + return wrap(getDelegate().getIndexField(position)); + } + + public IDBField getFieldSafe(String name) throws SchemaElementNotFoundException + { + return wrap(getDelegate().getFieldSafe(name)); + } + + public IDBField getField(String name) + { + return wrap(getDelegate().getField(name)); + } + + public IDBField getField(int position) + { + return wrap(getDelegate().getField(position)); + } + + public int getFieldCount() + { + return getDelegate().getFieldCount(); + } + + public IDBIndexField[] getIndexFields() + { + return wrap(getDelegate().getIndexFields(), IDBIndexField.class); + } + + public IDBField[] getFields() + { + return wrap(getDelegate().getFields(), IDBField.class); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBIndexField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBIndexField.java new file mode 100644 index 000000000..85164c0f9 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBIndexField.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndexField; + +/** + * @author Eike Stepper + */ +public final class DelegatingDBIndexField extends DelegatingDBSchemaElement implements InternalDBIndexField +{ + DelegatingDBIndexField(InternalDBIndexField delegate) + { + super(delegate); + } + + @Override + public InternalDBIndexField getDelegate() + { + return (InternalDBIndexField)super.getDelegate(); + } + + public IDBIndexField getWrapper() + { + return this; + } + + public int getPosition() + { + return getDelegate().getPosition(); + } + + public void setPosition(int position) + { + getDelegate().setPosition(position); + } + + @Override + public IDBIndex getParent() + { + return wrap(getDelegate().getParent()); + } + + public IDBIndex getIndex() + { + return wrap(getDelegate().getIndex()); + } + + public IDBField getField() + { + return wrap(getDelegate().getField()); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBSchema.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBSchema.java new file mode 100644 index 000000000..296359cf5 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBSchema.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.SchemaElementNotFoundException; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; + +import javax.sql.DataSource; + +import java.io.PrintStream; +import java.sql.Connection; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public final class DelegatingDBSchema extends DelegatingDBSchemaElement implements InternalDBSchema +{ + DelegatingDBSchema(InternalDBSchema delegate) + { + super(delegate); + } + + @Override + public InternalDBSchema getDelegate() + { + return (InternalDBSchema)super.getDelegate(); + } + + @Override + public void setDelegate(IDBSchemaElement delegate) + { + IDBTable[] wrapperTables = getTables(); + + IDBSchema delegateSchema = (IDBSchema)delegate; + super.setDelegate(delegateSchema); + + for (IDBTable wrapperTable : wrapperTables) + { + ((DelegatingDBSchemaElement)wrapperTable).setDelegate(delegateSchema.getTable(wrapperTable.getName())); + } + } + + public IDBSchema getWrapper() + { + return this; + } + + @Override + public IDBSchemaElement getParent() + { + return wrap(getDelegate().getParent()); + } + + public IDBTable addTable(String name) + { + return wrap(getDelegate().addTable(name)); + } + + public IDBTable removeTable(String name) + { + return wrap(getDelegate().removeTable(name)); + } + + public String createIndexName(IDBTable table, Type type, IDBField[] fields, int position) + { + return getDelegate().createIndexName(unwrap(table), type, fields, position); + } + + public boolean isLocked() + { + return getDelegate().isLocked(); + } + + public boolean lock() + { + return getDelegate().lock(); + } + + @SuppressWarnings("unchecked") + public T findElement(IDBSchemaElement prototype) + { + T unwrapped = (T)unwrap(prototype); + /** BEGIN SPECMATE PATCH */ + IDBSchemaElement element = getDelegate().findElement(unwrapped); + return (T)wrap(element); + /** END SPECMATE PATCH */ + } + + public boolean unlock() + { + return getDelegate().unlock(); + } + + public void assertUnlocked() throws DBException + { + getDelegate().assertUnlocked(); + } + + public IDBTable getTableSafe(String name) throws SchemaElementNotFoundException + { + return wrap(getDelegate().getTableSafe(name)); + } + + public IDBTable getTable(String name) + { + return wrap(getDelegate().getTable(name)); + } + + public IDBTable[] getTables() + { + IDBTable[] tables = getDelegate().getTables(); + IDBTable[] wrappers = new IDBTable[tables.length]; + for (int i = 0; i < tables.length; i++) + { + wrappers[i] = wrap(tables[i]); + } + + return wrappers; + } + + public Set create(IDBAdapter dbAdapter, Connection connection) throws DBException + { + return wrap(getDelegate().create(dbAdapter, connection)); + } + + public Set create(IDBAdapter dbAdapter, DataSource dataSource) throws DBException + { + return wrap(getDelegate().create(dbAdapter, dataSource)); + } + + public Set create(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException + { + return wrap(getDelegate().create(dbAdapter, connectionProvider)); + } + + public void drop(IDBAdapter dbAdapter, Connection connection) throws DBException + { + getDelegate().drop(dbAdapter, connection); + } + + public void drop(IDBAdapter dbAdapter, DataSource dataSource) throws DBException + { + getDelegate().drop(dbAdapter, dataSource); + } + + public void drop(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException + { + getDelegate().drop(dbAdapter, connectionProvider); + } + + public void export(Connection connection, PrintStream out) throws DBException + { + getDelegate().export(connection, out); + } + + public void export(DataSource dataSource, PrintStream out) throws DBException + { + getDelegate().export(dataSource, out); + } + + public void export(IDBConnectionProvider connectionProvider, PrintStream out) throws DBException + { + getDelegate().export(connectionProvider, out); + } + + public IDBSchemaDelta compare(IDBSchema oldSchema) + { + return getDelegate().compare(unwrap(oldSchema)); + } + + private static Set wrap(Set tables) + { + return wrap(tables, new HashSet()); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBSchemaElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBSchemaElement.java new file mode 100644 index 000000000..27f05e9f7 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBSchemaElement.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBSchemaVisitor; +import org.eclipse.net4j.spi.db.ddl.InternalDBField; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndexField; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchemaElement; +import org.eclipse.net4j.spi.db.ddl.InternalDBTable; +import org.eclipse.net4j.util.event.IListener; + +import java.io.IOException; +import java.io.Writer; +import java.util.Properties; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public abstract class DelegatingDBSchemaElement implements InternalDBSchemaElement +{ + private InternalDBSchemaElement delegate; + + DelegatingDBSchemaElement(InternalDBSchemaElement delegate) + { + this.delegate = delegate; + } + + public InternalDBSchemaElement getDelegate() + { + return delegate; + } + + public void setDelegate(IDBSchemaElement delegate) + { + if (this.delegate != null) + { + this.delegate.setWrapper(null); + } + + this.delegate = (InternalDBSchemaElement)delegate; + + if (this.delegate != null) + { + this.delegate.setWrapper(this); + } + } + + public final void setWrapper(IDBSchemaElement wrapper) + { + throw new UnsupportedOperationException(); + } + + public IDBSchemaElement getParent() + { + return wrap(getDelegate().getParent()); + } + + public final String getName() + { + return getDelegate().getName(); + } + + @Deprecated + public final void setName(String name) + { + getDelegate().setName(name); + } + + public final T getElement(Class type, String name) + { + return getDelegate().getElement(type, name); + } + + public final String getFullName() + { + return getDelegate().getFullName(); + } + + public final IDBSchema getSchema() + { + return wrap(getDelegate().getSchema()); + } + + public final Properties getProperties() + { + return getDelegate().getProperties(); + } + + public final String dumpToString() + { + return getDelegate().dumpToString(); + } + + public final void dump() + { + getDelegate().dump(); + } + + public final void dump(Writer writer) throws IOException + { + getDelegate().dump(writer); + } + + public final SchemaElementType getSchemaElementType() + { + return getDelegate().getSchemaElementType(); + } + + public final boolean isEmpty() + { + return getDelegate().isEmpty(); + } + + public final IDBSchemaElement[] getElements() + { + IDBSchemaElement[] elements = getDelegate().getElements(); + IDBSchemaElement[] wrappers = new IDBSchemaElement[elements.length]; + for (int i = 0; i < elements.length; i++) + { + wrappers[i] = wrap(elements[i]); + } + + return wrappers; + } + + public final void accept(IDBSchemaVisitor visitor) + { + getDelegate().accept(visitor); + } + + public final void remove() + { + getDelegate().remove(); + } + + public final void addListener(IListener listener) + { + getDelegate().addListener(listener); + } + + public final IListener[] getListeners() + { + return getDelegate().getListeners(); + } + + public final boolean hasListeners() + { + return getDelegate().hasListeners(); + } + + public final void removeListener(IListener listener) + { + getDelegate().removeListener(listener); + } + + @Override + public int hashCode() + { + return getDelegate().hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return getDelegate().equals(obj); + } + + public final int compareTo(IDBSchemaElement o) + { + return getDelegate().compareTo(unwrap(o)); + } + + @Override + public String toString() + { + return getDelegate().toString(); + } + + public static T wrap(T delegate) + { + if (delegate == null || delegate instanceof DelegatingDBSchemaElement) + { + return delegate; + } + + InternalDBSchemaElement internalDelegate = (InternalDBSchemaElement)delegate; + IDBSchemaElement wrapper = internalDelegate.getWrapper(); + if (wrapper == null) + { + SchemaElementType schemaElementType = internalDelegate.getSchemaElementType(); + switch (schemaElementType) + { + case SCHEMA: + wrapper = new DelegatingDBSchema((InternalDBSchema)internalDelegate); + break; + + case TABLE: + wrapper = new DelegatingDBTable((InternalDBTable)internalDelegate); + break; + + case FIELD: + wrapper = new DelegatingDBField((InternalDBField)internalDelegate); + break; + + case INDEX: + wrapper = new DelegatingDBIndex((InternalDBIndex)internalDelegate); + break; + + case INDEX_FIELD: + wrapper = new DelegatingDBIndexField((InternalDBIndexField)internalDelegate); + break; + + default: + throw new IllegalStateException("Illegal schema element type: " + schemaElementType); + } + + internalDelegate.setWrapper(wrapper); + } + + @SuppressWarnings("unchecked") + T result = (T)wrapper; + return result; + } + + public static T[] wrap(T[] delegates, Class type) + { + @SuppressWarnings("unchecked") + T[] wrappers = (T[])java.lang.reflect.Array.newInstance(type, delegates.length); + for (int i = 0; i < delegates.length; i++) + { + T wrapper = wrap(delegates[i]); + wrappers[i] = wrapper; + } + + return wrappers; + } + + public static Set wrap(Set delegates, Set wrappers) + { + for (T delegate : delegates) + { + wrappers.add(wrap(delegate)); + } + + return wrappers; + } + + public static T unwrap(T wrapper) + { + if (wrapper instanceof DelegatingDBSchemaElement) + { + @SuppressWarnings("unchecked") + T delegate = (T)((DelegatingDBSchemaElement)wrapper).getDelegate(); + return delegate; + } + + return wrapper; + } + + public static T[] unwrap(T[] wrappers, Class type) + { + @SuppressWarnings("unchecked") + T[] delegates = (T[])java.lang.reflect.Array.newInstance(type, wrappers.length); + for (int i = 0; i < wrappers.length; i++) + { + delegates[i] = unwrap(wrappers[i]); + } + + return delegates; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBTable.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBTable.java new file mode 100644 index 000000000..243a329ef --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/DelegatingDBTable.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.internal.db.ddl; + +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.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.SchemaElementNotFoundException; +import org.eclipse.net4j.spi.db.ddl.InternalDBTable; + +/** + * @author Eike Stepper + */ +public final class DelegatingDBTable extends DelegatingDBSchemaElement implements InternalDBTable +{ + DelegatingDBTable(InternalDBTable delegate) + { + super(delegate); + } + + @Override + public InternalDBTable getDelegate() + { + return (InternalDBTable)super.getDelegate(); + } + + @Override + public void setDelegate(IDBSchemaElement delegate) + { + IDBField[] wrapperFields = getFields(); + IDBIndex[] wrapperIndices = getIndices(); + + IDBTable delegateTable = (IDBTable)delegate; + super.setDelegate(delegateTable); + + for (IDBField wrapperField : wrapperFields) + { + IDBField delegateField = delegateTable.getField(wrapperField.getName()); + ((DelegatingDBSchemaElement)wrapperField).setDelegate(delegateField); + } + + for (IDBIndex wrapperIndex : wrapperIndices) + { + IDBIndex delegateIndex = delegateTable.getIndex(wrapperIndex.getName()); + ((DelegatingDBSchemaElement)wrapperIndex).setDelegate(delegateIndex); + } + } + + public IDBTable getWrapper() + { + return this; + } + + @Override + public IDBSchema getParent() + { + return wrap(getDelegate().getParent()); + } + + public IDBField addField(String name, DBType type) + { + return wrap(getDelegate().addField(name, type)); + } + + public IDBField addField(String name, DBType type, boolean notNull) + { + return wrap(getDelegate().addField(name, type, notNull)); + } + + public void removeField(IDBField fieldToRemove) + { + getDelegate().removeField(unwrap(fieldToRemove)); + } + + public IDBField addField(String name, DBType type, int precision) + { + return wrap(getDelegate().addField(name, type, precision)); + } + + public void removeIndex(IDBIndex indexToRemove) + { + getDelegate().removeIndex(unwrap(indexToRemove)); + } + + public IDBField addField(String name, DBType type, int precision, boolean notNull) + { + return wrap(getDelegate().addField(name, type, precision, notNull)); + } + + public IDBField addField(String name, DBType type, int precision, int scale) + { + return wrap(getDelegate().addField(name, type, precision, scale)); + } + + public IDBField addField(String name, DBType type, int precision, int scale, boolean notNull) + { + return wrap(getDelegate().addField(name, type, precision, scale, notNull)); + } + + public IDBField getFieldSafe(String name) throws SchemaElementNotFoundException + { + return wrap(getDelegate().getFieldSafe(name)); + } + + public IDBField getField(String name) + { + return wrap(getDelegate().getField(name)); + } + + public IDBField getField(int position) + { + return wrap(getDelegate().getField(position)); + } + + public int getFieldCount() + { + return getDelegate().getFieldCount(); + } + + public IDBField[] getFields() + { + return wrap(getDelegate().getFields(), IDBField.class); + } + + public IDBField[] getFields(String... fieldNames) throws SchemaElementNotFoundException + { + return wrap(getDelegate().getFields(fieldNames), IDBField.class); + } + + public boolean hasIndexFor(IDBField... fields) + { + return getDelegate().hasIndexFor(unwrap(fields, IDBField.class)); + } + + public IDBIndex addIndex(String name, Type type, IDBField... fields) + { + return wrap(getDelegate().addIndex(name, type, unwrap(fields, IDBField.class))); + } + + public IDBIndex addIndex(String name, Type type, String... fieldNames) throws SchemaElementNotFoundException + { + return wrap(getDelegate().addIndex(name, type, fieldNames)); + } + + public IDBIndex addIndexEmpty(String name, Type type) + { + return wrap(getDelegate().addIndexEmpty(name, type)); + } + + public IDBIndex addIndex(Type type, IDBField... fields) + { + return wrap(getDelegate().addIndex(type, unwrap(fields, IDBField.class))); + } + + public IDBIndex addIndex(Type type, String... fieldNames) throws SchemaElementNotFoundException + { + return wrap(getDelegate().addIndex(type, fieldNames)); + } + + public IDBIndex addIndexEmpty(Type type) + { + return wrap(getDelegate().addIndexEmpty(type)); + } + + public IDBIndex getIndexSafe(String name) throws SchemaElementNotFoundException + { + return wrap(getDelegate().getIndexSafe(name)); + } + + public IDBIndex getIndex(String name) + { + return wrap(getDelegate().getIndex(name)); + } + + public IDBIndex getIndex(int position) + { + return wrap(getDelegate().getIndex(position)); + } + + public int getIndexCount() + { + return getDelegate().getIndexCount(); + } + + public IDBIndex[] getIndices() + { + return wrap(getDelegate().getIndices(), IDBIndex.class); + } + + public IDBIndex getPrimaryKeyIndex() + { + return wrap(getDelegate().getPrimaryKeyIndex()); + } + + public String sqlInsert() + { + return getDelegate().sqlInsert(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java new file mode 100644 index 000000000..67c84ea98 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.delta.IDBDelta; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor.StopRecursion; +import org.eclipse.net4j.internal.db.ddl.DBNamedElement; +import org.eclipse.net4j.internal.db.ddl.DBSchemaElement; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.PositionProvider; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Eike Stepper + */ +public abstract class DBDelta extends DBNamedElement implements IDBDelta +{ + private static final IDBDelta[] NO_ELEMENTS = {}; + + private static final long serialVersionUID = 1L; + + private DBDelta parent; + + private ChangeKind changeKind; + + private transient IDBDelta[] elements; + + public DBDelta(DBDelta parent, String name, ChangeKind changeKind) + { + super(name); + this.parent = parent; + this.changeKind = changeKind; + } + + /** + * Constructor for deserialization. + */ + protected DBDelta() + { + } + + public DBDelta getParent() + { + return parent; + } + + public final ChangeKind getChangeKind() + { + return changeKind; + } + + public final int compareTo(IDBDelta delta2) + { + int result = getDeltaType().compareTo(delta2.getDeltaType()); + if (result == 0) + { + if (this instanceof PositionProvider && delta2 instanceof PositionProvider) + { + PositionProvider withPosition1 = (PositionProvider)this; + PositionProvider withPosition2 = (PositionProvider)delta2; + return withPosition1.getPosition() - withPosition2.getPosition(); + } + + result = getName().compareTo(delta2.getName()); + } + + return result; + } + + public final void accept(IDBDeltaVisitor visitor) + { + try + { + doAccept(visitor); + } + catch (StopRecursion ex) + { + return; + } + + for (IDBDelta delta : getElements()) + { + delta.accept(visitor); + } + } + + protected abstract void doAccept(IDBDeltaVisitor visitor); + + public final boolean isEmpty() + { + return getElements().length == 0; + } + + public final IDBDelta[] getElements() + { + if (elements == null) + { + List result = new ArrayList(); + collectElements(result); + + if (result.isEmpty()) + { + elements = NO_ELEMENTS; + } + else + { + elements = result.toArray(new IDBDelta[result.size()]); + Arrays.sort(elements); + } + } + + return elements; + } + + protected final void resetElements() + { + elements = null; + } + + protected abstract void collectElements(List elements); + + public void dump(Writer writer) throws IOException + { + int level = getLevel(); + for (int i = 0; i < level; i++) + { + writer.append(" "); + } + + writer.append(getChangeKind().toString()); + writer.append(" "); + writer.append(getDeltaType().toString()); + writer.append(" "); + writer.append(getName()); + dumpAdditionalProperties(writer); + writer.append(StringUtil.NL); + + for (IDBDelta delta : getElements()) + { + ((DBDelta)delta).dump(writer); + } + } + + protected void dumpAdditionalProperties(Writer writer) throws IOException + { + } + + private int getLevel() + { + if (parent == null) + { + return 0; + } + + return parent.getLevel() + 1; + } + + public static String getName(IDBSchemaElement element, IDBSchemaElement oldElement) + { + return oldElement == null ? element.getName() : oldElement.getName(); + } + + public static ChangeKind getChangeKind(Object object, Object oldObject) + { + return object == null ? ChangeKind.REMOVE : oldObject == null ? ChangeKind.ADD : ChangeKind.CHANGE; + } + + protected static void compare(T[] elements, T[] oldElements, SchemaElementComparator comparator) + { + for (int i = 0; i < elements.length; i++) + { + T element = elements[i]; + String name = element.getName(); + + T oldElement = DBSchemaElement.findElement(oldElements, name); + comparator.compare(element, oldElement); + } + + for (int i = 0; i < oldElements.length; i++) + { + T oldElement = oldElements[i]; + String name = oldElement.getName(); + + if (DBSchemaElement.findElement(elements, name) == null) + { + comparator.compare(null, oldElement); + } + } + } + + /** + * @author Eike Stepper + */ + public interface SchemaElementComparator + { + public void compare(T element, T oldElement); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDeltaWithPosition.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDeltaWithPosition.java new file mode 100644 index 000000000..69c3756d9 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDeltaWithPosition.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.delta.IDBDeltaWithPosition; + +/** + * @author Eike Stepper + */ +public abstract class DBDeltaWithPosition extends DBDeltaWithProperties implements IDBDeltaWithPosition +{ + private static final long serialVersionUID = 1L; + + public DBDeltaWithPosition(DBDelta parent, String name, ChangeKind changeKind) + { + super(parent, name, changeKind); + } + + /** + * Constructor for deserialization. + */ + protected DBDeltaWithPosition() + { + } + + public int getPosition() + { + Integer value = getPropertyValue(POSITION_PROPERTY); + if (value == null) + { + return 0; + } + + return value; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDeltaWithProperties.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDeltaWithProperties.java new file mode 100644 index 000000000..4f6f817ee --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDeltaWithProperties.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.delta.IDBDelta; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaWithPosition; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaWithProperties; +import org.eclipse.net4j.db.ddl.delta.IDBPropertyDelta; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public abstract class DBDeltaWithProperties extends DBDelta implements IDBDeltaWithProperties +{ + private static final long serialVersionUID = 1L; + + private Map> propertyDeltas = new HashMap>(); + + public DBDeltaWithProperties(DBDelta parent, String name, ChangeKind changeKind) + { + super(parent, name, changeKind); + } + + /** + * Constructor for deserialization. + */ + protected DBDeltaWithProperties() + { + } + + public DBPropertyDelta getPropertyDelta(String name) + { + name = name(name); + + @SuppressWarnings("unchecked") + DBPropertyDelta propertyDelta = (DBPropertyDelta)propertyDeltas.get(name); + return propertyDelta; + } + + public T getPropertyValue(String name) + { + return getPropertyValue(name, false); + } + + public T getPropertyValue(String name, boolean old) + { + IDBPropertyDelta propertyDelta = getPropertyDelta(name); + if (propertyDelta == null) + { + return null; + } + + if (old) + { + return propertyDelta.getOldValue(); + } + + return propertyDelta.getValue(); + } + + public final Map> getPropertyDeltas() + { + return Collections.unmodifiableMap(propertyDeltas); + } + + public IDBPropertyDelta[] getPropertyDeltasSortedByName() + { + DBPropertyDelta[] result = propertyDeltas.values().toArray(new DBPropertyDelta[propertyDeltas.size()]); + Arrays.sort(result); + return result; + } + + public final void addPropertyDelta(IDBPropertyDelta propertyDelta) + { + String name = propertyDelta.getName(); + propertyDeltas.put(name, propertyDelta); + resetElements(); + + if (IDBDeltaWithPosition.POSITION_PROPERTY.equals(name)) + { + DBDelta parent = getParent(); + if (parent != null) + { + parent.resetElements(); + } + } + } + + @Override + protected void collectElements(List elements) + { + elements.addAll(propertyDeltas.values()); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBFieldDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBFieldDelta.java new file mode 100644 index 000000000..32b75e39d --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBFieldDelta.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBPropertyDelta; +import org.eclipse.net4j.util.ObjectUtil; + +import java.text.MessageFormat; + +/** + * @author Eike Stepper + */ +public final class DBFieldDelta extends DBDeltaWithPosition implements IDBFieldDelta +{ + private static final long serialVersionUID = 1L; + + public DBFieldDelta(DBDelta parent, String name, ChangeKind changeKind) + { + super(parent, name, changeKind); + } + + public DBFieldDelta(DBDelta parent, IDBField field, IDBField oldField) + { + this(parent, getName(field, oldField), getChangeKind(field, oldField)); + + DBType type = field == null ? null : field.getType(); + DBType oldType = oldField == null ? null : oldField.getType(); + if (!ObjectUtil.equals(type, oldType)) + { + addPropertyDelta(new DBPropertyDelta(this, TYPE_PROPERTY, IDBPropertyDelta.Type.STRING, type, oldType)); + } + + Integer precision = field == null ? null : field.getPrecision(); + Integer oldPrecision = oldField == null ? null : oldField.getPrecision(); + if (!ObjectUtil.equals(precision, oldPrecision)) + { + addPropertyDelta(new DBPropertyDelta(this, PRECISION_PROPERTY, IDBPropertyDelta.Type.INTEGER, precision, oldPrecision)); + } + + Integer scale = field == null ? null : field.getScale(); + Integer oldScale = oldField == null ? null : oldField.getScale(); + if (!ObjectUtil.equals(scale, oldScale)) + { + addPropertyDelta(new DBPropertyDelta(this, SCALE_PROPERTY, IDBPropertyDelta.Type.INTEGER, scale, oldScale)); + } + + Boolean notNull = field == null ? null : field.isNotNull(); + Boolean oldNotNull = oldField == null ? null : oldField.isNotNull(); + if (!ObjectUtil.equals(notNull, oldNotNull)) + { + addPropertyDelta(new DBPropertyDelta(this, NOT_NULL_PROPERTY, IDBPropertyDelta.Type.BOOLEAN, notNull, oldNotNull)); + } + + Integer position = field == null ? null : field.getPosition(); + Integer oldPosition = oldField == null ? null : oldField.getPosition(); + if (!ObjectUtil.equals(position, oldPosition)) + { + addPropertyDelta(new DBPropertyDelta(this, POSITION_PROPERTY, IDBPropertyDelta.Type.INTEGER, position, oldPosition)); + } + } + + /** + * Constructor for deserialization. + */ + protected DBFieldDelta() + { + } + + public DeltaType getDeltaType() + { + return DeltaType.FIELD; + } + + @Override + public DBTableDelta getParent() + { + return (DBTableDelta)super.getParent(); + } + + public IDBField getSchemaElement(IDBSchema schema) + { + IDBTable table = getParent().getSchemaElement(schema); + if (table == null) + { + return null; + } + + return table.getField(getName()); + } + + @Override + public String toString() + { + return MessageFormat.format("DBFieldDelta[name={0}, kind={1}, propertyDeltas={2}]", getName(), getChangeKind(), getPropertyDeltas().values()); + } + + @Override + protected void doAccept(IDBDeltaVisitor visitor) + { + visitor.visit(this); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBIndexDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBIndexDelta.java new file mode 100644 index 000000000..b525b090b --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBIndexDelta.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.delta.IDBDelta; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBIndexDelta; +import org.eclipse.net4j.db.ddl.delta.IDBIndexFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBPropertyDelta; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.util.ObjectUtil; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public final class DBIndexDelta extends DBDeltaWithProperties implements IDBIndexDelta +{ + private static final long serialVersionUID = 1L; + + private Map indexFieldDeltas = new HashMap(); + + public DBIndexDelta(DBDelta parent, String name, ChangeKind changeKind) + { + super(parent, name, changeKind); + } + + public DBIndexDelta(DBDelta parent, IDBIndex index, IDBIndex oldIndex) + { + this(parent, getName(index, oldIndex), getChangeKind(index, oldIndex)); + + IDBIndex.Type type = index == null ? null : index.getType(); + IDBIndex.Type oldType = oldIndex == null ? null : oldIndex.getType(); + if (!ObjectUtil.equals(type, oldType)) + { + addPropertyDelta(new DBPropertyDelta(this, TYPE_PROPERTY, IDBPropertyDelta.Type.STRING, type, oldType)); + } + + Boolean optional = index == null ? null : ((InternalDBIndex)index).isOptional(); + Boolean oldOptional = oldIndex == null ? null : ((InternalDBIndex)oldIndex).isOptional(); + if (!ObjectUtil.equals(optional, oldOptional)) + { + addPropertyDelta(new DBPropertyDelta(this, OPTIONAL_PROPERTY, IDBPropertyDelta.Type.BOOLEAN, optional, oldOptional)); + } + + IDBIndexField[] indexFields = index == null ? InternalDBIndex.NO_INDEX_FIELDS : index.getIndexFields(); + IDBIndexField[] oldIndexFields = oldIndex == null ? InternalDBIndex.NO_INDEX_FIELDS : oldIndex.getIndexFields(); + compare(indexFields, oldIndexFields, new SchemaElementComparator() + { + public void compare(IDBIndexField indexField, IDBIndexField oldIndexField) + { + DBIndexFieldDelta indexFieldDelta = new DBIndexFieldDelta(DBIndexDelta.this, indexField, oldIndexField); + if (!indexFieldDelta.isEmpty()) + { + addIndexFieldDelta(indexFieldDelta); + } + } + }); + } + + /** + * Constructor for deserialization. + */ + protected DBIndexDelta() + { + } + + public DeltaType getDeltaType() + { + return DeltaType.INDEX; + } + + @Override + public DBTableDelta getParent() + { + return (DBTableDelta)super.getParent(); + } + + public int getIndexFieldDeltaCount() + { + return indexFieldDeltas.size(); + } + + public DBIndexFieldDelta getIndexFieldDelta(int position) + { + for (IDBIndexFieldDelta indexFieldDelta : indexFieldDeltas.values()) + { + if (indexFieldDelta.getPosition() == position) + { + return (DBIndexFieldDelta)indexFieldDelta; + } + } + + return null; + } + + public DBIndexFieldDelta getIndexFieldDelta(String name) + { + return (DBIndexFieldDelta)indexFieldDeltas.get(name); + } + + public Map getIndexFieldDeltas() + { + return Collections.unmodifiableMap(indexFieldDeltas); + } + + public DBIndexFieldDelta[] getIndexFieldDeltasSortedByPosition() + { + DBIndexFieldDelta[] result = indexFieldDeltas.values().toArray(new DBIndexFieldDelta[indexFieldDeltas.size()]); + Arrays.sort(result); + return result; + } + + public IDBIndex getSchemaElement(IDBSchema schema) + { + IDBTable table = getParent().getSchemaElement(schema); + if (table == null) + { + return null; + } + + return table.getIndex(getName()); + } + + @Override + public String toString() + { + return MessageFormat.format("DBIndexDelta[name={0}, kind={1}, propertyDeltas={2}, indexFieldDeltas={3}]", getName(), getChangeKind(), + getPropertyDeltas().values(), indexFieldDeltas.values()); + } + + public void addIndexFieldDelta(DBIndexFieldDelta indexFieldDelta) + { + indexFieldDeltas.put(indexFieldDelta.getName(), indexFieldDelta); + resetElements(); + } + + @Override + protected void doAccept(IDBDeltaVisitor visitor) + { + visitor.visit(this); + } + + @Override + protected void collectElements(List elements) + { + elements.addAll(indexFieldDeltas.values()); + super.collectElements(elements); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBIndexFieldDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBIndexFieldDelta.java new file mode 100644 index 000000000..cc3f13037 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBIndexFieldDelta.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBIndexFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBPropertyDelta; +import org.eclipse.net4j.util.ObjectUtil; + +import java.text.MessageFormat; + +/** + * @author Eike Stepper + */ +public final class DBIndexFieldDelta extends DBDeltaWithPosition implements IDBIndexFieldDelta +{ + private static final long serialVersionUID = 1L; + + public DBIndexFieldDelta(DBDelta parent, String name, ChangeKind changeKind) + { + super(parent, name, changeKind); + } + + public DBIndexFieldDelta(DBIndexDelta parent, IDBIndexField indexField, IDBIndexField oldIndexField) + { + this(parent, getName(indexField, oldIndexField), getChangeKind(indexField, oldIndexField)); + + Integer position = indexField == null ? null : indexField.getPosition(); + Integer oldPosition = oldIndexField == null ? null : oldIndexField.getPosition(); + if (!ObjectUtil.equals(position, oldPosition)) + { + addPropertyDelta(new DBPropertyDelta(this, POSITION_PROPERTY, IDBPropertyDelta.Type.INTEGER, position, oldPosition)); + } + } + + /** + * Constructor for deserialization. + */ + protected DBIndexFieldDelta() + { + } + + public DeltaType getDeltaType() + { + return DeltaType.INDEX_FIELD; + } + + @Override + public DBIndexDelta getParent() + { + return (DBIndexDelta)super.getParent(); + } + + public IDBIndexField getSchemaElement(IDBSchema schema) + { + IDBIndex index = getParent().getSchemaElement(schema); + if (index == null) + { + return null; + } + + return index.getIndexField(getName()); + } + + @Override + public String toString() + { + return MessageFormat.format("DBIndexFieldDelta[name={0}, kind={1}, propertyDeltas={2}]", getName(), getChangeKind(), getPropertyDeltas().values()); + } + + @Override + protected void doAccept(IDBDeltaVisitor visitor) + { + visitor.visit(this); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBPropertyDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBPropertyDelta.java new file mode 100644 index 000000000..e8379ec2d --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBPropertyDelta.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.ddl.delta.IDBDelta; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBPropertyDelta; + +import java.io.IOException; +import java.io.Writer; +import java.text.MessageFormat; +import java.util.List; + +/** + * @author Eike Stepper + */ +public final class DBPropertyDelta extends DBDelta implements IDBPropertyDelta +{ + private static final long serialVersionUID = 1L; + + private Type type; + + private T value; + + private T oldValue; + + public DBPropertyDelta(DBDelta parent, String name, Type type, T value, T oldValue) + { + super(parent, name, DBDelta.getChangeKind(value, oldValue)); + this.type = type; + this.value = value; + this.oldValue = oldValue; + } + + /** + * Constructor for deserialization. + */ + protected DBPropertyDelta() + { + } + + public DeltaType getDeltaType() + { + return DeltaType.PROPERTY; + } + + public IDBSchemaElement getSchemaElement(IDBSchema schema) + { + return null; + } + + public Type getType() + { + return type; + } + + public T getValue() + { + return value; + } + + public T getOldValue() + { + return oldValue; + } + + @Override + public String toString() + { + return MessageFormat.format("DBPropertyDelta[name={0}, kind={1}, type={2}, value={3}, oldValue={4}]", getName(), getChangeKind(), getType(), getValue(), + getOldValue()); + } + + @Override + protected void doAccept(IDBDeltaVisitor visitor) + { + visitor.visit(this); + } + + @Override + protected void collectElements(List elements) + { + // Do nothing + } + + @Override + protected void dumpAdditionalProperties(Writer writer) throws IOException + { + writer.append(", type="); + writer.append(getType().toString()); + writer.append(", value="); + writer.append(toString(getValue())); + writer.append(", oldValue="); + writer.append(toString(getOldValue())); + } + + private static CharSequence toString(Object object) + { + if (object == null) + { + return "null"; + } + + return object.toString(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBSchemaDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBSchemaDelta.java new file mode 100644 index 000000000..8cbccb286 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBSchemaDelta.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013, 2015, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +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.IDBIndexField; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.db.ddl.delta.IDBDelta; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBIndexDelta; +import org.eclipse.net4j.db.ddl.delta.IDBIndexFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.db.ddl.delta.IDBTableDelta; +import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public final class DBSchemaDelta extends DBDelta implements IDBSchemaDelta +{ + private static final long serialVersionUID = 1L; + + private Map tableDeltas = new HashMap(); + + public DBSchemaDelta(String name, ChangeKind changeKind) + { + super(null, name, changeKind); + } + + public DBSchemaDelta(IDBSchema schema, IDBSchema oldSchema) + { + this(schema.getName(), oldSchema == null ? ChangeKind.ADD : ChangeKind.CHANGE); + + IDBTable[] tables = schema.getTables(); + IDBTable[] oldTables = oldSchema == null ? InternalDBSchema.NO_TABLES : oldSchema.getTables(); + compare(tables, oldTables, new SchemaElementComparator() + { + public void compare(IDBTable table, IDBTable oldTable) + { + DBTableDelta tableDelta = new DBTableDelta(DBSchemaDelta.this, table, oldTable); + if (!tableDelta.isEmpty()) + { + addTableDelta(tableDelta); + } + } + }); + } + + /** + * Constructor for deserialization. + */ + protected DBSchemaDelta() + { + } + + public DeltaType getDeltaType() + { + return DeltaType.SCHEMA; + } + + public int getTableDeltaCount() + { + return tableDeltas.size(); + } + + public DBTableDelta getTableDelta(String name) + { + return (DBTableDelta)tableDeltas.get(name); + } + + public Map getTableDeltas() + { + return Collections.unmodifiableMap(tableDeltas); + } + + public IDBTableDelta[] getTableDeltasSortedByName() + { + IDBTableDelta[] result = tableDeltas.values().toArray(new IDBTableDelta[tableDeltas.size()]); + Arrays.sort(result); + return result; + } + + public void addTableDelta(IDBTableDelta tableDelta) + { + tableDeltas.put(tableDelta.getName(), tableDelta); + resetElements(); + } + + public IDBSchema getSchemaElement(IDBSchema schema) + { + return schema; + } + + public void applyTo(IDBSchema schema) + { + IDBDeltaVisitor visitor = new DBSchemaDelta.Applier(schema); + accept(visitor); + } + + @Override + public String toString() + { + return MessageFormat.format("DBSchemaDelta[name={0}, kind={1}, tableDeltas={2}]", getName(), getChangeKind(), tableDeltas.values()); + } + + @Override + protected void doAccept(IDBDeltaVisitor visitor) + { + visitor.visit(this); + } + + @Override + protected void collectElements(List elements) + { + elements.addAll(tableDeltas.values()); + } + + /** + * @author Eike Stepper + */ + public static class Applier extends IDBDeltaVisitor.Default + { + private final IDBSchema schema; + + public Applier(IDBSchema schema) + { + this.schema = schema; + } + + public final IDBSchema getSchema() + { + return schema; + } + + @Override + public void added(IDBTableDelta delta) + { + String name = delta.getName(); + schema.addTable(name); + } + + @Override + public void removed(IDBTableDelta delta) + { + IDBTable table = delta.getSchemaElement(schema); + table.remove(); + } + + @Override + public void changed(IDBTableDelta delta) + { + } + + @Override + public void added(IDBFieldDelta delta) + { + String name = delta.getName(); + DBType type = delta.getPropertyValue(IDBFieldDelta.TYPE_PROPERTY); + int precision = delta.getPropertyValue(IDBFieldDelta.PRECISION_PROPERTY); + int scale = delta.getPropertyValue(IDBFieldDelta.SCALE_PROPERTY); + boolean notNull = delta.getPropertyValue(IDBFieldDelta.NOT_NULL_PROPERTY); + + IDBTable table = delta.getParent().getSchemaElement(schema); + table.addField(name, type, precision, scale, notNull); + } + + @Override + public void removed(IDBFieldDelta delta) + { + IDBField field = delta.getSchemaElement(schema); + field.remove(); + } + + @Override + public void changed(IDBFieldDelta delta) + { + } + + @Override + public void added(IDBIndexDelta delta) + { + String name = delta.getName(); + IDBIndex.Type type = delta.getPropertyValue(IDBIndexDelta.TYPE_PROPERTY); + Boolean optional = delta.getPropertyValue(IDBIndexDelta.OPTIONAL_PROPERTY); + + IDBTable table = delta.getParent().getSchemaElement(schema); + IDBIndex index = table.addIndexEmpty(name, type); + DBUtil.setOptional(index, optional == Boolean.TRUE); + } + + @Override + public void removed(IDBIndexDelta delta) + { + IDBIndex index = delta.getSchemaElement(schema); + index.remove(); + } + + @Override + public void changed(IDBIndexDelta delta) + { + } + + @Override + public void added(IDBIndexFieldDelta delta) + { + IDBIndexDelta parent = delta.getParent(); + IDBTable table = parent.getParent().getSchemaElement(schema); + + String name = delta.getName(); + IDBField field = table.getField(name); + + IDBIndex index = parent.getSchemaElement(schema); + index.addIndexField(field); + } + + @Override + public void removed(IDBIndexFieldDelta delta) + { + IDBIndexField indexField = delta.getSchemaElement(schema); + indexField.remove(); + } + + @Override + public void changed(IDBIndexFieldDelta delta) + { + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java new file mode 100644 index 000000000..07e2927b9 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2013, 2016 Eike Stepper (Loehne, 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.net4j.internal.db.ddl.delta; + +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.db.ddl.delta.IDBDelta; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBIndexDelta; +import org.eclipse.net4j.db.ddl.delta.IDBTableDelta; +import org.eclipse.net4j.spi.db.ddl.InternalDBTable; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public final class DBTableDelta extends DBDelta implements IDBTableDelta +{ + private static final long serialVersionUID = 1L; + + private Map fieldDeltas = new HashMap(); + + private Map indexDeltas = new HashMap(); + + public DBTableDelta(DBDelta parent, String name, ChangeKind changeKind) + { + super(parent, name, changeKind); + } + + public DBTableDelta(DBSchemaDelta parent, IDBTable table, IDBTable oldTable) + { + this(parent, getName(table, oldTable), getChangeKind(table, oldTable)); + + IDBField[] fields = table == null ? InternalDBTable.NO_FIELDS : table.getFields(); + IDBField[] oldFields = oldTable == null ? InternalDBTable.NO_FIELDS : oldTable.getFields(); + compare(fields, oldFields, new SchemaElementComparator() + { + public void compare(IDBField field, IDBField oldField) + { + DBFieldDelta fieldDelta = new DBFieldDelta(DBTableDelta.this, field, oldField); + if (!fieldDelta.isEmpty()) + { + addFieldDelta(fieldDelta); + } + } + }); + + IDBIndex[] indices = table == null ? InternalDBTable.NO_INDICES : table.getIndices(); + IDBIndex[] oldIndices = oldTable == null ? InternalDBTable.NO_INDICES : oldTable.getIndices(); + compare(indices, oldIndices, new SchemaElementComparator() + { + public void compare(IDBIndex index, IDBIndex oldIndex) + { + DBIndexDelta indexDelta = new DBIndexDelta(DBTableDelta.this, index, oldIndex); + if (!indexDelta.isEmpty()) + { + addIndexDelta(indexDelta); + } + } + }); + } + + /** + * Constructor for deserialization. + */ + protected DBTableDelta() + { + } + + public DeltaType getDeltaType() + { + return DeltaType.TABLE; + } + + @Override + public DBSchemaDelta getParent() + { + return (DBSchemaDelta)super.getParent(); + } + + public int getFieldDeltaCount() + { + return fieldDeltas.size(); + } + + public int getIndexDeltaCount() + { + return indexDeltas.size(); + } + + public DBFieldDelta getFieldDelta(int position) + { + for (IDBFieldDelta fieldDelta : fieldDeltas.values()) + { + if (fieldDelta.getPosition() == position) + { + return (DBFieldDelta)fieldDelta; + } + } + + return null; + } + + public DBFieldDelta getFieldDelta(String name) + { + return (DBFieldDelta)fieldDeltas.get(name); + } + + public DBIndexDelta getIndexDelta(String name) + { + return (DBIndexDelta)indexDeltas.get(name); + } + + public Map getFieldDeltas() + { + return Collections.unmodifiableMap(fieldDeltas); + } + + public Map getIndexDeltas() + { + return Collections.unmodifiableMap(indexDeltas); + } + + public DBFieldDelta[] getFieldDeltasSortedByPosition() + { + DBFieldDelta[] result = fieldDeltas.values().toArray(new DBFieldDelta[fieldDeltas.size()]); + Arrays.sort(result); + return result; + } + + public DBIndexDelta[] getIndexDeltasSortedByName() + { + DBIndexDelta[] result = indexDeltas.values().toArray(new DBIndexDelta[indexDeltas.size()]); + Arrays.sort(result); + return result; + } + + public IDBTable getSchemaElement(IDBSchema schema) + { + return schema.getTable(getName()); + } + + @Override + public String toString() + { + return MessageFormat.format("DBTableDelta[name={0}, kind={1}, fieldDeltas={2}, indexDeltas={3}]", getName(), getChangeKind(), fieldDeltas.values(), + indexDeltas.values()); + } + + public void addFieldDelta(IDBFieldDelta fieldDelta) + { + fieldDeltas.put(fieldDelta.getName(), fieldDelta); + resetElements(); + } + + public void addIndexDelta(IDBIndexDelta indexDelta) + { + indexDeltas.put(indexDelta.getName(), indexDelta); + resetElements(); + } + + @Override + protected void doAccept(IDBDeltaVisitor visitor) + { + visitor.visit(this); + } + + @Override + protected void collectElements(List elements) + { + elements.addAll(fieldDeltas.values()); + elements.addAll(indexDeltas.values()); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/dml/DBParameter.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/dml/DBParameter.java new file mode 100644 index 000000000..15c992541 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/dml/DBParameter.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008, 2011-2013 Eike Stepper (Loehne, 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.net4j.internal.db.dml; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.dml.IDBParameter; +import org.eclipse.net4j.db.dml.IDBStatement; + +/** + * @author Eike Stepper + * @deprecated + */ +@Deprecated +public class DBParameter implements IDBParameter +{ + private IDBStatement statement; + + private int position; + + private DBType type; + + public DBParameter(IDBStatement statement, int position, DBType type) + { + this.statement = statement; + this.position = position; + this.type = type; + } + + public IDBStatement getStatement() + { + return statement; + } + + public int getPosition() + { + return position; + } + + public DBType getType() + { + return type; + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/dml/DBStatement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/dml/DBStatement.java new file mode 100644 index 000000000..1da408fb9 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/dml/DBStatement.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2008, 2009, 2011-2013 Eike Stepper (Loehne, 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.net4j.internal.db.dml; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBSchemaElement; +import org.eclipse.net4j.db.dml.IDBParameter; +import org.eclipse.net4j.db.dml.IDBStatement; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eike Stepper + * @deprecated + */ +@Deprecated +public class DBStatement implements IDBStatement +{ + private static final DBParameter[] NO_PARAMETERS = {}; + + private List parameters; + + private List sequence = new ArrayList(); + + public IDBParameter addParameter(DBType type) + { + int position = 0; + if (parameters == null) + { + parameters = new ArrayList(); + } + else + { + position = parameters.size(); + } + + DBParameter parameter = new DBParameter(this, position, type); + parameters.add(parameter); + return parameter; + } + + public IDBParameter addParameter(IDBField field) + { + return addParameter(field.getType()); + } + + public DBParameter[] getParameters() + { + if (parameters == null) + { + return NO_PARAMETERS; + } + + return parameters.toArray(new DBParameter[parameters.size()]); + } + + public void addSQL(String literal) + { + int tailPos = sequence.size() - 1; + Object tail = sequence.get(tailPos); + if (tail instanceof String) + { + sequence.set(tailPos, (String)tail + literal); + } + else + { + sequence.add(literal); + } + } + + public void addSQL(IDBParameter parameter) + { + sequence.add(parameter); + } + + public void addSQL(IDBSchemaElement schemaElement) + { + addSQL(schemaElement.getName()); + } + + public String getSQL() + { + StringBuilder builder = new StringBuilder(); + for (Object element : sequence) + { + if (element instanceof IDBParameter) + { + builder.append("?"); //$NON-NLS-1$ + } + else + { + builder.append(element); + } + } + + return builder.toString(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java new file mode 100644 index 000000000..3be0858f0 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java @@ -0,0 +1,1245 @@ +/* + * Copyright (c) 2008-2016 Eike Stepper (Loehne, 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 289445 + */ +package org.eclipse.net4j.spi.db; + +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.IDBConnectionProvider; +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.db.ddl.delta.IDBDelta.ChangeKind; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBFieldDelta; +import org.eclipse.net4j.db.ddl.delta.IDBIndexDelta; +import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; +import org.eclipse.net4j.db.ddl.delta.IDBTableDelta; +import org.eclipse.net4j.internal.db.bundle.OM; +import org.eclipse.net4j.internal.db.ddl.DBField; +import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import javax.sql.DataSource; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.Driver; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A useful base class for implementing custom {@link IDBAdapter DB adapters}. + * + * @author Eike Stepper + */ +public abstract class DBAdapter implements IDBAdapter +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SQL, DBAdapter.class); + + private static final String[] SQL92_RESERVED_WORDS = { "ABSOLUTE", "ACTION", "ADD", "AFTER", "ALL", "ALLOCATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + "ALTER", "AND", "ANY", "ARE", "ARRAY", "AS", "ASC", "ASENSITIVE", "ASSERTION", "ASYMMETRIC", "AT", "ATOMIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "AUTHORIZATION", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BIT", "BIT_LENGTH", "BLOB", "BOOLEAN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "BOTH", "BREADTH", "BY", "CALL", "CALLED", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAR", "CHARACTER", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ + "COMMIT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONSTRUCTOR", "CONTAINS", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "CONTINUE", "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS", "CUBE", "CURRENT", "CURRENT_DATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ + "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATA", "DATE", "DAY", "DEALLOCATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DEPTH", "DEREF", "DESC", "DESCRIBE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DO", "DOMAIN", "DOUBLE", "DROP", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ + "DYNAMIC", "EACH", "ELEMENT", "ELSE", "ELSEIF", "END", "EQUALS", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "EXECUTE", "EXISTS", "EXIT", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FILTER", "FIRST", "FLOAT", "FOR", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "FOREIGN", "FOUND", "FREE", "FROM", "FULL", "FUNCTION", "GENERAL", "GET", "GLOBAL", "GO", "GOTO", "GRANT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "GROUP", "GROUPING", "HANDLER", "HAVING", "HOLD", "HOUR", "IDENTITY", "IF", "IMMEDIATE", "IN", "INDICATOR", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "INITIALLY", "INNER", "INOUT", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "INTO", "IS", "ISOLATION", "ITERATE", "JOIN", "KEY", "LANGUAGE", "LARGE", "LAST", "LATERAL", "LEADING", "LEAVE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "LEFT", "LEVEL", "LIKE", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOOP", "LOWER", "MAP", "MATCH", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "MAX", "MEMBER", "MERGE", "METHOD", "MIN", "MINUTE", "MODIFIES", "MODULE", "MONTH", "MULTISET", "NAMES", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEXT", "NO", "NONE", "NOT", "NULL", "NULLIF", "NUMERIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "OBJECT", "OCTET_LENGTH", "OF", "OLD", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER", "ORDINALITY", "OUT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "OUTER", "OUTPUT", "OVER", "OVERLAPS", "PAD", "PARAMETER", "PARTIAL", "PARTITION", "PATH", "POSITION", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", "RANGE", "READ", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "READS", "REAL", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "RELATIVE", "RELEASE", "REPEAT", "RESIGNAL", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "RESTRICT", "RESULT", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "ROWS", "SAVEPOINT", "SCHEMA", "SCOPE", "SCROLL", "SEARCH", "SECOND", "SECTION", "SELECT", "SENSITIVE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "SESSION", "SESSION_USER", "SET", "SETS", "SIGNAL", "SIMILAR", "SIZE", "SMALLINT", "SOME", "SPACE", "SPECIFIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "SPECIFICTYPE", "SQL", "SQLCODE", "SQLERROR", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "START", "STATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ + "STATIC", "SUBMULTISET", "SUBSTRING", "SUM", "SYMMETRIC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ + "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ + "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "UNDER", "UNDO", "UNION", "UNIQUE", "UNKNOWN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ + "UNNEST", "UNTIL", "UPDATE", "UPPER", "USAGE", "USER", "USING", "VALUE", "VALUES", "VARCHAR", "VARYING", "VIEW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + "WHEN", "WHENEVER", "WHERE", "WHILE", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRITE", "YEAR", "ZONE" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ + + private String name; + + private String version; + + private Set reservedWords; + + public DBAdapter(String name, String version) + { + this.name = name; + this.version = version; + } + + public String getName() + { + return name; + } + + public String getVersion() + { + return version; + } + + /** + * @since 4.2 + * @deprecated As of 4.2 no longer supported because of IP issues for external build dependencies (the vendor driver libs). + */ + @Deprecated + public Driver getJDBCDriver() + { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.2 + * @deprecated As of 4.2 no longer supported because of IP issues for external build dependencies (the vendor driver libs). + */ + @Deprecated + public DataSource createJDBCDataSource() + { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.3 + */ + public IDBConnectionProvider createConnectionProvider(DataSource dataSource) + { + return DBUtil.createConnectionProvider(dataSource); + } + + /** + * @since 4.5 + */ + public Connection modifyConnection(Connection connection) + { + return connection; + } + + /** + * @since 4.2 + */ + public IDBSchema readSchema(Connection connection, String name) + { + return DBUtil.readSchema(this, connection, name); + } + + /** + * @since 4.2 + */ + public void readSchema(Connection connection, IDBSchema schema) + { + boolean wasTrackConstruction = DBField.isTrackConstruction(); + DBField.trackConstruction(false); + + try + { + String schemaName = schema.getName(); + + DatabaseMetaData metaData = connection.getMetaData(); + Set schemaNames = DBUtil.getAllSchemaNames(metaData); + if (!schemaNames.contains(schemaName)) + { + schemaName = null; + } + + ResultSet tables = readTables(connection, metaData, schemaName); + while (tables.next()) + { + String tableName = tables.getString(3); + + IDBTable table = schema.addTable(tableName); + readFields(connection, table); + readIndices(connection, metaData, table, schemaName); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBField.trackConstruction(wasTrackConstruction); + } + } + + /** + * @since 4.3 + */ + protected ResultSet readTables(Connection connection, DatabaseMetaData metaData, String schemaName) throws SQLException + { + return metaData.getTables(null, schemaName, null, new String[] { "TABLE" }); + } + + /** + * @since 4.2 + */ + protected void readFields(Connection connection, IDBTable table) throws SQLException + { + Statement statement = null; + ResultSet resultSet = null; + + try + { + statement = connection.createStatement(); + statement.setMaxRows(1); + resultSet = statement.executeQuery("SELECT * FROM " + table); + ResultSetMetaData metaData = resultSet.getMetaData(); + + for (int i = 0; i < metaData.getColumnCount(); i++) + { + int column = i + 1; + String name = metaData.getColumnName(column); + if (name == null) + { + // Bug 405924: Just to be sure in case this happens with Oracle. + continue; + } + + DBType type = DBType.getTypeByCode(metaData.getColumnType(column)); + int precision = metaData.getPrecision(column); + int scale = metaData.getScale(column); + boolean notNull = metaData.isNullable(column) == ResultSetMetaData.columnNoNulls; + + table.addField(name, type, precision, scale, notNull); + } + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(statement); + } + } + + /** + * @since 4.2 + */ + protected void readIndices(Connection connection, DatabaseMetaData metaData, IDBTable table, String schemaName) throws SQLException + { + String tableName = table.getName(); + + ResultSet primaryKeys = metaData.getPrimaryKeys(null, schemaName, tableName); + readIndices(connection, primaryKeys, table, 6, 0, 4, 5); + + ResultSet indexInfo = metaData.getIndexInfo(null, schemaName, tableName, false, false); + readIndices(connection, indexInfo, table, 6, 4, 9, 8); + } + + /** + * @since 4.2 + */ + protected void readIndices(Connection connection, ResultSet resultSet, IDBTable table, int indexNameColumn, int indexTypeColumn, int fieldNameColumn, + int fieldPositionColumn) throws SQLException + { + try + { + String indexName = null; + IDBIndex.Type indexType = null; + List fieldInfos = new ArrayList(); + + while (resultSet.next()) + { + String name = resultSet.getString(indexNameColumn); + if (name == null) + { + // Bug 405924: It seems that this can happen with Oracle. + continue; + } + + if (indexName != null && !indexName.equals(name)) + { + addIndex(connection, table, indexName, indexType, fieldInfos); + fieldInfos.clear(); + } + + indexName = name; + + if (indexTypeColumn == 0) + { + indexType = IDBIndex.Type.PRIMARY_KEY; + } + else + { + boolean nonUnique = resultSet.getBoolean(indexTypeColumn); + indexType = nonUnique ? IDBIndex.Type.NON_UNIQUE : IDBIndex.Type.UNIQUE; + } + + FieldInfo fieldInfo = new FieldInfo(); + fieldInfo.name = resultSet.getString(fieldNameColumn); + fieldInfo.position = resultSet.getShort(fieldPositionColumn); + fieldInfos.add(fieldInfo); + } + + if (indexName != null) + { + addIndex(connection, table, indexName, indexType, fieldInfos); + } + } + finally + { + DBUtil.close(resultSet); + } + } + + /** + * @since 4.2 + */ + protected void addIndex(Connection connection, IDBTable table, String name, IDBIndex.Type type, List fieldInfos) + { + IDBField[] fields = new IDBField[fieldInfos.size()]; + + Collections.sort(fieldInfos); + for (int i = 0; i < fieldInfos.size(); i++) + { + FieldInfo fieldInfo = fieldInfos.get(i); + IDBField field = table.getField(fieldInfo.name); + if (field == null) + { + throw new IllegalStateException("Field not found: " + fieldInfo.name); + } + + fields[i] = field; + } + + if (!isPrimaryKeyShadow(connection, table, name, type, fields)) + { + table.addIndex(name, type, fields); + } + } + + /** + * @since 4.2 + */ + protected boolean isPrimaryKeyShadow(Connection connection, IDBTable table, String name, IDBIndex.Type type, IDBField[] fields) + { + if (type != IDBIndex.Type.UNIQUE) + { + return false; + } + + IDBIndex primaryKey = table.getPrimaryKeyIndex(); + if (primaryKey == null) + { + return false; + } + + IDBField[] primaryKeyFields = primaryKey.getFields(); + return Arrays.equals(primaryKeyFields, fields); + } + + /** + * @since 4.2 + */ + public void updateSchema(final Connection connection, final IDBSchema schema, IDBSchemaDelta delta) throws DBException + { + // Apply delta to in-memory representation of the schema + delta.applyTo(schema); + + // Call DDL methods to update the database schema + IDBDeltaVisitor schemaUpdater = new IDBDeltaVisitor.Default() + { + @Override + public void visit(IDBTableDelta delta) + { + IDBTable table = delta.getSchemaElement(schema); + ChangeKind changeKind = delta.getChangeKind(); + + switch (changeKind) + { + case ADD: + createTable(connection, table, delta); + break; + + case CHANGE: + alterTable(connection, table, delta); + break; + + case REMOVE: + dropTable(connection, table, delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + } + + @Override + public void visit(IDBIndexDelta delta) + { + InternalDBIndex index = (InternalDBIndex)delta.getSchemaElement(schema); + ChangeKind changeKind = delta.getChangeKind(); + + switch (changeKind) + { + case ADD: + try + { + createIndex(connection, index, delta); + } + catch (RuntimeException ex) + { + if (!index.isOptional()) + { + throw ex; + } + } + break; + + case CHANGE: + dropIndex(connection, index, delta); + try + { + createIndex(connection, index, delta); + } + catch (RuntimeException ex) + { + if (!index.isOptional()) + { + throw ex; + } + } + break; + + case REMOVE: + dropIndex(connection, index, delta); + break; + + default: + throw illegalChangeKind(changeKind); + } + + stopRecursion(); + } + + @Override + public void visit(IDBFieldDelta delta) + { + stopRecursion(); + } + }; + + delta.accept(schemaUpdater); + } + + /** + * @since 4.2 + */ + protected void createTable(Connection connection, IDBTable table, IDBTableDelta delta) + { + CheckUtil.checkArg(delta.getChangeKind() == ChangeKind.ADD, "Not added: " + delta.getName()); + + StringBuilder builder = new StringBuilder(); + builder.append("CREATE TABLE "); //$NON-NLS-1$ + builder.append(delta.getName()); + builder.append(" ("); //$NON-NLS-1$ + appendFieldDefs(builder, table, createFieldDefinitions(table)); + builder.append(")"); //$NON-NLS-1$ + + DBUtil.execute(connection, builder); + } + + /** + * @since 4.2 + */ + protected void dropTable(Connection connection, IDBTable table, IDBTableDelta delta) + { + String sql = getDropTableSQL(table); + DBUtil.execute(connection, sql); + } + + /** + * @since 4.2 + */ + protected void alterTable(Connection connection, IDBTable table, IDBTableDelta delta) + { + for (IDBFieldDelta fieldDelta : delta.getFieldDeltas().values()) + { + ChangeKind changeKind = fieldDelta.getChangeKind(); + String fieldName = fieldDelta.getName(); + String tableName = table.getName(); + + switch (changeKind) + { + case ADD: + createField(connection, tableName, table.getField(fieldName)); + break; + + case CHANGE: + dropField(connection, tableName, fieldName); + createField(connection, tableName, table.getField(fieldName)); + break; + + case REMOVE: + dropField(connection, tableName, fieldName); + break; + + default: + throw IDBDeltaVisitor.Default.illegalChangeKind(changeKind); + } + } + } + + /** + * @since 4.6 + */ + protected void createField(Connection connection, String tableName, IDBField field) + { + DBUtil.execute(connection, "ALTER TABLE " + tableName + " ADD COLUMN " + field.getName() + " " + createFieldDefinition(field)); + } + + /** + * @since 4.6 + */ + protected void dropField(Connection connection, String tableName, String fieldName) + { + DBUtil.execute(connection, "ALTER TABLE " + tableName + " DROP COLUMN " + fieldName); + } + + /** + * @since 4.2 + */ + protected void createIndex(Connection connection, IDBIndex index, IDBIndexDelta delta) + { + StringBuilder builder = new StringBuilder(); + if (index.getType() == IDBIndex.Type.PRIMARY_KEY) + { + createPrimaryKey(index, builder); + } + else + { + createIndex(index, builder); + } + + createIndexFields(index, builder); + DBUtil.execute(connection, builder); + } + + /** + * @since 4.2 + */ + protected void createPrimaryKey(IDBIndex index, StringBuilder builder) + { + builder.append("ALTER TABLE "); //$NON-NLS-1$ + builder.append(index.getTable()); + builder.append(" ADD CONSTRAINT "); //$NON-NLS-1$ + builder.append(index); + builder.append(" PRIMARY KEY"); //$NON-NLS-1$ + } + + /** + * @since 4.2 + */ + protected void createIndex(IDBIndex index, StringBuilder builder) + { + builder.append("CREATE "); //$NON-NLS-1$ + if (index.getType() == IDBIndex.Type.UNIQUE) + { + builder.append("UNIQUE "); //$NON-NLS-1$ + } + + builder.append("INDEX "); //$NON-NLS-1$ + builder.append(index); + builder.append(" ON "); //$NON-NLS-1$ + builder.append(index.getTable()); + } + + /** + * @since 4.2 + */ + protected void createIndexFields(IDBIndex index, StringBuilder builder) + { + builder.append(" ("); //$NON-NLS-1$ + + IDBField[] fields = index.getFields(); + for (int i = 0; i < fields.length; i++) + { + if (i != 0) + { + builder.append(", "); //$NON-NLS-1$ + } + + addIndexField(builder, fields[i]); + } + + builder.append(")"); //$NON-NLS-1$ + } + + /** + * @since 4.2 + */ + protected void dropIndex(Connection connection, IDBIndex index, IDBIndexDelta delta) + { + StringBuilder builder = new StringBuilder(); + if (index.getType() == IDBIndex.Type.PRIMARY_KEY) + { + dropPrimaryKey(index, builder); + } + else + { + dropIndex(index, builder); + } + + DBUtil.execute(connection, builder); + } + + /** + * @since 4.2 + */ + protected void dropPrimaryKey(IDBIndex index, StringBuilder builder) + { + builder.append("ALTER TABLE "); //$NON-NLS-1$ + builder.append(index.getTable()); + builder.append(" DROP CONSTRAINT "); //$NON-NLS-1$ + builder.append(index); + } + + /** + * @since 4.2 + */ + protected void dropIndex(IDBIndex index, StringBuilder builder) + { + } + + public Set createTables(Iterable tables, Connection connection) throws DBException + { + Set createdTables = new HashSet(); + + for (IDBTable table : tables) + { + Statement statement = null; + + try + { + statement = connection.createStatement(); + if (createTable(table, statement)) + { + createdTables.add(table); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(statement); + } + } + + return createdTables; + } + + public boolean createTable(IDBTable table, Statement statement) throws DBException + { + boolean created = true; + + try + { + doCreateTable(table, statement); + } + catch (SQLException ex) + { + created = false; + if (TRACER.isEnabled()) + { + TRACER.trace("-- " + ex.getMessage()); //$NON-NLS-1$ + } + } + + validateTable(table, statement); + return created; + } + + public Collection dropTables(Iterable tables, Connection connection) throws DBException + { + List droppedTables = new ArrayList(); + + for (IDBTable table : tables) + { + Statement statement = null; + + try + { + statement = connection.createStatement(); + if (dropTable(table, statement)) + { + droppedTables.add(table); + } + } + catch (SQLException ex) + { + OM.LOG.error(ex); + } + finally + { + DBUtil.close(statement); + } + } + + return droppedTables; + } + + public boolean dropTable(IDBTable table, Statement statement) + { + try + { + String sql = getDropTableSQL(table); + if (TRACER.isEnabled()) + { + TRACER.trace(sql); + } + + statement.execute(sql); + return true; + } + catch (SQLException ex) + { + if (TRACER.isEnabled()) + { + TRACER.trace(ex.getMessage()); + } + + return false; + } + } + + protected String getDropTableSQL(IDBTable table) + { + return "DROP TABLE " + table; //$NON-NLS-1$ + } + + /** + * @since 2.0 + */ + public int getMaxTableNameLength() + { + // Ansi SQL 92 default value + return 128; + } + + /** + * @since 2.0 + */ + public int getMaxFieldNameLength() + { + // Ansi SQL 92 default value + return 128; + } + + /** + * @since 4.2 + */ + public int getFieldLength(DBType type) + { + return getDefaultDBLength(type); + } + + public boolean isTypeIndexable(DBType type) + { + switch (type) + { + case CLOB: + case BLOB: + case LONGVARCHAR: + case LONGVARBINARY: + case VARBINARY: + case BINARY: + return false; + + default: + return true; + } + } + + @Override + public String toString() + { + return getName() + "-" + getVersion(); //$NON-NLS-1$ + } + + /** + * @since 4.3 + */ + public String convertString(PreparedStatement preparedStatement, int parameterIndex, String value) + { + return value; + } + + /** + * @since 4.3 + */ + public String convertString(ResultSet resultSet, int columnIndex, String value) + { + return value; + } + + /** + * @since 4.3 + */ + public String convertString(ResultSet resultSet, String columnLabel, String value) + { + return value; + } + + /** + * @since 2.0 + */ + protected void doCreateTable(IDBTable table, Statement statement) throws SQLException + { + StringBuilder builder = new StringBuilder(); + builder.append("CREATE TABLE "); //$NON-NLS-1$ + builder.append(table); + builder.append(" ("); //$NON-NLS-1$ + appendFieldDefs(builder, table, createFieldDefinitions(table)); + String constraints = createConstraints(table); + if (constraints != null) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(constraints); + } + + builder.append(")"); //$NON-NLS-1$ + String sql = builder.toString(); + if (TRACER.isEnabled()) + { + TRACER.trace(sql); + } + + statement.execute(sql); + + IDBIndex[] indices = table.getIndices(); + for (int i = 0; i < indices.length; i++) + { + InternalDBIndex index = (InternalDBIndex)indices[i]; + + try + { + createIndex(index, statement, i); + } + catch (SQLException ex) + { + if (!index.isOptional()) + { + throw ex; + } + } + catch (RuntimeException ex) + { + if (!index.isOptional()) + { + throw ex; + } + } + } + } + + /** + * @since 2.0 + */ + protected void createIndex(IDBIndex index, Statement statement, int num) throws SQLException + { + IDBTable table = index.getTable(); + StringBuilder builder = new StringBuilder(); + builder.append("CREATE "); //$NON-NLS-1$ + if (index.getType() == IDBIndex.Type.UNIQUE || index.getType() == IDBIndex.Type.PRIMARY_KEY) + { + builder.append("UNIQUE "); //$NON-NLS-1$ + } + + builder.append("INDEX "); //$NON-NLS-1$ + builder.append(index); + builder.append(" ON "); //$NON-NLS-1$ + builder.append(table); + createIndexFields(index, builder); + String sql = builder.toString(); + if (TRACER.isEnabled()) + { + TRACER.trace(sql); + } + + statement.execute(sql); + } + + protected void addIndexField(StringBuilder builder, IDBField field) + { + builder.append(field); + } + + /** + * @since 2.0 + */ + protected String createConstraints(IDBTable table) + { + return null; + } + + /** + * @since 2.0 + */ + protected String createFieldDefinition(IDBField field) + { + return getTypeName(field) + (field.isNotNull() ? " NOT NULL" : ""); //$NON-NLS-1$ //$NON-NLS-2$ + } + + // protected String getTypeName(DBType type) + // { + // new DBField(null,null) + // return getTypeName(field); + // } + + protected String getTypeName(IDBField field) + { + DBType type = field.getType(); + switch (type) + { + case BOOLEAN: + case BIT: + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + case FLOAT: + case REAL: + case DOUBLE: + case DATE: + case TIME: + case TIMESTAMP: + case LONGVARCHAR: + case LONGVARBINARY: + case BLOB: + case CLOB: + return type.toString(); + + case CHAR: + case VARCHAR: + case BINARY: + case VARBINARY: + return type.toString() + field.formatPrecision(); + + case NUMERIC: + case DECIMAL: + return type.toString() + field.formatPrecisionAndScale(); + } + + throw new IllegalArgumentException("Unknown type: " + type); //$NON-NLS-1$ + } + + public String[] getSQL92ReservedWords() + { + return SQL92_RESERVED_WORDS; + } + + public boolean isReservedWord(String word) + { + if (reservedWords == null) + { + reservedWords = new HashSet(); + for (String reservedWord : getReservedWords()) + { + reservedWords.add(reservedWord.toUpperCase()); + } + } + + word = word.toUpperCase(); + return reservedWords.contains(word); + } + + /** + * @since 2.0 + */ + protected void validateTable(IDBTable table, Statement statement) throws DBException + { + int maxRows = 1; + + try + { + maxRows = statement.getMaxRows(); + statement.setMaxRows(1); + + String sql = null; + + try + { + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + appendFieldNames(builder, table); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + sql = builder.toString(); + + if (TRACER.isEnabled()) + { + TRACER.format("{0}", sql); //$NON-NLS-1$ + } + + ResultSet resultSet = statement.executeQuery(sql); + + try + { + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + if (columnCount != table.getFieldCount()) + { + throw new DBException("DBTable " + table + " has " + columnCount + " columns instead of " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + table.getFieldCount()); + } + } + finally + { + DBUtil.close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException("Problem with table " + table, ex, sql); + } + finally + { + if (maxRows != 1) + { + statement.setMaxRows(maxRows); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + /** + * @since 4.2 + */ + protected String[] createFieldDefinitions(IDBTable table) + { + IDBField[] fields = table.getFields(); + int fieldCount = fields.length; + + String[] result = new String[fieldCount]; + for (int i = 0; i < fieldCount; i++) + { + IDBField field = fields[i]; + result[i] = createFieldDefinition(field); + } + + return result; + } + + public void appendFieldNames(Appendable appendable, IDBTable table) + { + try + { + IDBField[] fields = table.getFields(); + for (int i = 0; i < fields.length; i++) + { + IDBField field = fields[i]; + if (i != 0) + { + appendable.append(", "); //$NON-NLS-1$ + } + + String fieldName = field.getName(); + appendable.append(fieldName); + } + } + catch (IOException canNotHappen) + { + } + } + + /** + * @since 4.2 + */ + protected void appendFieldDefs(Appendable appendable, IDBTable table, String[] defs) + { + try + { + IDBField[] fields = table.getFields(); + for (int i = 0; i < fields.length; i++) + { + IDBField field = fields[i]; + if (i != 0) + { + appendable.append(", "); //$NON-NLS-1$ + } + + String fieldName = field.getName(); + appendable.append(fieldName); + appendable.append(" "); //$NON-NLS-1$ + appendable.append(defs[i]); + } + } + catch (IOException canNotHappen) + { + } + } + + /** + * @since 3.0 + */ + public DBType adaptType(DBType type) + { + return type; + } + + /** + * @since 4.0 + */ + public boolean isValidFirstChar(char ch) + { + return true; + } + + /** + * @since 4.0 + */ + public boolean isDuplicateKeyException(SQLException ex) + { + String sqlState = ex.getSQLState(); + return "23001".equals(sqlState); + } + + /** + * @since 4.2 + */ + public boolean isTableNotFoundException(SQLException ex) + { + String sqlState = ex.getSQLState(); + return "42S02".equals(sqlState); + } + + /** + * @since 4.2 + */ + public boolean isColumnNotFoundException(SQLException ex) + { + String sqlState = ex.getSQLState(); + return "42S22".equals(sqlState); + } + + /** + * @since 4.2 + */ + public String sqlRenameField(IDBField field, String oldName) + { + return "ALTER TABLE " + field.getTable() + " RENAME COLUMN " + oldName + " TO " + field; + } + + /** + * @since 4.2 + */ + public String sqlModifyField(IDBField field) + { + String tableName = field.getTable().getName(); + String fieldName = field.getName(); + + String definition = createFieldDefinition(field); + return sqlModifyField(tableName, fieldName, definition); + } + + /** + * @since 4.2 + */ + protected String sqlModifyField(String tableName, String fieldName, String definition) + { + return "ALTER TABLE " + tableName + " ALTER COLUMN " + fieldName + " " + definition; + } + + /** + * @since 4.2 + */ + public String format(PreparedStatement stmt) + { + return stmt.toString(); + } + + /** + * @since 4.2 + */ + public String format(ResultSet resultSet) + { + try + { + StringBuilder builder = new StringBuilder(); + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + for (int i = 0; i < columnCount; i++) + { + if (i != 0) + { + builder.append(", "); + } + + builder.append(metaData.getColumnName(i + 1).toLowerCase()); + builder.append("="); + builder.append(resultSet.getObject(i + 1)); + } + + return builder.toString(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + /** + * @since 4.2 + */ + public static int getDefaultDBLength(DBType type) + { + return type == DBType.VARCHAR ? 32672 : IDBField.DEFAULT; + } + + /** + * @since 4.2 + * @author Eike Stepper + */ + protected static final class FieldInfo implements Comparable + { + public String name; + + public int position; + + public int compareTo(FieldInfo o) + { + return position - o.position; + } + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBSchema.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBSchema.java new file mode 100644 index 000000000..66b40084f --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBSchema.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2008-2013 Eike Stepper (Loehne, 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.net4j.spi.db; + +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.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; + +import javax.sql.DataSource; + +import java.io.PrintStream; +import java.sql.Connection; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +/** + * @author Eike Stepper + * @deprecated As of 4.2 call {@link DBUtil#createSchema(String)}, {@link DBUtil#readSchema(IDBAdapter, Connection, IDBSchema)}, + * {@link DBUtil#readSchema(IDBAdapter, Connection, String)} or {@link DBUtil#copySchema(IDBSchema)}. + */ +@Deprecated +public class DBSchema extends org.eclipse.net4j.internal.db.ddl.DBSchema +{ + private static final long serialVersionUID = 1L; + + public DBSchema(String name) + { + super(name); + } + + /** + * @since 4.2 + */ + public DBSchema(IDBSchema source) + { + super(source); + } + + /** + * Constructor for deserialization. + * + * @since 4.2 + */ + protected DBSchema() + { + } + + @Override + public IDBSchema getSchema() + { + return super.getSchema(); + } + + @Override + public String getFullName() + { + return super.getFullName(); + } + + @Override + public IDBTable addTable(String name) throws DBException + { + return super.addTable(name); + } + + @Override + public IDBTable removeTable(String name) + { + return super.removeTable(name); + } + + @Override + public IDBTable getTable(String name) + { + return super.getTable(name); + } + + @Override + public IDBTable[] getTables() + { + return super.getTables(); + } + + @Override + public boolean isLocked() + { + return super.isLocked(); + } + + @Override + public boolean lock() + { + return super.lock(); + } + + @Override + public void assertUnlocked() throws DBException + { + super.assertUnlocked(); + } + + @Override + public Set create(IDBAdapter dbAdapter, Connection connection) throws DBException + { + return super.create(dbAdapter, connection); + } + + @Override + public Set create(IDBAdapter dbAdapter, DataSource dataSource) throws DBException + { + return super.create(dbAdapter, dataSource); + } + + @Override + public Set create(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException + { + return super.create(dbAdapter, connectionProvider); + } + + @Override + public void drop(IDBAdapter dbAdapter, Connection connection) throws DBException + { + super.drop(dbAdapter, connection); + } + + @Override + public void drop(IDBAdapter dbAdapter, DataSource dataSource) throws DBException + { + super.drop(dbAdapter, dataSource); + } + + @Override + public void drop(IDBAdapter dbAdapter, IDBConnectionProvider connectionProvider) throws DBException + { + super.drop(dbAdapter, connectionProvider); + } + + @Override + public void export(Connection connection, PrintStream out) throws DBException + { + super.export(connection, out); + } + + @Override + public void export(DataSource dataSource, PrintStream out) throws DBException + { + super.export(dataSource, out); + } + + @Override + public void export(IDBConnectionProvider connectionProvider, PrintStream out) throws DBException + { + super.export(connectionProvider, out); + } + + @Override + public String getName() + { + return super.getName(); + } + + @Override + public String toString() + { + return super.toString(); + } + + @Override + public void addListener(IListener listener) + { + super.addListener(listener); + } + + @Override + public void removeListener(IListener listener) + { + super.removeListener(listener); + } + + @Override + public boolean hasListeners() + { + return super.hasListeners(); + } + + @Override + public IListener[] getListeners() + { + return super.getListeners(); + } + + @Override + public void fireEvent() + { + super.fireEvent(); + } + + @Override + public void fireEvent(IEvent event) + { + super.fireEvent(event); + } + + @Override + public void fireEvent(IEvent event, IListener[] listeners) + { + super.fireEvent(event, listeners); + } + + @Override + protected void fireThrowable(Throwable throwable) + { + super.fireThrowable(throwable); + } + + @Override + protected ExecutorService getNotificationService() + { + return super.getNotificationService(); + } + + @Override + protected void firstListenerAdded() + { + super.firstListenerAdded(); + } + + @Override + protected void lastListenerRemoved() + { + super.lastListenerRemoved(); + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + } +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBElement.java new file mode 100644 index 000000000..a692f137e --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBElement.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBElement; + +/** + * @since 4.2 + * @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 InternalDBElement extends IDBElement +{ + +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBField.java new file mode 100644 index 000000000..0de9ead03 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBField.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBField; + +/** + * @since 4.2 + * @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 InternalDBField extends IDBField, InternalDBSchemaElement +{ + public IDBField getWrapper(); + + public void setPosition(int position); + + public Exception getConstructionStackTrace(); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBIndex.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBIndex.java new file mode 100644 index 000000000..530085956 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBIndex.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBIndexField; + +/** + * @since 4.2 + * @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 InternalDBIndex extends IDBIndex, InternalDBSchemaElement +{ + public static final IDBIndexField[] NO_INDEX_FIELDS = {}; + + public IDBIndex getWrapper(); + + public void removeIndexField(IDBIndexField indexFieldToRemove); + + /** + * @since 4.5 + */ + public boolean isOptional(); + + /** + * @since 4.5 + */ + public void setOptional(boolean optional); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBIndexField.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBIndexField.java new file mode 100644 index 000000000..67862ff97 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBIndexField.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBIndexField; + +/** + * @since 4.2 + * @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 InternalDBIndexField extends IDBIndexField, InternalDBSchemaElement +{ + public IDBIndexField getWrapper(); + + public void setPosition(int position); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBNamedElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBNamedElement.java new file mode 100644 index 000000000..7203fe8f4 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBNamedElement.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBNamedElement; + +import java.io.IOException; +import java.io.Writer; + +/** + * @since 4.2 + * @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 InternalDBNamedElement extends IDBNamedElement, InternalDBElement +{ + public void setName(String name); + + public String dumpToString(); + + public void dump(); + + public void dump(Writer writer) throws IOException; +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBSchema.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBSchema.java new file mode 100644 index 000000000..a3e2ec6a4 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBSchema.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.DBException; +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; + +/** + * @since 4.2 + * @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 InternalDBSchema extends IDBSchema, InternalDBSchemaElement +{ + public static final IDBTable[] NO_TABLES = {}; + + public IDBSchema getWrapper(); + + public IDBTable addTable(String name); + + public IDBTable removeTable(String name); + + public String createIndexName(IDBTable table, IDBIndex.Type type, IDBField[] fields, int position); + + public boolean lock(); + + public boolean unlock(); + + public void assertUnlocked() throws DBException; +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBSchemaElement.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBSchemaElement.java new file mode 100644 index 000000000..cbf818fae --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBSchemaElement.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBSchemaElement; + +/** + * @since 4.2 + * @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 InternalDBSchemaElement extends IDBSchemaElement, InternalDBNamedElement +{ + public IDBSchemaElement getWrapper(); + + public void setWrapper(IDBSchemaElement wrapper); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBTable.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBTable.java new file mode 100644 index 000000000..fab2bfdca --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/InternalDBTable.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Eike Stepper (Loehne, 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.net4j.spi.db.ddl; + +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBTable; + +/** + * @since 4.2 + * @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 InternalDBTable extends IDBTable, InternalDBSchemaElement +{ + public static final IDBField[] NO_FIELDS = {}; + + public static final IDBIndex[] NO_INDICES = {}; + + public IDBTable getWrapper(); + + public void removeField(IDBField fieldToRemove); + + public void removeIndex(IDBIndex indexToRemove); +} diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/package-info.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/package-info.java new file mode 100644 index 000000000..5789a9363 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/ddl/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2013, 2015 Eike Stepper (Loehne, 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 for the Net4j DB framework. + */ +package org.eclipse.net4j.spi.db.ddl; diff --git a/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/package-info.java b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/package-info.java new file mode 100644 index 000000000..672406424 --- /dev/null +++ b/bundles/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011-2013, 2015 Eike Stepper (Loehne, 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 for the Net4j DB framework. + */ +package org.eclipse.net4j.spi.db; diff --git a/bundles/settings.gradle b/bundles/settings.gradle index e61382cee..4b7e0e484 100644 --- a/bundles/settings.gradle +++ b/bundles/settings.gradle @@ -1,131 +1,133 @@ -/* - * Master Gradle initialization script - * - * Depends on bnd_* values from gradle.properties. - */ - -import aQute.bnd.build.Workspace - -/* Exclude the oracle bundle from the build as we cannot distribute the necessary oracle driver */ -startParameter.excludedTaskNames += ':specmate-dbprovider-oracle:build' -startParameter.excludedTaskNames += ':specmate-dbprovider-oracle:check' - -/* Add bnd as a script dependency */ -buildscript { - dependencies { - def bndURI = rootDir.toURI().resolve(bnd_jar) - if (bndURI.scheme != 'file') { - /* If not a local file, copy to a local file in cnf/cache */ - def cnfCache = mkdir("${rootDir}/${bnd_cnf}/cache") - def bndJarFile = new File(cnfCache, 'biz.aQute.bnd.gradle.jar') - if (!bndJarFile.exists()) { - println "Downloading ${bndURI} to ${bndJarFile} ..." - bndURI.toURL().withInputStream { is -> - bndJarFile.withOutputStream { os -> - def bos = new BufferedOutputStream( os ) - bos << is - } - } - } - bndURI = bndJarFile.toURI() - } - classpath files(bndURI) - - /* After the rootProject is created, pass URI to projects */ - gradle.rootProject { rootProject -> - rootProject.ext.bndURI = bndURI - } - } -} - -/* Initialize the bnd workspace */ -def workspace = Workspace.getWorkspace(rootDir, bnd_cnf) -if (workspace == null) { - throw new GradleException("Unable to load workspace ${rootDir}/${bnd_cnf}") -} - -/* Add cnf project to the graph */ -include bnd_cnf - -/* Start with the declared build project name */ -def defaultProjectName = bnd_build - -/* If in a subproject, use the subproject name */ -for (def currentDir = startParameter.currentDir; currentDir != rootDir; currentDir = currentDir.parentFile) { - defaultProjectName = currentDir.name -} - -/* Build a set of project names we need to include from the specified tasks */ -def projectNames = startParameter.taskNames.collect { taskName -> - def elements = taskName.split(':') - switch (elements.length) { - case 1: - return defaultProjectName - case 2: - return elements[0].empty ? bnd_build : elements[0] - default: - return elements[0].empty ? elements[1] : elements[0] - } -}.toSet() - -/* Include the default project name if in a subproject or no tasks specified */ -if ((startParameter.currentDir != rootDir) || projectNames.empty) { - projectNames += defaultProjectName -} - -/* If bnd_build used but declared empty, add all non-private folders of rootDir */ -if (projectNames.remove('')) { - rootDir.eachDir { - def projectName = it.name - if (!projectName.startsWith('.')) { - projectNames += projectName - } - } -} - -/* Add each project and its dependencies to the graph */ -projectNames.each { projectName -> - include projectName - def project = getBndProject(workspace, projectName) - project?.dependson.each { - include it.name - } -} - -/* Get the bnd project for the specified project name */ -def getBndProject(Workspace workspace, String projectName) { - def project = workspace.getProject(projectName) - if (project == null) { - return null - } - project.prepare() - if (project.isValid()) { - return project - } - - project.getInfo(workspace, "${rootDir} :") - def errorCount = 0 - project.warnings.each { - println "Warning: ${it}" - } - project.errors.each { - println "Error : ${it}" - errorCount++ - } - if (!project.isOk()) { - def str = 'even though no errors were reported' - if (errorCount == 1) { - str = 'one error was reported' - } else if (errorCount > 1) { - str = "${errorCount} errors were reported" - } - throw new GradleException("Project ${rootDir}/${projectName} is invalid, ${str}") - } - throw new GradleException("Project ${rootDir}/${projectName} is not a valid bnd project") -} - -/* After the rootProject is created, set up some properties. */ -gradle.rootProject { rootProject -> - rootProject.ext.bndWorkspace = workspace - rootProject.ext.cnf = rootProject.project(bnd_cnf) -} +/* + * Master Gradle initialization script + * + * Depends on bnd_* values from gradle.properties. + */ + +import aQute.bnd.build.Workspace + +/* Exclude the oracle bundle from the build as we cannot distribute the necessary oracle driver */ +startParameter.excludedTaskNames += ':specmate-dbprovider-oracle:build' +startParameter.excludedTaskNames += ':specmate-dbprovider-oracle:check' +startParameter.excludedTaskNames += ':org.eclipse.net4j.db.oracle:build' +startParameter.excludedTaskNames += ':org.eclipse.net4j.db.oracle:check' + +/* Add bnd as a script dependency */ +buildscript { + dependencies { + def bndURI = rootDir.toURI().resolve(bnd_jar) + if (bndURI.scheme != 'file') { + /* If not a local file, copy to a local file in cnf/cache */ + def cnfCache = mkdir("${rootDir}/${bnd_cnf}/cache") + def bndJarFile = new File(cnfCache, 'biz.aQute.bnd.gradle.jar') + if (!bndJarFile.exists()) { + println "Downloading ${bndURI} to ${bndJarFile} ..." + bndURI.toURL().withInputStream { is -> + bndJarFile.withOutputStream { os -> + def bos = new BufferedOutputStream( os ) + bos << is + } + } + } + bndURI = bndJarFile.toURI() + } + classpath files(bndURI) + + /* After the rootProject is created, pass URI to projects */ + gradle.rootProject { rootProject -> + rootProject.ext.bndURI = bndURI + } + } +} + +/* Initialize the bnd workspace */ +def workspace = Workspace.getWorkspace(rootDir, bnd_cnf) +if (workspace == null) { + throw new GradleException("Unable to load workspace ${rootDir}/${bnd_cnf}") +} + +/* Add cnf project to the graph */ +include bnd_cnf + +/* Start with the declared build project name */ +def defaultProjectName = bnd_build + +/* If in a subproject, use the subproject name */ +for (def currentDir = startParameter.currentDir; currentDir != rootDir; currentDir = currentDir.parentFile) { + defaultProjectName = currentDir.name +} + +/* Build a set of project names we need to include from the specified tasks */ +def projectNames = startParameter.taskNames.collect { taskName -> + def elements = taskName.split(':') + switch (elements.length) { + case 1: + return defaultProjectName + case 2: + return elements[0].empty ? bnd_build : elements[0] + default: + return elements[0].empty ? elements[1] : elements[0] + } +}.toSet() + +/* Include the default project name if in a subproject or no tasks specified */ +if ((startParameter.currentDir != rootDir) || projectNames.empty) { + projectNames += defaultProjectName +} + +/* If bnd_build used but declared empty, add all non-private folders of rootDir */ +if (projectNames.remove('')) { + rootDir.eachDir { + def projectName = it.name + if (!projectName.startsWith('.')) { + projectNames += projectName + } + } +} + +/* Add each project and its dependencies to the graph */ +projectNames.each { projectName -> + include projectName + def project = getBndProject(workspace, projectName) + project?.dependson.each { + include it.name + } +} + +/* Get the bnd project for the specified project name */ +def getBndProject(Workspace workspace, String projectName) { + def project = workspace.getProject(projectName) + if (project == null) { + return null + } + project.prepare() + if (project.isValid()) { + return project + } + + project.getInfo(workspace, "${rootDir} :") + def errorCount = 0 + project.warnings.each { + println "Warning: ${it}" + } + project.errors.each { + println "Error : ${it}" + errorCount++ + } + if (!project.isOk()) { + def str = 'even though no errors were reported' + if (errorCount == 1) { + str = 'one error was reported' + } else if (errorCount > 1) { + str = "${errorCount} errors were reported" + } + throw new GradleException("Project ${rootDir}/${projectName} is invalid, ${str}") + } + throw new GradleException("Project ${rootDir}/${projectName} is not a valid bnd project") +} + +/* After the rootProject is created, set up some properties. */ +gradle.rootProject { rootProject -> + rootProject.ext.bndWorkspace = workspace + rootProject.ext.cnf = rootProject.project(bnd_cnf) +} diff --git a/bundles/specmate-auth-test/bnd.bnd b/bundles/specmate-auth-test/bnd.bnd index 6d08fce60..13a70f3df 100644 --- a/bundles/specmate-auth-test/bnd.bnd +++ b/bundles/specmate-auth-test/bnd.bnd @@ -55,9 +55,11 @@ Test-Cases: \ jul.to.slf4j;version='[1.7.12,1.7.13)',\ log4j.over.slf4j;version='[1.7.12,1.7.13)',\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ + org.apache.commons.lang3;version='[3.3.2,3.3.3)',\ org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ + org.apache.felix.scr;version='[2.0.8,2.0.9)',\ org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ @@ -96,9 +98,14 @@ Test-Cases: \ org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ org.json;version=snapshot,\ org.slf4j.api;version='[1.7.2,1.7.3)',\ + slf4j.api;version='[1.7.12,1.7.13)',\ + specmate-auth;version=snapshot,\ + specmate-auth-api;version=snapshot,\ specmate-common;version=snapshot,\ specmate-config;version=snapshot,\ specmate-config-api;version=snapshot,\ + specmate-connectors;version=snapshot,\ + specmate-emfrest-api;version=snapshot,\ specmate-logging;version=snapshot,\ specmate-logging-slf4j;version=snapshot,\ specmate-logging-slf4j-julbridge;version=snapshot,\ @@ -112,6 +119,15 @@ Test-Cases: \ org.apache.felix.scr;version='[2.0.8,2.0.9)',\ specmate-rest;version=snapshot,\ specmate-scheduler;version=snapshot,\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ + specmate-scheduler;version=snapshot,\ org.apache.servicemix.bundles.jakarta-regexp;version='[1.4.0,1.4.1)',\ org.apache.servicemix.bundles.lucene;version='[7.2.0,7.2.1)',\ org.apache.servicemix.bundles.lucene-queries;version='[7.2.0,7.2.1)',\ diff --git a/bundles/specmate-cdo-server/bnd.bnd b/bundles/specmate-cdo-server/bnd.bnd index a68bee384..acd398f58 100644 --- a/bundles/specmate-cdo-server/bnd.bnd +++ b/bundles/specmate-cdo-server/bnd.bnd @@ -13,6 +13,10 @@ specmate-migration-api;version=latest,\ specmate-config-api;version=latest,\ org.eclipse.equinox.common,\ + org.eclipse.emf.cdo.net4j,\ + specmate-persistency-api;version=latest,\ + org.eclipse.emf.ecore,\ + org.eclipse.emf.common,\ specmate-model-gen;version=latest Private-Package: com.specmate.cdoserver.internal Export-Package: \ diff --git a/bundles/specmate-cdo-server/src/com/specmate/cdoserver/config/SpecmateCDOServerConfig.java b/bundles/specmate-cdo-server/src/com/specmate/cdoserver/config/SpecmateCDOServerConfig.java index 3ed4f0b34..e164fc538 100644 --- a/bundles/specmate-cdo-server/src/com/specmate/cdoserver/config/SpecmateCDOServerConfig.java +++ b/bundles/specmate-cdo-server/src/com/specmate/cdoserver/config/SpecmateCDOServerConfig.java @@ -23,6 +23,11 @@ public class SpecmateCDOServerConfig { public static final String KEY_REPOSITORY_NAME = "cdo.repositoryName"; public static final String KEY_CDO_USER = "cdo.user"; public static final String KEY_CDO_PASSWORD = "cdo.password"; + public static final String KEY_CDO_MASTER = "cdo.master"; + public static final String KEY_CDO_MASTER_REPOSITORY = "cdo.masterRepositoryName"; + + public static final String KEY_CDO_MASTER_USER = "cdo.masterUser"; + public static final String KEY_CDO_MASTER_PASSWORD = "cdo.masterPassword"; private ConfigurationAdmin configurationAdmin; @@ -38,12 +43,25 @@ public class SpecmateCDOServerConfig { private String cdoPassword; + private String cdoMaster; + + private String cdoMasterRepository; + + private String cdoMasterUser; + + private String cdoMasterPassword; + + @Activate private void activate() throws SpecmateException { this.serverPort = configService.getConfigurationProperty(KEY_SERVER_HOST_PORT); this.repositoryName = configService.getConfigurationProperty(KEY_REPOSITORY_NAME); this.cdoUser = configService.getConfigurationProperty(KEY_CDO_USER); this.cdoPassword = configService.getConfigurationProperty(KEY_CDO_PASSWORD); + this.cdoMaster = configService.getConfigurationProperty(KEY_CDO_MASTER); + this.cdoMasterRepository=configService.getConfigurationProperty(KEY_CDO_MASTER_REPOSITORY); + this.cdoMasterUser = configService.getConfigurationProperty(KEY_CDO_MASTER_USER); + this.cdoMasterPassword = configService.getConfigurationProperty(KEY_CDO_MASTER_PASSWORD); Dictionary properties = new Hashtable<>(); if (!StringUtil.isEmpty(serverPort) && !StringUtil.isEmpty(repositoryName) && !StringUtil.isEmpty(cdoUser) @@ -52,6 +70,13 @@ private void activate() throws SpecmateException { properties.put(KEY_REPOSITORY_NAME, repositoryName); properties.put(KEY_CDO_USER, cdoUser); properties.put(KEY_CDO_PASSWORD, cdoPassword); + if(this.cdoMaster!=null && this.cdoMasterRepository!=null && this.cdoMasterUser != null && this.cdoMasterPassword!=null){ + properties.put(KEY_CDO_MASTER, this.cdoMaster); + properties.put(KEY_CDO_MASTER_REPOSITORY, this.cdoMasterRepository); + properties.put(KEY_CDO_MASTER_REPOSITORY, this.cdoMasterRepository); + properties.put(KEY_CDO_MASTER_USER, this.cdoMasterUser); + properties.put(KEY_CDO_MASTER_PASSWORD, this.cdoMasterPassword); + } logService.log(LogService.LOG_DEBUG, "Configuring CDO with:\n" + OSGiUtil.configDictionaryToString(properties)); OSGiUtil.configureService(configurationAdmin, PID, properties); diff --git a/bundles/specmate-cdo-server/src/com/specmate/cdoserver/internal/SpecmateCDOServer.java b/bundles/specmate-cdo-server/src/com/specmate/cdoserver/internal/SpecmateCDOServer.java index d211d0f99..aa97c7f6d 100644 --- a/bundles/specmate-cdo-server/src/com/specmate/cdoserver/internal/SpecmateCDOServer.java +++ b/bundles/specmate-cdo-server/src/com/specmate/cdoserver/internal/SpecmateCDOServer.java @@ -1,20 +1,46 @@ package com.specmate.cdoserver.internal; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.eclipse.emf.cdo.common.CDOCommonRepository.State; +import org.eclipse.emf.cdo.common.revision.CDORevisionCache; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.util.RepositoryStateChangedEvent; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.etypes.EtypesPackage; +import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; +import org.eclipse.emf.cdo.net4j.CDONet4jUtil; +import org.eclipse.emf.cdo.net4j.ReconnectingCDOSessionConfiguration; import org.eclipse.emf.cdo.server.CDOServerUtil; import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IRepositorySynchronizer; +import org.eclipse.emf.cdo.server.IStore; import org.eclipse.emf.cdo.server.net4j.CDONet4jServerUtil; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.session.CDOSessionConfiguration.SessionOpenedEvent; +import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.spi.server.InternalSessionManager; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.net4j.Net4jUtil; import org.eclipse.net4j.acceptor.IAcceptor; +import org.eclipse.net4j.connector.IConnector; import org.eclipse.net4j.tcp.TCPUtil; import org.eclipse.net4j.util.StringUtil; 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.lifecycle.ILifecycle; +import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.om.trace.PrintTraceHandler; import org.eclipse.net4j.util.security.IAuthenticator; +import org.eclipse.net4j.util.security.PasswordCredentialsProvider; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; @@ -26,9 +52,11 @@ import com.specmate.cdoserver.config.SpecmateCDOServerConfig; import com.specmate.common.exception.SpecmateException; import com.specmate.common.exception.SpecmateInternalException; +import com.specmate.common.exception.SpecmateValidationException; import com.specmate.dbprovider.api.DBConfigChangedCallback; import com.specmate.dbprovider.api.IDBProvider; import com.specmate.migration.api.IMigratorService; +import com.specmate.persistency.IPackageProvider; import com.specmate.model.administration.ErrorCode; @Component(immediate = true, configurationPid = SpecmateCDOServerConfig.PID, configurationPolicy = ConfigurationPolicy.REQUIRE) @@ -52,14 +80,29 @@ public class SpecmateCDOServer implements DBConfigChangedCallback, ICDOServer { /** Reference to the migration service */ private IMigratorService migrationService; + /** The name of the repository */ private String repositoryName; + /** User name for the cdo server */ private String cdoUser; + /** Password for the cdo server */ private String cdoPassword; + protected String masterHostAndPort; + private LogService logService; + private String masterRepositoryNme; + + private String masterUser; + + private String masterPassword; + + private IPackageProvider packageProvider; + + private boolean isClone; + private boolean active = false; @Activate @@ -79,8 +122,9 @@ public void deactivate() { * @param properties * @throws SpecmateInternalException * if the configuration is invalid + * @throws SpecmateValidationException */ - private void readConfig(Map properties) throws SpecmateInternalException { + private void readConfig(Map properties) throws SpecmateInternalException, SpecmateValidationException { this.hostAndPort = (String) properties.get(SpecmateCDOServerConfig.KEY_SERVER_HOST_PORT); if (StringUtil.isEmpty(this.hostAndPort)) { throw new SpecmateInternalException(ErrorCode.CONFIGURATION, "No server host and port given."); @@ -100,6 +144,28 @@ private void readConfig(Map properties) throws SpecmateInternalE if (StringUtil.isEmpty(this.cdoPassword)) { throw new SpecmateInternalException(ErrorCode.CONFIGURATION, "No CDO password given"); } + + this.masterHostAndPort = (String) properties.get(SpecmateCDOServerConfig.KEY_CDO_MASTER); + if (!StringUtil.isEmpty(this.masterHostAndPort)) { + this.isClone = true; + this.masterRepositoryNme = (String) properties.get(SpecmateCDOServerConfig.KEY_CDO_MASTER_REPOSITORY); + if (StringUtil.isEmpty(this.masterRepositoryNme)) { + throw new SpecmateValidationException( + "Server should be configured as clone, but no master repository is given."); + } + + this.masterUser = (String) properties.get(SpecmateCDOServerConfig.KEY_CDO_MASTER_USER); + if (StringUtil.isEmpty(this.masterUser)) { + throw new SpecmateValidationException( + "Server should be configured as clone, but no master user is given."); + } + + this.masterPassword = (String) properties.get(SpecmateCDOServerConfig.KEY_CDO_MASTER_PASSWORD); + if (StringUtil.isEmpty(this.masterPassword)) { + throw new SpecmateValidationException( + "Server should be configured as clone, but no master password is given."); + } + } } /** @@ -132,12 +198,16 @@ public void shutdown() { private void createServer() throws SpecmateException { createContainer(); createRepository(); - createAcceptors(); + if (!isClone) { + createAcceptors(); + } } /** Creates and prepares the container */ private void createContainer() { this.container = IPluginContainer.INSTANCE; + OMPlatform.INSTANCE.setDebugging(true); + OMPlatform.INSTANCE.addTraceHandler(PrintTraceHandler.CONSOLE); Net4jUtil.prepareContainer(container); TCPUtil.prepareContainer(container); CDONet4jServerUtil.prepareContainer(container); @@ -146,12 +216,27 @@ private void createContainer() { /** Create a CDO repository */ private void createRepository() throws SpecmateException { Map props = new HashMap<>(); - props.put(IRepository.Props.OVERRIDE_UUID, "specmate"); + props.put(IRepository.Props.OVERRIDE_UUID, this.repositoryName); props.put(IRepository.Props.SUPPORTING_AUDITS, "true"); - props.put(IRepository.Props.SUPPORTING_BRANCHES, "false"); + props.put(IRepository.Props.SUPPORTING_BRANCHES, "true"); + props.put(IRepository.Props.ID_GENERATION_LOCATION, "CLIENT"); - this.repository = (InternalRepository) CDOServerUtil.createRepository(this.repositoryName, - dbProviderService.createStore(), props); + IStore store = dbProviderService.createStore(); + + if (!StringUtil.isEmpty(this.masterHostAndPort)) { + if (StringUtil.isEmpty(this.masterRepositoryNme)) { + throw new SpecmateInternalException(ErrorCode.CONFIGURATION, "Should be configured as clone bu not master repository name is given."); + } + + logService.log(LogService.LOG_INFO, "Configuring as clone of " + this.masterHostAndPort); + IRepositorySynchronizer synchronizer = createRepositorySynchronizer(this.masterHostAndPort, + this.masterRepositoryNme); + this.repository = (InternalRepository) CDOServerUtil.createOfflineClone(this.repositoryName, store, props, + synchronizer); + } else { + logService.log(LogService.LOG_INFO, "Configuring as master"); + this.repository = (InternalRepository) CDOServerUtil.createRepository(this.repositoryName, store, props); + } InternalSessionManager sessionManager = (InternalSessionManager) CDOServerUtil.createSessionManager(); sessionManager.setAuthenticator(new IAuthenticator() { @@ -163,9 +248,95 @@ public void authenticate(String userID, char[] password) throws SecurityExceptio } }); repository.setSessionManager(sessionManager); + if (isClone) { + List packages = new ArrayList<>(); + packages.addAll(packageProvider.getPackages()); + packages.add(EcorePackage.eINSTANCE); + packages.add(EresourcePackage.eINSTANCE); + packages.add(EtypesPackage.eINSTANCE); + repository.setInitialPackages(packages.toArray(new EPackage[0])); + repository.addListener(new IListener() { + @Override + public void notifyEvent(IEvent event) { + if (event instanceof RepositoryStateChangedEvent) { + RepositoryStateChangedEvent rsce = (RepositoryStateChangedEvent) event; + State newState = rsce.getNewState(); + if (newState == State.OFFLINE || newState == State.ONLINE) { + createAcceptors(); + } + } + + } + }); + } CDOServerUtil.addRepository(IPluginContainer.INSTANCE, repository); } + /** + * Creates a repository synchronizer which connects to the master repository to + * synchronize between master and client.. + */ + protected IRepositorySynchronizer createRepositorySynchronizer(String connectorDescription, String repositoryName) { + CDOSessionConfigurationFactory factory = createSessionConfigurationFactory(connectorDescription, + repositoryName); + + IRepositorySynchronizer synchronizer = CDOServerUtil.createRepositorySynchronizer(factory); + synchronizer.setRetryInterval(2); + synchronizer.setMaxRecommits(10); + synchronizer.setRecommitInterval(2); + + return synchronizer; + } + + /** + * creates a CDOSessionConfigurationFactory for the offline clone. It + * instantiates a connection to the master repository. + */ + protected CDOSessionConfigurationFactory createSessionConfigurationFactory(final String connectorDescription, + final String repositoryName) { + return new CDOSessionConfigurationFactory() { + @Override + public CDONet4jSessionConfiguration createSessionConfiguration() { +// IConnector connector = createConnector(SpecmateCDOServer.this.masterHostAndPort); +// connector.setOpenChannelTimeout(6000000); + return SpecmateCDOServer.this.createSessionConfiguration(repositoryName); + } + }; + } + + protected CDONet4jSessionConfiguration createSessionConfiguration(String repositoryName) { + ReconnectingCDOSessionConfiguration configuration = CDONet4jUtil.createReconnectingSessionConfiguration(this.masterHostAndPort, repositoryName, container); +// configuration.setConnector(connector); +// configuration.setRepositoryName(repositoryName); + configuration.setHeartBeatEnabled(false); + configuration.setHeartBeatPeriod(5000); + configuration.setHeartBeatTimeout(10000); + configuration.setConnectorTimeout(10000); + configuration.setReconnectInterval(2000); + configuration.setMaxReconnectAttempts(10); + configuration.setRevisionManager(CDORevisionUtil.createRevisionManager(CDORevisionCache.NOOP)); + configuration.setCredentialsProvider(new PasswordCredentialsProvider(this.masterUser, this.masterPassword)); + configuration.addListener(new IListener() { + @Override + public void notifyEvent(IEvent event) { + if (event instanceof SessionOpenedEvent) { + SessionOpenedEvent e = (SessionOpenedEvent) event; + CDOSession session = e.getOpenedSession(); + logService.log(LogService.LOG_INFO, "Opened master session " + session); + + session.addListener(new LifecycleEventAdapter() { + @Override + protected void onAboutToDeactivate(ILifecycle lifecycle) { + logService.log(LogService.LOG_INFO, "Closing master session " + lifecycle); + } + }); + } + } + }); + + return configuration; + } + /** Creates the TCP acceptor */ private void createAcceptors() { logService.log(LogService.LOG_INFO, "Starting server on " + this.hostAndPort); @@ -203,4 +374,9 @@ public void setMigrationService(IMigratorService migrationService) { public void setLogService(LogService logService) { this.logService = logService; } + + @Reference + public void setModelProvider(IPackageProvider provider) { + this.packageProvider = provider; + } } diff --git a/bundles/specmate-common/bnd.bnd b/bundles/specmate-common/bnd.bnd index 1c1739203..73ee49ecf 100644 --- a/bundles/specmate-common/bnd.bnd +++ b/bundles/specmate-common/bnd.bnd @@ -8,9 +8,7 @@ Export-Package: \ org.eclipse.osgi.services,\ org.eclipse.emf.common,\ org.eclipse.emf.ecore,\ - org.eclipse.emf.edit,\ org.eclipse.emf.cdo,\ - org.eclipse.emf.cdo.ui,\ specmate-model-gen;version=latest,\ org.eclipse.emf.cdo.common,\ org.eclipse.net4j.util,\ diff --git a/bundles/specmate-common/src/com/specmate/common/OSGiUtil.java b/bundles/specmate-common/src/com/specmate/common/OSGiUtil.java index 7cb4a7ec7..68d3df0e0 100644 --- a/bundles/specmate-common/src/com/specmate/common/OSGiUtil.java +++ b/bundles/specmate-common/src/com/specmate/common/OSGiUtil.java @@ -5,6 +5,7 @@ import java.util.Enumeration; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.osgi.service.cm.Configuration; @@ -55,4 +56,10 @@ public static void configureFactory(ConfigurationAdmin configurationAdmin, Strin } } + + public static void saveAddToProperties(Dictionary properties, String key, Object value){ + if(key!=null && value!=null){ + properties.put(key,value); + } + } } diff --git a/bundles/specmate-config/config/specmate-config-clone.properties b/bundles/specmate-config/config/specmate-config-clone.properties new file mode 100644 index 000000000..2baee0e2f --- /dev/null +++ b/bundles/specmate-config/config/specmate-config-clone.properties @@ -0,0 +1,68 @@ +# Logging +# Choose from debug, info, warning, error +logging.level = info + +# CDO Persistency Settings +## CDO Common +### Repository name, in case of Oracle, must be identical to the schema name +cdo.repositoryName = specmatedev2 +cdo.user = cdoUser +cdo.password = cdoPass + +## CDO Server +### TCP host and port where the CDO server should listen +cdo.serverHostAndPort = localhost:2037 + +### The master CDO with which to synchronize +cdo.master=localhost:2036 +### The name of the repository in the master server +cdo.masterRepositoryName=specmatedev1 +### User name for connecting to the master server +cdo.masterUser = cdoUser +### Password for connecting to the master server +cdo.masterPassword = cdoPass + +## CDO Client +### Name of the CDO resource to use +cdo.resourceName = specmate_resource +### Folder for saving recovery data +cdo.recoveryFolder = ./recovery + +## H2 +### JDBC connection string for the H2 database +h2.jdbcConnection = jdbc:h2:./database/specmate_clone + + +## Oracle +### JDBC connection string for the oracle database +#oracle.jdbcConnection = jdbc:oracle:thin:@localhost:1521/XE +#oracle.username = specmatedev2 +#oracle.password = specmate + +# Connectors General Settings +## Time in seconds between polling the connectors, set to -1 to disable polling, default: 20 +connectorPollTime = -1 + +# Sarch Service +search.allowedFields = extId, type, name, description +search.lucene.location = ./database/lucene_clone +search.maxResults = 100 + +# Projects +## List of project names, each project listed here needs to be configured below +project.projects = artificial + +## Config for project ppmtest + +### Config Connector +project.artificial.connector.pid = com.specmate.ArtificialRequirementsConnector +project.artificial.connector.artificalRequirements.count = 10000 +project.artificial.connector.artificalRequirements.simulatedDelay = 1000 +project.artificial.connector.connectorID = artificial + +# User session +## Number of minutes a session is valid after the last http request +session.maxIdleMinutes = 720 +## Persist sessions in database or keep in memory +session.persistent = true + diff --git a/bundles/specmate-config/config/specmate-config-master.properties b/bundles/specmate-config/config/specmate-config-master.properties new file mode 100644 index 000000000..9600ca6bb --- /dev/null +++ b/bundles/specmate-config/config/specmate-config-master.properties @@ -0,0 +1,69 @@ +# Logging +# Choose from debug, info, warning, error +logging.level = info + +# CDO Persistency Settings +## CDO Common +### Repository name, in case of Oracle, must be identical to the schema name +cdo.repositoryName = specmatedev1 +cdo.user = cdoUser +cdo.password = cdoPass + +## CDO Server +### TCP host and port where the CDO server should listen +cdo.serverHostAndPort = localhost:2036 + +## CDO Client +### Name of the CDO resource to use +cdo.resourceName = specmate_resource + +## H2 +### JDBC connection string for the H2 database +h2.jdbcConnection = jdbc:h2:./database/specmate_master + + +## Oracle +### JDBC connection string for the oracle database +#oracle.jdbcConnection = jdbc:oracle:thin:@localhost:1521/XE + +#oracle.username = specmatedev1 +#oracle.password = specmate + +# Connectors General Settings +## cron string to schedule, when connectors are triggered. +## Set to "disabled" (without quotes) to disable polling. +## default: disabled +## generic value (will trigger every hour): hour +## example: day 13 14 5 will trigger every day at 13:14:05 +## example: hour 14 5 will trigger every hour at xx:14:05 +## example: minute 5 will trigger every minute at xx:xx:05 +## missing numbers are replaced by 0s. +## example: day 13 will trigger every day at 13:00:00 +# connectorPollSchedule = disabled +connectorPollSchedule = day + +# Sarch Service +search.allowedFields = extId, type, name, description +search.lucene.location = ./database/lucene_master +search.maxResults = 100 + +# Projects +## List of project names, each project listed here needs to be configured below +project.projects = artificial + +## Config for project ppmtest + +### Config Connector +project.artificial.connector.pid = com.specmate.ArtificialRequirementsConnector +project.artificial.connector.artificalRequirements.count = 20000 +project.artificial.connector.artificalRequirements.simulatedDelay = 10 +project.artificial.connector.connectorID = artificial + + + +# User session +## Number of minutes a session is valid after the last http request +session.maxIdleMinutes = 720 +## Persist sessions in database or keep in memory +session.persistent = true + diff --git a/bundles/specmate-config/config/specmate-config.properties b/bundles/specmate-config/config/specmate-config.properties index 6b551a0f7..665fe08de 100644 --- a/bundles/specmate-config/config/specmate-config.properties +++ b/bundles/specmate-config/config/specmate-config.properties @@ -1,3 +1,82 @@ +<<<<<<< HEAD +# Logging +# Choose from debug, info, warning, error +logging.level = info + +# CDO Persistency Settings +## CDO Common +### Repository name, in case of Oracle, must be identical to the schema name +cdo.repositoryName = specmate_repository +cdo.user = cdoUser +cdo.password = cdoPass + +## CDO Server +### TCP host and port where the CDO server should listen +cdo.serverHostAndPort = localhost:2036 + +## CDO Client +### Name of the CDO resource to use +cdo.resourceName = specmate_resource +### CDO host to connect to. If client and server are started in same process, should match cdo.serverHostAndPort +cdo.host = localhost:2036 + +## H2 +### JDBC connection string for the H2 database +h2.jdbcConnection = jdbc:h2:./database/specmate + + +## Oracle +### JDBC connection string for the oracle database +#oracle.jdbcConnection = + +#oracle.username = +#oracle.password = + +# Connectors General Settings +## cron string to schedule, when connectors are triggered. +## Set to "disabled" (without quotes) to disable polling. +## default: disabled +## generic value (will trigger every hour): hour +## example: day 13 14 5 will trigger every day at 13:14:05 +## example: hour 14 5 will trigger every hour at xx:14:05 +## example: minute 5 will trigger every minute at xx:xx:05 +## missing numbers are replaced by 0s. +## example: day 13 will trigger every day at 13:00:00 +# connectorPollSchedule = disabled +connectorPollSchedule = day + +# Sarch Service +search.allowedFields = extId, type, name, description +search.lucene.location = ./database/lucene +search.maxResults = 100 + +# Projects +## List of project names, each project listed here needs to be configured below +# test-data needn't be configured +project.projects = + +## Config for project test-data +project.test-data.library = libfolder1, libfolder2, libfolder3 + +project.test-data.library.libfolder1.name = Lib Folder 1 +project.test-data.library.libfolder1.description = Templates for type 1 requirements + +project.test-data.library.libfolder2.name = Lib Folder 2 +project.test-data.library.libfolder2.description = Templates for type 2 requirements + +project.test-data.library.libfolder3.name = Lib Folder 3 +project.test-data.library.libfolder3.description = Templates for type 3 requirements + + + + +# User session +## Number of minutes a session is valid after the last http request +session.maxIdleMinutes = 720 +## Persist sessions in database or keep in memory +session.persistent = true + +======= # Logging # Choose from debug, info, warning, error logging.level = info @@ -82,3 +161,4 @@ session.persistent = true +>>>>>>> refs/remotes/origin/develop diff --git a/bundles/specmate-config/src/com/specmate/config/internal/ConfigService.java b/bundles/specmate-config/src/com/specmate/config/internal/ConfigService.java index 678781009..dd2e501bb 100644 --- a/bundles/specmate-config/src/com/specmate/config/internal/ConfigService.java +++ b/bundles/specmate-config/src/com/specmate/config/internal/ConfigService.java @@ -77,6 +77,7 @@ private void readConfigurationFile() { if (!StringUtils.isEmpty(configurationFileLocation)) { File file = new File(configurationFileLocation); configInputStream = new FileInputStream(file); + logService.log(LogService.LOG_INFO, "Using configuration file " + file.getAbsolutePath()); } else { URL configUrl = bundleContext.getBundle().getResource("config/specmate-config.properties"); configInputStream = configUrl.openStream(); diff --git a/bundles/specmate-connectors/src/com/specmate/connectors/internal/ArtificialRequirementsConnector.java b/bundles/specmate-connectors/src/com/specmate/connectors/internal/ArtificialRequirementsConnector.java new file mode 100644 index 000000000..0c2eb3061 --- /dev/null +++ b/bundles/specmate-connectors/src/com/specmate/connectors/internal/ArtificialRequirementsConnector.java @@ -0,0 +1,126 @@ +package com.specmate.connectors.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.log.LogService; + +import com.specmate.common.exception.SpecmateException; +import com.specmate.common.exception.SpecmateValidationException; +import com.specmate.connectors.api.IRequirementsSource; +import com.specmate.connectors.config.ProjectConfigService; +import com.specmate.connectors.internal.config.ArtificialRequirementsConnectorConfig; +import com.specmate.model.base.BaseFactory; +import com.specmate.model.base.Folder; +import com.specmate.model.base.IContainer; +import com.specmate.model.requirements.Requirement; +import com.specmate.model.requirements.RequirementsFactory; + +@Component(immediate=true, configurationPid = ArtificialRequirementsConnectorConfig.PID, configurationPolicy = ConfigurationPolicy.REQUIRE) +public class ArtificialRequirementsConnector implements IRequirementsSource { + + private Collection requirements; + private String id; + private Folder folder; + private int numberOfRequirements; + private int simulateDelay; + private LogService logService; + + @Activate + public void activate(Map properties) throws SpecmateValidationException { + readConfig(properties); + } + + private void readConfig(Map properties) throws SpecmateValidationException { + this.id=(String)properties.get(ProjectConfigService.KEY_CONNECTOR_ID); + String numberOfRequirementsString = (String)properties.get(ArtificialRequirementsConnectorConfig.KEY_NUMBER_OF_REQUIREMENTS); + String simulatedDelayString = (String)properties.get(ArtificialRequirementsConnectorConfig.KEY_DELAY); + if(StringUtils.isEmpty(id)) { + throw new SpecmateValidationException("No id given for artifical requirements connector"); + } + try { + this.numberOfRequirements= Integer.parseInt(numberOfRequirementsString); + } catch (Exception e) { + throw new SpecmateValidationException("Invalid number of requirements " + numberOfRequirementsString); + } + if(!StringUtils.isEmpty(simulatedDelayString)) { + try { + this.simulateDelay= Integer.parseInt(simulatedDelayString); + } catch (Exception e) { + throw new SpecmateValidationException("Invalid simulated delay " + simulatedDelayString); + } + } else { + this.simulateDelay=0; + } + } + + @Override + public Collection getRequirements() throws SpecmateException { + if(this.requirements==null) { + this.requirements = generateRequirements(); + } + return this.requirements; + } + + private Collection generateRequirements() { + logService.log(LogService.LOG_INFO, "Generating " + numberOfRequirements + " artifical rquirements."); + ArrayList requirements = new ArrayList<>(); + for(int i=1;i0) { + try { + Thread.sleep(simulateDelay); + } catch (InterruptedException e) { + } + } + return requirements; + } + + + /** The id for this connector. */ + @Override + public String getId() { + return this.id; + } + + @Override + public IContainer getContainerForRequirement(Requirement requirement) throws SpecmateException { + if(this.folder==null) { + this.folder = BaseFactory.eINSTANCE.createFolder(); + this.folder.setId("default"); + this.folder.setName("default"); + } + if(this.simulateDelay>0) { + try { + Thread.sleep(simulateDelay); + } catch (InterruptedException e) { + } + } + return this.folder; + } + + @Override + public boolean authenticate(String username, String password) throws SpecmateException { + return true; + } + + /** Service reference */ + @Reference + public void setLogService(LogService logService) { + this.logService = logService; + } + + +} diff --git a/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorService.java b/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorService.java index 31cb7409d..085633e8c 100644 --- a/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorService.java +++ b/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorService.java @@ -1,119 +1,126 @@ -package com.specmate.connectors.internal; - -import static com.specmate.connectors.internal.config.ConnectorServiceConfig.KEY_POLL_SCHEDULE; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.eclipse.emf.cdo.common.id.CDOWithID; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.log.LogService; - -import com.specmate.common.exception.SpecmateException; -import com.specmate.connectors.api.IRequirementsSource; -import com.specmate.connectors.internal.config.ConnectorServiceConfig; -import com.specmate.persistency.IPersistencyService; -import com.specmate.persistency.ITransaction; -import com.specmate.persistency.validation.TopLevelValidator; -import com.specmate.scheduler.Scheduler; -import com.specmate.scheduler.SchedulerIteratorFactory; -import com.specmate.scheduler.SchedulerTask; -import com.specmate.search.api.IModelSearchService; - -@Component(immediate = true, configurationPid = ConnectorServiceConfig.PID, configurationPolicy = ConfigurationPolicy.REQUIRE) -public class ConnectorService { - CDOWithID id; - List requirementsSources = new ArrayList<>(); - private LogService logService; - private IPersistencyService persistencyService; - private IModelSearchService modelSearchService; - private ITransaction transaction; - - @Activate - public void activate(Map properties) throws SpecmateException { - validateConfig(properties); - - String schedule = (String) properties.get(KEY_POLL_SCHEDULE); - if (schedule == null) { - return; - } - - this.transaction = this.persistencyService.openTransaction(); - this.transaction.removeValidator(TopLevelValidator.class.getName()); - - new Thread(new Runnable() { - @Override - public void run() { - - // Ensure that requirements source are loaded. - while (requirementsSources.size() == 0) { - try { - logService.log(LogService.LOG_INFO, "No requirement sources here yet. Waiting."); - // Requirements Sources could be added after the - // component is activated - Thread.sleep(20 * 1000); - } catch (InterruptedException e) { - logService.log(LogService.LOG_ERROR, e.getMessage()); - } - } - - try { - SchedulerTask connectorRunnable = new ConnectorTask(requirementsSources, transaction, logService); - connectorRunnable.run(); - modelSearchService.startReIndex(); - Scheduler scheduler = new Scheduler(); - scheduler.schedule(connectorRunnable, SchedulerIteratorFactory.create(schedule)); - } catch (SpecmateException e) { - e.printStackTrace(); - logService.log(LogService.LOG_ERROR, "Could not create schedule iterator.", e); - } - } - }, "connector-service-initializer").start(); - - } - - private void validateConfig(Map properties) throws SpecmateException { - SchedulerIteratorFactory.validate((String) properties.get(KEY_POLL_SCHEDULE)); - logService.log(LogService.LOG_DEBUG, "Connector service config validated."); - } - - @Deactivate - public void deactivate() { - transaction.close(); - } - - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) - public void addRequirementsConnector(IRequirementsSource source) { - this.requirementsSources.add(source); - } - - public void removeRequirementsConnector(IRequirementsSource source) { - this.requirementsSources.remove(source); - } - - @Reference - public void setLogService(LogService logService) { - this.logService = logService; - } - - @Reference - public void setPersistency(IPersistencyService persistencyService) { - this.persistencyService = persistencyService; - } - - @Reference - public void setModelSearchService(IModelSearchService modelSearchService) { - this.modelSearchService = modelSearchService; - } - - public void unsetPersistency(IPersistencyService persistencyService) { - this.persistencyService = null; - } -} +package com.specmate.connectors.internal; + +import static com.specmate.connectors.internal.config.ConnectorServiceConfig.KEY_POLL_SCHEDULE; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.cdo.common.id.CDOWithID; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.log.LogService; + +import com.specmate.common.exception.SpecmateException; +import com.specmate.common.exception.SpecmateValidationException; +import com.specmate.connectors.api.IRequirementsSource; +import com.specmate.connectors.internal.config.ConnectorServiceConfig; +import com.specmate.persistency.IPersistencyService; +import com.specmate.persistency.ITransaction; +import com.specmate.persistency.validation.TopLevelValidator; +import com.specmate.scheduler.Scheduler; +import com.specmate.scheduler.SchedulerIteratorFactory; +import com.specmate.scheduler.SchedulerTask; +import com.specmate.search.api.IModelSearchService; + +@Component(immediate = true, configurationPid = ConnectorServiceConfig.PID, configurationPolicy = ConfigurationPolicy.REQUIRE) +public class ConnectorService { + CDOWithID id; + List requirementsSources = new ArrayList<>(); + private LogService logService; + private IPersistencyService persistencyService; + private IModelSearchService modelSearchService; + private ITransaction transaction; + + @Activate + public void activate(Map properties) throws SpecmateValidationException, SpecmateException { + validateConfig(properties); + + String schedule = (String) properties.get(KEY_POLL_SCHEDULE); + if (schedule == null) { + return; + } + + this.transaction = this.persistencyService.openTransaction(); + this.transaction.removeValidator(TopLevelValidator.class.getName()); + + new Thread(new Runnable() { + @Override + public void run() { + + // Ensure that requirements source are loaded. + while(requirementsSources.size() == 0) { + try { + logService.log(LogService.LOG_INFO, "No requirement sources here yet. Waiting."); + // Requirements Sources could be added after the + // component is activated + Thread.sleep(20 * 1000); + } catch (InterruptedException e) { + logService.log(LogService.LOG_ERROR, e.getMessage()); + } + } + + SchedulerTask connectorRunnable = new ConnectorTask(requirementsSources, transaction, logService); + connectorRunnable.run(); + try { + modelSearchService.startReIndex(); + } catch (SpecmateException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + Scheduler scheduler = new Scheduler(); + try { + scheduler.schedule(connectorRunnable, SchedulerIteratorFactory.create(schedule)); + } catch (SpecmateException e) { + e.printStackTrace(); + } + } + }, "connector-service-initializer").start(); + + } + + private void validateConfig(Map properties) throws SpecmateException { + SchedulerIteratorFactory.validate((String) properties.get(KEY_POLL_SCHEDULE)); + logService.log(LogService.LOG_DEBUG, "Connector service config validated."); + } + + @Deactivate + public void deactivate() { + if(this.transaction!=null) { + transaction.close(); + } + } + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + public void addRequirementsConnector(IRequirementsSource source) { + this.requirementsSources.add(source); + } + + public void removeRequirementsConnector(IRequirementsSource source) { + this.requirementsSources.remove(source); + } + + @Reference + public void setLogService(LogService logService) { + this.logService = logService; + } + + @Reference + public void setPersistency(IPersistencyService persistencyService) { + this.persistencyService = persistencyService; + } + + @Reference + public void setModelSearchService(IModelSearchService modelSearchService) { + this.modelSearchService = modelSearchService; + } + + public void unsetPersistency(IPersistencyService persistencyService) { + this.persistencyService = null; + } +} diff --git a/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorTask.java b/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorTask.java index 026dd2129..6c08cb511 100644 --- a/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorTask.java +++ b/bundles/specmate-connectors/src/com/specmate/connectors/internal/ConnectorTask.java @@ -81,7 +81,9 @@ public Object doChange() throws SpecmateException { } } catch (Exception e) { logService.log(LogService.LOG_ERROR, e.getMessage()); - transaction.rollback(); + try { + transaction.rollback(); + } catch (SpecmateException rbe) {} } } diff --git a/bundles/specmate-connectors/src/com/specmate/connectors/internal/config/ArtificialRequirementsConnectorConfig.java b/bundles/specmate-connectors/src/com/specmate/connectors/internal/config/ArtificialRequirementsConnectorConfig.java new file mode 100644 index 000000000..65531c45b --- /dev/null +++ b/bundles/specmate-connectors/src/com/specmate/connectors/internal/config/ArtificialRequirementsConnectorConfig.java @@ -0,0 +1,10 @@ +package com.specmate.connectors.internal.config; + +public class ArtificialRequirementsConnectorConfig { + public static final String PID = "com.specmate.ArtificialRequirementsConnector"; + public static final String KEY_NUMBER_OF_REQUIREMENTS = "artificalRequirements.count"; + public static final Object KEY_DELAY = "artificalRequirements.simulatedDelay"; + + + +} diff --git a/bundles/specmate-dbprovider-api/src/com/specmate/dbprovider/api/migration/SQLMapper.java b/bundles/specmate-dbprovider-api/src/com/specmate/dbprovider/api/migration/SQLMapper.java index 3fe7a510b..4bb80d4b2 100644 --- a/bundles/specmate-dbprovider-api/src/com/specmate/dbprovider/api/migration/SQLMapper.java +++ b/bundles/specmate-dbprovider-api/src/com/specmate/dbprovider/api/migration/SQLMapper.java @@ -21,6 +21,7 @@ public SQLMapper(Connection connection, String packageName, String sourceVersion this.targetVersion = targetVersion; } + protected String insertExternalObjectReference(String objectName) throws SpecmateException { return getInsertExternalReferenceQuery(getBaseURI(objectName), getLatestId() - 1); } @@ -58,16 +59,16 @@ protected boolean hasDefault(Object defaultValue) { return defaultValue != null ? true : false; } - protected void executeChange(String alterString, String objectName, String attributeName, boolean setDefault) throws SpecmateException { + protected void executeChange(String alterString, String objectName, String attributeName, boolean setDefault) + throws SpecmateException { String failmsg = "Migration: Could not add column " + attributeName + " to table " + objectName + "."; List queries = new ArrayList<>(); queries.add(alterString); - + if (setDefault) { queries.add("UPDATE " + objectName + " SET " + attributeName + " = DEFAULT"); } - - queries.add(insertExternalAttributeReference(objectName, attributeName)); + SQLUtil.executeStatements(queries, connection, failmsg); } } diff --git a/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/AttributeToSQLMapper.java b/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/AttributeToSQLMapper.java index 040a8d980..c52d65e08 100644 --- a/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/AttributeToSQLMapper.java +++ b/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/AttributeToSQLMapper.java @@ -114,15 +114,16 @@ private void migrateNewReference(String objectName, String attributeName, String List queries = new ArrayList<>(); queries.add("ALTER TABLE " + objectName + " ADD COLUMN " + attributeName + " INTEGER"); - queries.add("CREATE TABLE " + tableNameList + " (" + "CDO_SOURCE BIGINT NOT NULL, " - + "CDO_VERSION INTEGER NOT NULL, " + "CDO_IDX INTEGER NOT NULL, " + "CDO_VALUE " + type + ")"); + queries.add("CREATE TABLE " + tableNameList + " (" + "CDO_SOURCE VARCHAR(255) NOT NULL, " + + "CDO_BRANCH INTEGER NOT NULL, " + "CDO_VERSION INTEGER NOT NULL, " + "CDO_IDX INTEGER NOT NULL, " + + "CDO_VALUE " + type + ")"); queries.add("CREATE UNIQUE INDEX " + SQLUtil.createTimebasedIdentifier("PK", H2ProviderConfig.MAX_ID_LENGTH) - + " ON " + tableNameList + " (CDO_SOURCE ASC, CDO_VERSION ASC, CDO_IDX ASC)"); + + " ON " + tableNameList + " (CDO_SOURCE ASC, CDO_BRANCH ASC, CDO_VERSION ASC, CDO_IDX ASC)"); queries.add("ALTER TABLE " + tableNameList + " ADD CONSTRAINT " + SQLUtil.createTimebasedIdentifier("C", H2ProviderConfig.MAX_ID_LENGTH) - + " PRIMARY KEY (CDO_SOURCE, CDO_VERSION, CDO_IDX)"); + + " PRIMARY KEY (CDO_SOURCE, CDO_BRANCH, CDO_VERSION, CDO_IDX)"); SQLUtil.executeStatements(queries, connection, failmsg); } diff --git a/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/H2Provider.java b/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/H2Provider.java index a788a41ef..fc97d6c9b 100644 --- a/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/H2Provider.java +++ b/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/H2Provider.java @@ -68,8 +68,10 @@ public void deactivate() throws SpecmateException { } private void readConfig(Map properties) throws SpecmateException { + this.jdbcConnection = (String) properties.get(H2ProviderConfig.KEY_JDBC_CONNECTION); + String failmsg = " not defined in configuration."; if (StringUtils.isNullOrEmpty(this.jdbcConnection)) { throw new SpecmateInternalException(ErrorCode.PERSISTENCY, "JDBC connection not defined in configuration."); } @@ -115,7 +117,8 @@ public boolean isVirginDB() throws SpecmateException { public IStore createStore() { JdbcDataSource jdataSource = new JdbcDataSource(); jdataSource.setURL(this.jdbcConnection); - IMappingStrategy jmappingStrategy = CDODBUtil.createHorizontalMappingStrategy(true, false); + IMappingStrategy jmappingStrategy = CDODBUtil.createHorizontalMappingStrategy(true, true); + jmappingStrategy.getProperties().put(IMappingStrategy.Props.EAGER_TABLE_CREATION,"true"); IDBAdapter h2dbAdapter = new H2Adapter(); IDBConnectionProvider jdbConnectionProvider = DBUtil.createConnectionProvider(jdataSource); return CDODBUtil.createStore(jmappingStrategy, h2dbAdapter, jdbConnectionProvider); diff --git a/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/ObjectToSQLMapper.java b/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/ObjectToSQLMapper.java index 4b280086e..82fb1e0d3 100644 --- a/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/ObjectToSQLMapper.java +++ b/bundles/specmate-dbprovider-h2/src/specmate/dbprovider/h2/ObjectToSQLMapper.java @@ -22,21 +22,21 @@ public void newObject(String tableName) throws SpecmateException { String failmsg = "Migration: Could not add table " + tableName + "."; List queries = new ArrayList<>(); - queries.add("CREATE TABLE " + tableName + "(" + "CDO_ID BIGINT NOT NULL, " + "CDO_VERSION INTEGER NOT NULL, " - + "CDO_CREATED BIGINT NOT NULL, " + "CDO_REVISED BIGINT NOT NULL, " + "CDO_RESOURCE BIGINT NOT NULL, " - + "CDO_CONTAINER BIGINT NOT NULL, " + "CDO_FEATURE INTEGER NOT NULL)"); + queries.add("CREATE TABLE " + tableName + "(" + "CDO_ID VARCHAR(255) NOT NULL, " + + "CDO_VERSION INTEGER NOT NULL, " + "CDO_BRANCH INTEGER NOT NULL, " + "CDO_CREATED BIGINT NOT NULL, " + + "CDO_REVISED BIGINT NOT NULL, " + "CDO_RESOURCE VARCHAR(255) NOT NULL, " + + "CDO_CONTAINER VARCHAR(255) NOT NULL, " + "CDO_FEATURE INTEGER NOT NULL)"); queries.add("CREATE UNIQUE INDEX " + SQLUtil.createTimebasedIdentifier("PK", H2ProviderConfig.MAX_ID_LENGTH) - + " ON " + tableName + " (CDO_ID ASC, CDO_VERSION ASC)"); + + " ON " + tableName + " (CDO_ID ASC, CDO_VERSION ASC, CDO_BRANCH ASC)"); queries.add("CREATE INDEX " + SQLUtil.createTimebasedIdentifier("I", H2ProviderConfig.MAX_ID_LENGTH) + " ON " + tableName + " (CDO_REVISED ASC)"); queries.add("ALTER TABLE " + tableName + " ADD CONSTRAINT " + SQLUtil.createTimebasedIdentifier("C", H2ProviderConfig.MAX_ID_LENGTH) - + " PRIMARY KEY (CDO_ID, CDO_VERSION)"); + + " PRIMARY KEY (CDO_ID, CDO_VERSION, CDO_BRANCH)"); - queries.add(insertExternalObjectReference(tableName)); SQLUtil.executeStatements(queries, connection, failmsg); } } diff --git a/bundles/specmate-dbprovider-oracle/bnd.bnd b/bundles/specmate-dbprovider-oracle/bnd.bnd index e89f81102..05d7231fa 100644 --- a/bundles/specmate-dbprovider-oracle/bnd.bnd +++ b/bundles/specmate-dbprovider-oracle/bnd.bnd @@ -8,15 +8,14 @@ org.eclipse.emf.cdo.server,\ specmate-config-api;version=latest,\ org.apache.commons.lang3,\ - org.eclipse.emf.cdo.server.db,\ - org.eclipse.net4j.db,\ + org.eclipse.emf.cdo.server.db;version=latest,\ + org.eclipse.net4j.db;version=latest,\ org.eclipse.net4j.db.oracle,\ specmate-model-gen;version=latest Private-Package: \ specmate.dbprovider.oracle,\ - oracle.*,\ - specmate.dbprovider.oracle.config,\ - + oracle.* +Export-Package: specmate.dbprovider.oracle.config Import-Package: \ com.oracle.common.internal.net.ipclw.mql.*;resolution:=optional, \ com.oracle.common.io.*;resolution:=optional, \ @@ -27,5 +26,5 @@ Import-Package: \ oracle.security.pki.*;resolution:=optional, \ javax.resource*;resolution:=optional, \ sun.*;resolution:=optional, \ - oracle.*;resolution:=optional, \ - *\ \ No newline at end of file + oracle.*;resolution:=optional,\ + *;resolution:=optional diff --git a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/AttributeToSQLMapper.java b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/AttributeToSQLMapper.java index 6b78908b7..ef1c1b9d0 100644 --- a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/AttributeToSQLMapper.java +++ b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/AttributeToSQLMapper.java @@ -124,13 +124,14 @@ private void migrateNewReference(String objectName, String attributeName, String List queries = new ArrayList<>(); queries.add("ALTER TABLE " + objectName + " ADD " + attributeName + " NUMBER"); - queries.add("CREATE TABLE " + tableNameList + " (" + "CDO_SOURCE NUMBER NOT NULL, " - + "CDO_VERSION NUMBER NOT NULL, " + "CDO_IDX NUMBER NOT NULL, " + "CDO_VALUE " + type + ")"); + queries.add("CREATE TABLE " + tableNameList + " (" + "CDO_SOURCE VARCHAR2(255) NOT NULL, " + + "CDO_BRANCH NUMBER NOT NULL, " + "CDO_VERSION NUMBER NOT NULL, " + "CDO_IDX NUMBER NOT NULL, " + + "CDO_VALUE " + type + ")"); queries.add("CREATE UNIQUE INDEX " + SQLUtil.createTimebasedIdentifier("PK", OracleProviderConfig.MAX_ID_LENGTH) - + " ON " + tableNameList + " (CDO_SOURCE ASC, CDO_VERSION ASC, CDO_IDX ASC)"); + + " ON " + tableNameList + " (CDO_SOURCE ASC, CDO_BRANCH ASC, CDO_VERSION ASC, CDO_IDX ASC)"); queries.add("ALTER TABLE " + tableNameList + " ADD CONSTRAINT " + SQLUtil.createTimebasedIdentifier("C", OracleProviderConfig.MAX_ID_LENGTH) - + " PRIMARY KEY (CDO_SOURCE, CDO_VERSION, CDO_IDX)"); + + " PRIMARY KEY (CDO_SOURCE, CDO_BRANCH, CDO_VERSION, CDO_IDX)"); queries.add(insertExternalAttributeReference(objectName, attributeName)); SQLUtil.executeStatements(queries, connection, failmsg); } diff --git a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/ObjectToSQLMapper.java b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/ObjectToSQLMapper.java index 70e0c25d6..ac359138c 100644 --- a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/ObjectToSQLMapper.java +++ b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/ObjectToSQLMapper.java @@ -22,17 +22,17 @@ public void newObject(String tableName) throws SpecmateException { String failmsg = "Migration: Could not add table " + tableName + "."; List queries = new ArrayList<>(); - queries.add("CREATE TABLE " + tableName + "(" + "CDO_ID NUMBER NOT NULL, " + "CDO_VERSION NUMBER NOT NULL, " - + "CDO_CREATED NUMBER NOT NULL, " + "CDO_REVISED NUMBER NOT NULL, " + "CDO_RESOURCE NUMBER NOT NULL, " - + "CDO_CONTAINER NUMBER NOT NULL, " + "CDO_FEATURE NUMBER NOT NULL)"); + queries.add("CREATE TABLE " + tableName + "(" + "CDO_ID VARCHAR2(255) NOT NULL, " + + "CDO_VERSION NUMBER NOT NULL, " + "CDO_BRANCH NUMBER NOT NULL, " + "CDO_CREATED NUMBER NOT NULL, " + + "CDO_REVISED NUMBER NOT NULL, " + "CDO_RESOURCE VARCHAR2(255) NOT NULL, " + + "CDO_CONTAINER VARCHAR2(255) NOT NULL, " + "CDO_FEATURE NUMBER NOT NULL)"); queries.add("CREATE UNIQUE INDEX " + SQLUtil.createTimebasedIdentifier("PK", OracleProviderConfig.MAX_ID_LENGTH) - + " ON " + tableName + " (CDO_ID ASC, CDO_VERSION ASC)"); + + " ON " + tableName + " (CDO_ID ASC, CDO_VERSION ASC, CDO_BRANCH ASC)"); queries.add("CREATE INDEX " + SQLUtil.createTimebasedIdentifier("I", OracleProviderConfig.MAX_ID_LENGTH) + " ON " + tableName + " (CDO_REVISED ASC)"); queries.add("ALTER TABLE " + tableName + " ADD CONSTRAINT " + SQLUtil.createTimebasedIdentifier("C", OracleProviderConfig.MAX_ID_LENGTH) - + " PRIMARY KEY (CDO_ID, CDO_VERSION)"); - queries.add(insertExternalObjectReference(tableName)); + + " PRIMARY KEY (CDO_ID, CDO_VERSION, CDO_BRANCH)"); SQLUtil.executeStatements(queries, connection, failmsg); } diff --git a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/OracleProvider.java b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/OracleProvider.java index 50cadd973..deef48a0c 100644 --- a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/OracleProvider.java +++ b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/OracleProvider.java @@ -96,7 +96,7 @@ public IStore createStore() throws SpecmateException { odataSource.setURL(this.jdbcConnection); odataSource.setUser(this.username); odataSource.setPassword(this.password); - IMappingStrategy omappingStrategy = CDODBUtil.createHorizontalMappingStrategy(true); + IMappingStrategy omappingStrategy = CDODBUtil.createHorizontalMappingStrategy(true,true); IDBAdapter odbAdapter = new OracleAdapter(); IDBConnectionProvider odbConnectionProvider = DBUtil.createConnectionProvider(odataSource); store = CDODBUtil.createStore(omappingStrategy, odbAdapter, odbConnectionProvider); diff --git a/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/config/package-info.java b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/config/package-info.java new file mode 100644 index 000000000..92b506590 --- /dev/null +++ b/bundles/specmate-dbprovider-oracle/src/specmate/dbprovider/oracle/config/package-info.java @@ -0,0 +1,2 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +package specmate.dbprovider.oracle.config; diff --git a/bundles/specmate-emfrest/src/com/specmate/emfrest/internal/rest/SpecmateResource.java b/bundles/specmate-emfrest/src/com/specmate/emfrest/internal/rest/SpecmateResource.java index 601b77e6e..d0bda8a82 100644 --- a/bundles/specmate-emfrest/src/com/specmate/emfrest/internal/rest/SpecmateResource.java +++ b/bundles/specmate-emfrest/src/com/specmate/emfrest/internal/rest/SpecmateResource.java @@ -137,102 +137,63 @@ private String getAuthenticationToken(HttpHeaders headers) { private Object handleRequest(String serviceName, RestServiceChecker checkRestService, RestServiceExcecutor executeRestService, boolean commitTransaction) { - SortedSet services = serviceProvider.getAllRestServices(serviceName); - + if (services.isEmpty()) { + logService.log(LogService.LOG_ERROR, "No suitable service found."); + return Response.status(Status.NOT_FOUND).build(); + } for (IRestService service : services) { - if (!checkRestService.checkIfApplicable(service)) { - continue; - } - - if (commitTransaction && statusService.getCurrentStatus().isReadOnly() - && !(service instanceof IStatusService)) { - logService.log(LogService.LOG_ERROR, "Attempt to access writing resource when in read-only mode"); - - Status status = Status.SERVICE_UNAVAILABLE; - ProblemDetail pd = AdministrationFactory.eINSTANCE.createProblemDetail(); - pd.setStatus(status.getStatusCode()); - pd.setEcode(ErrorCode.IN_MAINTENANCE_MODE); - - return Response.status(status).entity(pd).build(); - } - - IHistogram histogram; - ITimer timer = null; - try { - histogram = metricsService.createHistogram(service.getServiceName(), - "Service time for service " + service.getServiceName()); - timer = histogram.startTimer(); - } catch (SpecmateException e) { - logService.log(LogService.LOG_ERROR, "Could not obtain metric.", e); - } - - try { - - RestResult result; + if (checkRestService.checkIfApplicable(service)) { + if (commitTransaction && statusService.getCurrentStatus().isReadOnly() + && !(service instanceof IStatusService)) { + logService.log(LogService.LOG_ERROR, "Attempt to access writing resource when in read-only mode"); + return Response.status(Status.FORBIDDEN).build(); + } + IHistogram histogram; + ITimer timer = null; try { - if (commitTransaction) { - result = transaction.doAndCommit(() -> executeRestService.executeRestService(service)); - return result.getResponse(); - } else { - result = executeRestService.executeRestService(service); - return result.getResponse(); - } - } catch (SpecmateValidationException e) { - transaction.rollback(); - - logService.log(LogService.LOG_ERROR, e.getMessage()); - - Status status = Status.BAD_REQUEST; - ProblemDetail pd = AdministrationFactory.eINSTANCE.createProblemDetail(); - pd.setStatus(status.getStatusCode()); - pd.setEcode(e.getErrorcode()); - pd.setDetail(e.getValidatorName()); - pd.setInstance(e.getValidatedObjectName()); - - return Response.status(status).entity(pd).build(); - } catch (SpecmateAuthorizationException e) { - transaction.rollback(); - logService.log(LogService.LOG_ERROR, e.getMessage()); - - Status status = Status.UNAUTHORIZED; - ProblemDetail pd = AdministrationFactory.eINSTANCE.createProblemDetail(); - pd.setStatus(status.getStatusCode()); - pd.setEcode(e.getErrorcode()); - pd.setDetail(e.getMessage()); - - return Response.status(status).entity(pd).build(); - + histogram = metricsService.createHistogram(service.getServiceName(), + "Service time for service " + service.getServiceName()); + timer = histogram.startTimer(); } catch (SpecmateException e) { - transaction.rollback(); - logService.log(LogService.LOG_ERROR, e.getMessage()); + logService.log(LogService.LOG_ERROR, "Could not obtain metric.", e); + } - Status status = Status.INTERNAL_SERVER_ERROR; - ProblemDetail pd = AdministrationFactory.eINSTANCE.createProblemDetail(); - pd.setStatus(status.getStatusCode()); - pd.setEcode(e.getErrorcode()); - pd.setDetail(e.getMessage()); + try { - return Response.status(status).entity(pd).build(); - } + RestResult result; + + try { + if (commitTransaction) { + result = transaction.doAndCommit(() -> executeRestService.executeRestService(service)); + return result.getResponse(); + } else { + result = executeRestService.executeRestService(service); + return result.getResponse(); + } + } catch (SpecmateValidationException e) { + try { + transaction.rollback(); + } catch (SpecmateException rb) {} + logService.log(LogService.LOG_ERROR, e.getLocalizedMessage()); + return Response.status(Status.BAD_REQUEST).build(); + } catch (SpecmateException e) { + try { + transaction.rollback(); + } catch (SpecmateException rbe) {} + logService.log(LogService.LOG_ERROR, e.getLocalizedMessage()); + return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + } - } finally { - if (timer != null) { - timer.observeDuration(); + } finally { + if (timer != null) { + timer.observeDuration(); + } } } } - - logService.log(LogService.LOG_ERROR, "No suitable service found."); - - Status status = Status.NOT_FOUND; - ProblemDetail pd = AdministrationFactory.eINSTANCE.createProblemDetail(); - pd.setStatus(status.getStatusCode()); - pd.setEcode(ErrorCode.NO_SUCH_SERVICE); - pd.setDetail(serviceName); - - return Response.status(status).entity(pd).build(); + return Response.status(Status.NOT_FOUND).build(); } @Path("/{id:[^_][^/]*(?=/)}") diff --git a/bundles/specmate-integration-test/bnd.bnd b/bundles/specmate-integration-test/bnd.bnd index 50a2e45b8..396af69a4 100644 --- a/bundles/specmate-integration-test/bnd.bnd +++ b/bundles/specmate-integration-test/bnd.bnd @@ -71,6 +71,7 @@ Bundle-Version: 0.0.0.${tstamp} osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ osgi.identity;filter:='(osgi.identity=specmate-common)',\ osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ osgi.identity;filter:='(osgi.identity=specmate-logging)',\ @@ -121,13 +122,13 @@ Bundle-Version: 0.0.0.${tstamp} javax.validation.api;version='[1.1.0,1.1.1)',\ javax.ws.rs-api;version='[2.0.1,2.0.2)',\ jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ @@ -136,7 +137,6 @@ Bundle-Version: 0.0.0.${tstamp} org.eclipse.equinox.metatype;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.preferences;version='[3.5.200,3.5.201)',\ org.eclipse.equinox.registry;version='[3.5.400,3.5.401)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ org.eclipse.osgi.services;version='[3.4.0,3.4.1)',\ org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ @@ -165,20 +165,6 @@ Bundle-Version: 0.0.0.${tstamp} specmate-model-support;version=snapshot,\ specmate-ui-core;version=snapshot,\ specmate-connectors;version=snapshot,\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.cdo.net4j;version='[4.1.400,4.1.401)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ - org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.h2;version='[4.2.300,4.2.301)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ com.google.guava;version='[21.0.0,21.0.1)',\ specmate-hp-connector;version=snapshot,\ specmate-testspecification;version=snapshot,\ @@ -211,7 +197,6 @@ Bundle-Version: 0.0.0.${tstamp} specmate-jettystarter;version=snapshot,\ specmate-auth;version=snapshot,\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ @@ -245,39 +230,55 @@ Bundle-Version: 0.0.0.${tstamp} specmate-metrics;version=snapshot,\ org.h2;version='[1.3.168,1.3.169)',\ specmate-cdo-server;version=snapshot,\ - org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ specmate-rest;version=snapshot,\ specmate-scheduler;version=snapshot,\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.eclipse.net4j.db;version=snapshot,\ specmate-config;version=snapshot,\ - org.apache.servicemix.bundles.automaton;version='[1.11.0,1.11.1)',\ - org.apache.servicemix.bundles.generex;version='[1.0.2,1.0.3)',\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db.h2;version='[4.3.100,4.3.101)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ + com.google.guava;version='[18.0.0,18.0.1)',\ + com.google.inject;version='[3.0.0,3.0.1)',\ com.ibm.icu;version='[63.1.0,63.1.1)',\ io.reactivex.rxjava2.rxjava;version='[2.2.3,2.2.4)',\ it.unimi.dsi.fastutil;version='[8.2.2,8.2.3)',\ javax.money.api;version='[1.0.1,1.0.2)',\ joda-time;version='[2.10.1,2.10.2)',\ + log4j;version='[1.2.17,1.2.18)',\ + org.antlr.runtime;version='[3.2.0,3.2.1)',\ org.apache.commons.commons-compress;version='[1.18.0,1.18.1)',\ org.apache.commons.commons-pool2;version='[2.6.0,2.6.1)',\ org.apache.commons.lang;version='[2.6.0,2.6.1)',\ - org.apache.ivy;version='[2.4.0,2.4.1)',\ - org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ - specmate-nlp;version=snapshot,\ - com.google.guava;version='[18.0.0,18.0.1)',\ org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ - specmate-model-generation;version=snapshot,\ org.apache.commons.logging;version='[1.2.0,1.2.1)',\ - log4j;version='[1.2.17,1.2.18)',\ - specmate-cause-effect-patterns;version=snapshot,\ - com.google.inject;version='[3.0.0,3.0.1)',\ - org.antlr.runtime;version='[3.2.0,3.2.1)',\ + org.apache.ivy;version='[2.4.0,2.4.1)',\ + org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ + org.apache.servicemix.bundles.automaton;version='[1.11.0,1.11.1)',\ + org.apache.servicemix.bundles.generex;version='[1.0.2,1.0.3)',\ org.eclipse.xtend.lib;version='[2.10.0,2.10.1)',\ org.eclipse.xtend.lib.macro;version='[2.10.0,2.10.1)',\ org.eclipse.xtext;version='[2.10.0,2.10.1)',\ org.eclipse.xtext.smap;version='[2.10.0,2.10.1)',\ org.eclipse.xtext.util;version='[2.10.0,2.10.1)',\ - org.eclipse.xtext.xbase.lib;version='[2.10.0,2.10.1)' + org.eclipse.xtext.xbase.lib;version='[2.10.0,2.10.1)',\ + org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ + slf4j.api;version='[1.7.25,1.7.26)',\ + specmate-cause-effect-patterns;version=snapshot,\ + specmate-model-generation;version=snapshot,\ + specmate-nlp;version=snapshot -runproperties: \ jetty.http.port=8088,\ @@ -296,4 +297,7 @@ Bundle-Version: 0.0.0.${tstamp} Local Private-Package: \ com.specmate.test.integration,\ - com.specmate.test.integration.support \ No newline at end of file + com.specmate.test.integration.support +Import-Package: \ + ,\ + * \ No newline at end of file diff --git a/bundles/specmate-integration-test/src/com/specmate/test/integration/IntegrationTestBase.java b/bundles/specmate-integration-test/src/com/specmate/test/integration/IntegrationTestBase.java index 872ae1b1d..522233cae 100644 --- a/bundles/specmate-integration-test/src/com/specmate/test/integration/IntegrationTestBase.java +++ b/bundles/specmate-integration-test/src/com/specmate/test/integration/IntegrationTestBase.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.Dictionary; +import java.util.Hashtable; import org.junit.Assert; import org.osgi.framework.BundleContext; @@ -124,7 +125,13 @@ public Object doChange() throws SpecmateException { transaction.close(); } - private ConfigurationAdmin getConfigAdmin() throws SpecmateException { + protected Dictionary getDBProviderProperites() { + Dictionary properties = new Hashtable<>(); + properties.put(H2ProviderConfig.KEY_JDBC_CONNECTION, "jdbc:h2:mem:specmate;DB_CLOSE_DELAY=-1"); + return properties; + } + + protected ConfigurationAdmin getConfigAdmin() throws SpecmateException { ServiceTracker configAdminTracker = new ServiceTracker<>(context, ConfigurationAdmin.class.getName(), null); configAdminTracker.open(); diff --git a/bundles/specmate-logging/src/com/specmate/logging/internal/SpecmateLogReader.java b/bundles/specmate-logging/src/com/specmate/logging/internal/SpecmateLogReader.java index 376dd861f..f80841696 100644 --- a/bundles/specmate-logging/src/com/specmate/logging/internal/SpecmateLogReader.java +++ b/bundles/specmate-logging/src/com/specmate/logging/internal/SpecmateLogReader.java @@ -2,6 +2,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -20,6 +21,8 @@ public class SpecmateLogReader implements LogListener { /** The log level threshold */ private int logLevel; + + private static Pattern LOG_FILTER = Pattern.compile(".*Durable locking is not enabled.*"); private static Map level2String = new HashMap<>(); @@ -88,6 +91,9 @@ public void logged(LogEntry entry) { if (entry.getLevel() > this.logLevel) { return; } + if(LOG_FILTER.matcher(entry.getMessage()).matches()){ + return; + } String message = getStringFromLevel(entry.getLevel()) + ":" + entry.getBundle().getSymbolicName() + ":" + entry.getMessage(); if (entry.getLevel() <= LogService.LOG_WARNING) { diff --git a/bundles/specmate-metrics/src/com/specmate/metrics/internal/MetricsServiceImpl.java b/bundles/specmate-metrics/src/com/specmate/metrics/internal/MetricsServiceImpl.java index 1dfc89368..de8b84427 100644 --- a/bundles/specmate-metrics/src/com/specmate/metrics/internal/MetricsServiceImpl.java +++ b/bundles/specmate-metrics/src/com/specmate/metrics/internal/MetricsServiceImpl.java @@ -88,7 +88,7 @@ private String getMetricName(String name) { } @Override - public IGauge createGauge(String name, String description) throws SpecmateException { + public synchronized IGauge createGauge(String name, String description) throws SpecmateException { String theName = getMetricName(name); IGauge gauge = checkIfCreated(IGauge.class, theName, description); if (gauge == null) { @@ -99,7 +99,7 @@ public IGauge createGauge(String name, String description) throws SpecmateExcept } @Override - public IHistogram createHistogram(String name, String description) throws SpecmateException { + public synchronized IHistogram createHistogram(String name, String description) throws SpecmateException { String theName = getMetricName(name); IHistogram histogram = checkIfCreated(IHistogram.class, theName, description); if (histogram == null) { @@ -110,7 +110,7 @@ public IHistogram createHistogram(String name, String description) throws Specma } @Override - public ICounter createCounter(String name, String description) throws SpecmateException { + public synchronized ICounter createCounter(String name, String description) throws SpecmateException { String theName = getMetricName(name); ICounter counter = checkIfCreated(ICounter.class, theName, description); if (counter == null) { diff --git a/bundles/specmate-migration-test/.project b/bundles/specmate-migration-test/.project index f82ae399b..8aa52a624 100644 --- a/bundles/specmate-migration-test/.project +++ b/bundles/specmate-migration-test/.project @@ -25,6 +25,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.jdt.core.javanature diff --git a/bundles/specmate-migration-test/bnd.bnd b/bundles/specmate-migration-test/bnd.bnd index 439bea8ab..02bbba831 100644 --- a/bundles/specmate-migration-test/bnd.bnd +++ b/bundles/specmate-migration-test/bnd.bnd @@ -1,11 +1,11 @@ -Bundle-Version: 0.0.0.${tstamp} -Test-Cases: \ - com.specmate.migration.test.AddAttributeTest,\ - com.specmate.migration.test.AddSeveralAttributesTest,\ - com.specmate.migration.test.AddObjectTest,\ - com.specmate.migration.test.RenamedAttributeTest,\ - com.specmate.migration.test.ChangedTypesTest,\ - com.specmate.migration.test.OnlyMetaChangeTest +Bundle-Version: 0.0.0.${tstamp} +Test-Cases: \ + com.specmate.migration.test.AddAttributeTest,\ + com.specmate.migration.test.AddSeveralAttributesTest,\ + com.specmate.migration.test.AddObjectTest,\ + com.specmate.migration.test.RenamedAttributeTest,\ + com.specmate.migration.test.ChangedTypesTest,\ + com.specmate.migration.test.OnlyMetaChangeTest -buildpath: \ org.eclipse.emf,\ org.eclipse.emf.cdo,\ @@ -13,8 +13,6 @@ Test-Cases: \ org.eclipse.emf.ecore,\ org.eclipse.emf.cdo.common,\ org.eclipse.net4j.util,\ - org.eclipse.emf.edit,\ - org.eclipse.emf.cdo.edit,\ org.eclipse.core.runtime,\ org.apache.servicemix.bundles.junit,\ specmate-common;version=latest,\ @@ -55,6 +53,7 @@ Test-Cases: \ osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\ osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ osgi.identity;filter:='(osgi.identity=specmate-common)',\ osgi.identity;filter:='(osgi.identity=specmate-logging)',\ osgi.identity;filter:='(osgi.identity=specmate-logging-slf4j)',\ @@ -80,6 +79,7 @@ Test-Cases: \ javax.validation.api;version='[1.1.0,1.1.1)',\ javax.ws.rs-api;version='[2.0.1,2.0.2)',\ jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ @@ -87,13 +87,6 @@ Test-Cases: \ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ - org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ @@ -102,7 +95,6 @@ Test-Cases: \ org.eclipse.equinox.metatype;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.preferences;version='[3.5.200,3.5.201)',\ org.eclipse.equinox.registry;version='[3.5.400,3.5.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ org.eclipse.osgi.services;version='[3.4.0,3.4.1)',\ org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ @@ -124,12 +116,6 @@ Test-Cases: \ specmate-model-gen;version=snapshot,\ specmate-model-support;version=snapshot,\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.net4j;version='[4.1.400,4.1.401)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ @@ -140,9 +126,6 @@ Test-Cases: \ specmate-config-api;version=snapshot,\ specmate-emfrest-api;version=snapshot,\ specmate-dbprovider-api;version=snapshot,\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.h2;version='[4.2.300,4.2.301)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ specmate-dbprovider-h2;version=snapshot,\ com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ @@ -153,19 +136,35 @@ Test-Cases: \ specmate-metrics;version=snapshot,\ org.h2;version='[1.3.168,1.3.169)',\ specmate-cdo-server;version=snapshot,\ - org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.eclipse.net4j.db;version=snapshot,\ specmate-rest;version=snapshot,\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db.h2;version='[4.3.100,4.3.101)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ com.google.guava;version='[21.0.0,21.0.1)',\ + log4j;version='[1.2.17,1.2.18)',\ org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ - log4j;version='[1.2.17,1.2.18)' --runvm: -ea, -Djdk.crypto.KeyAgreement.legacyKDF=true --runproperties: \ - jetty.http.port=8088,\ - osgi.console=,\ - jetty.home.bundle=specmate-jettystarter,\ - jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ - tester.dir=testdir,\ - tester.trace=true,\ - tester.continuous=true,\ - osgi.compatibility.bootdelegation=true + slf4j.api;version='[1.7.25,1.7.26)' +-runvm: -ea, -Djdk.crypto.KeyAgreement.legacyKDF=true +-runproperties:\ + jetty.http.port=8088,\ + osgi.console=,\ + jetty.home.bundle=specmate-jettystarter,\ + jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ + tester.dir=testdir,\ + tester.trace=true,\ + tester.continuous=true,\ + osgi.compatibility.bootdelegation=true diff --git a/bundles/specmate-migration-test/src/com/specmate/migration/test/AddObjectTest.java b/bundles/specmate-migration-test/src/com/specmate/migration/test/AddObjectTest.java index 4c3360aa9..ff2981c24 100644 --- a/bundles/specmate-migration-test/src/com/specmate/migration/test/AddObjectTest.java +++ b/bundles/specmate-migration-test/src/com/specmate/migration/test/AddObjectTest.java @@ -47,6 +47,7 @@ protected void checkMigrationPostconditions() throws Exception { doc0.setOwner("Pelle"); doc0.getContents().add(d1); + rootFolder.getContents().add(doc0); transaction.doAndCommit(new IChange() { @Override public Object doChange() throws SpecmateException { @@ -65,7 +66,6 @@ public Object doChange() throws SpecmateException { assertNotNull(diagram); assertTrue(diagram instanceof Diagram); - transaction.close(); } } diff --git a/bundles/specmate-migration-test/src/com/specmate/migration/test/AddSeveralAttributesTest.java b/bundles/specmate-migration-test/src/com/specmate/migration/test/AddSeveralAttributesTest.java index dc4b34abe..f8067ee52 100644 --- a/bundles/specmate-migration-test/src/com/specmate/migration/test/AddSeveralAttributesTest.java +++ b/bundles/specmate-migration-test/src/com/specmate/migration/test/AddSeveralAttributesTest.java @@ -61,6 +61,7 @@ protected void checkMigrationPostconditions() throws Exception { d1.setLinked(true); d1.setBooleanlinked(true); + rootFolder.getContents().add(d1); transaction.doAndCommit(new IChange() { @Override public Object doChange() throws SpecmateException { @@ -68,7 +69,6 @@ public Object doChange() throws SpecmateException { return null; } }); - transaction.close(); } } diff --git a/bundles/specmate-migration-test/src/com/specmate/migration/test/ChangedTypesTest.java b/bundles/specmate-migration-test/src/com/specmate/migration/test/ChangedTypesTest.java index 3443cb220..0396f1790 100644 --- a/bundles/specmate-migration-test/src/com/specmate/migration/test/ChangedTypesTest.java +++ b/bundles/specmate-migration-test/src/com/specmate/migration/test/ChangedTypesTest.java @@ -37,18 +37,19 @@ protected void checkMigrationPostconditions() throws Exception { Sketch s0 = (Sketch) obj; /* - * Contrary to the documentation [1], variables of type byte are *not* stored as - * TINYINT, but as SMALLINT. Hence we omit here the test for the conversion of - * type byte to X. + * Contrary to the documentation [1], variables of type byte are *not* + * stored as TINYINT, but as SMALLINT. Hence we omit here the test for + * the conversion of type byte to X. * * [1] http://www.h2database.com/html/datatypes.html#tinyint_type */ /* - * The following assertions check whether the conversion from X to Y has been - * performed successfully. Note that these are all positive tests. Negative - * test, i.e. tests that cover failure cases, would require a new test model - * with invalid conversions and are currently not implemented. + * The following assertions check whether the conversion from X to Y has + * been performed successfully. Note that these are all positive tests. + * Negative test, i.e. tests that cover failure cases, would require a + * new test model with invalid conversions and are currently not + * implemented. */ // Short to int, long, float and long conversion. diff --git a/bundles/specmate-migration-test/src/com/specmate/migration/test/RenamedAttributeTest.java b/bundles/specmate-migration-test/src/com/specmate/migration/test/RenamedAttributeTest.java index 32a36e1d9..e72755c8e 100644 --- a/bundles/specmate-migration-test/src/com/specmate/migration/test/RenamedAttributeTest.java +++ b/bundles/specmate-migration-test/src/com/specmate/migration/test/RenamedAttributeTest.java @@ -36,15 +36,15 @@ protected void checkMigrationPostconditions() throws Exception { assertTrue(diagram instanceof Diagram); Diagram d0 = (Diagram) diagram; assertTrue(d0.isIstested()); + d0.setIstested(false); - transaction.doAndCommit(new IChange() { + transaction.doAndCommit(new IChange() { @Override public Object doChange() throws SpecmateException { d0.setIstested(false); return null; } }); - transaction.close(); } } diff --git a/bundles/specmate-migration-test/src/com/specmate/migration/test/support/TestMigratorImpl.java b/bundles/specmate-migration-test/src/com/specmate/migration/test/support/TestMigratorImpl.java index 0cef20ba5..a2ca396eb 100644 --- a/bundles/specmate-migration-test/src/com/specmate/migration/test/support/TestMigratorImpl.java +++ b/bundles/specmate-migration-test/src/com/specmate/migration/test/support/TestMigratorImpl.java @@ -157,7 +157,7 @@ private void migrateObjectAdded(Connection connection) throws SpecmateException aAdded.migrateNewBooleanAttribute(document, tested, false); aAdded.migrateNewLongAttribute(document, length, null); aAdded.migrateNewStringAttribute(document, owner, null); - aAdded.migrateNewObjectReference(document, contents); + aAdded.migrateNewStringReference(document, contents); } private void migrateAttributeRenamed(Connection connection) throws SpecmateException { diff --git a/bundles/specmate-migration/src/com/specmate/migration/internal/services/MigratorService.java b/bundles/specmate-migration/src/com/specmate/migration/internal/services/MigratorService.java index 199a6aa4a..09397b383 100644 --- a/bundles/specmate-migration/src/com/specmate/migration/internal/services/MigratorService.java +++ b/bundles/specmate-migration/src/com/specmate/migration/internal/services/MigratorService.java @@ -37,6 +37,7 @@ public class MigratorService implements IMigratorService { private static final int MIGRATOR_TIMEOUT = 1000; private static final String TABLE_PACKAGE_UNITS = "CDO_PACKAGE_UNITS"; private static final String TABLE_PACKAGE_INFOS = "CDO_PACKAGE_INFOS"; + private static final String TABLE_CDO_OBJECTS = "CDO_OBJECTS"; private static final String TABLE_EXTERNAL_REFS = "CDO_EXTERNAL_REFS"; private LogService logService; @@ -143,16 +144,35 @@ private String getTargetModelVersion() { private void updatePackageUnits() throws SpecmateException { removeOldPackageUnits(); writeCurrentPackageUnits(); - updateExternalRefs(); + // external refs table not used anymore + // updateExternalRefs(); + updateCDOObjects(); } - private void updateExternalRefs() throws SpecmateException { + // private void updateExternalRefs() throws SpecmateException { + // Connection connection = dbProviderService.getConnection(); + // PreparedStatement stmt; + // try { + // stmt = connection.prepareStatement( + // "update " + TABLE_EXTERNAL_REFS + " set + // URI=REGEXP_REPLACE(URI,'http://specmate.com/\\d+'," + // + "'http://specmate.com/" + getTargetModelVersion() + "')"); + // stmt.execute(); + // stmt.close(); + // } catch (SQLException e) { + // throw new SpecmateException("Migration: Could not update external references + // table."); + // } + // + // } + + private void updateCDOObjects() throws SpecmateException { Connection connection = this.dbProviderService.getConnection(); PreparedStatement stmt; try { - stmt = connection.prepareStatement( - "update " + TABLE_EXTERNAL_REFS + " set URI=REGEXP_REPLACE(URI,'http://specmate.com/\\d+'," - + "'http://specmate.com/" + getTargetModelVersion() + "')"); + stmt = connection.prepareStatement("update " + TABLE_CDO_OBJECTS + + " set CDO_CLASS=REGEXP_REPLACE(CDO_CLASS,'http://specmate.com/\\d+'," + "'http://specmate.com/" + + getTargetModelVersion() + "')"); stmt.execute(); stmt.close(); } catch (SQLException e) { @@ -222,6 +242,7 @@ private void writeCurrentPackageUnits() throws SpecmateException { } private void performMigration(String fromVersion) throws SpecmateException { + String currentModelVersion = fromVersion; String targetModelVersion = getTargetModelVersion(); diff --git a/bundles/specmate-model-gen/bnd.bnd b/bundles/specmate-model-gen/bnd.bnd index af926c2a5..d798b8601 100644 --- a/bundles/specmate-model-gen/bnd.bnd +++ b/bundles/specmate-model-gen/bnd.bnd @@ -25,16 +25,14 @@ Private-Package: \ com.specmate.usermodel.impl,\ com.specmate.model.processes.impl,\ com.specmate.model.history.impl --buildpath: \ - org.eclipse.emf,\ - org.eclipse.emf.ecore,\ - org.eclipse.emf.common,\ - org.eclipse.emf.cdo,\ - org.eclipse.emf.cdo.common,\ - org.eclipse.net4j.util,\ - org.eclipse.osgi,\ - org.eclipse.emf.edit,\ - org.eclipse.emf.cdo.edit,\ +-buildpath: \ + org.eclipse.emf,\ + org.eclipse.emf.ecore,\ + org.eclipse.emf.common,\ + org.eclipse.emf.cdo,\ + org.eclipse.emf.cdo.common,\ + org.eclipse.net4j.util,\ + org.eclipse.osgi,\ org.eclipse.core.runtime diff --git a/bundles/specmate-model-generation/generated/buildfiles b/bundles/specmate-model-generation/generated/buildfiles deleted file mode 100644 index 47666f55f..000000000 --- a/bundles/specmate-model-generation/generated/buildfiles +++ /dev/null @@ -1 +0,0 @@ -/Users/Dominik/Documents/Arbeit/Qualicen/Specmate/bundles/specmate-model-generation/generated/specmate-model-generation.jar diff --git a/bundles/specmate-model-generators-typescript/bin/.gitignore b/bundles/specmate-model-generators-typescript/bin/.gitignore deleted file mode 100644 index c2d9872a1..000000000 --- a/bundles/specmate-model-generators-typescript/bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/com/ diff --git a/bundles/specmate-model-generators-typescript/src/com/specmate/model/generators/typescript/GenerateAngular.java b/bundles/specmate-model-generators-typescript/src/com/specmate/model/generators/typescript/GenerateAngular.java index 4143a40d1..6dbc7a093 100644 --- a/bundles/specmate-model-generators-typescript/src/com/specmate/model/generators/typescript/GenerateAngular.java +++ b/bundles/specmate-model-generators-typescript/src/com/specmate/model/generators/typescript/GenerateAngular.java @@ -396,14 +396,20 @@ public void registerResourceFactories(ResourceSet resourceSet) { /* * TODO If you need additional resource factories registrations, you can register them here. the following line - * (in comment) is an example of the resource factory registration for UML. + * (in comment) is an example of the resource factory registration. * * If you want to use the generator in stand alone, the resource factory registration will be required. * * To learn more about the registration of Resource Factories, have a look at the Acceleo documentation (Help -> Help Contents). */ - // resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION, UMLResource.Factory.INSTANCE); + // resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(XyzResource.FILE_EXTENSION, XyzResource.Factory.INSTANCE); + + /* + * Some metamodels require a very complex setup for standalone usage. For example, if you want to use a generator + * targetting UML models in standalone, you NEED to use the following: + */ + // UMLResourcesUtil.init(resourceSet) } } diff --git a/bundles/specmate-model-support/.project b/bundles/specmate-model-support/.project index 0606ab43b..0c22d7953 100644 --- a/bundles/specmate-model-support/.project +++ b/bundles/specmate-model-support/.project @@ -25,6 +25,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.jdt.core.javanature diff --git a/bundles/specmate-nlp/bin/com/specmate/nlp/api/packageinfo b/bundles/specmate-nlp/bin/com/specmate/nlp/api/packageinfo deleted file mode 100644 index 60c8a9215..000000000 --- a/bundles/specmate-nlp/bin/com/specmate/nlp/api/packageinfo +++ /dev/null @@ -1 +0,0 @@ -version 1.0.0 diff --git a/bundles/specmate-nlp/bin/com/specmate/nlp/matcher/packageinfo b/bundles/specmate-nlp/bin/com/specmate/nlp/matcher/packageinfo deleted file mode 100644 index 60c8a9215..000000000 --- a/bundles/specmate-nlp/bin/com/specmate/nlp/matcher/packageinfo +++ /dev/null @@ -1 +0,0 @@ -version 1.0.0 diff --git a/bundles/specmate-nlp/bin/com/specmate/nlp/packageinfo b/bundles/specmate-nlp/bin/com/specmate/nlp/packageinfo deleted file mode 100644 index 60c8a9215..000000000 --- a/bundles/specmate-nlp/bin/com/specmate/nlp/packageinfo +++ /dev/null @@ -1 +0,0 @@ -version 1.0.0 diff --git a/bundles/specmate-nlp/bin/com/specmate/nlp/util/packageinfo b/bundles/specmate-nlp/bin/com/specmate/nlp/util/packageinfo deleted file mode 100644 index 60c8a9215..000000000 --- a/bundles/specmate-nlp/bin/com/specmate/nlp/util/packageinfo +++ /dev/null @@ -1 +0,0 @@ -version 1.0.0 diff --git a/bundles/specmate-persistency-api/src/com/specmate/persistency/ITransaction.java b/bundles/specmate-persistency-api/src/com/specmate/persistency/ITransaction.java index bf707e7b3..8b3ca163b 100644 --- a/bundles/specmate-persistency-api/src/com/specmate/persistency/ITransaction.java +++ b/bundles/specmate-persistency-api/src/com/specmate/persistency/ITransaction.java @@ -20,8 +20,9 @@ public interface ITransaction extends IView { public static final String COMMENT_DATA_SEPARATOR = "|"; public static final String COMMENT_RECORD_SEPARATOR = ";"; - /** Rolls back changes made in this transaction since the last commit */ - public void rollback(); + /** Rolls back changes made in this transaction since the last commit + * @throws SpecmateException */ + public void rollback() throws SpecmateException; /** Closes the transaction. */ @Override diff --git a/bundles/specmate-persistency-cdo/bnd.bnd b/bundles/specmate-persistency-cdo/bnd.bnd index 607803570..dd8210876 100644 --- a/bundles/specmate-persistency-cdo/bnd.bnd +++ b/bundles/specmate-persistency-cdo/bnd.bnd @@ -6,12 +6,10 @@ Bundle-Version: 0.0.0.${tstamp} org.eclipse.emf.cdo,\ org.eclipse.net4j,\ org.eclipse.net4j.util,\ - org.eclipse.net4j.jvm,\ org.eclipse.net4j.tcp,\ org.eclipse.osgi.services,\ org.eclipse.emf.common,\ org.eclipse.emf.ecore,\ - org.eclipse.emf.edit,\ org.eclipse.emf.cdo.server.net4j,\ org.eclipse.emf.cdo.common,\ org.eclipse.equinox.common,\ diff --git a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyService.java b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyService.java index 3ce9f89e4..21a96e85a 100644 --- a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyService.java +++ b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyService.java @@ -1,5 +1,11 @@ package com.specmate.persistency.cdo.internal; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,11 +15,15 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.CDOCommonRepository.State; import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; +import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.net4j.CDONet4jSession; +import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; import org.eclipse.emf.cdo.net4j.CDONet4jUtil; import org.eclipse.emf.cdo.net4j.CDOSessionRecoveryEvent; import org.eclipse.emf.cdo.net4j.ReconnectingCDOSessionConfiguration; @@ -23,12 +33,14 @@ import org.eclipse.emf.cdo.util.CommitException; import org.eclipse.emf.cdo.view.CDOAdapterPolicy; import org.eclipse.emf.cdo.view.CDOView; +import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.spi.cdo.CDOMergingConflictResolver; +import org.eclipse.emf.spi.cdo.DefaultCDOMerger; import org.eclipse.net4j.Net4jUtil; import org.eclipse.net4j.connector.IConnector; import org.eclipse.net4j.tcp.TCPUtil; @@ -72,6 +84,15 @@ @Component(service = IPersistencyService.class, configurationPolicy = ConfigurationPolicy.REQUIRE, configurationPid = CDOPersistencyServiceConfig.PID) public class CDOPersistencyService implements IPersistencyService, IListener { + /** The name of the CDO main branch */ + private static final String MAIN_BRANCH_NAME = "MAIN"; + + /** Prefix of the name of all CDO offline branches */ + private static final String OFFLINE_BRANCH_PREFIX = "Offline"; + + /** Name of the file where we store the last branch id */ + private static final String LAST_BRANCH_ID_FILE_NAME = "lastBranchId"; + /** The CDO container */ private IManagedContainer container; @@ -128,8 +149,16 @@ public class CDOPersistencyService implements IPersistencyService, IListener { private String cdoPassword; + protected CDOBranch currentOfflineBranch; + + private Object offlineBranchChanging = new Object(); + + private String recoveryFolder; + + private boolean wasOffline = true; + @Activate - public void activate(Map properties) throws SpecmateException { + public void activate(Map properties) throws SpecmateException, SpecmateValidationException { readConfig(properties); this.transactionGauge = metricsService.createGauge("Transactions", "The number of open transactions"); start(); @@ -140,12 +169,13 @@ public void deactivate() { this.shutdown(); } - private void readConfig(Map properties) throws SpecmateException { + private void readConfig(Map properties) throws SpecmateValidationException, SpecmateInternalException { this.repositoryName = (String) properties.get(CDOPersistencyServiceConfig.KEY_REPOSITORY_NAME); this.resourceName = (String) properties.get(CDOPersistencyServiceConfig.KEY_RESOURCE_NAME); this.hostAndPort = (String) properties.get(CDOPersistencyServiceConfig.KEY_SERVER_HOST_PORT); this.cdoUser = (String) properties.get(CDOPersistencyServiceConfig.KEY_CDO_USER); this.cdoPassword = (String) properties.get(CDOPersistencyServiceConfig.KEY_CDO_PASSWORD); + this.recoveryFolder = (String) properties.get(CDOPersistencyServiceConfig.KEY_RECOVERY_FOLDER); if (StringUtils.isEmpty(this.repositoryName)) { throw new SpecmateInternalException(ErrorCode.CONFIGURATION, "Repository name is empty."); @@ -164,16 +194,29 @@ private void readConfig(Map properties) throws SpecmateException if (StringUtil.isEmpty(this.cdoPassword)) { throw new SpecmateInternalException(ErrorCode.CONFIGURATION, "No CDO password given"); } + if (this.recoveryFolder != null && !Files.isDirectory(Paths.get(this.recoveryFolder))) { + throw new SpecmateValidationException("Revovery folder " + this.recoveryFolder + " not found"); + } } @Override public synchronized void start() throws SpecmateException { startPersistency(); + loadLastBranch(); + mergeIfNecessary(); updateOpenViews(); openEventView(); this.active = true; } + private void mergeIfNecessary() { + if (currentOfflineBranch != null && currentOfflineBranch.getName().contains(OFFLINE_BRANCH_PREFIX)) { + if (session.getRepositoryInfo().getState().equals(State.ONLINE)) { + merge(session, State.ONLINE); + } + } + } + private void openEventView() throws SpecmateException { this.eventView = openCDOView(); } @@ -216,15 +259,18 @@ private void createContainer() { } private void createSession() { + connector = TCPUtil.getConnector(container, this.hostAndPort); + PasswordCredentialsProvider credentialsProvider = new PasswordCredentialsProvider(this.cdoUser, this.cdoPassword); - ReconnectingCDOSessionConfiguration configuration = CDONet4jUtil - .createReconnectingSessionConfiguration(this.hostAndPort, this.repositoryName, container); - configuration.setHeartBeatEnabled(false); - configuration.setConnectorTimeout(60000); + CDONet4jSessionConfiguration configuration = CDONet4jUtil.createNet4jSessionConfiguration(); + // configuration.setHeartBeatEnabled(true); + // configuration.setConnectorTimeout(60000); configuration.setSignalTimeout(60000); configuration.setCredentialsProvider(credentialsProvider); + configuration.setConnector(connector); + configuration.setRepositoryName(this.repositoryName); configuration.setPassiveUpdateEnabled(true); configuration.setPassiveUpdateMode(PassiveUpdateMode.ADDITIONS); session = configuration.openNet4jSession(); @@ -245,23 +291,127 @@ public void notifyEvent(final IEvent event) { } } }); + + session.addListener(new IListener() { + + @Override + public void notifyEvent(IEvent event) { + if (event instanceof CDOCommonRepository.StateChangedEvent) { + CDOCommonRepository.StateChangedEvent e = (CDOCommonRepository.StateChangedEvent) event; + State newState = e.getNewState(); + logService.log(LogService.LOG_INFO, "State changed to " + newState); + merge(session, newState); + } + } + + }); + registerPackages(); createModelResource(); } + private void merge(final CDONet4jSession session, State newState) { + if (newState == State.ONLINE && wasOffline) { + CDOTransaction newTransaction = null; + try { + if (this.currentOfflineBranch != null && !this.currentOfflineBranch.getName().equals(MAIN_BRANCH_NAME)) { + newTransaction = session.openTransaction(session.getBranchManager().getMainBranch()); + newTransaction.merge(CDOPersistencyService.this.currentOfflineBranch, + new DefaultCDOMerger.PerFeature.ManyValued()); + newTransaction.commit(); + } + setAllTransactionsTo(session.getBranchManager().getMainBranch()); + this.currentOfflineBranch = null; + } catch (CommitException ex) { + ex.printStackTrace(); + } finally { + if (newTransaction != null) { + newTransaction.close(); + } + wasOffline = false; + } + } else if (newState == State.OFFLINE) { + wasOffline = true; + } + } + + private void setAllTransactionsTo(CDOBranch currentBranch) { + logService.log(LogService.LOG_INFO, "Switching to branch " + currentBranch.getName()); + persistBranchId(currentBranch); + for (TransactionImpl transaction : openTransactions) { + transaction.getInternalTransaction().setBranch(currentBranch); + } + for (ViewImpl view : openViews) { + view.getInternalView().setBranch(currentBranch); + } + } + + private void persistBranchId(CDOBranch currentBranch) { + FileOutputStream fos = null; + OutputStreamWriter writer = null; + try { + fos = new FileOutputStream(getFullBranchIdFilePath(), false); + writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8); + writer.write(Integer.toString(currentBranch.getID())); + } catch (IOException e) { + logService.log(LogService.LOG_DEBUG, "Could not persist branch id to file " + LAST_BRANCH_ID_FILE_NAME); + } finally { + try { + if (writer != null) { + writer.close(); + } + if (fos != null) { + fos.close(); + } + } catch (IOException e) { + + } + } + } + + private void loadLastBranch() { + Integer lastBranchId = loadBranchId(); + if (lastBranchId != null) { + this.currentOfflineBranch = session.getBranchManager().getBranch(lastBranchId); + logService.log(LogService.LOG_INFO, "Restoring last offline branch " + this.currentOfflineBranch.getName()); + setAllTransactionsTo(currentOfflineBranch); + } + } + + private String getFullBranchIdFilePath() { + String prefix = this.recoveryFolder != null ? recoveryFolder + "/" : ""; + return prefix + LAST_BRANCH_ID_FILE_NAME; + } + + private Integer loadBranchId() { + byte[] encoded; + try { + encoded = Files.readAllBytes(Paths.get(getFullBranchIdFilePath())); + } catch (IOException e) { + return null; + } + return new Integer(new String(encoded, StandardCharsets.UTF_8)); + } + private void createModelResource() { String resourceName = this.resourceName; CDOTransaction transaction = session.openTransaction(); + transaction.enableDurableLocking(); transaction.getOrCreateResource(resourceName); try { - transaction.commit(); + if (transaction.isDirty()) { + transaction.commit(); + } } catch (CommitException e) { logService.log(LogService.LOG_ERROR, "Could not create resource " + resourceName, e); + } finally { + transaction.close(); } } private void registerPackages() { CDOTransaction transaction = session.openTransaction(); + transaction.enableDurableLocking(); CDOResource resource = transaction.getOrCreateResource("dummy"); for (EPackage pack : packageProvider.getPackages()) { if (session.getPackageRegistry().getEPackage(pack.getNsURI()) == null) { @@ -272,11 +422,14 @@ private void registerPackages() { } } try { - transaction.commit(); + if (transaction.isDirty()) { + transaction.commit(); + } } catch (Exception e) { logService.log(LogService.LOG_ERROR, "Could not commit packages to dummy resource", e); + } finally { + transaction.close(); } - transaction.close(); } private void installListener() { @@ -293,7 +446,7 @@ public ITransaction openTransaction(boolean attachCommitListeners) throws Specma return openTransaction(attachCommitListeners, this.resourceName); } - private ITransaction openTransaction(boolean attachCommitListeners, String alterantiveResourceName) + public ITransaction openTransaction(boolean attachCommitListeners, String alterantiveResourceName) throws SpecmateException { if (!this.active) { throw new SpecmateInternalException(ErrorCode.PERSISTENCY, @@ -303,9 +456,13 @@ private ITransaction openTransaction(boolean attachCommitListeners, String alter TransactionImpl transaction = new TransactionImpl(this, cdoTransaction, alterantiveResourceName, logService, statusService, attachCommitListeners ? listeners : Collections.emptyList()); - this.openTransactions.add(transaction); + synchronized (this.offlineBranchChanging) { + if (currentOfflineBranch != null) { + cdoTransaction.setBranch(currentOfflineBranch); + } + this.openTransactions.add(transaction); + } this.transactionGauge.inc(); - return transaction; } @@ -323,7 +480,13 @@ public IView openView() throws SpecmateException { CDOView cdoView = openCDOView(); ViewImpl view = new ViewImpl(this, cdoView, this.resourceName, logService); - this.openViews.add(view); + synchronized (this.offlineBranchChanging) { + if (currentOfflineBranch != null) { + cdoView.setBranch(currentOfflineBranch); + } + this.openViews.add(view); + } + this.transactionGauge.inc(); return view; } @@ -334,16 +497,36 @@ public void closedView(ViewImpl viewImpl) { } /* package */CDOTransaction openCDOTransaction() throws SpecmateException { + CDOTransaction transaction = session.openTransaction(); + + transaction.enableDurableLocking(); transaction.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); transaction.options().setInvalidationNotificationEnabled(true); transaction.options().addConflictResolver(new CDOMergingConflictResolver()); + transaction.options().setCommitInfoTimeout(20000); + + transaction.addListener(new IListener() { + + @Override + public void notifyEvent(IEvent event) { + if (event instanceof CDOViewTargetChangedEvent) { + CDOViewTargetChangedEvent bce = (CDOViewTargetChangedEvent) event; + CDOBranch branch = bce.getBranchPoint().getBranch(); + handleBranchChange(branch); + } + + } + }); + logService.log(LogService.LOG_DEBUG, "Transaction initialized: " + transaction.getViewID()); return transaction; + } /* package */CDOView openCDOView() throws SpecmateException { CDOView view = session.openView(); + view.enableDurableLocking(); view.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); view.options().setInvalidationNotificationEnabled(true); logService.log(LogService.LOG_DEBUG, "View initialized: " + view.getViewID()); @@ -449,6 +632,7 @@ private Optional resolveUri(CDOView eventView, CDOID id, int version) { CDORevision revision = getSession().getRevisionManager().getRevisionByVersion(id, eventView.getBranch().getVersion(version), 0, true); CDOView view = getSession().openView(revision.getTimeStamp()); + view.enableDurableLocking(); Optional uri = resolveUri(view, id); view.close(); return uri; @@ -525,4 +709,26 @@ public void setMetricsService(IMetricsService metricsService) { public void setLogService(LogService logService) { this.logService = logService; } + + private void handleBranchChange(CDOBranch branch) { + synchronized (this.offlineBranchChanging) { + String currentOfflineBranchName = "nobranch"; + if (currentOfflineBranch != null) { + currentOfflineBranchName = currentOfflineBranch.getName(); + } + + if (branch.getName().equals(currentOfflineBranchName)) { + return; + } + if (branch.getName().equals(MAIN_BRANCH_NAME)) { + currentOfflineBranch = null; + return; + } + if (branch.getName().contains(OFFLINE_BRANCH_PREFIX)) { + logService.log(LogService.LOG_INFO, "New offline branch created: " + branch.getName()); + this.currentOfflineBranch = branch; + setAllTransactionsTo(this.currentOfflineBranch); + } + } + } } diff --git a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyServiceConfig.java b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyServiceConfig.java index 50b92e386..01797de53 100644 --- a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyServiceConfig.java +++ b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/CDOPersistencyServiceConfig.java @@ -34,6 +34,7 @@ public class CDOPersistencyServiceConfig { public static final String KEY_CDO_USER = "cdo.user"; public static final String KEY_CDO_PASSWORD = "cdo.password"; public static final String KEY_SERVER_HOST_PORT = "cdo.serverHostAndPort"; + public static final String KEY_RECOVERY_FOLDER = "cdo.recoveryFolder"; private ConfigurationAdmin configurationAdmin; private IConfigService configService; private LogService logService; @@ -47,6 +48,7 @@ public class CDOPersistencyServiceConfig { private Configuration configuration; private String cdoUser; private String cdoPassword; + private String recoveryFolder; /** * Configures the CDO persistency service. @@ -60,6 +62,7 @@ private void activate() throws SpecmateException { this.cdoUser = this.configService.getConfigurationProperty(KEY_CDO_USER); this.cdoPassword = this.configService.getConfigurationProperty(KEY_CDO_PASSWORD); this.host = this.configService.getConfigurationProperty(KEY_SERVER_HOST_PORT); + this.recoveryFolder = configService.getConfigurationProperty(KEY_RECOVERY_FOLDER); this.connected = false; String[] hostport = StringUtils.split(this.host, ":"); if (hostport == null || !(hostport.length == 2)) { @@ -114,13 +117,14 @@ private void startMonitoringThread() { private void registerConfiguration() throws SpecmateException { Dictionary properties = new Hashtable<>(); if (!StringUtil.isEmpty(this.specmateRepository) && !StringUtil.isEmpty(this.specmateResource) - && !StringUtil.isEmpty(this.host) && !StringUtil.isEmpty(this.cdoUser) + && !StringUtil.isEmpty(this.host) && !StringUtil.isEmpty(this.cdoUser) && !StringUtil.isEmpty(this.cdoPassword)) { properties.put(KEY_REPOSITORY_NAME, this.specmateRepository); properties.put(KEY_RESOURCE_NAME, this.specmateResource); properties.put(KEY_SERVER_HOST_PORT, this.host); properties.put(KEY_CDO_USER, this.cdoUser); properties.put(KEY_CDO_PASSWORD, this.cdoPassword); + OSGiUtil.saveAddToProperties(properties, KEY_RECOVERY_FOLDER, this.recoveryFolder); this.logService.log(LogService.LOG_DEBUG, "Configuring CDO with:\n" + OSGiUtil.configDictionaryToString(properties)); this.configuration = OSGiUtil.configureService(this.configurationAdmin, PID, properties); diff --git a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/TransactionImpl.java b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/TransactionImpl.java index 2210bf818..81324aa9c 100644 --- a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/TransactionImpl.java +++ b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/TransactionImpl.java @@ -14,6 +14,7 @@ import org.eclipse.emf.cdo.util.CommitException; import org.eclipse.emf.cdo.view.CDOQuery; import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; import org.osgi.service.log.LogService; import com.specmate.administration.api.IStatusService; @@ -61,7 +62,7 @@ public TransactionImpl(CDOPersistencyService persistency, CDOTransaction transac this.transaction = transaction; this.logService = logService; this.statusService = statusService; - changeListeners = listeners; + this.changeListeners = listeners; validators = new ArrayList<>(); validators.add(new IDValidator()); @@ -101,27 +102,45 @@ private void commit(T object) throws SpecmateException { SpecmateEcoreUtil.unsetAllReferences(transaction.getObject(id.getID())); } } catch (SpecmateValidationException s) { - transaction.rollback(); - logService.log(LogService.LOG_ERROR, "Error during commit due to invalid data.", s); - throw s; + try { + rollback(); + } catch (SpecmateException rbe) { + } + throw (new SpecmateInternalException(ErrorCode.PERSISTENCY, "Error while preparing commit, transaction rolled back", s)); } setMetadata(object, detachedObjects); transaction.commit(); } catch (CommitException e) { - transaction.rollback(); - logService.log(LogService.LOG_DEBUG, "Error during commit, transaction rolled back.", e); + try { + transaction.rollback(); + } catch (Exception ex) { + } + String message = "Error during commit, transaction rolled back"; + logService.log(LogService.LOG_ERROR, message); + throw new SpecmateInternalException(ErrorCode.PERSISTENCY, "Error during commit, transaction rolled back.", + e); + + } catch (TimeoutRuntimeException e) { + try { + transaction.rollback(); + } catch (Exception ex) { + } + String message = "Timeout occured while comitting, probably too high load. Try setting up the timeout or reduce the load."; + logService.log(LogService.LOG_ERROR, message); throw new SpecmateInternalException(ErrorCode.PERSISTENCY, "Error during commit, transaction rolled back.", e); } } @Override - public T doAndCommit(IChange change) throws SpecmateException { + public T doAndCommit(IChange change) throws SpecmateException, SpecmateValidationException { int maxAttempts = 10; boolean success = false; int attempts = 1; T result = null; + SpecmateException lastException = null; + while (!success && attempts <= maxAttempts) { result = change.doChange(); @@ -129,6 +148,7 @@ public T doAndCommit(IChange change) throws SpecmateException { try { commit(result); } catch (SpecmateInternalException e) { + lastException = e; try { Thread.sleep(attempts * 50); } catch (InterruptedException ie) { @@ -221,7 +241,6 @@ protected void newObject(CDOID id, String className, Map query(String queryString, Object context) { - CDOQuery cdoQuery = transaction.createQuery("ocl", queryString, context); + CDOQuery cdoQuery = this.transaction.createQuery("ocl", queryString, context); return cdoQuery.getResult(); } diff --git a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/ViewImpl.java b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/ViewImpl.java index a5c2e9aec..52f08f2ac 100644 --- a/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/ViewImpl.java +++ b/bundles/specmate-persistency-cdo/src/com/specmate/persistency/cdo/internal/ViewImpl.java @@ -66,4 +66,8 @@ public void close() { persistency.closedView(this); } + public CDOView getInternalView() { + return view; + } + } diff --git a/bundles/specmate-std-env/.options b/bundles/specmate-std-env/.options new file mode 100644 index 000000000..fa0b4a609 --- /dev/null +++ b/bundles/specmate-std-env/.options @@ -0,0 +1,133 @@ + javassist/debug=true + javax.annotation-api/debug=true + javax.validation.api/debug=true + javax.ws.rs-api/debug=true + jul.to.slf4j/debug=true + log4j.over.slf4j/debug=true + org.apache.felix.gogo.command/debug=true + org.apache.felix.gogo.runtime/debug=true + org.apache.felix.gogo.shell/debug=true + org.eclipse.core.contenttype/debug=true + org.eclipse.core.jobs/debug=true + org.eclipse.core.runtime/debug=true + org.eclipse.emf.ecore.change/debug=true + org.eclipse.equinox.app/debug=true + org.eclipse.equinox.cm/debug=true + org.eclipse.equinox.common/debug=true + org.eclipse.equinox.event/debug=true + org.eclipse.equinox.log/debug=true + org.eclipse.equinox.metatype/debug=true + org.eclipse.equinox.preferences/debug=true + org.eclipse.equinox.registry/debug=true + org.eclipse.osgi.services/debug=true + org.glassfish.hk2.api/debug=true + org.glassfish.hk2.external.aopalliance-repackaged/debug=true + org.glassfish.hk2.external.javax.inject/debug=true + org.glassfish.hk2.locator/debug=true + org.glassfish.hk2.osgi-resource-locator/debug=true + org.glassfish.hk2.utils/debug=true + org.glassfish.jersey.bundles.repackaged.jersey-guava/debug=true + org.glassfish.jersey.containers.jersey-container-servlet/debug=true + org.glassfish.jersey.containers.jersey-container-servlet-core/debug=true + org.glassfish.jersey.core.jersey-client/debug=true + org.glassfish.jersey.core.jersey-common/debug=true + org.glassfish.jersey.core.jersey-server/debug=true + org.glassfish.jersey.media.jersey-media-sse/debug=true + org.json/debug=true + org.slf4j.api/debug=true + slf4j.api/debug=true + specmate-common/debug=true + specmate-emfjson/debug=true + specmate-emfrest/debug=true + specmate-logging/debug=true + specmate-logging-slf4j/debug=true + specmate-logging-slf4j-julbridge/debug=true + specmate-model-gen/debug=true + specmate-persistency-api/debug=true + specmate-persistency-cdo/debug=true + specmate-model-support/debug=true + specmate-ui-core/debug=true + specmate-dummy-data/debug=true + specmate-config/debug=true + specmate-connectors/debug=true + org.eclipse.emf.cdo.ecore.retrofit/debug=true + org.eclipse.emf.common/debug=true + org.eclipse.emf.ecore/debug=true + org.eclipse.emf.ecore.xmi/debug=true + com.google.guava/debug=true + specmate-hp-connector/debug=true + specmate-testspecification/debug=true + org.apache.felix.scr/debug=true + org.apache.commons.fileupload/debug=true + org.apache.commons.io/debug=true + org.apache.felix.webconsole/debug=true + org.eclipse.equinox.http.servlet/debug=true + org.eclipse.jetty.deploy/debug=true + org.eclipse.jetty.http/debug=true + org.eclipse.jetty.io/debug=true + org.eclipse.jetty.osgi-servlet-api/debug=true + org.eclipse.jetty.osgi.boot/debug=true + org.eclipse.jetty.osgi.httpservice/debug=true + org.eclipse.jetty.rewrite/debug=true + org.eclipse.jetty.security/debug=true + org.eclipse.jetty.server/debug=true + org.eclipse.jetty.servlet/debug=true + org.eclipse.jetty.util/debug=true + org.eclipse.jetty.webapp/debug=true + org.eclipse.jetty.xml/debug=true + org.eclipse.equinox.http.jetty/debug=true + org.eclipse.jetty.continuation/debug=true + org.eclipse.jetty.http/debug=true + org.eclipse.jetty.io/debug=true + org.eclipse.jetty.security/debug=true + org.eclipse.jetty.server/debug=true + org.eclipse.jetty.servlet/debug=true + org.eclipse.jetty.util/debug=true + specmate-jettystarter/debug=true + lpg.runtime.java/debug=true + org.eclipse.ocl/debug=true + org.eclipse.ocl.common/debug=true + org.eclipse.ocl.ecore/debug=true + org.sat4j.core/debug=true + org.jgrapht.core/debug=true + org.apache.commons.cli/debug=true + org.sat4j.maxsat/debug=true + org.sat4j.pb/debug=true + specmate-file-connector/debug=true + org.apache.servicemix.bundles.jakarta-regexp/debug=true + org.apache.servicemix.bundles.lucene/debug=true + org.apache.servicemix.bundles.lucene-queries/debug=true + org.apache.servicemix.bundles.lucene-queryparser/debug=true + org.apache.servicemix.bundles.lucene-sandbox/debug=true + specmate-search/debug=true + specmate-migration/debug=true + specmate-administration/debug=true + specmate-emfrest-api/debug=true + specmate-trello-connector/debug=true + specmate-auth-api/debug=true + specmate-auth/debug=true + specmate-dbprovider-api/debug=true + specmate-config-api/debug=true + com.diffplug.osgi.extension.sun.misc/debug=true + io.prometheus.simpleclient/debug=true + io.prometheus.simpleclient_common/debug=true + io.prometheus.simpleclient_servlet/debug=true + specmate-metrics/debug=true + io.prometheus.simpleclient_hotspot/debug=true + specmate-cdo-server/debug=true + org.apache.commons.lang3/debug=true + org.eclipse.emf.cdo/debug=true + org.eclipse.emf.cdo.server.net4j/debug=true + org.eclipse.emf.cdo.server.ocl/debug=true + org.eclipse.net4j/debug=true + org.eclipse.net4j.db.jdbc/debug=true + org.eclipse.net4j.tcp/debug=true + org.eclipse.net4j.util/debug=true + org.eclipse.emf.cdo.common/debug=true + org.eclipse.emf.cdo.server/debug=true + org.eclipse.emf.cdo.net4j/debug=true + org.eclipse.emf.cdo.server.db/debug=true + org.eclipse.net4j.db.oracle/debug=true + org.objectweb.asm.all.debug/debug=true + specmate-dbprovider-oracle/debug=true + org.eclipse.net4j.db/debug=true diff --git a/bundles/specmate-std-env/dev-specmate-all-oracle.bndrun b/bundles/specmate-std-env/dev-specmate-all-oracle.bndrun index 0ad68b747..f0d627c4c 100644 --- a/bundles/specmate-std-env/dev-specmate-all-oracle.bndrun +++ b/bundles/specmate-std-env/dev-specmate-all-oracle.bndrun @@ -7,6 +7,7 @@ osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ osgi.identity;filter:='(osgi.identity=specmate-common)',\ osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ osgi.identity;filter:='(osgi.identity=specmate-logging)',\ @@ -46,8 +47,13 @@ osgi.identity;filter:='(osgi.identity=specmate-dbprovider-api)',\ osgi.identity;filter:='(osgi.identity=specmate-jira-connector)',\ osgi.identity;filter:='(osgi.identity=specmate-dbprovider-oracle)',\ - osgi.identity;filter:='(osgi.identity=specmate-nlp)',\ - osgi.identity;filter:='(osgi.identity=specmate-model-generation)',\ + osgi.identity;filter:='(osgi.identity=specmate-config-api)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.net4j.db)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.emf.cdo.server.db)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.net4j.db.jdbc)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.net4j.db.oracle)',\ + osgi.identity;filter:='(osgi.identity=specmate-nlp)',\ + osgi.identity;filter:='(osgi.identity=specmate-model-generation)',\ bnd.identity;id='log4j' -runbundles: \ javassist;version='[3.18.1,3.18.2)',\ @@ -55,13 +61,13 @@ javax.validation.api;version='[1.1.0,1.1.1)',\ javax.ws.rs-api;version='[2.0.1,2.0.2)',\ jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ @@ -86,6 +92,7 @@ org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ org.json;version=snapshot,\ org.slf4j.api;version='[1.7.2,1.7.3)',\ + slf4j.api;version='[1.7.12,1.7.13)',\ specmate-common;version=snapshot,\ specmate-emfjson;version=snapshot,\ specmate-emfrest;version=snapshot,\ @@ -100,18 +107,6 @@ specmate-dummy-data;version=snapshot,\ specmate-config;version=snapshot,\ specmate-connectors;version=snapshot,\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.cdo.net4j;version='[4.1.400,4.1.401)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ - org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ com.google.guava;version='[21.0.0,21.0.1)',\ specmate-hp-connector;version=snapshot,\ specmate-testspecification;version=snapshot,\ @@ -143,7 +138,6 @@ org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ specmate-jettystarter;version=snapshot,\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ @@ -166,9 +160,6 @@ specmate-auth-api;version=snapshot,\ specmate-auth;version=snapshot,\ specmate-dbprovider-api;version=snapshot,\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ - specmate-config-api;version=snapshot,\ com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ @@ -176,45 +167,36 @@ specmate-metrics;version=snapshot,\ io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ specmate-cdo-server;version=snapshot,\ - org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ - specmate-jira-connector;version=snapshot,\ + specmate-dbprovider-oracle;version=snapshot,\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.apache.commons.lang3;version='[3.3.2,3.3.3)',\ + org.eclipse.net4j.db;version=snapshot,\ + specmate-config-api;version=snapshot,\ specmate-rest;version=snapshot,\ specmate-scheduler;version=snapshot,\ - org.eclipse.net4j.db.oracle;version='[1.1.200,1.1.201)',\ - org.objectweb.asm.all.debug;version='[5.0.2,5.0.3)',\ - specmate-dbprovider-oracle;version=snapshot,\ - com.google.guava;version='[18.0.0,18.0.1)',\ - org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ - com.ibm.icu;version='[63.1.0,63.1.1)',\ - io.reactivex.rxjava2.rxjava;version='[2.2.3,2.2.4)',\ - it.unimi.dsi.fastutil;version='[8.2.2,8.2.3)',\ - javax.money.api;version='[1.0.1,1.0.2)',\ - joda-time;version='[2.10.1,2.10.2)',\ - org.apache.commons.commons-compress;version='[1.18.0,1.18.1)',\ - org.apache.commons.commons-pool2;version='[2.6.0,2.6.1)',\ - org.apache.commons.lang;version='[2.6.0,2.6.1)',\ - org.apache.ivy;version='[2.4.0,2.4.1)',\ - org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ - specmate-nlp;version=snapshot,\ - specmate-model-generation;version=snapshot,\ - com.atlassian.fugue;version='[2.7.0,2.7.1)',\ - com.atlassian.sal.api;version='[3.0.7,3.0.8)',\ - com.atlassian.util.concurrent.atlassian-util-concurrent;version='[3.0.0,3.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ - org.apache.commons.logging;version='[1.1.1,1.1.2)',\ - org.apache.httpcomponents.httpasyncclient;version='[4.1.3,4.1.4)',\ - org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\ - org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\ - org.apache.servicemix.bundles.jcip-annotations;version='[1.0.0,1.0.1)',\ - org.apache.servicemix.bundles.spring-beans;version='[4.1.7,4.1.8)',\ - org.apache.servicemix.bundles.spring-core;version='[4.1.7,4.1.8)',\ - org.codehaus.jettison.jettison;version='[1.1.0,1.1.1)',\ - log4j;version='[1.2.17,1.2.18)' + com.sun.jersey.core;version='[1.19.0,1.19.1)',\ + com.sun.jersey.jersey-server;version='[1.19.0,1.19.1)',\ + javax.el;version='[2.2.0,2.2.1)',\ + javax.ws.rs.jsr311-api;version='[1.1.1,1.1.2)',\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.db.oracle;version=snapshot,\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ + specmate-jira-connector;version=snapshot -runproperties: \ - jetty.http.port=8080,\ + jetty.http.port=8081,\ jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ osgi.console=,\ jetty.home.bundle=specmate-jettystarter,\ diff --git a/bundles/specmate-std-env/dev-specmate-all.bndrun b/bundles/specmate-std-env/dev-specmate-all.bndrun index 70e29885f..bffd0daa8 100644 --- a/bundles/specmate-std-env/dev-specmate-all.bndrun +++ b/bundles/specmate-std-env/dev-specmate-all.bndrun @@ -7,6 +7,7 @@ osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ osgi.identity;filter:='(osgi.identity=specmate-common)',\ osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ osgi.identity;filter:='(osgi.identity=specmate-logging)',\ @@ -49,187 +50,187 @@ osgi.identity;filter:='(osgi.identity=specmate-nlp)',\ osgi.identity;filter:='(osgi.identity=specmate-model-generation)',\ bnd.identity;id='log4j' + + +-runproperties: \ + jetty.http.port=8080,\ + osgi.console=,\ + jetty.home.bundle=specmate-jettystarter,\ + jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ + osgi.compatibility.bootdelegation=true +-runrepos: \ + Workspace,\ + Local +-runvm: -Xmx6000m, -Djdk.crypto.KeyAgreement.legacyKDF=true -runbundles: \ + com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ + com.google.guava;version='[21.0.0,21.0.1)',\ + io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ javassist;version='[3.18.1,3.18.2)',\ javax.annotation-api;version='[1.2.0,1.2.1)',\ javax.validation.api;version='[1.1.0,1.1.1)',\ javax.ws.rs-api;version='[2.0.1,2.0.2)',\ jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ + lpg.runtime.java;version='[2.0.17,2.0.18)',\ + org.apache.commons.cli;version='[1.4.0,1.4.1)',\ + org.apache.commons.fileupload;version='[1.3.1,1.3.2)',\ + org.apache.commons.io;version='[2.4.0,2.4.1)',\ org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ + org.apache.felix.scr;version='[2.0.8,2.0.9)',\ + org.apache.felix.webconsole;version='[4.3.0,4.3.1)',\ + org.apache.servicemix.bundles.jakarta-regexp;version='[1.4.0,1.4.1)',\ + org.apache.servicemix.bundles.lucene;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queries;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queryparser;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-sandbox;version='[7.2.0,7.2.1)',\ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.eclipse.equinox.http.jetty;version='[3.0.200,3.0.201)',\ + org.eclipse.equinox.http.servlet;version='[1.1.500,1.1.501)',\ org.eclipse.equinox.log;version='[1.2.300,1.2.301)',\ org.eclipse.equinox.metatype;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.preferences;version='[3.5.200,3.5.201)',\ org.eclipse.equinox.registry;version='[3.5.400,3.5.401)',\ - org.eclipse.osgi.services;version='[3.4.0,3.4.1)',\ - org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ - org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ - org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ - org.glassfish.hk2.locator;version='[2.4.0,2.4.1)',\ - org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ - org.glassfish.hk2.utils;version='[2.4.0,2.4.1)',\ - org.glassfish.jersey.bundles.repackaged.jersey-guava;version='[2.17.0,2.17.1)',\ - org.glassfish.jersey.containers.jersey-container-servlet;version='[2.17.0,2.17.1)',\ - org.glassfish.jersey.containers.jersey-container-servlet-core;version='[2.17.0,2.17.1)',\ - org.glassfish.jersey.core.jersey-client;version='[2.17.0,2.17.1)',\ - org.glassfish.jersey.core.jersey-common;version='[2.17.0,2.17.1)',\ - org.glassfish.jersey.core.jersey-server;version='[2.17.0,2.17.1)',\ - org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ - org.json;version=snapshot,\ - org.slf4j.api;version='[1.7.2,1.7.3)',\ - specmate-common;version=snapshot,\ - specmate-emfjson;version=snapshot,\ - specmate-emfrest;version=snapshot,\ - specmate-logging;version=snapshot,\ - specmate-logging-slf4j;version=snapshot,\ - specmate-logging-slf4j-julbridge;version=snapshot,\ - specmate-model-gen;version=snapshot,\ - specmate-persistency-api;version=snapshot,\ - specmate-persistency-cdo;version=snapshot,\ - specmate-model-support;version=snapshot,\ - specmate-ui-core;version=snapshot,\ - specmate-dummy-data;version=snapshot,\ - specmate-config;version=snapshot,\ - specmate-connectors;version=snapshot,\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.cdo.net4j;version='[4.1.400,4.1.401)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ - org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ - com.google.guava;version='[21.0.0,21.0.1)',\ - specmate-hp-connector;version=snapshot,\ - specmate-testspecification;version=snapshot,\ - org.apache.felix.scr;version='[2.0.8,2.0.9)',\ - org.apache.commons.fileupload;version='[1.3.1,1.3.2)',\ - org.apache.commons.io;version='[2.4.0,2.4.1)',\ - org.apache.felix.webconsole;version='[4.3.0,4.3.1)',\ - org.eclipse.equinox.http.servlet;version='[1.1.500,1.1.501)',\ + org.eclipse.jetty.continuation;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.deploy;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.http;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.http;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.io;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.io;version='[9.4.6,9.4.7)',\ org.eclipse.jetty.osgi-servlet-api;version='[3.1.0,3.1.1)',\ org.eclipse.jetty.osgi.boot;version='[9.4.6,9.4.7)',\ org.eclipse.jetty.osgi.httpservice;version='[9.4.6,9.4.7)',\ org.eclipse.jetty.rewrite;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.security;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.security;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.server;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.server;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.servlet;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.servlet;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ org.eclipse.jetty.util;version='[9.4.6,9.4.7)',\ org.eclipse.jetty.webapp;version='[9.4.6,9.4.7)',\ org.eclipse.jetty.xml;version='[9.4.6,9.4.7)',\ - org.eclipse.equinox.http.jetty;version='[3.0.200,3.0.201)',\ - org.eclipse.jetty.continuation;version='[8.1.16,8.1.17)',\ - org.eclipse.jetty.http;version='[8.1.16,8.1.17)',\ - org.eclipse.jetty.io;version='[8.1.16,8.1.17)',\ - org.eclipse.jetty.security;version='[8.1.16,8.1.17)',\ - org.eclipse.jetty.server;version='[8.1.16,8.1.17)',\ - org.eclipse.jetty.servlet;version='[8.1.16,8.1.17)',\ - org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ - specmate-jettystarter;version=snapshot,\ - lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ + org.eclipse.net4j.db;version=snapshot,\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ - org.sat4j.core;version='[2.3.5,2.3.6)',\ + org.eclipse.osgi.services;version='[3.4.0,3.4.1)',\ + org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.locator;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ + org.glassfish.hk2.utils;version='[2.4.0,2.4.1)',\ + org.glassfish.jersey.bundles.repackaged.jersey-guava;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.containers.jersey-container-servlet;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.containers.jersey-container-servlet-core;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-client;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-common;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-server;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ + org.h2;version='[1.3.168,1.3.169)',\ org.jgrapht.core;version='[1.0.1,1.0.2)',\ - org.apache.commons.cli;version='[1.4.0,1.4.1)',\ + org.json;version=snapshot,\ + org.sat4j.core;version='[2.3.5,2.3.6)',\ org.sat4j.maxsat;version='[2.3.5,2.3.6)',\ org.sat4j.pb;version='[2.3.5,2.3.6)',\ - specmate-file-connector;version=snapshot,\ - org.apache.servicemix.bundles.jakarta-regexp;version='[1.4.0,1.4.1)',\ - org.apache.servicemix.bundles.lucene;version='[7.2.0,7.2.1)',\ - org.apache.servicemix.bundles.lucene-queries;version='[7.2.0,7.2.1)',\ - org.apache.servicemix.bundles.lucene-queryparser;version='[7.2.0,7.2.1)',\ - org.apache.servicemix.bundles.lucene-sandbox;version='[7.2.0,7.2.1)',\ - specmate-search;version=snapshot,\ - specmate-migration;version=snapshot,\ + org.slf4j.api;version='[1.7.2,1.7.3)',\ specmate-administration;version=snapshot,\ - specmate-emfrest-api;version=snapshot,\ - specmate-trello-connector;version=snapshot,\ - specmate-auth-api;version=snapshot,\ specmate-auth;version=snapshot,\ + specmate-auth-api;version=snapshot,\ + specmate-cdo-server;version=snapshot,\ + specmate-common;version=snapshot,\ + specmate-config;version=snapshot,\ + specmate-config-api;version=snapshot,\ + specmate-connectors;version=snapshot,\ specmate-dbprovider-api;version=snapshot,\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ - org.eclipse.net4j.db.h2;version='[4.2.300,4.2.301)',\ specmate-dbprovider-h2;version=snapshot,\ - specmate-config-api;version=snapshot,\ - com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ - io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ - io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ - io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ - specmate-metrics;version=snapshot,\ - io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ - org.h2;version='[1.3.168,1.3.169)',\ - specmate-cdo-server;version=snapshot,\ - org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ + specmate-dummy-data;version=snapshot,\ + specmate-emfjson;version=snapshot,\ + specmate-emfrest;version=snapshot,\ + specmate-emfrest-api;version=snapshot,\ + specmate-file-connector;version=snapshot,\ + specmate-hp-connector;version=snapshot,\ + specmate-jettystarter;version=snapshot,\ specmate-jira-connector;version=snapshot,\ + specmate-metrics;version=snapshot,\ + specmate-migration;version=snapshot,\ + specmate-model-gen;version=snapshot,\ + specmate-model-support;version=snapshot,\ + specmate-persistency-api;version=snapshot,\ + specmate-persistency-cdo;version=snapshot,\ specmate-rest;version=snapshot,\ specmate-scheduler;version=snapshot,\ + specmate-search;version=snapshot,\ + specmate-testspecification;version=snapshot,\ + specmate-trello-connector;version=snapshot,\ + specmate-ui-core;version=snapshot,\ + specmate-logging;version=snapshot,\ + specmate-logging-slf4j;version=snapshot,\ + specmate-logging-slf4j-julbridge;version=snapshot,\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db.h2;version='[4.3.100,4.3.101)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ + com.atlassian.fugue;version='[2.7.0,2.7.1)',\ + com.atlassian.sal.api;version='[3.0.7,3.0.8)',\ + com.atlassian.util.concurrent.atlassian-util-concurrent;version='[3.0.0,3.0.1)',\ + com.google.guava;version='[18.0.0,18.0.1)',\ + com.google.inject;version='[3.0.0,3.0.1)',\ com.ibm.icu;version='[63.1.0,63.1.1)',\ io.reactivex.rxjava2.rxjava;version='[2.2.3,2.2.4)',\ it.unimi.dsi.fastutil;version='[8.2.2,8.2.3)',\ javax.money.api;version='[1.0.1,1.0.2)',\ joda-time;version='[2.10.1,2.10.2)',\ + log4j;version='[1.2.17,1.2.18)',\ + org.antlr.runtime;version='[3.2.0,3.2.1)',\ + org.apache.commons.codec;version='[1.10.0,1.10.1)',\ org.apache.commons.commons-compress;version='[1.18.0,1.18.1)',\ org.apache.commons.commons-pool2;version='[2.6.0,2.6.1)',\ org.apache.commons.lang;version='[2.6.0,2.6.1)',\ - org.apache.ivy;version='[2.4.0,2.4.1)',\ - org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ - specmate-model-generation;version=snapshot,\ - specmate-nlp;version=snapshot,\ - com.google.guava;version='[18.0.0,18.0.1)',\ org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ - com.atlassian.fugue;version='[2.7.0,2.7.1)',\ - com.atlassian.sal.api;version='[3.0.7,3.0.8)',\ - com.atlassian.util.concurrent.atlassian-util-concurrent;version='[3.0.0,3.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ org.apache.commons.logging;version='[1.1.1,1.1.2)',\ org.apache.httpcomponents.httpasyncclient;version='[4.1.3,4.1.4)',\ org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\ org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\ + org.apache.ivy;version='[2.4.0,2.4.1)',\ + org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ org.apache.servicemix.bundles.jcip-annotations;version='[1.0.0,1.0.1)',\ org.apache.servicemix.bundles.spring-beans;version='[4.1.7,4.1.8)',\ org.apache.servicemix.bundles.spring-core;version='[4.1.7,4.1.8)',\ org.codehaus.jettison.jettison;version='[1.1.0,1.1.1)',\ - log4j;version='[1.2.17,1.2.18)',\ - com.google.inject;version='[3.0.0,3.0.1)',\ - org.antlr.runtime;version='[3.2.0,3.2.1)',\ org.eclipse.xtend.lib;version='[2.10.0,2.10.1)',\ org.eclipse.xtend.lib.macro;version='[2.10.0,2.10.1)',\ org.eclipse.xtext;version='[2.10.0,2.10.1)',\ org.eclipse.xtext.smap;version='[2.10.0,2.10.1)',\ org.eclipse.xtext.util;version='[2.10.0,2.10.1)',\ org.eclipse.xtext.xbase.lib;version='[2.10.0,2.10.1)',\ - specmate-cause-effect-patterns;version=snapshot - --runproperties: \ - jetty.http.port=8080,\ - jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ - osgi.console=,\ - jetty.home.bundle=specmate-jettystarter,\ - osgi.compatibility.bootdelegation=true --runrepos: \ - Workspace,\ - Local --runvm: -Xmx6000m\n\ - -Djdk.crypto.KeyAgreement.legacyKDF=true \ No newline at end of file + org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ + slf4j.api;version='[1.7.25,1.7.26)',\ + specmate-cause-effect-patterns;version=snapshot,\ + specmate-model-generation;version=snapshot,\ + specmate-nlp;version=snapshot \ No newline at end of file diff --git a/bundles/specmate-std-env/prod-specmate-all.bndrun b/bundles/specmate-std-env/prod-specmate-all.bndrun index 19a12d555..37726389b 100644 --- a/bundles/specmate-std-env/prod-specmate-all.bndrun +++ b/bundles/specmate-std-env/prod-specmate-all.bndrun @@ -5,6 +5,7 @@ osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ osgi.identity;filter:='(osgi.identity=specmate-common)',\ osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ osgi.identity;filter:='(osgi.identity=specmate-logging)',\ @@ -40,6 +41,7 @@ osgi.identity;filter:='(osgi.identity=specmate-auth-api)',\ osgi.identity;filter:='(osgi.identity=specmate-auth)',\ osgi.identity;filter:='(osgi.identity=specmate-dbprovider-api)',\ + bnd.identity;id='specmate-dbprovider-oracle',\ osgi.identity;filter:='(osgi.identity=specmate-dbprovider-h2)',\ osgi.identity;filter:='(osgi.identity=specmate-jira-connector)',\ osgi.identity;filter:='(osgi.identity=specmate-nlp)',\ @@ -51,6 +53,7 @@ javax.validation.api;version='[1.1.0,1.1.1)',\ javax.ws.rs-api;version='[2.0.1,2.0.2)',\ jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ @@ -79,6 +82,7 @@ org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ org.json;version=snapshot,\ org.slf4j.api;version='[1.7.2,1.7.3)',\ + slf4j.api;version='[1.7.12,1.7.13)',\ specmate-common;version=snapshot,\ specmate-emfjson;version=snapshot,\ specmate-emfrest;version=snapshot,\ @@ -92,18 +96,10 @@ specmate-ui-core;version=snapshot,\ specmate-config;version=snapshot,\ specmate-connectors;version=snapshot,\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.cdo.net4j;version='[4.1.400,4.1.401)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ com.google.guava;version='[21.0.0,21.0.1)',\ specmate-hp-connector;version=snapshot,\ specmate-testspecification;version=snapshot,\ @@ -134,7 +130,6 @@ org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ specmate-jettystarter;version=snapshot,\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ @@ -157,10 +152,6 @@ specmate-auth-api;version=snapshot,\ specmate-auth;version=snapshot,\ specmate-dbprovider-api;version=snapshot,\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ - org.eclipse.net4j.db.h2;version='[4.2.300,4.2.301)',\ - specmate-dbprovider-h2;version=snapshot,\ specmate-config-api;version=snapshot,\ com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ @@ -168,47 +159,43 @@ io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ specmate-metrics;version=snapshot,\ io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ - org.h2;version='[1.3.168,1.3.169)',\ specmate-cdo-server;version=snapshot,\ - org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ - specmate-jira-connector;version=snapshot,\ + org.apache.commons.lang3;version='[3.3.2,3.3.3)',\ + org.eclipse.emf.cdo;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.common;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.400,4.1.401)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.200,4.2.201)',\ + org.eclipse.net4j;version='[4.6.0,4.6.1)',\ + org.eclipse.net4j.tcp;version='[4.1.500,4.1.501)',\ + org.eclipse.net4j.util;version='[3.7.0,3.7.1)',\ + specmate-dbprovider-oracle;version=snapshot,\ specmate-rest;version=snapshot,\ - specmate-scheduler;version=snapshot,\ - com.ibm.icu;version='[63.1.0,63.1.1)',\ - io.reactivex.rxjava2.rxjava;version='[2.2.3,2.2.4)',\ - it.unimi.dsi.fastutil;version='[8.2.2,8.2.3)',\ - javax.money.api;version='[1.0.1,1.0.2)',\ - joda-time;version='[2.10.1,2.10.2)',\ - org.apache.commons.commons-compress;version='[1.18.0,1.18.1)',\ - org.apache.commons.commons-pool2;version='[2.6.0,2.6.1)',\ - org.apache.commons.lang;version='[2.6.0,2.6.1)',\ - org.apache.ivy;version='[2.4.0,2.4.1)',\ - org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ - specmate-model-generation;version=snapshot,\ - specmate-nlp;version=snapshot,\ - com.google.guava;version='[18.0.0,18.0.1)',\ - org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ - com.atlassian.fugue;version='[2.7.0,2.7.1)',\ - com.atlassian.sal.api;version='[3.0.7,3.0.8)',\ - com.atlassian.util.concurrent.atlassian-util-concurrent;version='[3.0.0,3.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ - org.apache.commons.logging;version='[1.1.1,1.1.2)',\ - org.apache.httpcomponents.httpasyncclient;version='[4.1.3,4.1.4)',\ - org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\ - org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\ - org.apache.servicemix.bundles.jcip-annotations;version='[1.0.0,1.0.1)',\ - org.apache.servicemix.bundles.spring-beans;version='[4.1.7,4.1.8)',\ - org.apache.servicemix.bundles.spring-core;version='[4.1.7,4.1.8)',\ - org.codehaus.jettison.jettison;version='[1.1.0,1.1.1)',\ - log4j;version='[1.2.17,1.2.18)' + specmate-scheduler;version=snapshot + org.eclipse.emf.cdo;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.common;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.400,4.1.401)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.200,4.2.201)',\ + org.eclipse.net4j;version='[4.6.0,4.6.1)',\ + org.eclipse.net4j.db;version=snapshot,\ + org.eclipse.net4j.db.jdbc;version='[4.3.200,4.3.201)',\ + org.eclipse.net4j.db.oracle;version='[1.1.300,1.1.301)',\ + org.eclipse.net4j.tcp;version='[4.1.500,4.1.501)',\ + org.eclipse.net4j.util;version='[3.7.0,3.7.1)',\ + org.objectweb.asm.all.debug;version='[5.0.2,5.0.3)',\ + specmate-dbprovider-oracle;version=snapshot + specmate-rest;version=snapshot -runproperties: \ jetty.http.port=8080,\ jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ osgi.console=,\ jetty.home.bundle=specmate-jettystarter,\ + jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ osgi.compatibility.bootdelegation=true -runrepos: \ Workspace,\ diff --git a/bundles/specmate-std-env/prod-specmate-cdo-server-oracle.bndrun b/bundles/specmate-std-env/prod-specmate-cdo-server-oracle.bndrun index e6a3bc099..878424cf1 100644 --- a/bundles/specmate-std-env/prod-specmate-cdo-server-oracle.bndrun +++ b/bundles/specmate-std-env/prod-specmate-cdo-server-oracle.bndrun @@ -40,12 +40,7 @@ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ @@ -56,12 +51,6 @@ org.eclipse.equinox.metatype;version='[1.4.0,1.4.1)',\ org.eclipse.equinox.preferences;version='[3.5.200,3.5.201)',\ org.eclipse.equinox.registry;version='[3.5.400,3.5.401)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ - org.eclipse.net4j.db.oracle;version='[1.1.200,1.1.201)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ @@ -77,8 +66,24 @@ specmate-model-support;version=snapshot,\ specmate-persistency-api;version=snapshot,\ specmate-config-api;version=snapshot,\ +<<<<<<< HEAD + org.apache.commons.lang3;version='[3.3.2,3.3.3)',\ + org.eclipse.emf.cdo;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.common;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.400,4.1.401)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.200,4.2.201)',\ + org.eclipse.net4j;version='[4.6.0,4.6.1)',\ + org.eclipse.net4j.db;version=snapshot,\ + org.eclipse.net4j.db.jdbc;version='[4.3.200,4.3.201)',\ + org.eclipse.net4j.db.oracle;version='[1.1.300,1.1.301)',\ + org.eclipse.net4j.tcp;version='[4.1.500,4.1.501)',\ + org.eclipse.net4j.util;version='[3.7.0,3.7.1)' +======= org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ org.objectweb.asm.all.debug;version='[5.0.2,5.0.3)',\ com.google.guava;version='[21.0.0,21.0.1)',\ org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)' \ No newline at end of file + slf4j.api;version='[1.7.25,1.7.26)' +>>>>>>> refs/remotes/origin/develop diff --git a/bundles/specmate-std-env/prod-specmate-cdo-server.bndrun b/bundles/specmate-std-env/prod-specmate-cdo-server.bndrun index 7e59271da..bc217ed22 100644 --- a/bundles/specmate-std-env/prod-specmate-cdo-server.bndrun +++ b/bundles/specmate-std-env/prod-specmate-cdo-server.bndrun @@ -27,7 +27,6 @@ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ @@ -42,35 +41,89 @@ specmate-logging-slf4j;version=snapshot,\ specmate-logging-slf4j-julbridge;version=snapshot,\ specmate-config;version=snapshot,\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ - org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ org.apache.felix.scr;version='[2.0.8,2.0.9)',\ specmate-config-api;version=snapshot,\ specmate-cdo-server;version=snapshot,\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ - org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ specmate-migration;version=snapshot,\ specmate-model-gen;version=snapshot,\ specmate-persistency-api;version=snapshot,\ specmate-model-support;version=snapshot,\ org.h2;version='[1.3.168,1.3.169)',\ specmate-dbprovider-api;version=snapshot,\ - org.eclipse.net4j.db;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.db.h2;version='[4.2.300,4.2.301)',\ - org.eclipse.net4j.db.jdbc;version='[4.3.100,4.3.101)',\ specmate-dbprovider-h2;version=snapshot,\ + com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ + com.google.guava;version='[21.0.0,21.0.1)',\ + io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ + javax.annotation-api;version='[1.2.0,1.2.1)',\ + javax.validation.api;version='[1.1.0,1.1.1)',\ + javax.ws.rs-api;version='[2.0.1,2.0.2)',\ + org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ + org.apache.servicemix.bundles.jakarta-regexp;version='[1.4.0,1.4.1)',\ + org.apache.servicemix.bundles.lucene;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queries;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queryparser;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-sandbox;version='[7.2.0,7.2.1)',\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.jetty.osgi-servlet-api;version='[3.1.0,3.1.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db;version=snapshot,\ + org.eclipse.net4j.db.h2;version='[4.3.100,4.3.101)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ + org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ + org.glassfish.hk2.utils;version='[2.4.0,2.4.1)',\ + org.glassfish.jersey.bundles.repackaged.jersey-guava;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-client;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-common;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-server;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ + org.json;version=snapshot,\ + slf4j.api;version='[1.7.25,1.7.26)',\ + specmate-administration;version=snapshot,\ + specmate-auth-api;version=snapshot,\ + specmate-connectors;version=snapshot,\ + specmate-dummy-data;version=snapshot,\ + specmate-emfrest-api;version=snapshot,\ + specmate-metrics;version=snapshot,\ + specmate-persistency-cdo;version=snapshot,\ + specmate-rest;version=snapshot,\ + specmate-scheduler;version=snapshot,\ + specmate-search;version=snapshot + org.eclipse.emf.cdo;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.common;version='[4.6.0,4.6.1)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.400,4.1.401)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.200,4.2.201)',\ + org.eclipse.net4j;version='[4.6.0,4.6.1)',\ + org.eclipse.net4j.db;version=snapshot,\ + org.eclipse.net4j.db.h2;version='[4.3.0,4.3.1)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.200,4.3.201)',\ + org.eclipse.net4j.tcp;version='[4.1.500,4.1.501)',\ + org.eclipse.net4j.util;version='[3.7.0,3.7.1)' +======= org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\ com.google.guava;version='[21.0.0,21.0.1)',\ javax.annotation-api;version='[1.2.0,1.2.1)',\ @@ -111,6 +164,7 @@ specmate-administration;version=snapshot,\ specmate-metrics;version=snapshot,\ specmate-persistency-cdo;version=snapshot +>>>>>>> refs/remotes/origin/develop -runproperties: \ osgi.console=,\ diff --git a/bundles/specmate-std-env/prod-specmate-no-cdo-server.bndrun b/bundles/specmate-std-env/prod-specmate-no-cdo-server.bndrun index aa7722785..7d6fb63d6 100644 --- a/bundles/specmate-std-env/prod-specmate-no-cdo-server.bndrun +++ b/bundles/specmate-std-env/prod-specmate-no-cdo-server.bndrun @@ -4,6 +4,7 @@ osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ osgi.identity;filter:='(osgi.identity=specmate-common)',\ osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ osgi.identity;filter:='(osgi.identity=specmate-logging)',\ @@ -43,18 +44,15 @@ osgi.identity;filter:='(osgi.identity=specmate-model-generation)',\ bnd.identity;id='log4j' -runbundles: \ - specmate-logging;version=snapshot,\ - specmate-logging-slf4j;version=snapshot,\ - specmate-logging-slf4j-julbridge;version=snapshot,\ javassist;version='[3.18.1,3.18.2)',\ javax.annotation-api;version='[1.2.0,1.2.1)',\ javax.validation.api;version='[1.1.0,1.1.1)',\ javax.ws.rs-api;version='[2.0.1,2.0.2)',\ jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ - org.eclipse.emf.ecore.change;version='[2.11.0,2.11.1)',\ org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ @@ -82,6 +80,9 @@ specmate-common;version=snapshot,\ specmate-emfjson;version=snapshot,\ specmate-emfrest;version=snapshot,\ + specmate-logging;version=snapshot,\ + specmate-logging-slf4j;version=snapshot,\ + specmate-logging-slf4j-julbridge;version=snapshot,\ specmate-model-gen;version=snapshot,\ specmate-persistency-api;version=snapshot,\ specmate-persistency-cdo;version=snapshot,\ @@ -89,17 +90,6 @@ specmate-ui-core;version=snapshot,\ specmate-config;version=snapshot,\ specmate-connectors;version=snapshot,\ - org.eclipse.emf.cdo;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.common;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.cdo.ecore.retrofit;version='[4.2.300,4.2.301)',\ - org.eclipse.emf.cdo.net4j;version='[4.1.400,4.1.401)',\ - org.eclipse.emf.cdo.server;version='[4.5.0,4.5.1)',\ - org.eclipse.emf.common;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore;version='[2.12.0,2.12.1)',\ - org.eclipse.emf.ecore.xmi;version='[2.12.0,2.12.1)',\ - org.eclipse.net4j;version='[4.5.0,4.5.1)',\ - org.eclipse.net4j.tcp;version='[4.1.400,4.1.401)',\ - org.eclipse.net4j.util;version='[3.6.0,3.6.1)',\ com.google.guava;version='[21.0.0,21.0.1)',\ specmate-hp-connector;version=snapshot,\ specmate-testspecification;version=snapshot,\ @@ -130,7 +120,6 @@ org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ specmate-jettystarter;version=snapshot,\ lpg.runtime.java;version='[2.0.17,2.0.18)',\ - org.eclipse.emf.cdo.server.ocl;version='[4.2.100,4.2.101)',\ org.eclipse.ocl;version='[3.6.200,3.6.201)',\ org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ @@ -158,41 +147,62 @@ io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ - org.eclipse.emf.cdo.server.net4j;version='[4.1.300,4.1.301)',\ specmate-metrics;version=snapshot,\ specmate-dbprovider-api;version=snapshot,\ specmate-rest;version=snapshot,\ specmate-scheduler;version=snapshot,\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ specmate-jira-connector;version=snapshot,\ + com.atlassian.fugue;version='[2.7.0,2.7.1)',\ + com.atlassian.sal.api;version='[3.0.7,3.0.8)',\ + com.atlassian.util.concurrent.atlassian-util-concurrent;version='[3.0.0,3.0.1)',\ com.google.guava;version='[18.0.0,18.0.1)',\ - org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ + com.google.inject;version='[3.0.0,3.0.1)',\ com.ibm.icu;version='[63.1.0,63.1.1)',\ io.reactivex.rxjava2.rxjava;version='[2.2.3,2.2.4)',\ it.unimi.dsi.fastutil;version='[8.2.2,8.2.3)',\ javax.money.api;version='[1.0.1,1.0.2)',\ joda-time;version='[2.10.1,2.10.2)',\ + log4j;version='[1.2.17,1.2.18)',\ + org.antlr.runtime;version='[3.2.0,3.2.1)',\ + org.apache.commons.codec;version='[1.10.0,1.10.1)',\ org.apache.commons.commons-compress;version='[1.18.0,1.18.1)',\ org.apache.commons.commons-pool2;version='[2.6.0,2.6.1)',\ org.apache.commons.lang;version='[2.6.0,2.6.1)',\ - org.apache.ivy;version='[2.4.0,2.4.1)',\ - org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ - specmate-model-generation;version=snapshot,\ - specmate-nlp;version=snapshot,\ - com.atlassian.fugue;version='[2.7.0,2.7.1)',\ - com.atlassian.sal.api;version='[3.0.7,3.0.8)',\ - com.atlassian.util.concurrent.atlassian-util-concurrent;version='[3.0.0,3.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.1)',\ + org.apache.commons.lang3;version='[3.5.0,3.5.1)',\ org.apache.commons.logging;version='[1.1.1,1.1.2)',\ org.apache.httpcomponents.httpasyncclient;version='[4.1.3,4.1.4)',\ org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\ org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\ + org.apache.ivy;version='[2.4.0,2.4.1)',\ + org.apache.opennlp.tools;version='[1.9.1,1.9.2)',\ org.apache.servicemix.bundles.jcip-annotations;version='[1.0.0,1.0.1)',\ org.apache.servicemix.bundles.spring-beans;version='[4.1.7,4.1.8)',\ org.apache.servicemix.bundles.spring-core;version='[4.1.7,4.1.8)',\ org.codehaus.jettison.jettison;version='[1.1.0,1.1.1)',\ - log4j;version='[1.2.17,1.2.18)' + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)',\ + org.eclipse.xtend.lib;version='[2.10.0,2.10.1)',\ + org.eclipse.xtend.lib.macro;version='[2.10.0,2.10.1)',\ + org.eclipse.xtext;version='[2.10.0,2.10.1)',\ + org.eclipse.xtext.smap;version='[2.10.0,2.10.1)',\ + org.eclipse.xtext.util;version='[2.10.0,2.10.1)',\ + org.eclipse.xtext.xbase.lib;version='[2.10.0,2.10.1)',\ + org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ + slf4j.api;version='[1.7.25,1.7.26)',\ + specmate-cause-effect-patterns;version=snapshot,\ + specmate-model-generation;version=snapshot,\ + specmate-nlp;version=snapshot -runproperties: \ jetty.http.port=8080,\ diff --git a/bundles/specmate-std-env/recovery/lastBranchId b/bundles/specmate-std-env/recovery/lastBranchId new file mode 100644 index 000000000..03d0e45b7 --- /dev/null +++ b/bundles/specmate-std-env/recovery/lastBranchId @@ -0,0 +1 @@ +-4 \ No newline at end of file diff --git a/bundles/specmate-std-env/specmate-clone.bndrun b/bundles/specmate-std-env/specmate-clone.bndrun new file mode 100644 index 000000000..432375089 --- /dev/null +++ b/bundles/specmate-std-env/specmate-clone.bndrun @@ -0,0 +1,200 @@ +-runfw: org.eclipse.osgi;version='[3.10.2.v20150203-1939,3.10.2.v20150203-1939]' +-runee: JavaSE-1.8 +-runrequires: \ + osgi.identity;filter:='(osgi.identity=specmate-cdo-server)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\ + osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ + osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ + osgi.identity;filter:='(osgi.identity=specmate-common)',\ + osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ + osgi.identity;filter:='(osgi.identity=specmate-logging)',\ + osgi.identity;filter:='(osgi.identity=specmate-logging-slf4j)',\ + osgi.identity;filter:='(osgi.identity=specmate-logging-slf4j-julbridge)',\ + osgi.identity;filter:='(osgi.identity=specmate-persistency-api)',\ + osgi.identity;filter:='(osgi.identity=org.glassfish.jersey.containers.jersey-container-servlet)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.event)',\ + osgi.identity;filter:='(osgi.identity=specmate-emfrest)',\ + osgi.identity;filter:='(osgi.identity=specmate-model-gen)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.cm)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.metatype)',\ + osgi.identity;filter:='(osgi.identity=specmate-model-support)',\ + osgi.identity;filter:='(osgi.identity=specmate-ui-core)',\ + osgi.identity;filter:='(osgi.identity=specmate-dummy-data)',\ + osgi.identity;filter:='(osgi.identity=specmate-config)',\ + osgi.identity;filter:='(osgi.identity=specmate-connectors)',\ + osgi.identity;filter:='(osgi.identity=specmate-testspecification)',\ + osgi.identity;filter:='(osgi.identity=specmate-hp-connector)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.scr)',\ + osgi.identity;filter:='(&(osgi.identity=org.apache.felix.webconsole)(version>=4.3.0))',\ + osgi.identity;filter:='(&(osgi.identity=org.eclipse.jetty.osgi.boot)(version>=9.4.6))',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.jetty.osgi.httpservice)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.jetty.rewrite)',\ + osgi.identity;filter:='(osgi.identity=specmate-jettystarter)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.emf.cdo.server.ocl)',\ + osgi.identity;filter:='(osgi.identity=org.json)',\ + osgi.identity;filter:='(osgi.identity=specmate-file-connector)',\ + osgi.identity;filter:='(osgi.identity=specmate-search)',\ + osgi.identity;filter:='(osgi.identity=specmate-migration)',\ + osgi.identity;filter:='(osgi.identity=specmate-persistency-cdo)',\ + osgi.identity;filter:='(osgi.identity=specmate-administration)',\ + osgi.identity;filter:='(osgi.identity=org.apache.commons.fileupload)',\ + osgi.identity;filter:='(osgi.identity=specmate-trello-connector)',\ + osgi.identity;filter:='(osgi.identity=specmate-auth-api)',\ + osgi.identity;filter:='(osgi.identity=specmate-auth)',\ + osgi.identity;filter:='(osgi.identity=specmate-dbprovider-api)',\ + bnd.identity;version='latest';id='specmate-model-gen',\ + bnd.identity;version='latest';id='specmate-model-support',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.net4j.db)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.emf.cdo.server.db)',\ + bnd.identity;version='latest';id='specmate-dbprovider-h2' +-runbundles: \ + javassist;version='[3.18.1,3.18.2)',\ + javax.annotation-api;version='[1.2.0,1.2.1)',\ + javax.validation.api;version='[1.1.0,1.1.1)',\ + javax.ws.rs-api;version='[2.0.1,2.0.2)',\ + jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ + org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ + org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ + org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ + org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ + org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ + org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ + org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ + org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ + org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.eclipse.equinox.log;version='[1.2.300,1.2.301)',\ + org.eclipse.equinox.metatype;version='[1.4.0,1.4.1)',\ + org.eclipse.equinox.preferences;version='[3.5.200,3.5.201)',\ + org.eclipse.equinox.registry;version='[3.5.400,3.5.401)',\ + org.eclipse.osgi.services;version='[3.4.0,3.4.1)',\ + org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.locator;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ + org.glassfish.hk2.utils;version='[2.4.0,2.4.1)',\ + org.glassfish.jersey.bundles.repackaged.jersey-guava;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.containers.jersey-container-servlet;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.containers.jersey-container-servlet-core;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-client;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-common;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-server;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ + org.json;version=snapshot,\ + org.slf4j.api;version='[1.7.2,1.7.3)',\ + slf4j.api;version='[1.7.12,1.7.13)',\ + specmate-common;version=snapshot,\ + specmate-emfjson;version=snapshot,\ + specmate-emfrest;version=snapshot,\ + specmate-logging;version=snapshot,\ + specmate-logging-slf4j;version=snapshot,\ + specmate-logging-slf4j-julbridge;version=snapshot,\ + specmate-model-gen;version=snapshot,\ + specmate-persistency-api;version=snapshot,\ + specmate-persistency-cdo;version=snapshot,\ + specmate-model-support;version=snapshot,\ + specmate-ui-core;version=snapshot,\ + specmate-dummy-data;version=snapshot,\ + specmate-config;version=snapshot,\ + specmate-connectors;version=snapshot,\ + com.google.guava;version='[21.0.0,21.0.1)',\ + specmate-hp-connector;version=snapshot,\ + specmate-testspecification;version=snapshot,\ + org.apache.felix.scr;version='[2.0.8,2.0.9)',\ + org.apache.commons.fileupload;version='[1.3.1,1.3.2)',\ + org.apache.commons.io;version='[2.4.0,2.4.1)',\ + org.apache.felix.webconsole;version='[4.3.0,4.3.1)',\ + org.eclipse.equinox.http.servlet;version='[1.1.500,1.1.501)',\ + org.eclipse.jetty.deploy;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.http;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.io;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.osgi-servlet-api;version='[3.1.0,3.1.1)',\ + org.eclipse.jetty.osgi.boot;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.osgi.httpservice;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.rewrite;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.security;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.server;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.servlet;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.util;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.webapp;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.xml;version='[9.4.6,9.4.7)',\ + org.eclipse.equinox.http.jetty;version='[3.0.200,3.0.201)',\ + org.eclipse.jetty.continuation;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.http;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.io;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.security;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.server;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.servlet;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ + specmate-jettystarter;version=snapshot,\ + lpg.runtime.java;version='[2.0.17,2.0.18)',\ + org.eclipse.ocl;version='[3.6.200,3.6.201)',\ + org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ + org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ + org.sat4j.core;version='[2.3.5,2.3.6)',\ + org.jgrapht.core;version='[1.0.1,1.0.2)',\ + org.apache.commons.cli;version='[1.4.0,1.4.1)',\ + org.sat4j.maxsat;version='[2.3.5,2.3.6)',\ + org.sat4j.pb;version='[2.3.5,2.3.6)',\ + specmate-file-connector;version=snapshot,\ + org.apache.servicemix.bundles.jakarta-regexp;version='[1.4.0,1.4.1)',\ + org.apache.servicemix.bundles.lucene;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queries;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queryparser;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-sandbox;version='[7.2.0,7.2.1)',\ + specmate-search;version=snapshot,\ + specmate-migration;version=snapshot,\ + specmate-administration;version=snapshot,\ + specmate-emfrest-api;version=snapshot,\ + specmate-trello-connector;version=snapshot,\ + specmate-auth-api;version=snapshot,\ + specmate-auth;version=snapshot,\ + specmate-dbprovider-api;version=snapshot,\ + specmate-config-api;version=snapshot,\ + com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ + io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ + specmate-metrics;version=snapshot,\ + io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ + specmate-cdo-server;version=snapshot,\ + org.apache.commons.lang3;version='[3.3.2,3.3.3)',\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + org.eclipse.emf.cdo.server;version=snapshot,\ + specmate-rest;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + org.eclipse.net4j.db;version=snapshot,\ + specmate-scheduler;version=snapshot,\ + org.h2;version='[1.3.168,1.3.169)',\ + specmate-dbprovider-h2;version=snapshot,\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db.h2;version='[4.3.100,4.3.101)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)' + +-runproperties: \ + jetty.http.port=8082,\ + osgi.console=,\ + jetty.home.bundle=specmate-jettystarter,\ + jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ + osgi.compatibility.bootdelegation=true +-runrepos: \ + Workspace,\ + Local +-runvm: -Xmx6000m, -Djdk.crypto.KeyAgreement.legacyKDF=true +-runprogramargs: --configurationFile ../specmate-config/config/specmate-config-clone.properties + \ No newline at end of file diff --git a/bundles/specmate-std-env/specmate-master.bndrun b/bundles/specmate-std-env/specmate-master.bndrun new file mode 100644 index 000000000..6474773c0 --- /dev/null +++ b/bundles/specmate-std-env/specmate-master.bndrun @@ -0,0 +1,200 @@ +-runfw: org.eclipse.osgi;version='[3.10.2.v20150203-1939,3.10.2.v20150203-1939]' +-runee: JavaSE-1.8 +-runrequires: \ + osgi.identity;filter:='(osgi.identity=specmate-cdo-server)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\ + osgi.identity;filter:='(osgi.identity=org.glassfish.hk2.locator)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.log)',\ + osgi.identity;filter:='(osgi.identity=jul.to.slf4j)',\ + osgi.identity;filter:='(osgi.identity=log4j.over.slf4j)',\ + osgi.identity;filter:='(osgi.identity=specmate-common)',\ + osgi.identity;filter:='(osgi.identity=specmate-emfjson)',\ + osgi.identity;filter:='(osgi.identity=specmate-logging)',\ + osgi.identity;filter:='(osgi.identity=specmate-logging-slf4j)',\ + osgi.identity;filter:='(osgi.identity=specmate-logging-slf4j-julbridge)',\ + osgi.identity;filter:='(osgi.identity=specmate-persistency-api)',\ + osgi.identity;filter:='(osgi.identity=org.glassfish.jersey.containers.jersey-container-servlet)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.event)',\ + osgi.identity;filter:='(osgi.identity=specmate-emfrest)',\ + osgi.identity;filter:='(osgi.identity=specmate-model-gen)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.cm)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.equinox.metatype)',\ + osgi.identity;filter:='(osgi.identity=specmate-model-support)',\ + osgi.identity;filter:='(osgi.identity=specmate-ui-core)',\ + osgi.identity;filter:='(osgi.identity=specmate-dummy-data)',\ + osgi.identity;filter:='(osgi.identity=specmate-config)',\ + osgi.identity;filter:='(osgi.identity=specmate-connectors)',\ + osgi.identity;filter:='(osgi.identity=specmate-testspecification)',\ + osgi.identity;filter:='(osgi.identity=specmate-hp-connector)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.scr)',\ + osgi.identity;filter:='(&(osgi.identity=org.apache.felix.webconsole)(version>=4.3.0))',\ + osgi.identity;filter:='(&(osgi.identity=org.eclipse.jetty.osgi.boot)(version>=9.4.6))',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.jetty.osgi.httpservice)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.jetty.rewrite)',\ + osgi.identity;filter:='(osgi.identity=specmate-jettystarter)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.emf.cdo.server.ocl)',\ + osgi.identity;filter:='(osgi.identity=org.json)',\ + osgi.identity;filter:='(osgi.identity=specmate-file-connector)',\ + osgi.identity;filter:='(osgi.identity=specmate-search)',\ + osgi.identity;filter:='(osgi.identity=specmate-migration)',\ + osgi.identity;filter:='(osgi.identity=specmate-persistency-cdo)',\ + osgi.identity;filter:='(osgi.identity=specmate-administration)',\ + osgi.identity;filter:='(osgi.identity=org.apache.commons.fileupload)',\ + osgi.identity;filter:='(osgi.identity=specmate-trello-connector)',\ + osgi.identity;filter:='(osgi.identity=specmate-auth-api)',\ + osgi.identity;filter:='(osgi.identity=specmate-auth)',\ + osgi.identity;filter:='(osgi.identity=specmate-dbprovider-api)',\ + bnd.identity;version='latest';id='specmate-model-gen',\ + bnd.identity;version='latest';id='specmate-model-support',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.net4j.db)',\ + osgi.identity;filter:='(osgi.identity=org.eclipse.emf.cdo.server.db)',\ + bnd.identity;version='latest';id='specmate-dbprovider-h2' +-runbundles: \ + javassist;version='[3.18.1,3.18.2)',\ + javax.annotation-api;version='[1.2.0,1.2.1)',\ + javax.validation.api;version='[1.1.0,1.1.1)',\ + javax.ws.rs-api;version='[2.0.1,2.0.2)',\ + jul.to.slf4j;version='[1.7.12,1.7.13)',\ + log4j.over.slf4j;version='[1.7.12,1.7.13)',\ + org.apache.felix.gogo.command;version='[0.10.0,0.10.1)',\ + org.apache.felix.gogo.runtime;version='[0.10.0,0.10.1)',\ + org.apache.felix.gogo.shell;version='[0.10.0,0.10.1)',\ + org.eclipse.core.contenttype;version='[3.4.200,3.4.201)',\ + org.eclipse.core.jobs;version='[3.6.1,3.6.2)',\ + org.eclipse.core.runtime;version='[3.10.0,3.10.1)',\ + org.eclipse.equinox.app;version='[1.3.200,1.3.201)',\ + org.eclipse.equinox.cm;version='[1.1.0,1.1.1)',\ + org.eclipse.equinox.common;version='[3.6.200,3.6.201)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.eclipse.equinox.log;version='[1.2.300,1.2.301)',\ + org.eclipse.equinox.metatype;version='[1.4.0,1.4.1)',\ + org.eclipse.equinox.preferences;version='[3.5.200,3.5.201)',\ + org.eclipse.equinox.registry;version='[3.5.400,3.5.401)',\ + org.eclipse.osgi.services;version='[3.4.0,3.4.1)',\ + org.glassfish.hk2.api;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.locator;version='[2.4.0,2.4.1)',\ + org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ + org.glassfish.hk2.utils;version='[2.4.0,2.4.1)',\ + org.glassfish.jersey.bundles.repackaged.jersey-guava;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.containers.jersey-container-servlet;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.containers.jersey-container-servlet-core;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-client;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-common;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.core.jersey-server;version='[2.17.0,2.17.1)',\ + org.glassfish.jersey.media.jersey-media-sse;version='[2.17.0,2.17.1)',\ + org.json;version=snapshot,\ + org.slf4j.api;version='[1.7.2,1.7.3)',\ + slf4j.api;version='[1.7.12,1.7.13)',\ + specmate-common;version=snapshot,\ + specmate-emfjson;version=snapshot,\ + specmate-emfrest;version=snapshot,\ + specmate-logging;version=snapshot,\ + specmate-logging-slf4j;version=snapshot,\ + specmate-logging-slf4j-julbridge;version=snapshot,\ + specmate-model-gen;version=snapshot,\ + specmate-persistency-api;version=snapshot,\ + specmate-persistency-cdo;version=snapshot,\ + specmate-model-support;version=snapshot,\ + specmate-ui-core;version=snapshot,\ + specmate-dummy-data;version=snapshot,\ + specmate-config;version=snapshot,\ + specmate-connectors;version=snapshot,\ + com.google.guava;version='[21.0.0,21.0.1)',\ + specmate-hp-connector;version=snapshot,\ + specmate-testspecification;version=snapshot,\ + org.apache.felix.scr;version='[2.0.8,2.0.9)',\ + org.apache.commons.fileupload;version='[1.3.1,1.3.2)',\ + org.apache.commons.io;version='[2.4.0,2.4.1)',\ + org.apache.felix.webconsole;version='[4.3.0,4.3.1)',\ + org.eclipse.equinox.http.servlet;version='[1.1.500,1.1.501)',\ + org.eclipse.jetty.deploy;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.http;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.io;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.osgi-servlet-api;version='[3.1.0,3.1.1)',\ + org.eclipse.jetty.osgi.boot;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.osgi.httpservice;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.rewrite;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.security;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.server;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.servlet;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.util;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.webapp;version='[9.4.6,9.4.7)',\ + org.eclipse.jetty.xml;version='[9.4.6,9.4.7)',\ + org.eclipse.equinox.http.jetty;version='[3.0.200,3.0.201)',\ + org.eclipse.jetty.continuation;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.http;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.io;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.security;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.server;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.servlet;version='[8.1.16,8.1.17)',\ + org.eclipse.jetty.util;version='[8.1.16,8.1.17)',\ + specmate-jettystarter;version=snapshot,\ + lpg.runtime.java;version='[2.0.17,2.0.18)',\ + org.eclipse.ocl;version='[3.6.200,3.6.201)',\ + org.eclipse.ocl.common;version='[1.4.200,1.4.201)',\ + org.eclipse.ocl.ecore;version='[3.6.200,3.6.201)',\ + org.sat4j.core;version='[2.3.5,2.3.6)',\ + org.jgrapht.core;version='[1.0.1,1.0.2)',\ + org.apache.commons.cli;version='[1.4.0,1.4.1)',\ + org.sat4j.maxsat;version='[2.3.5,2.3.6)',\ + org.sat4j.pb;version='[2.3.5,2.3.6)',\ + specmate-file-connector;version=snapshot,\ + org.apache.servicemix.bundles.jakarta-regexp;version='[1.4.0,1.4.1)',\ + org.apache.servicemix.bundles.lucene;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queries;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-queryparser;version='[7.2.0,7.2.1)',\ + org.apache.servicemix.bundles.lucene-sandbox;version='[7.2.0,7.2.1)',\ + specmate-search;version=snapshot,\ + specmate-migration;version=snapshot,\ + specmate-administration;version=snapshot,\ + specmate-emfrest-api;version=snapshot,\ + specmate-trello-connector;version=snapshot,\ + specmate-auth-api;version=snapshot,\ + specmate-auth;version=snapshot,\ + specmate-dbprovider-api;version=snapshot,\ + specmate-config-api;version=snapshot,\ + com.diffplug.osgi.extension.sun.misc;version='[0.0.0,0.0.1)',\ + io.prometheus.simpleclient;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_common;version='[0.4.0,0.4.1)',\ + io.prometheus.simpleclient_servlet;version='[0.4.0,0.4.1)',\ + specmate-metrics;version=snapshot,\ + io.prometheus.simpleclient_hotspot;version='[0.4.0,0.4.1)',\ + specmate-cdo-server;version=snapshot,\ + org.apache.commons.lang3;version='[3.3.2,3.3.3)',\ + org.eclipse.emf.cdo.server;version=snapshot,\ + org.eclipse.emf.cdo.net4j;version=snapshot,\ + specmate-rest;version=snapshot,\ + org.eclipse.net4j.db;version=snapshot,\ + org.eclipse.emf.cdo.server.db;version=snapshot,\ + specmate-scheduler;version=snapshot,\ + org.h2;version='[1.3.168,1.3.169)',\ + specmate-dbprovider-h2;version=snapshot,\ + org.eclipse.emf.cdo;version='[4.6.100,4.6.101)',\ + org.eclipse.emf.cdo.common;version='[4.7.0,4.7.1)',\ + org.eclipse.emf.cdo.server.net4j;version='[4.1.500,4.1.501)',\ + org.eclipse.emf.cdo.server.ocl;version='[4.2.300,4.2.301)',\ + org.eclipse.emf.common;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore;version='[2.14.0,2.14.1)',\ + org.eclipse.emf.ecore.change;version='[2.12.0,2.12.1)',\ + org.eclipse.emf.ecore.xmi;version='[2.14.0,2.14.1)',\ + org.eclipse.net4j;version='[4.7.0,4.7.1)',\ + org.eclipse.net4j.db.h2;version='[4.3.100,4.3.101)',\ + org.eclipse.net4j.db.jdbc;version='[4.3.300,4.3.301)',\ + org.eclipse.net4j.tcp;version='[4.1.600,4.1.601)',\ + org.eclipse.net4j.util;version='[3.8.0,3.8.1)' + +-runproperties: \ + jetty.http.port=8083,\ + osgi.console=,\ + jetty.home.bundle=specmate-jettystarter,\ + jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ + osgi.compatibility.bootdelegation=true +-runrepos: \ + Workspace,\ + Local +-runvm: -Xmx6000m, -Djdk.crypto.KeyAgreement.legacyKDF=true +-runprogramargs: --configurationFile ../specmate-config/config/specmate-config-master.properties + \ No newline at end of file diff --git a/system-tests/master-clone-test/.classpath b/system-tests/master-clone-test/.classpath new file mode 100644 index 000000000..9bd561b8c --- /dev/null +++ b/system-tests/master-clone-test/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/system-tests/master-clone-test/.gitignore b/system-tests/master-clone-test/.gitignore new file mode 100644 index 000000000..85bba7d3e --- /dev/null +++ b/system-tests/master-clone-test/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/database/ diff --git a/system-tests/master-clone-test/.project b/system-tests/master-clone-test/.project new file mode 100644 index 000000000..6e717c94c --- /dev/null +++ b/system-tests/master-clone-test/.project @@ -0,0 +1,17 @@ + + + master-clone-test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/system-tests/master-clone-test/MasterCloneTest.launch b/system-tests/master-clone-test/MasterCloneTest.launch new file mode 100644 index 000000000..8bb4c5062 --- /dev/null +++ b/system-tests/master-clone-test/MasterCloneTest.launch @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/system-tests/master-clone-test/jar/.gitignore b/system-tests/master-clone-test/jar/.gitignore new file mode 100644 index 000000000..87762bd13 --- /dev/null +++ b/system-tests/master-clone-test/jar/.gitignore @@ -0,0 +1 @@ +!*.jar \ No newline at end of file diff --git a/system-tests/master-clone-test/jar/commons-cli-1.4.jar b/system-tests/master-clone-test/jar/commons-cli-1.4.jar new file mode 100644 index 000000000..22deb3089 Binary files /dev/null and b/system-tests/master-clone-test/jar/commons-cli-1.4.jar differ diff --git a/system-tests/master-clone-test/jar/commons-collections4-4.3.jar b/system-tests/master-clone-test/jar/commons-collections4-4.3.jar new file mode 100644 index 000000000..307012f13 Binary files /dev/null and b/system-tests/master-clone-test/jar/commons-collections4-4.3.jar differ diff --git a/system-tests/master-clone-test/jar/commons-lang3-3.5.jar b/system-tests/master-clone-test/jar/commons-lang3-3.5.jar new file mode 100644 index 000000000..6328c8de4 Binary files /dev/null and b/system-tests/master-clone-test/jar/commons-lang3-3.5.jar differ diff --git a/system-tests/master-clone-test/jar/hk2-api-2.5.0-b42.jar b/system-tests/master-clone-test/jar/hk2-api-2.5.0-b42.jar new file mode 100644 index 000000000..c72ce9873 Binary files /dev/null and b/system-tests/master-clone-test/jar/hk2-api-2.5.0-b42.jar differ diff --git a/system-tests/master-clone-test/jar/hk2-locator-2.5.0-b42.jar b/system-tests/master-clone-test/jar/hk2-locator-2.5.0-b42.jar new file mode 100644 index 000000000..d05a71b31 Binary files /dev/null and b/system-tests/master-clone-test/jar/hk2-locator-2.5.0-b42.jar differ diff --git a/system-tests/master-clone-test/jar/hk2-utils-2.5.0-b42.jar b/system-tests/master-clone-test/jar/hk2-utils-2.5.0-b42.jar new file mode 100644 index 000000000..fa5066f6b Binary files /dev/null and b/system-tests/master-clone-test/jar/hk2-utils-2.5.0-b42.jar differ diff --git a/system-tests/master-clone-test/jar/javax.annotation-api-1.2.jar b/system-tests/master-clone-test/jar/javax.annotation-api-1.2.jar new file mode 100644 index 000000000..9ab39ffa4 Binary files /dev/null and b/system-tests/master-clone-test/jar/javax.annotation-api-1.2.jar differ diff --git a/system-tests/master-clone-test/jar/javax.inject-2.5.0-b42.jar b/system-tests/master-clone-test/jar/javax.inject-2.5.0-b42.jar new file mode 100644 index 000000000..b4ed714e2 Binary files /dev/null and b/system-tests/master-clone-test/jar/javax.inject-2.5.0-b42.jar differ diff --git a/system-tests/master-clone-test/jar/javax.ws.rs-api-2.1.jar b/system-tests/master-clone-test/jar/javax.ws.rs-api-2.1.jar new file mode 100644 index 000000000..e64b2d531 Binary files /dev/null and b/system-tests/master-clone-test/jar/javax.ws.rs-api-2.1.jar differ diff --git a/system-tests/master-clone-test/jar/jersey-client.jar b/system-tests/master-clone-test/jar/jersey-client.jar new file mode 100644 index 000000000..5a130a7c5 Binary files /dev/null and b/system-tests/master-clone-test/jar/jersey-client.jar differ diff --git a/system-tests/master-clone-test/jar/jersey-common.jar b/system-tests/master-clone-test/jar/jersey-common.jar new file mode 100644 index 000000000..3b35ecc24 Binary files /dev/null and b/system-tests/master-clone-test/jar/jersey-common.jar differ diff --git a/system-tests/master-clone-test/jar/jersey-hk2.jar b/system-tests/master-clone-test/jar/jersey-hk2.jar new file mode 100644 index 000000000..2d80a4384 Binary files /dev/null and b/system-tests/master-clone-test/jar/jersey-hk2.jar differ diff --git a/system-tests/master-clone-test/jar/json-20180130.jar b/system-tests/master-clone-test/jar/json-20180130.jar new file mode 100644 index 000000000..bc2cd4103 Binary files /dev/null and b/system-tests/master-clone-test/jar/json-20180130.jar differ diff --git a/system-tests/master-clone-test/recovery/.gitignore b/system-tests/master-clone-test/recovery/.gitignore new file mode 100644 index 000000000..ab92e392d --- /dev/null +++ b/system-tests/master-clone-test/recovery/.gitignore @@ -0,0 +1 @@ +/lastBranchId diff --git a/system-tests/master-clone-test/resources/specmate-config-clone.properties b/system-tests/master-clone-test/resources/specmate-config-clone.properties new file mode 100644 index 000000000..3208b3e57 --- /dev/null +++ b/system-tests/master-clone-test/resources/specmate-config-clone.properties @@ -0,0 +1,68 @@ +# Logging +# Choose from debug, info, warning, error +logging.level = info + +# CDO Persistency Settings +## CDO Common +### Repository name, in case of Oracle, must be identical to the schema name +cdo.repositoryName = specmatedev2 +cdo.user = cdoUser +cdo.password = cdoPass + +## CDO Server +### TCP host and port where the CDO server should listen +cdo.serverHostAndPort = localhost:2037 + +### The master CDO with which to synchronize +cdo.master=localhost:2036 +### The name of the repository in the master server +cdo.masterRepositoryName=specmatedev1 +### User name for connecting to the master server +cdo.masterUser = cdoUser +### Password for connecting to the master server +cdo.masterPassword = cdoPass + +## CDO Client +### Name of the CDO resource to use +cdo.resourceName = specmate_resource +### Folder for saving recovery data +cdo.recoveryFolder = ./recovery + +## H2 +### JDBC connection string for the H2 database +h2.jdbcConnection = jdbc:h2:./database/specmate_clone + + +## Oracle +### JDBC connection string for the oracle database +#oracle.jdbcConnection = jdbc:oracle:thin:@localhost:1521/XE +#oracle.username = specmatedev2 +#oracle.password = specmate + +# Connectors General Settings +## Time in seconds between polling the connectors, set to -1 to disable polling, default: 20 +connectorPollTime = -1 + +# Sarch Service +search.allowedFields = extId, type, name, description +search.lucene.location = ./database/lucene_clone +search.maxResults = 100 + +# Projects +## List of project names, each project listed here needs to be configured below +project.projects = artificial + +## Config for project ppmtest + +### Config Connector +project.artificial.connector.pid = com.specmate.ArtificialRequirementsConnector +project.artificial.connector.artificalRequirements.count = 200 +project.artificial.connector.artificalRequirements.simulatedDelay = 1000 +project.artificial.connector.connectorID = artificial + +# User session +## Number of minutes a session is valid after the last http request +session.maxIdleMinutes = 720 +## Persist sessions in database or keep in memory +session.persistent = true + diff --git a/system-tests/master-clone-test/resources/specmate-config-master.properties b/system-tests/master-clone-test/resources/specmate-config-master.properties new file mode 100644 index 000000000..28e4c330c --- /dev/null +++ b/system-tests/master-clone-test/resources/specmate-config-master.properties @@ -0,0 +1,69 @@ +# Logging +# Choose from debug, info, warning, error +logging.level = info + +# CDO Persistency Settings +## CDO Common +### Repository name, in case of Oracle, must be identical to the schema name +cdo.repositoryName = specmatedev1 +cdo.user = cdoUser +cdo.password = cdoPass + +## CDO Server +### TCP host and port where the CDO server should listen +cdo.serverHostAndPort = localhost:2036 + +## CDO Client +### Name of the CDO resource to use +cdo.resourceName = specmate_resource + +## H2 +### JDBC connection string for the H2 database +h2.jdbcConnection = jdbc:h2:./database/specmate_master + + +## Oracle +### JDBC connection string for the oracle database +#oracle.jdbcConnection = jdbc:oracle:thin:@localhost:1521/XE + +#oracle.username = specmatedev1 +#oracle.password = specmate + +# Connectors General Settings +## cron string to schedule, when connectors are triggered. +## Set to "disabled" (without quotes) to disable polling. +## default: disabled +## generic value (will trigger every hour): hour +## example: day 13 14 5 will trigger every day at 13:14:05 +## example: hour 14 5 will trigger every hour at xx:14:05 +## example: minute 5 will trigger every minute at xx:xx:05 +## missing numbers are replaced by 0s. +## example: day 13 will trigger every day at 13:00:00 +# connectorPollSchedule = disabled +connectorPollSchedule = day + +# Sarch Service +search.allowedFields = extId, type, name, description +search.lucene.location = ./database/lucene_master +search.maxResults = 100 + +# Projects +## List of project names, each project listed here needs to be configured below +project.projects = artificial + +## Config for project ppmtest + +### Config Connector +project.artificial.connector.pid = com.specmate.ArtificialRequirementsConnector +project.artificial.connector.artificalRequirements.count = 200 +project.artificial.connector.artificalRequirements.simulatedDelay = 10 +project.artificial.connector.connectorID = artificial + + + +# User session +## Number of minutes a session is valid after the last http request +session.maxIdleMinutes = 720 +## Persist sessions in database or keep in memory +session.persistent = true + diff --git a/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/.classpath b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/.classpath new file mode 100644 index 000000000..e9bcd511f --- /dev/null +++ b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/.classpath @@ -0,0 +1,5 @@ + + + + + diff --git a/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/.project b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/.project new file mode 100644 index 000000000..24f1b3c57 --- /dev/null +++ b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/.project @@ -0,0 +1,17 @@ + + + masterclonetest + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/MasterCloneTest.java b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/MasterCloneTest.java new file mode 100644 index 000000000..3f88cf731 --- /dev/null +++ b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/MasterCloneTest.java @@ -0,0 +1,394 @@ +package com.specmate.systemtest.masterclonetest; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; + +import javax.security.sasl.AuthenticationException; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.json.JSONArray; +import org.json.JSONObject; + +public class MasterCloneTest { + public static final String ANSI_RESET = "\u001B[0m"; + public static final String ANSI_GREEN = "\u001B[32m"; + public static final String ANSI_YELLOW = "\u001B[33m"; + public static final String ANSI_BLUE = "\u001B[34m"; + public static final String ANSI_PURPLE = "\u001B[35m"; + private static final String LOGIN_JSON = "{\"___nsuri\":\"http://specmate.com/20190125/model/user\",\"className\":\"User\",\"userName\":\"user\",\"passWord\":\"password\",\"projectName\":\"artificial\"}"; + private static final String CEG_JSON = "{\"___nsuri\":\"http://specmate.com/20190125/model/requirements\",\"name\":\"TestCeg1\",\"className\":\"CEGModel\",\"id\":\"TestCeg1\"}"; + private static final String CEG_NODE1_JSON = "{\"condition\":\"Condition3\",\"___nsuri\":\"http://specmate.com/20190125/model/requirements\",\"name\":\"TestCegNode4\",\"variable\":\"Variable2\",\"className\":\"CEGNode\",\"id\":\"TestCegNode4\",\"type\":\"OR\"}"; + private static final String CEG_NODE2_JSON = "{\"condition\":\"Condition6\",\"___nsuri\":\"http://specmate.com/20190125/model/requirements\",\"name\":\"TestCegNode7\",\"variable\":\"Variable5\",\"className\":\"CEGNode\",\"id\":\"TestCegNode7\",\"type\":\"OR\"}"; + private static final String CEG_CONNECTION_JSON = "{\"negate\":false,\"___nsuri\":\"http://specmate.com/20190125/model/requirements\",\"name\":\"TestConnection8\",\"className\":\"CEGConnection\",\"id\":\"TestConnection8\",\"source\":{\"___proxy\":\"true\",\"url\":\"artificial/default/requirement1/TestCeg1/TestCegNode4\"},\"target\":{\"___proxy\":\"true\",\"url\":\"artificial/default/requirement1/TestCeg1/TestCegNode7\"}}"; + private static final String CEG_TESTSPEC_JSON = "{\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"TestSpecification9\",\"className\":\"TestSpecification\",\"id\":\"TestSpecification9\"}"; + +// private static final String LOGIN_JSON_2 = "{\"___nsuri\":\"http://specmate.com/20190125/model/user\",\"className\":\"User\",\"userName\":\"user\",\"passWord\":\"password\",\"projectName\":\"test-data\"}"; +// private static final String TEST_JSON = "[{\"condition\":\"gedrückt\",\"parameter\":{\"___proxy\":true,\"className\":\"TestParameter\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestParameter-1\"},\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"ParameterAssignment-1\",\"className\":\"ParameterAssignment\",\"id\":\"ParameterAssignment-1\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestCase-3/ParameterAssignment-1\"},{\"condition\":\"not gedrückt\",\"parameter\":{\"___proxy\":true,\"className\":\"TestParameter\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestParameter-2\"},\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"ParameterAssignment-2\",\"className\":\"ParameterAssignment\",\"id\":\"ParameterAssignment-2\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestCase-3/ParameterAssignment-2\"},{\"condition\":\"gedrückt\",\"parameter\":{\"___proxy\":true,\"className\":\"TestParameter\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestParameter-3\"},\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"ParameterAssignment-3\",\"className\":\"ParameterAssignment\",\"id\":\"ParameterAssignment-3\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestCase-3/ParameterAssignment-3\"},{\"condition\":\"markiert\",\"parameter\":{\"___proxy\":true,\"className\":\"TestParameter\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestParameter-4\"},\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"ParameterAssignment-4\",\"className\":\"ParameterAssignment\",\"id\":\"ParameterAssignment-4\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestCase-3/ParameterAssignment-4\"},{\"condition\":\"not gelöscht\",\"parameter\":{\"___proxy\":true,\"className\":\"TestParameter\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestParameter-5\"},\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"ParameterAssignment-5\",\"className\":\"ParameterAssignment\",\"id\":\"ParameterAssignment-5\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestCase-3/ParameterAssignment-5\"},{\"condition\":\"markiert\",\"parameter\":{\"___proxy\":true,\"className\":\"TestParameter\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestParameter-6\"},\"___nsuri\":\"http://specmate.com/20190125/model/testspecification\",\"name\":\"ParameterAssignment-6\",\"className\":\"ParameterAssignment\",\"id\":\"ParameterAssignment-6\",\"url\":\"test-data/evalFolder/EvalRequirement-1/EvalModel-1/947a55cf-7bd7-21cf-5a36-b5003bdbdccc/TestCase-3/ParameterAssignment-6\"}]"; +// private static final String CEG_JSON = "{\"entries\":[{\"deletedObjects\":[],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"changes\":[{\"newValue\":\"New Node 2019-05-24 11:39:55\",\"feature\":\"name\",\"isCreate\":true,\"isDelete\":false,\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"objectName\":\"New Node 2019-05-24 11:39:55\",\"className\":\"Change\",\"objectType\":\"CEGNode\"}],\"className\":\"HistoryEntry\",\"user\":\"user\",\"timestamp\":\"1558690812479\"},{\"deletedObjects\":[],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"changes\":[{\"newValue\":\"New Node 2019-05-24 11:39:56\",\"feature\":\"name\",\"isCreate\":true,\"isDelete\":false,\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"objectName\":\"New Node 2019-05-24 11:39:56\",\"className\":\"Change\",\"objectType\":\"CEGNode\"}],\"className\":\"HistoryEntry\",\"user\":\"user\",\"timestamp\":\"1558690812479\"},{\"deletedObjects\":[],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"changes\":[{\"newValue\":\"New Node 2019-05-24 11:39:58\",\"feature\":\"name\",\"isCreate\":true,\"isDelete\":false,\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"objectName\":\"New Node 2019-05-24 11:39:58\",\"className\":\"Change\",\"objectType\":\"CEGNode\"}],\"className\":\"HistoryEntry\",\"user\":\"user\",\"timestamp\":\"1558690812479\"},{\"deletedObjects\":[],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"changes\":[{\"newValue\":\"New Connection 2019-05-24 11:40:01\",\"feature\":\"name\",\"isCreate\":true,\"isDelete\":false,\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"objectName\":\"New Connection 2019-05-24 11:40:01\",\"className\":\"Change\",\"objectType\":\"CEGConnection\"}],\"className\":\"HistoryEntry\",\"user\":\"user\",\"timestamp\":\"1558690812479\"},{\"deletedObjects\":[],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"changes\":[{\"newValue\":\"New Connection 2019-05-24 11:40:02\",\"feature\":\"name\",\"isCreate\":true,\"isDelete\":false,\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"objectName\":\"New Connection 2019-05-24 11:40:02\",\"className\":\"Change\",\"objectType\":\"CEGConnection\"}],\"className\":\"HistoryEntry\",\"user\":\"user\",\"timestamp\":\"1558690812479\"},{\"deletedObjects\":[],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"changes\":[{\"newValue\":\"CEG\",\"feature\":\"name\",\"isCreate\":true,\"isDelete\":false,\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"objectName\":\"CEG\",\"className\":\"Change\",\"objectType\":\"CEGModel\"}],\"className\":\"HistoryEntry\",\"user\":\"user\",\"timestamp\":\"1558690786401\"}],\"___nsuri\":\"http://specmate.com/20190125/model/history\",\"className\":\"History\"}"; + + public static void main(String[] args) throws Exception { + new MasterCloneTest(args); + } + + private RestClient masterClient; + private RestClient cloneClient; + private String currentSessionId; + private String lastActive; + private String projectName = "artificial"; + private Process masterProc; + private Process cloneProc; + private String master; + private String clone; + private String masterArgs; + private String cloneArgs; + private String specmate; + private CommandLine cmd; + private String urlBeginn = "/services/rest/"; + private String topFolder = "default"; + + public MasterCloneTest(String[] args) throws ParseException, IOException, InterruptedException { + masterClient = new RestClient("http://localhost:8080"); + cloneClient = new RestClient("http://localhost:8081"); + + cmd = parseCommandLine(args); + if (cmd == null) { + return; + } + + master = cmd.getOptionValue("m"); + clone = cmd.getOptionValue("c"); + masterArgs = cmd.getOptionValue("am"); + cloneArgs = cmd.getOptionValue("ac"); + specmate = cmd.getOptionValue("s"); + + File masterFile = new File(master); + if (!masterFile.exists()) { + System.err.println(masterFile.getAbsolutePath() + " does not exist."); + System.exit(1); + } + + File cloneFile = new File(clone); + if (!cloneFile.exists()) { + System.err.println(cloneFile.getAbsolutePath() + " does not exist."); + System.exit(1); + } + + File specmateJar = new File(specmate); + if (!specmateJar.exists()) { + System.err.println(specmateJar.getAbsolutePath() + " does not exist."); + System.exit(1); + } + + masterProc = startSpecmate("master", ANSI_YELLOW, specmate, master, masterArgs); + Thread.sleep(20000); + cloneProc = startSpecmate("clone", ANSI_GREEN, specmate, clone, cloneArgs); + Thread.sleep(20000); + + performTests(); + + System.in.read(); + masterProc.destroy(); + cloneProc.destroy(); + } + + private Process startSpecmate(String name, String color, String specmate, String config, String args) + throws IOException { + ProcessBuilder procBuilder = new ProcessBuilder("java", args, "-jar", specmate, "--configurationFile", config); + Process process = procBuilder.start(); + System.out.println(name + " started"); + startStreamReader(name, color, process.getInputStream(), System.out); + startStreamReader(name, color, process.getErrorStream(), System.err); + return process; + } + + private void startStreamReader(String name, String color, InputStream inputStream, PrintStream outputStream) { + new Thread() { + @Override + public void run() { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + try { + while ((line = reader.readLine()) != null) { + outputStream.println(color + name + ": " + line + ANSI_RESET); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }; + }.start(); + } + + private CommandLine parseCommandLine(String[] args) throws ParseException { + // create Options object + Options options = new Options(); + + options.addOption("m", "master", true, "Config file for the master"); + options.addOption("c", "clone", true, "Config file for the clone"); + options.addOption("s", "specmate", true, "Path to the specmate jar file"); + options.addOption("am", "argsMaster", true, "Java system arguments for the master"); + options.addOption("ac", "argsClone", true, "Java system arguments for the clone"); + + CommandLineParser parser = new DefaultParser(); + CommandLine cmdl = parser.parse(options, args); + + if (!cmdl.hasOption("m") || !cmdl.hasOption("c") || !cmdl.hasOption("s")) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java -jar masterclonetest.jar", options); + return null; + } + + return cmdl; + } + + private void performTests() { + try { +// testLogin(); +// killBoth(); +// restartBoth(); + testCreateModel(); + } catch (Exception e) { + System.exit(1); + } + System.exit(0); + } + + private void testCreateModel() throws InterruptedException, ParseException, IOException { + String ceg = "ceg"; + String cegNode1 = "cegNode1"; + String cegNode2 = "cegNode2"; + String connection = "connection"; + String testSpec = "testSpec"; + String testCase = "testCase"; + loginOnMaster(); + verifyLoggedInOnMaster(); + postToMaster(ceg); + postToMaster(cegNode1); + postToMaster(cegNode2); + postToMaster(connection); + postToMaster(testSpec); + killMaster(); +// postToMaster(testCase); + postToClone(testCase); + restartMaster(); + retrieveNumberOFTestCasesMaster(); + deleteCEGModell(); + } + + private void retrieveNumberOFTestCasesMaster() { + System.out.println("Retrieve Test Cases from Master"); + RestResult retrieve = masterClient.getList(urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/TestSpecification9/list"); + + JSONArray retrievedTestChilds = retrieve.getPayload(); + int numberTestChilds = retrievedTestChilds.length(); + System.out.println("Number of Test Cases: " + numberTestChilds); + if (numberTestChilds != 4) { + System.out.println("Not the right amount of Test Cases!"); + System.exit(1); + } + } + + private void postToMaster(String object) { + String url = ""; + switch (object) { + case "ceg": + System.out.println("Post CEG To Master"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/list"; + masterClient.post(url, new JSONObject(CEG_JSON)); + break; + case "cegNode1": + System.out.println("Post CEG Node1 To Master"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + masterClient.post(url, new JSONObject(CEG_NODE1_JSON)); + break; + case "cegNode2": + System.out.println("Post CEG Node2 To Master"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + masterClient.post(url, new JSONObject(CEG_NODE2_JSON)); + break; + case "connection": + System.out.println("Post Connection To Master"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + masterClient.post(url, new JSONObject(CEG_CONNECTION_JSON)); + break; + case "testSpec": + System.out.println("Post Test Specification To Master"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + masterClient.post(url, new JSONObject(CEG_TESTSPEC_JSON)); + break; + case "testCase": + System.out.println("Post Test Cases To Master"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/TestSpecification9/generateTests"; + masterClient.post(url, null); + break; + } + } + + private void postToClone(String object) { + String url = ""; + switch (object) { + case "ceg": + System.out.println("Post CEG to Clone"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/list"; + cloneClient.post(url, new JSONObject(CEG_JSON)); + break; + case "cegNode1": + System.out.println("Post CEG Node1 to Clone"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + cloneClient.post(url, new JSONObject(CEG_NODE1_JSON)); + break; + case "cegNode2": + System.out.println("Post CEG Node2 to Clone"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + cloneClient.post(url, new JSONObject(CEG_NODE2_JSON)); + break; + case "connection": + System.out.println("Post Connection to Clone"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + cloneClient.post(url, new JSONObject(CEG_CONNECTION_JSON)); + break; + case "testSpec": + System.out.println("Post Test Specification to Clone"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/list"; + cloneClient.post(url, new JSONObject(CEG_TESTSPEC_JSON)); + break; + case "testCase": + System.out.println("Post Test Cases to Clone"); + url = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/TestSpecification9/generateTests"; + cloneClient.post(url, null); + break; + } + } + + private void deleteCEGModell() { + System.out.println("Delete CEG Model"); + String deleteUrl = urlBeginn + projectName + "/" + topFolder + "/requirement1/TestCeg1/delete"; + masterClient.delete(deleteUrl); + } + + private void testLogin() throws ParseException, IOException, InterruptedException { + loginOnMaster(); + verifyLoggedInOnMaster(); + killMaster(); +// killClone(); +// restartClone(); + verifyLoggedInOnClone(); +// restartMaster(); +// verifyLoggedInOnMaster(); +// killMaster(); +// verifyLoggedInOnClone(); + logoutOnClone(); + restartMaster(); +// logoutOnMaster(); + verifyLoggedOutOnMaster(); + } + + private void restartBoth() throws ParseException, IOException, InterruptedException { + restartMaster(); + restartClone(); + } + + private void killBoth() throws InterruptedException { + killMaster(); + killClone(); + } + + private void killMaster() throws InterruptedException { + System.out.println("destroy master"); + masterProc.destroy(); + Thread.sleep(60000); + } + + private void killClone() throws InterruptedException { + System.out.println("destroy clone"); + cloneProc.destroy(); + Thread.sleep(60000); + } + + private void restartMaster() throws ParseException, IOException, InterruptedException { + System.out.println("restart master"); + masterProc = startSpecmate("master", ANSI_YELLOW, specmate, master, masterArgs); + Thread.sleep(30000); + } + + private void restartClone() throws ParseException, IOException, InterruptedException { + System.out.println("restart clone"); + masterProc = startSpecmate("clone", ANSI_GREEN, specmate, clone, cloneArgs); + Thread.sleep(30000); + } + + private void loginOnMaster() { + System.out.println("login master"); + RestResult result = masterClient.post("/services/rest/login", new JSONObject(LOGIN_JSON)); + currentSessionId = result.getPayload().getString("id"); + lastActive = result.getPayload().getString("lastActive"); + + String authorizationHeader = "Token " + currentSessionId; + + // TODO:besser + masterClient.setCookie("specmate-user-token", getSpecmateToken()); + cloneClient.setCookie("specmate-user-token", getSpecmateToken()); + masterClient.setHeader("Authorization", authorizationHeader); + cloneClient.setHeader("Authorization", authorizationHeader); + } + + private String getSpecmateToken() { + projectName = "artificial"; + //projectName = "test-data"; + return "{\"session\":{\"lastActive\":\"" + + lastActive + + "\",\"allowedPathPattern\":\".+services/rest/" + + projectName + + "/.*\",\"___nsuri\":\"http://specmate.com/20190125/model/user\",\"libraryFolders\":[],\"TargetSystem\":\"NONE\",\"className\":\"UserSession\",\"id\":\"" + + currentSessionId + + "\",\"userName\":\"user\",\"SourceSystem\":\"ALL\",\"url\":\"" + + currentSessionId + + "\"},\"project\":\"" + + projectName + + "\",\"libraryFolders\":[]}"; + } + + private void verifyLoggedInOnMaster() throws AuthenticationException { + RestResult result = masterClient.getList("services/rest/" + projectName + "/list"); + System.out.println("Status Code Master: " + result.getResponse().getStatus()); + if (result.getResponse().getStatus() != 200) { + throw new AuthenticationException("Not logged in"); + } + } + + private void verifyLoggedInOnClone() throws AuthenticationException { + RestResult result = cloneClient.getList("services/rest/" + projectName + "/list"); + System.out.println("Status Code Clone: " + result.getResponse().getStatus()); + if (result.getResponse().getStatus() != 200) { + throw new AuthenticationException("Not logged in"); + } + } + + private void logoutOnClone() { + System.out.println("logout clone"); + RestResult result = cloneClient.get("/services/rest/logout"); + } + + private void logoutOnMaster() { + System.out.println("logout master"); + RestResult result = masterClient.get("/services/rest/logout"); + } + + private void verifyLoggedOutOnMaster() throws AuthenticationException { + RestResult result = masterClient.getList("services/rest/" + projectName + "/list"); + System.out.println("Status Code Master: " + result.getResponse().getStatus()); + if (result.getResponse().getStatus() == 200) { + throw new AuthenticationException("Still logged in"); + } + } + +} diff --git a/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/RestClient.java b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/RestClient.java new file mode 100644 index 000000000..c27c46ac2 --- /dev/null +++ b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/RestClient.java @@ -0,0 +1,147 @@ +package com.specmate.systemtest.masterclonetest; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONTokener; + +public class RestClient { + + private Client restClient; + private String restUrl; + private int timeout; + private Map cookies = new HashMap<>(); + private Map headers = new HashMap<>(); + + public RestClient(String restUrl, int timeout) { + restClient = initializeClient(); + this.restUrl = restUrl; + this.timeout = timeout; + } + + public void close() { + restClient.close(); + } + + public RestClient(String restUrl) { + this(restUrl, 5000); + } + + private Client initializeClient() { + ClientConfig config = new ClientConfig(); + config.property(ClientProperties.CONNECT_TIMEOUT, timeout); + config.property(ClientProperties.READ_TIMEOUT, timeout); + Client client = ClientBuilder.newBuilder().withConfig(config).build(); + return client; + } + + private Response rawGet(String url, String... params) { + Invocation.Builder invocationBuilder = getInvocationBuilder(url, params); + Response response = invocationBuilder.get(); + return response; + } + + private Invocation.Builder getInvocationBuilder(String url, String... params) { + UriBuilder uriBuilder = UriBuilder.fromUri(restUrl); + uriBuilder.path(url); + for (int i = 0; i < params.length; i += 2) { + if (i < params.length - 1) { + uriBuilder.queryParam(params[i], params[i + 1]); + } + } + WebTarget getTarget = restClient.target(uriBuilder); + Invocation.Builder invocationBuilder = getTarget.request(); + for (Entry cookie : cookies.entrySet()) { + invocationBuilder.cookie(cookie.getKey(), cookie.getValue()); + } + for (Entry header : headers.entrySet()) { + invocationBuilder.header(header.getKey(), header.getValue()); + } + return invocationBuilder; + } + + public RestResult get(String url, String... params) { + Response response = rawGet(url, params); + String result = response.readEntity(String.class); + if (response.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) { + if(isJSON(result)) { + JSONTokener token = new JSONTokener(result); + JSONObject obj = new JSONObject(token); + return new RestResult<>(response, url, obj); + } + + } + return new RestResult<>(response, url, null); + + } + + private boolean isJSON(String result) { + String trimmed = result.trim(); + return trimmed.startsWith("{") || trimmed.startsWith("["); + } + + public RestResult getList(String url, String... params) { + Response response = rawGet(url, params); + String result = response.readEntity(String.class); + if (response.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) { + JSONTokener token = new JSONTokener(result); + JSONArray arr = new JSONArray(token); + return new RestResult<>(response, url, arr); + } else { + return new RestResult<>(response, url, null); + } + } + + public RestResult post(String url, JSONObject jsonObject) { + Invocation.Builder invocationBuilder = getInvocationBuilder(url); + Entity entity; + if (jsonObject == null) { + entity = null; + } else { + entity = Entity.entity(jsonObject.toString(), "application/json;charset=utf-8"); + } + Response response = invocationBuilder.post(entity); + String result = response.readEntity(String.class); + if (response.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) { + return new RestResult<>(response, url, new JSONObject(new JSONTokener(result))); + } else { + return new RestResult<>(response, url, null); + } + } + + public RestResult put(String url, JSONObject objectJson) { + Invocation.Builder invocationBuilder = getInvocationBuilder(url); + Response response = invocationBuilder.put(Entity.json(objectJson.toString())); + return new RestResult<>(response, url, null); + } + + public RestResult delete(String url) { + Invocation.Builder invocationBuilder = getInvocationBuilder(url); + Response response = invocationBuilder.delete(); + return new RestResult<>(response, url, null); + } + + public void setCookie(String key, String value) { + cookies.put(key, value); + } + + public void setHeader(String key, String value) { + headers.put(key, value); + } + +} diff --git a/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/RestResult.java b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/RestResult.java new file mode 100644 index 000000000..694bac2a1 --- /dev/null +++ b/system-tests/master-clone-test/src/com/specmate/systemtest/masterclonetest/RestResult.java @@ -0,0 +1,44 @@ +package com.specmate.systemtest.masterclonetest; + +import javax.ws.rs.core.Response; + +public class RestResult { + + private Response response; + private String url; + private T payload; + + public RestResult(Response response, String url, T payload) { + super(); + this.response = response; + this.url = url; + this.payload = payload; + } + + public Response getResponse() { + return response; + } + + public void setResponse(Response response) { + this.response = response; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public T getPayload() { + return payload; + } + + public void setPayload(T payload) { + this.payload = payload; + } + + + +} diff --git a/system-tests/master-clone-test/workspace/.metadata/.log b/system-tests/master-clone-test/workspace/.metadata/.log new file mode 100644 index 000000000..a3908dc57 --- /dev/null +++ b/system-tests/master-clone-test/workspace/.metadata/.log @@ -0,0 +1,314 @@ +!SESSION 2019-06-07 15:53:23.218 ----------------------------------------------- +eclipse.buildId=unknown +java.version=1.8.0_211 +java.vendor=Oracle Corporation +BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=de_DE + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:23.218 +!MESSAGE Durable locking is not enabled for view Transaction[2:1] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[2:1] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CloseViewIndication.indicating(CloseViewIndication.java:37) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CDOServerIndication.indicating(CDOServerIndication.java:108) + at org.eclipse.net4j.signal.IndicationWithResponse.doExtendedInput(IndicationWithResponse.java:100) + at org.eclipse.net4j.signal.Signal.doInput(Signal.java:380) + at org.eclipse.net4j.signal.IndicationWithResponse.execute(IndicationWithResponse.java:73) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CDOServerReadIndication.execute(CDOServerReadIndication.java:36) + at org.eclipse.net4j.signal.Signal.runSync(Signal.java:286) + at org.eclipse.net4j.signal.Signal.run(Signal.java:165) + at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.lang.Thread.run(Unknown Source) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:23.515 +!MESSAGE Durable locking is not enabled for view Transaction[2:5] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[2:5] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CloseViewIndication.indicating(CloseViewIndication.java:37) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CDOServerIndication.indicating(CDOServerIndication.java:108) + at org.eclipse.net4j.signal.IndicationWithResponse.doExtendedInput(IndicationWithResponse.java:100) + at org.eclipse.net4j.signal.Signal.doInput(Signal.java:380) + at org.eclipse.net4j.signal.IndicationWithResponse.execute(IndicationWithResponse.java:73) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CDOServerReadIndication.execute(CDOServerReadIndication.java:36) + at org.eclipse.net4j.signal.Signal.runSync(Signal.java:286) + at org.eclipse.net4j.signal.Signal.run(Signal.java:165) + at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.lang.Thread.run(Unknown Source) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:23.538 +!MESSAGE Durable locking is not enabled for view Transaction[2:6] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[2:6] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CloseViewIndication.indicating(CloseViewIndication.java:37) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CDOServerIndication.indicating(CDOServerIndication.java:108) + at org.eclipse.net4j.signal.IndicationWithResponse.doExtendedInput(IndicationWithResponse.java:100) + at org.eclipse.net4j.signal.Signal.doInput(Signal.java:380) + at org.eclipse.net4j.signal.IndicationWithResponse.execute(IndicationWithResponse.java:73) + at org.eclipse.emf.cdo.server.internal.net4j.protocol.CDOServerReadIndication.execute(CDOServerReadIndication.java:36) + at org.eclipse.net4j.signal.Signal.runSync(Signal.java:286) + at org.eclipse.net4j.signal.Signal.run(Signal.java:165) + at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.lang.Thread.run(Unknown Source) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:33.323 +!MESSAGE Durable locking is not enabled for view Transaction[1:1] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[1:1] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.handleCommitInfo(SynchronizableRepository.java:319) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$CommitRunnable.doRun(RepositorySynchronizer.java:658) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$RetryingRunnable.run(RepositorySynchronizer.java:581) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:26) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:1) + at org.eclipse.net4j.util.concurrent.QueueWorker.doWork(QueueWorker.java:78) + at org.eclipse.net4j.util.concurrent.QueueWorker.work(QueueWorker.java:70) + at org.eclipse.net4j.util.concurrent.Worker$WorkerThread.run(Worker.java:217) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:33.715 +!MESSAGE Durable locking is not enabled for view Transaction[1:2] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[1:2] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.handleCommitInfo(SynchronizableRepository.java:319) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$CommitRunnable.doRun(RepositorySynchronizer.java:658) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$RetryingRunnable.run(RepositorySynchronizer.java:581) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:26) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:1) + at org.eclipse.net4j.util.concurrent.QueueWorker.doWork(QueueWorker.java:78) + at org.eclipse.net4j.util.concurrent.QueueWorker.work(QueueWorker.java:70) + at org.eclipse.net4j.util.concurrent.Worker$WorkerThread.run(Worker.java:217) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:33.902 +!MESSAGE Durable locking is not enabled for view Transaction[1:3] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[1:3] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.handleCommitInfo(SynchronizableRepository.java:319) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$CommitRunnable.doRun(RepositorySynchronizer.java:658) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$RetryingRunnable.run(RepositorySynchronizer.java:581) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:26) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:1) + at org.eclipse.net4j.util.concurrent.QueueWorker.doWork(QueueWorker.java:78) + at org.eclipse.net4j.util.concurrent.QueueWorker.work(QueueWorker.java:70) + at org.eclipse.net4j.util.concurrent.Worker$WorkerThread.run(Worker.java:217) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:33.977 +!MESSAGE Durable locking is not enabled for view Transaction[1:4] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[1:4] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.handleCommitInfo(SynchronizableRepository.java:319) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$CommitRunnable.doRun(RepositorySynchronizer.java:658) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$RetryingRunnable.run(RepositorySynchronizer.java:581) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:26) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:1) + at org.eclipse.net4j.util.concurrent.QueueWorker.doWork(QueueWorker.java:78) + at org.eclipse.net4j.util.concurrent.QueueWorker.work(QueueWorker.java:70) + at org.eclipse.net4j.util.concurrent.Worker$WorkerThread.run(Worker.java:217) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:34.040 +!MESSAGE Durable locking is not enabled for view Transaction[1:5] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[1:5] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.handleCommitInfo(SynchronizableRepository.java:319) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$CommitRunnable.doRun(RepositorySynchronizer.java:658) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$RetryingRunnable.run(RepositorySynchronizer.java:581) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:26) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:1) + at org.eclipse.net4j.util.concurrent.QueueWorker.doWork(QueueWorker.java:78) + at org.eclipse.net4j.util.concurrent.QueueWorker.work(QueueWorker.java:70) + at org.eclipse.net4j.util.concurrent.Worker$WorkerThread.run(Worker.java:217) + +!ENTRY org.eclipse.net4j.util 4 0 2019-06-07 15:53:34.118 +!MESSAGE Durable locking is not enabled for view Transaction[1:6] +!STACK 0 +java.lang.IllegalStateException: Durable locking is not enabled for view Transaction[1:6] + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockOnMaster(SynchronizableRepository.java:702) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlockThrough(SynchronizableRepository.java:710) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.unlock(SynchronizableRepository.java:693) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:98) + at org.eclipse.emf.cdo.internal.server.LockingManager$2.onRemoved(LockingManager.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter$1.removed(ContainerEventAdapter.java:93) + at org.eclipse.net4j.util.container.SingleDeltaContainerEvent.accept(SingleDeltaContainerEvent.java:91) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyContainerEvent(ContainerEventAdapter.java:69) + at org.eclipse.net4j.util.container.ContainerEventAdapter.notifyEvent(ContainerEventAdapter.java:58) + at org.eclipse.net4j.util.event.Notifier.fireEventSafe(Notifier.java:167) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:113) + at org.eclipse.net4j.util.event.Notifier.fireEvent(Notifier.java:89) + at org.eclipse.net4j.util.container.Container.fireEvent(Container.java:62) + at org.eclipse.net4j.util.container.Container.fireContainerEvent(Container.java:67) + at org.eclipse.net4j.util.container.Container.fireElementRemovedEvent(Container.java:77) + at org.eclipse.emf.cdo.internal.server.Session.viewClosed(Session.java:392) + at org.eclipse.emf.cdo.internal.server.View.doDeactivate(View.java:374) + at org.eclipse.net4j.util.lifecycle.Lifecycle.internalDeactivate(Lifecycle.java:129) + at org.eclipse.net4j.util.lifecycle.Lifecycle.deactivate(Lifecycle.java:167) + at org.eclipse.emf.cdo.internal.server.View.close(View.java:366) + at org.eclipse.emf.cdo.internal.server.syncing.SynchronizableRepository.handleCommitInfo(SynchronizableRepository.java:319) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$CommitRunnable.doRun(RepositorySynchronizer.java:658) + at org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer$RetryingRunnable.run(RepositorySynchronizer.java:581) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:26) + at org.eclipse.net4j.util.concurrent.QueueRunner2.work(QueueRunner2.java:1) + at org.eclipse.net4j.util.concurrent.QueueWorker.doWork(QueueWorker.java:78) + at org.eclipse.net4j.util.concurrent.QueueWorker.work(QueueWorker.java:70) + at org.eclipse.net4j.util.concurrent.Worker$WorkerThread.run(Worker.java:217) + +!ENTRY specmate-emfrest 4 0 2019-06-07 15:54:38.839 +!MESSAGE E9: Could not commit after 11 attempts.