diff options
228 files changed, 49659 insertions, 0 deletions
diff --git a/org.eclipse.emf.cdo.server.db/.classpath b/org.eclipse.emf.cdo.server.db/.classpath new file mode 100644 index 0000000000..64c5e31b7a --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.emf.cdo.server.db/.options b/org.eclipse.emf.cdo.server.db/.options new file mode 100644 index 0000000000..f4f74f846d --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.options @@ -0,0 +1,3 @@ +# Debugging and tracing options + +org.eclipse.emf.cdo.server.db/debug = true diff --git a/org.eclipse.emf.cdo.server.db/.project b/org.eclipse.emf.cdo.server.db/.project new file mode 100644 index 0000000000..4bb79d4880 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.project @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.emf.cdo.server.db</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.emf.cdo.releng.version.VersionBuilder</name> + <arguments> + <dictionary> + <key>release.path</key> + <value>/org.eclipse.emf.cdo.releng/release.xml</value> + </dictionary> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + <nature>org.eclipse.emf.cdo.releng.version.VersionNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.emf.cdo.server.db/.settings/.api_filters b/org.eclipse.emf.cdo.server.db/.settings/.api_filters new file mode 100644 index 0000000000..475cf591c8 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/.api_filters @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.emf.cdo.server.db" version="2">
+ <resource path="src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java" type="org.eclipse.emf.cdo.server.internal.db.DBStoreAccessor">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="DurableLocking"/>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="DBStoreAccessor"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..eca460f479 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Mon Jul 04 13:01:28 CEST 2011
+eclipse.preferences.version=1
+encoding//model/org.eclipse.emf.cdo.defs.ecorediag=UTF-8
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..611d1a92fb --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,373 @@ +#Fri Sep 02 05:40:11 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,*.ucls,doc-files/,package.html,package-info.java
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled
+org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,HIGH,LOW,LOW,LOW,LOW,LOW
+org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX,PERF,MEM,POLISH,@generated NOT,@ADDED
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=2
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs new file mode 100644 index 0000000000..4658ec1435 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs @@ -0,0 +1,3 @@ +#Fri Sep 02 05:38:34 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.launching.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE=ignore
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..4277817dad --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,120 @@ +#Thu Feb 04 09:44:24 CET 2010 +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=false +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_parentheses_in_expressions=true +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_EMFT +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_EMFT +formatter_settings_version=11 +org.eclipse.jdt.ui.exception.name=ex +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=org.eclipse.emf.cdo;org.eclipse.emf.internal.cdo;org.eclipse.net4j;org.eclipse.internal.net4j;org.eclipse.emf;org.eclipse;com;org;javax;java; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.keywordthis=false +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.staticondemandthreshold=99 +org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="false" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment"/><template autoinsert\="false" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment"/><template autoinsert\="false" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment"/><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n * \r\n * Contributors\:\r\n * Eike Stepper - initial API and implementation\r\n */</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author Eike Stepper\r\n */</template><template autoinsert\="false" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment"/><template autoinsert\="false" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment"/><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="false" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment"/><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="false" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">${exception_var}.printStackTrace();</template><template autoinsert\="false" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">${body_statement}</template><template autoinsert\="false" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates> +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=true +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=true +sp_cleanup.use_this_for_non_static_field_access=true +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=true +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000000..864e30fe5d --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,3 @@ +#Thu Feb 04 09:44:24 CET 2010 +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..b050639a54 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Thu Feb 04 09:44:24 CET 2010 +eclipse.preferences.version=1 +project.repository.kind=bugzilla +project.repository.url=https\://bugs.eclipse.org/bugs diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..2f50f36c0c --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs @@ -0,0 +1,3 @@ +#Thu Feb 04 09:44:24 CET 2010 +commit.comment.template=[${task.id}] ${task.description} \r\n${task.url} +eclipse.preferences.version=1 diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 0000000000..f8d0a0660e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs @@ -0,0 +1,94 @@ +#Thu Feb 04 09:44:24 CET 2010 +ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error +CLASS_ELEMENT_TYPE_ADDED_METHOD=Error +CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error +CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error +CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error +CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error +ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error +ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error +ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +FIELD_ELEMENT_TYPE_ADDED_VALUE=Error +FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error +FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error +FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error +FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error +FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error +ILLEGAL_EXTEND=Warning +ILLEGAL_IMPLEMENT=Warning +ILLEGAL_INSTANTIATE=Warning +ILLEGAL_OVERRIDE=Warning +ILLEGAL_REFERENCE=Warning +INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error +INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error +INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error +INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error +INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error +INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +INVALID_JAVADOC_TAG=Ignore +INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Ignore +LEAK_EXTEND=Warning +LEAK_FIELD_DECL=Warning +LEAK_IMPLEMENT=Warning +LEAK_METHOD_PARAM=Warning +LEAK_METHOD_RETURN_TYPE=Warning +METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error +METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error +METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +UNUSED_PROBLEM_FILTERS=Ignore +automatically_removed_unused_problem_filters=Disabled +eclipse.preferences.version=1 +incompatible_api_component_version=Error +incompatible_api_component_version_include_major_without_breaking_change=Disabled +incompatible_api_component_version_include_minor_without_api_change=Disabled +invalid_since_tag_version=Error +malformed_since_tag=Error +missing_since_tag=Error +report_api_breakage_when_major_version_incremented=Disabled +report_resolution_errors_api_component=Warning diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs new file mode 100644 index 0000000000..c6b96bb45e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs @@ -0,0 +1,31 @@ +#Thu Feb 04 09:44:24 CET 2010 +compilers.f.unresolved-features=1 +compilers.f.unresolved-plugins=1 +compilers.incompatible-environment=1 +compilers.p.build=1 +compilers.p.build.bin.includes=1 +compilers.p.build.java.compliance=1 +compilers.p.build.missing.output=2 +compilers.p.build.output.library=1 +compilers.p.build.source.library=1 +compilers.p.build.src.includes=1 +compilers.p.deprecated=1 +compilers.p.discouraged-class=1 +compilers.p.internal=1 +compilers.p.missing-packages=1 +compilers.p.missing-version-export-package=1 +compilers.p.missing-version-import-package=1 +compilers.p.missing-version-require-bundle=1 +compilers.p.no-required-att=0 +compilers.p.not-externalized-att=2 +compilers.p.unknown-attribute=1 +compilers.p.unknown-class=1 +compilers.p.unknown-element=1 +compilers.p.unknown-identifier=1 +compilers.p.unknown-resource=1 +compilers.p.unresolved-ex-points=0 +compilers.p.unresolved-import=0 +compilers.s.create-docs=false +compilers.s.doc-folder=doc +compilers.s.open-tags=1 +eclipse.preferences.version=1 diff --git a/org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF b/org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..b3af04016e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: org.eclipse.emf.cdo.server.db;singleton:=true +Bundle-Version: 4.1.0.qualifier +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Bundle-ActivationPolicy: lazy +Bundle-Activator: org.eclipse.emf.cdo.server.internal.db.bundle.OM$Activator +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-ClassPath: . +Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)", + org.eclipse.net4j.db;bundle-version="[4.0.0,5.0.0)";visibility:=reexport, + org.eclipse.emf.cdo.server;bundle-version="[4.0.0,5.0.0)";visibility:=reexport +Export-Package: org.eclipse.emf.cdo.server.db;version="4.1.0", + org.eclipse.emf.cdo.server.db.mapping;version="4.1.0", + org.eclipse.emf.cdo.server.internal.db;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db", + org.eclipse.emf.cdo.server.internal.db.bundle;version="4.1.0";x-internal:=true, + org.eclipse.emf.cdo.server.internal.db.jdbc;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db", + org.eclipse.emf.cdo.server.internal.db.mapping;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db", + org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db", + org.eclipse.emf.cdo.server.internal.db.messages;version="4.1.0";x-internal:=true diff --git a/org.eclipse.emf.cdo.server.db/about.html b/org.eclipse.emf.cdo.server.db/about.html new file mode 100644 index 0000000000..d35d5aed64 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 5, 2007</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("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 <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("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 <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html> diff --git a/org.eclipse.emf.cdo.server.db/about.ini b/org.eclipse.emf.cdo.server.db/about.ini new file mode 100644 index 0000000000..32006ae5d6 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/about.ini @@ -0,0 +1,15 @@ +# about.ini +# contains information about a feature +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# "%key" are externalized strings defined in about.properties +# This file does not need to be translated. + +# Property "aboutText" contains blurb for "About" dialog (translated) +aboutText=%featureText + +# Property "featureImage" contains path to feature image (32x32) +featureImage=modeling32.png + +# Property "appName" contains name of the application (translated) +appName=%featureName + diff --git a/org.eclipse.emf.cdo.server.db/about.mappings b/org.eclipse.emf.cdo.server.db/about.mappings new file mode 100644 index 0000000000..bddaab4310 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/about.mappings @@ -0,0 +1,6 @@ +# about.mappings +# contains fill-ins for about.properties +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# This file does not need to be translated. + +0=@build@
\ No newline at end of file diff --git a/org.eclipse.emf.cdo.server.db/about.properties b/org.eclipse.emf.cdo.server.db/about.properties new file mode 100644 index 0000000000..949ccadf4e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/about.properties @@ -0,0 +1,31 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Eike Stepper - initial API and implementation + +# NLS_MESSAGEFORMAT_VAR + +# ============================================================================== +# Do not change the properties between this line and the last line containing: +# %%% END OF TRANSLATED PROPERTIES %%% +# Instead, either redefine an existing property, or create a new property, +# append it to the end of the file, and change the code to use the new name. +# ============================================================================== + +featureName = CDO Model Repository Server DB +featureText = CDO Model Repository Server DB\n\ +Version: {featureVersion}\n\ +Build id: {0}\n\ +\n\ +Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. All rights reserved.\n\ +\n\ +Visit http://www.eclipse.org/cdo + +# ============================================================================== +# %%% END OF TRANSLATED PROPERTIES %%% +# The above properties have been shipped for translation. +# ============================================================================== diff --git a/org.eclipse.emf.cdo.server.db/build.properties b/org.eclipse.emf.cdo.server.db/build.properties new file mode 100644 index 0000000000..7962001d93 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/build.properties @@ -0,0 +1,29 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Eike Stepper - initial API and implementation + +# NLS_MESSAGEFORMAT_VAR + +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + .options,\ + about.html,\ + copyright.txt,\ + plugin.xml,\ + schema/,\ + plugin.properties,\ + about.ini,\ + about.mappings,\ + about.properties,\ + modeling32.png +src.includes = about.html,\ + copyright.txt + +org.eclipse.emf.cdo.releng.javadoc.project = org.eclipse.emf.cdo.doc diff --git a/org.eclipse.emf.cdo.server.db/copyright.txt b/org.eclipse.emf.cdo.server.db/copyright.txt new file mode 100644 index 0000000000..e921242cf0 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/copyright.txt @@ -0,0 +1,8 @@ +Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html + +Contributors: + Eike Stepper - initial API and implementation
\ No newline at end of file diff --git a/org.eclipse.emf.cdo.server.db/modeling32.png b/org.eclipse.emf.cdo.server.db/modeling32.png Binary files differnew file mode 100644 index 0000000000..6b08de2ada --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/modeling32.png diff --git a/org.eclipse.emf.cdo.server.db/plugin.properties b/org.eclipse.emf.cdo.server.db/plugin.properties new file mode 100644 index 0000000000..ade0c88bdc --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/plugin.properties @@ -0,0 +1,13 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Eike Stepper - initial API and implementation + +pluginName = CDO Model Repository Server DB +providerName = Eclipse Modeling Project + +extension-point.name = CDO Mapping Strategies
\ No newline at end of file diff --git a/org.eclipse.emf.cdo.server.db/plugin.xml b/org.eclipse.emf.cdo.server.db/plugin.xml new file mode 100644 index 0000000000..6abb1a84a7 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/plugin.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<!-- + Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + Contributors: + Eike Stepper - initial API and implementation +--> + +<plugin> + + <extension-point id="mappingStrategies" name="%extension-point.name" schema="schema/mappingStrategies.exsd"/> + + <extension + point="org.eclipse.net4j.util.factories"> + <factory + class="org.eclipse.emf.cdo.server.internal.db.DBBrowserPage$Factory" + productGroup="org.eclipse.emf.cdo.server.browserPages" + type="db"> + </factory> + </extension> + + <extension + point="org.eclipse.emf.cdo.server.storeFactories"> + <storeFactory + class="org.eclipse.emf.cdo.server.internal.db.DBStoreFactory" + storeType="db"> + </storeFactory> + </extension> + + <extension + point="org.eclipse.emf.cdo.server.db.mappingStrategies"> + <mappingStrategy + class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalMappingStrategy" + type="horizontal"/> + <mappingStrategy + class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalNonAuditMappingStrategy" + type="horizontalNonAuditing"/> + <mappingStrategy + class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategy" + type="horizontalAuditing"/> + <mappingStrategy + class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategy" + type="horizontalBranching"/> + <mappingStrategy + class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategyWithRanges" + type="horizontalAuditingWithRanges"/> + <mappingStrategy + class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategyWithRanges" + type="horizontalBranchingWithRanges"/> + </extension> + +</plugin> diff --git a/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd b/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd new file mode 100644 index 0000000000..c708d5537b --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd @@ -0,0 +1,113 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.emf.cdo.server.db"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.emf.cdo.server.db" id="mappingStrategies" name="CDO Mapping Strategies"/> + </appInfo> + <documentation> + [Enter description of this extension point.] + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="mappingStrategy" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="mappingStrategy"> + <complexType> + <attribute name="type" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn=":org.eclipse.emf.cdo.server.db.IMappingStrategy"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + [Enter the first release in which this extension point appears.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + [Enter information about supplied implementation of this extension point.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.<br> +All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema> diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java new file mode 100644 index 0000000000..b5cb29a567 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + */ +package org.eclipse.emf.cdo.server.db; + +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.DBBrowserPage; +import org.eclipse.emf.cdo.server.internal.db.DBStore; +import org.eclipse.emf.cdo.server.internal.db.SmartPreparedStatementCache; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategyWithRanges; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategyWithRanges; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalNonAuditMappingStrategy; + +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.container.IManagedContainer; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +/** + * @author Eike Stepper + */ +public final class CDODBUtil +{ + /** + * @since 2.0 + */ + public static final int DEFAULT_STATEMENT_CACHE_CAPACITY = 200; + + /** + * @since 2.0 + */ + public static final String EXT_POINT_MAPPING_STRATEGIES = "mappingStrategies"; //$NON-NLS-1$ + + /** + * @since 4.1 + */ + public static final String PROP_WITH_RANGES = "withRanges"; + + /** + * @since 4.1 + */ + public static final String PROP_COPY_ON_BRANCH = "copyOnBranch"; + + private CDODBUtil() + { + } + + /** + * @since 4.0 + */ + public static void prepareContainer(IManagedContainer container) + { + container.registerFactory(new DBBrowserPage.Factory()); + } + + /** + * @since 2.0 + */ + public static IDBStore createStore(IMappingStrategy mappingStrategy, IDBAdapter dbAdapter, + IDBConnectionProvider dbConnectionProvider) + { + DBStore store = new DBStore(); + store.setMappingStrategy(mappingStrategy); + store.setDBAdapter(dbAdapter); + store.setDbConnectionProvider(dbConnectionProvider); + return store; + } + + /** + * @since 2.0 + */ + public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing) + { + return createHorizontalMappingStrategy(auditing, false, false); + } + + /** + * @since 3.0 + */ + public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing, boolean branching) + { + return createHorizontalMappingStrategy(auditing, branching, false); + } + + /** + * @since 4.1 + */ + public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing, boolean branching, boolean withRanges) + { + if (branching) + { + if (auditing) + { + if (withRanges) + { + return new HorizontalBranchingMappingStrategyWithRanges(); + } + + return new HorizontalBranchingMappingStrategy(); + } + + throw new IllegalArgumentException("Misconfiguration: Branching requires Auditing!"); + } + + if (auditing) + { + if (withRanges) + { + return new HorizontalAuditMappingStrategyWithRanges(); + } + + return new HorizontalAuditMappingStrategy(); + } + + return new HorizontalNonAuditMappingStrategy(); + } + + /** + * Creates a horizontal {@link IMappingStrategy mapping strategy} that supports all valid combinations of auditing and + * branching. + * + * @since 4.1 + */ + public static IMappingStrategy createHorizontalMappingStrategy() + { + return new HorizontalMappingStrategy(); + } + + /** + * Can only be used when Eclipse is running. In standalone scenarios create the mapping strategy instance by directly + * calling the constructor of the mapping strategy class. + * + * @see #createHorizontalMappingStrategy(boolean) + * @see #createHorizontalMappingStrategy(boolean, boolean) + * @since 2.0 + */ + public static IMappingStrategy createMappingStrategy(String type) + { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, EXT_POINT_MAPPING_STRATEGIES); + for (final IConfigurationElement element : elements) + { + if ("mappingStrategy".equals(element.getName())) //$NON-NLS-1$ + { + String typeAttr = element.getAttribute("type"); //$NON-NLS-1$ + if (ObjectUtil.equals(typeAttr, type)) + { + try + { + return (IMappingStrategy)element.createExecutableExtension("class"); //$NON-NLS-1$ + } + catch (CoreException ex) + { + throw WrappedException.wrap(ex); + } + } + } + } + + return null; + } + + /** + * Creates a prepared statement cache with the {@link CDODBUtil#DEFAULT_STATEMENT_CACHE_CAPACITY default capacity}. + * + * @since 2.0 + * @see CDODBUtil#createStatementCache(int) + */ + public static IPreparedStatementCache createStatementCache() + { + return createStatementCache(DEFAULT_STATEMENT_CACHE_CAPACITY); + } + + /** + * Creates a prepared statement cache with the given capacity. + * + * @since 2.0 + */ + public static IPreparedStatementCache createStatementCache(int capacity) + { + return new SmartPreparedStatementCache(capacity); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java new file mode 100644 index 0000000000..c1c6d23498 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + */ +package org.eclipse.emf.cdo.server.db; + +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; + +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.ddl.IDBSchema; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IDBStore extends IStore, IDBConnectionProvider, CanHandleClientAssignedIDs +{ + /** + * @since 2.0 + */ + public IMappingStrategy getMappingStrategy(); + + /** + * @since 4.0 + */ + public IIDHandler getIDHandler(); + + public IDBAdapter getDBAdapter(); + + public IDBSchema getDBSchema(); + + /** + * Get the meta data manager associated with this DBStore. + * + * @since 2.0 + */ + public IMetaDataManager getMetaDataManager(); + + /** + * @since 2.0 + */ + public IDBStoreAccessor getReader(ISession session); + + /** + * @since 2.0 + */ + public IDBStoreAccessor getWriter(ITransaction transaction); + + /** + * @since 4.0 + */ + public interface Props + { + public static final String CONNECTION_KEEPALIVE_PERIOD = "connectionKeepAlivePeriod"; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java new file mode 100644 index 0000000000..2d507942be --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db; + +import org.eclipse.emf.cdo.server.IStoreAccessor; + +import java.sql.Connection; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IDBStoreAccessor extends IStoreAccessor.Raw +{ + public IDBStore getStore(); + + public Connection getConnection(); + + /** + * @since 2.0 + */ + public IPreparedStatementCache getStatementCache(); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java new file mode 100644 index 0000000000..67f9bea078 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db; + +import org.eclipse.emf.cdo.server.IStoreChunkReader; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IDBStoreChunkReader extends IStoreChunkReader +{ + /** + * @since 2.0 + */ + public IDBStoreAccessor getAccessor(); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java new file mode 100644 index 0000000000..0bbee5d23a --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java @@ -0,0 +1,89 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Comparator;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IIDHandler extends Comparator<CDOID>
+{
+ public IDBStore getStore();
+
+ public DBType getDBType();
+
+ public Set<ObjectType> getObjectIDTypes();
+
+ public ITypeMapping getObjectTypeMapping();
+
+ public CDOID createCDOID(String val);
+
+ public boolean isLocalCDOID(CDOID id);
+
+ public CDOID getLastObjectID();
+
+ public void setLastObjectID(CDOID lastObjectID);
+
+ /**
+ * @since 4.1
+ */
+ public void adjustLastObjectID(CDOID maxID);
+
+ public CDOID getNextLocalObjectID();
+
+ public void setNextLocalObjectID(CDOID nextLocalObjectID);
+
+ public CDOID getNextCDOID(CDORevision revision);
+
+ public void appendCDOID(StringBuilder builder, CDOID id);
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException;
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException;
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException;
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException;
+
+ public CDOID getMinCDOID();
+
+ public CDOID getMaxCDOID();
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime);
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id);
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime, OMMonitor fork)
+ throws IOException;
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java new file mode 100644 index 0000000000..972d735afd --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 271444: [DB] Multiple refactorings + * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations + * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations + */ +package org.eclipse.emf.cdo.server.db; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.EPackage; + +import java.io.IOException; +import java.sql.Connection; +import java.util.Collection; + +/** + * @author Eike Stepper + * @since 2.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IMetaDataManager +{ + /** + * Returns the meta ID of the given {@link EModelElement}. <code> getMetaID(getMetaInstance(x))</code> yields + * <code>x</code> + * + * @param modelElement + * the element + * @return the corresponding ID + * @since 4.0 + */ + public CDOID getMetaID(EModelElement modelElement, long commitTime); + + /** + * Returns the {@link EModelElement} referred to by the given ID. <code> getMetaInstance(getMetaID(m))</code> yields + * <code>m</code> + * + * @since 4.0 + */ + public EModelElement getMetaInstance(CDOID id); + + /** + * Loads a package unit from the database. + * + * @param connection + * the DB connection to read from. + * @param packageUnit + * the package unit to load. + * @return the loaded package unit. + * @since 2.0 + */ + public EPackage[] loadPackageUnit(Connection connection, InternalCDOPackageUnit packageUnit); + + /** + * @since 4.0 + */ + public void clearMetaIDMappings(); + + /** + * Reads information about package units present in the database. + * + * @param connection + * the DB connection to read from. + * @return a collection of package unit information records which can be passed to + * {@link IMetaDataManager#loadPackageUnit(Connection, InternalCDOPackageUnit)} in order to read the EPackage. + * @since 2.0 + */ + public Collection<InternalCDOPackageUnit> readPackageUnits(Connection connection); + + /** + * Write package units to the database. + * + * @param connection + * the DB connection to write to. + * @param packageUnits + * the package units to write. + * @param monitor + * the monitor to indicate progress. + * @since 2.0 + */ + public void writePackageUnits(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor); + + /** + * @since 3.0 + */ + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) + throws IOException; + + /** + * @since 4.0 + */ + public Collection<InternalCDOPackageUnit> rawImport(Connection connection, CDODataInput in, long fromCommitTime, + long toCommitTime, OMMonitor monitor) throws IOException; + +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java new file mode 100644 index 0000000000..4c81edc459 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.db; + +import java.sql.Connection; +import java.sql.PreparedStatement; + +/** + * @author Stefan Winkler + * @since 2.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IPreparedStatementCache +{ + public void setConnection(Connection connection); + + public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability); + + public void releasePreparedStatement(PreparedStatement ps); + + /** + * An enum for the degree of probability to which a prepared statement is reused later on. This is used for managing + * the cache of prepared statements so that statements which are more likely reused are kept in the cache longer. Rule + * of thumb: + * <ul> + * <li>For global statements which are used regularly (such as lookup object in cdo_objects) use + * {@link ReuseProbability#MAX MAX}. + * <li>For constant object-specific statements which are used regularly use {@link ReuseProbability#HIGH HIGH}. + * <li>For object-specific statements which are assembled from constants which are used regularly use + * {@link ReuseProbability#MEDIUM MEDIUM}. + * <li>For all other dynamic statements, like queries, use {@link ReuseProbability#LOW LOW} + * </ul> + * + * @author Stefan Winkler + * @since 2.0 + * @noextend This interface is not intended to be extended by clients. + */ + public static enum ReuseProbability + { + MAX, HIGH, MEDIUM, LOW; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java new file mode 100644 index 0000000000..745e96eab9 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java @@ -0,0 +1,287 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 271444: [DB] Multiple refactorings + * Stefan Winkler - bug 275303: [DB] DBStore does not handle BIG_INTEGER and BIG_DECIMAL + * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations + * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations + * Stefan Winkler - bug 285270: [DB] Support XSD based models + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.revision.CDORevisionData; +import org.eclipse.emf.cdo.server.internal.db.DBAnnotation; +import org.eclipse.emf.cdo.server.internal.db.MetaDataManager; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingRegistry; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common + * types. Implementors should provide a constructor which the factory (see below) can use and implement + * {@link #getResultSetValue(ResultSet)}. If needed, {@link #doSetValue(PreparedStatement, int, Object)} can also be + * overridden as a counterpart to {@link #getResultSetValue(ResultSet)}. Finally, an implementor should also implement a + * suitable factory for the {@link TypeMappingRegistry} and register it either manually using + * {@link IManagedContainer#registerFactory(org.eclipse.net4j.util.factory.IFactory)} or using the Net4j Extension Point + * <code>factories</code>. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 4.0 + */ +public abstract class AbstractTypeMapping implements ITypeMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractTypeMapping.class); + + private IMappingStrategy mappingStrategy; + + private EStructuralFeature feature; + + private IDBField field; + + private DBType dbType; + + /** + * Create a new type mapping + */ + public AbstractTypeMapping() + { + super(); + } + + public final void setMappingStrategy(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + } + + public final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final void setFeature(EStructuralFeature feature) + { + this.feature = feature; + } + + public final EStructuralFeature getFeature() + { + return feature; + } + + public final void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision revision) + throws SQLException + { + setValue(stmt, index, getRevisionValue(revision)); + } + + public final void setDefaultValue(PreparedStatement stmt, int index) throws SQLException + { + setValue(stmt, index, getDefaultValue()); + } + + public final void setValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + if (value == CDORevisionData.NIL) + { + if (TRACER.isEnabled()) + { + TRACER.format("TypeMapping for {0}: converting Revision.NIL to DB-null", feature.getName()); //$NON-NLS-1$ + } + + stmt.setNull(index, getSqlType()); + } + else if (value == null) + { + if (feature.isMany() || getDefaultValue() == null) + { + if (TRACER.isEnabled()) + { + TRACER.format("TypeMapping for {0}: writing Revision.null as DB.null", feature.getName()); //$NON-NLS-1$ + } + + stmt.setNull(index, getSqlType()); + } + else + { + if (TRACER.isEnabled()) + { + TRACER.format("TypeMapping for {0}: converting Revision.null to default value", feature.getName()); //$NON-NLS-1$ + } + + setDefaultValue(stmt, index); + } + } + else + { + doSetValue(stmt, index, value); + } + } + + public final void createDBField(IDBTable table) + { + createDBField(table, mappingStrategy.getFieldName(feature)); + } + + public final void createDBField(IDBTable table, String fieldName) + { + DBType fieldType = getDBType(); + int fieldLength = getDBLength(fieldType); + field = table.addField(fieldName, fieldType, fieldLength); + } + + public final void setDBField(IDBTable table, String fieldName) + { + field = table.getField(fieldName); + } + + public final IDBField getField() + { + return field; + } + + public final void readValueToRevision(ResultSet resultSet, InternalCDORevision revision) throws SQLException + { + Object value = readValue(resultSet); + revision.setValue(getFeature(), value); + } + + public final Object readValue(ResultSet resultSet) throws SQLException + { + Object value = getResultSetValue(resultSet); + if (resultSet.wasNull()) + { + if (feature.isMany()) + { + if (TRACER.isEnabled()) + { + TRACER.format("TypeMapping for {0}: read db.null - setting Revision.null", feature.getName()); //$NON-NLS-1$ + } + + value = null; + } + else + { + if (getDefaultValue() == null) + { + if (TRACER.isEnabled()) + { + TRACER.format( + "TypeMapping for {0}: read db.null - setting Revision.null, because of default", feature.getName()); //$NON-NLS-1$ + } + + value = null; + } + else + { + if (TRACER.isEnabled()) + { + TRACER.format("TypeMapping for {0}: read db.null - setting Revision.NIL", feature.getName()); //$NON-NLS-1$ + } + + value = CDORevisionData.NIL; + } + } + } + + return value; + } + + protected Object getDefaultValue() + { + return feature.getDefaultValue(); + } + + protected final Object getRevisionValue(InternalCDORevision revision) + { + return revision.getValue(getFeature()); + } + + /** + * Implementors could override this method to convert a given value to the database representation and set it to the + * prepared statement. + * + * @param stmt + * the {@link PreparedStatement} which is used for DB access + * @param index + * the parameter index in the statement which should be set + * @param value + * the value of the feature which should be written into the DB + */ + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + stmt.setObject(index, value, getSqlType()); + } + + /** + * Returns the SQL type of this TypeMapping. The default implementation considers the type map held by the + * {@link MetaDataManager meta-data manager}. Subclasses may override. + * + * @return The sql type of this TypeMapping. + */ + protected int getSqlType() + { + return getDBType().getCode(); + } + + public final void setDBType(DBType dbType) + { + this.dbType = dbType; + } + + public DBType getDBType() + { + return dbType; + } + + protected int getDBLength(DBType type) + { + String value = DBAnnotation.COLUMN_LENGTH.getValue(feature); + if (value != null) + { + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException e) + { + OM.LOG.error("Illegal columnLength annotation of feature " + feature.getName()); + } + } + + // TODO: implement DBAdapter.getDBLength + // mappingStrategy.getStore().getDBAdapter().getDBLength(type); + // which should then return the correct default field length for the db type + return type == DBType.VARCHAR ? 32672 : IDBField.DEFAULT; + } + + /** + * Subclasses should implement this method to read the value from the result set. Typical implementations should look + * similar to this one: <code>resultSet.getString(getField().getName())</code> + * + * @param resultSet + * the result set to read from + * @return the result value read (this has to be compatible with the {@link #feature}. + */ + protected abstract Object getResultSetValue(ResultSet resultSet) throws SQLException; + +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java new file mode 100644 index 0000000000..4170dbfae6 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Descriptor; + +import org.eclipse.net4j.util.factory.Factory; +import org.eclipse.net4j.util.factory.ProductCreationException; + +/** + * Abstract implementation for {@link ITypeMapping.Factory}. Implementors should implement their custom + * {@link #create(String)} method and construct the factory using their custom descriptor. Subclasses must have a + * default constructor! + * + * @author Stefan Winkler + * @since 4.0 + */ +public abstract class AbstractTypeMappingFactory extends Factory implements + org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Factory +{ + private ITypeMapping.Descriptor descriptor; + + public AbstractTypeMappingFactory(Descriptor descriptor) + { + super(PRODUCT_GROUP, descriptor.getFactoryType()); + this.descriptor = descriptor; + } + + public abstract ITypeMapping create(String description) throws ProductCreationException; + + public final Descriptor getDescriptor() + { + return descriptor; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java new file mode 100644 index 0000000000..9775d42fd5 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.util.List; +import java.util.Set; + +/** + * Basic interface for class mappings. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface IClassMapping +{ + /** + * @since 3.0 + */ + public EClass getEClass(); + + /** + * Returns all DB tables which are used by this class and all its contained features. + * + * @return a collection of all tables of this class and all its contained features. + * @since 3.0 + */ + public List<IDBTable> getDBTables(); + + /** + * Get the mapping of the many-valued feature. + * + * @param feature + * the feature for which the mapping should be returned. <code>feature.isMany()</code> has to be + * <code>true</code>. + * @return the list mapping corresponding to the feature. + */ + public IListMapping getListMapping(EStructuralFeature feature); + + /** + * @since 3.0 + */ + public List<IListMapping> getListMappings(); + + /** + * @since 4.0 + */ + public List<ITypeMapping> getValueMappings(); + + /** + * Read a revision. The branch and timestamp to be read are derived from the branchPoint which is set to the Revision. + * Note that non-audit stores only support {@link CDOBranchPoint#UNSPECIFIED_DATE} and non-branching stores only + * support the main branch. + * + * @param accessor + * the accessor to use. + * @param revision + * the revision object into which the data should be read. The revision has to be have its ID set to the + * requested object's ID. The version is ignored, as the version parameter is used to determine the version + * to be read. + * @param listChunk + * the chunk size to read attribute lists. + * @return <code>true</code>, if the revision has been found and read correctly. <code>false</code> if the revision + * could not be found. In this case, the content of <code>revision</code> is undefined. + */ + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk); + + /** + * Write the revision data to the database. + * + * @param accessor + * the accessor to use. + * @param revision + * the revision to write. + * @param mapType + * <code>true</code> if the type of the object is supposed to be mapped, <code>false</code> otherwise. + * @param revise + * <code>true</code> if the previous revision is supposed to be revised, <code>false</code> otherwise. + * @param monitor + * the monitor to indicate progress. + * @since 4.0 + */ + public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise, + OMMonitor monitor); + + /** + * Detaches (deletes) a CDO object leaving a "ghost" revision behind. + * + * @param accessor + * the accessor to use. + * @param id + * the id to revise. + * @param version + * the last valid version. + * @param timeStamp + * the timestamp of detach. + * @param monitor + * the monitor to indicate progress. + * @since 3.0 + */ + public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor monitor); + + /** + * Create a prepared statement which returns all IDs of instances of the corresponding class. + * + * @param accessor + * the accessor to use to create the statement + * @return the prepared statement ready to be executed using <code>result.executeQuery()</code>. + * @since 3.0 + */ + public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor); + + /** + * Create a prepared statement which returns all IDs of instances of the corresponding class. + * + * @param accessor + * the accessor to use to create the statement + * @param folderId + * the ID of the containing folder. <code>0</code> means none. + * @param name + * the name of the resource node to look up + * @param exactMatch + * if <code>true</code>, <code>name</code> must match exactly, otherwise all resource nodes starting with + * <code>name</code> are returned. + * @param branchPoint + * a branchPoint (branch and timestamp). A timestamp in the past if past versions should be looked up. In + * case of no audit support, this must be {@link CDORevision#UNSPECIFIED_DATE}. In case of non branching + * support the branch id must be equal to {@link CDOBranch#MAIN_BRANCH_ID}. + * @return the prepared statement ready to be executed using <code>result.executeQuery()</code>. + * @throws ImplementationError + * if called on a mapping which does not map an <code>EClass instanceof CDOResourceNode</code>. + * @since 3.0 + */ + public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, + boolean exactMatch, CDOBranchPoint branchPoint); + + /** + * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following + * conditions are met: + * <ul> + * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>. + * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to + * <code>revision.getTimeStamp()</code>. + * </ul> + * + * @see IMappingStrategy#handleRevisions(IDBStoreAccessor, org.eclipse.emf.ecore.EClass, CDOBranch, long, boolean, + * CDORevisionHandler) + * @since 4.0 + */ + public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler); + + /** + * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges. + * DetachedCDORevisions must also be considered! + * + * @see IStoreAccessor#readChangeSet(OMMonitor, CDOChangeSetSegment...) + * @since 3.0 + */ + public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments); + + /** + * Retrieve cross-references from DB + * + * @param idString + * a string of the form "(id1, id2, id3, ...)" which can be used directly in SQL to form the where-part + * "SELECT * FROM foobar WHERE foobar.target IN [idString]". + * @see IStoreAccessor#queryXRefs(QueryXRefsContext) + * @since 4.0 + */ + public boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java new file mode 100644 index 0000000000..5a1b13f4b4 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +/** + * Interface which complements {@link IClassMapping} with methods to facilitate audit support. + * + * @see IMappingStrategy#hasAuditSupport() + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface IClassMappingAuditSupport +{ + /** + * Read a specific version of a revision. If this method returns <code>true</code> it is guaranteed that + * <code>revision.getVersion() == version</code> + * + * @param storeAccessor + * the accessor to use. + * @param revision + * the revision object into which the data should be read. The revision has to be have its ID set to the + * requested object's ID. The version is ignored, as the version parameter is used to determine the version + * to be read. + * @param listChunk + * the chunk size to read attribute lists. + * @return <code>true</code>, if the revision has been found and read correctly. <code>false</code> if the revision + * could not be found. In this case, the content of <code>revision</code> is undefined. + * @since 3.0 + */ + public boolean readRevisionByVersion(IDBStoreAccessor storeAccessor, InternalCDORevision revision, int listChunk); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java new file mode 100644 index 0000000000..86bf12ed72 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * Interface which complements {@link IClassMapping} with methods to facilitate revision delta support. + * + * @see IMappingStrategy#hasDeltaSupport() + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface IClassMappingDeltaSupport +{ + /** + * Write a revision delta. + * + * @param accessor + * the accessor to use. + * @param delta + * the delta to write. + * @param created + * the creation timestamp of the new version + * @param monitor + * the monitor to report progress. + */ + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, + OMMonitor monitor); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java new file mode 100644 index 0000000000..bb09fa2e0d --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.ddl.IDBTable; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.Collection; +import java.util.List; + +/** + * Interface for mapping features with <code>isMany() == true</code>. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface IListMapping +{ + /** + * Return the mapped feature. + * + * @return the mapped feature. + */ + public EStructuralFeature getFeature(); + + /** + * Returns all DB tables which are used by this feature. + * + * @return a collection of all tables of this feature. + */ + public Collection<IDBTable> getDBTables(); + + /** + * Write a complete list of values to the database. + * + * @param accessor + * the accessor to use. + * @param revision + * the revision containing the list to be written. + */ + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision); + + /** + * Read the list size and the complete list or the first part of it. + * + * @param accessor + * the accessor to use. + * @param revision + * the revision into which the list values should be read. + * @param listChunk + * indicating the lazy loading behavior: {@link CDORevision#UNCHUNKED} means that the whole list should be + * read. Else, if <code>listChunk >= 0</code>, the list is filled with {@link InternalCDOList#UNINITIALIZED} + * and only the first <code>listChunk</code> values are read. + */ + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk); + + /** + * Used to load-on-demand chunks of a list. + * + * @param dbStoreChunkReader + * the chunkReader to use + * @param chunks + * the chunks to read + * @param where + * the where-clause to use in order to read the chunks. + */ + public void readChunks(IDBStoreChunkReader dbStoreChunkReader, List<Chunk> chunks, String where); + + /** + * Hook with which a list mapping is notified that a containing object has been revised. Can be implemented in order + * to clean up lists of revised objects. + * + * @param accessor + * the accessor to use. + * @param id + * the ID of the object which has been revised. + * @param revised + * the timestamp at which the object was revised. + * @since 3.0 + */ + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised); + + /** + * Retrieve cross-references from DB. + * + * @see IClassMapping#queryXRefs(IDBStoreAccessor, IStoreAccessor.QueryXRefsContext, String) + * @see IStoreAccessor#queryXRefs(IStoreAccessor.QueryXRefsContext) + * @since 4.0 + */ + public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java new file mode 100644 index 0000000000..5ed5698512 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; + +/** + * Interface to complement {@link IListMapping} in order to provide list delta processing support. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface IListMappingDeltaSupport +{ + /** + * Process a set of CDOFeatureDeltas for a many-valued feature. + * + * @param accessor + * the accessor to use + * @param id + * the ID of the revision affected + * @param oldVersion + * the original version of the revision + * @param newVersion + * the new revision of the revision (after the change) + * @param created + * the creation date for the new revision + * @param delta + * the {@link CDOListFeatureDelta} which contains the list deltas. + * @since 4.0 + */ + public void processDelta(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + long created, CDOListFeatureDelta delta); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java new file mode 100644 index 0000000000..90c6516cc6 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java @@ -0,0 +1,348 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.internal.db.DBStore; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; + +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.sql.Connection; +import java.util.Map; +import java.util.Set; + +/** + * The mapping strategy acts as a connection between the DBStore and the database management (and OR-mapping) classes. + * The {@link DBStore} uses methods of this interface to create and lookup mappings (or mappers, as they could also be + * named as such) and to get properties and informations about the mappings used. The mapping classes (e.g., instances + * of IClassMapping and IListMapping) also use this class as a central point of information and as a resource of common + * functionalities. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface IMappingStrategy +{ + /** + * Name of the integer property that configures the maximum length for table names. A value of zero indicates the + * value of the {@link IDBAdapter#getMaxTableNameLength() db adapter} to be used. + */ + public static final String PROP_MAX_TABLE_NAME_LENGTH = "maxTableNameLength"; //$NON-NLS-1$ + + /** + * Name of the integer property that configures the maximum length for column names. A value of zero indicates the + * value of the {@link IDBAdapter#getMaxFieldNameLength() db adapter} to be used. + */ + public static final String PROP_MAX_FIELD_NAME_LENGTH = "maxFieldNameLength"; //$NON-NLS-1$ + + /** + * Name of the String property that specifies a common prefix for table names. + */ + public static final String PROP_TABLE_NAME_PREFIX = "tableNamePrefix"; //$NON-NLS-1$ + + /** + * Name of the boolean property that configures whether the table names are made of simple class names or of qualified + * class names. + */ + public static final String PROP_QUALIFIED_NAMES = "qualifiedNames"; //$NON-NLS-1$ + + /** + * Name of the boolean property that configures whether table names and column names are always suffixed with the + * internal DBID or only in cases where generated names violate the naming constraints of the underlying backend. + */ + public static final String PROP_FORCE_NAMES_WITH_ID = "forceNamesWithID"; //$NON-NLS-1$ + + /** + * Name of the integer property that configures the size of the object type in-memory cache. Possible configuration + * values are: + * <ul> + * <li>0 (zero). Don't use memory caching. + * <li>>0. Use memory caching with the cache size given. + * </ul> + * Default is a memory cache size of 10,000,000. + * <p> + * + * @since 4.0 + */ + public static final String PROP_OBJECT_TYPE_CACHE_SIZE = "objectTypeCacheSize"; //$NON-NLS-1$ + + /** + * @return the store, this MappingStrategy instance belongs to. + */ + public IDBStore getStore(); + + /** + * Set the store to which this MappingStrategy instance belongs. Should only be called by the {@link DBStore}, and + * only once to initialize the connection between {@link DBStore} and mapping strategy. + * + * @param dbStore + * the DBStore instance to which this MappingStrategy instance belongs. + */ + public void setStore(IDBStore dbStore); + + /** + * Factory for value mappings of single-valued attributes. + * + * @param feature + * the feature for which a mapping should be created. It must hold <code>feature.isMany() == false</code>. + * @return the mapping created. + */ + public ITypeMapping createValueMapping(EStructuralFeature feature); + + /** + * Factory for value mappings of multi-valued-attributes. + * + * @param containingClass + * the class containing the feature. + * @param feature + * the feature for which a mapping should be created. It must hold <code>feature.isMany() == true</code>. + */ + public IListMapping createListMapping(EClass containingClass, EStructuralFeature feature); + + /** + * Create a suitable table name which can be used to map the given element. Should only be called by mapping classes. + * + * @param element + * the element for which the name should be created. It must hold: + * <code>element instanceof EClass || element instanceof EPackage</code>. + * @return the created table name. It is guaranteed that the table name is compatible with the chosen database. + */ + public String getTableName(ENamedElement element); + + /** + * Create a suitable table name which can be used to map the given element. Should only be called by mapping classes. + * Should only be called by mapping classes. + * + * @param containingClass + * the class containeng the feature. + * @param feature + * the feature for which the table name should be created. + * @return the created table name. It is guaranteed that the table name is compatible with the chosen database. + */ + public String getTableName(EClass containingClass, EStructuralFeature feature); + + /** + * Create a suitable column name which can be used to map the given element. Should only be called by mapping classes. + * + * @param feature + * the feature for which the column name should be created. + * @return the created column name. It is guaranteed that the name is compatible with the chosen database. + */ + public String getFieldName(EStructuralFeature feature); + + /** + * Create and initialize the mapping infrastructure for the given packages. Should be called from the DBStore or the + * DBStoreAccessor. + * + * @param connection + * the connection to use. + * @param packageUnits + * the packages whose elements should be mapped. + * @param monitor + * the monitor to report progress. + */ + public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor); + + /** + * Remove the mapping infrastructure for the given packages. Should be called from the DBStore or the DBStoreAccessor. + * + * @param connection + * the connection to use. + * @param packageUnits + * the packages for which the mappings should be removed + * @since 4.0 + */ + // Bugzilla 298632 + public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits); + + /** + * Look up an existing class mapping for the given class. Before this method is called, the class mapping must have + * been initialized by calling {@link #createMapping(Connection, InternalCDOPackageUnit[], OMMonitor)} on its + * containing package. + * + * @param eClass + * the class to look up. + * @return the class mapping. + */ + public IClassMapping getClassMapping(EClass eClass); + + /** + * Returns all class mappings of this strategy. + * + * @since 4.0 + */ + public Map<EClass, IClassMapping> getClassMappings(); + + /** + * Returns all class mappings of this strategy. + * + * @since 4.0 + */ + public Map<EClass, IClassMapping> getClassMappings(boolean createOnDemand); + + /** + * Query if this mapping supports revision deltas. <br> + * If this method returns <code>true</code>, it is guaranteed that all class mappings returned by + * {@link #getClassMapping(EClass)} implement {@link IClassMappingDeltaSupport}. + * + * @return <code>true</code> if revision deltas are supported, <code>false</code> else. + */ + public boolean hasDeltaSupport(); + + /** + * Query if this mapping supports audits. <br> + * If this method returns <code>true</code>, it is guaranteed that all class mappings returned by + * {@link #getClassMapping(EClass)} implement {@link IClassMappingAuditSupport}. + * + * @return <code>true</code> if audits are supported, <code>false</code> else. + */ + public boolean hasAuditSupport(); + + /** + * Query if this mapping supports branches. <br> + * + * @return <code>true</code> if branches are supported, <code>false</code> else. + * @since 3.0 + */ + public boolean hasBranchingSupport(); + + /** + * Executes a resource query. + * + * @param accessor + * the accessor to use. + * @param context + * the context from which the query parameters are read and to which the result is written. + */ + public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context); + + /** + * Executes a cross reference query. + * + * @param accessor + * the accessor to use. + * @param context + * the context from which the query parameters are read and to which the result is written. + * @since 3.0 + */ + public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context); + + /** + * Read the type (i.e. class) of the object referred to by a given ID. + * + * @param accessor + * the accessor to use to look up the type. + * @param id + * the ID of the object for which the type is to be determined. + * @return the type of the object. + */ + public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id); + + /** + * Get an iterator over all instances of objects in the store. + * + * @param accessor + * the accessor to use. + * @return the iterator. + */ + public CloseableIterator<CDOID> readObjectIDs(IDBStoreAccessor accessor); + + /** + * Return the maximum object id used in the store. This is used by the DBStore if a previous crash is discovered + * during the startup process. Should only be called by the DBStore and only during startup. + * + * @param dbAdapter + * the dbAdapter to use to access the database + * @param connection + * the connection to use to access the database + * @since 4.0 + */ + public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection); + + /** + * Returns the configuration properties of this mapping strategy. + * + * @since 4.0 + */ + public Map<String, String> getProperties(); + + /** + * Set configuration properties for this mapping strategy. Should only be called by the factory creating the mapping + * strategy instance. + * + * @param properties + * the configuration properties to set. + */ + public void setProperties(Map<String, String> properties); + + /** + * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following + * conditions are met: + * <ul> + * <li>The <code>eClass</code> parameter is <code>null</code> or equal to <code>revision.getEClass()</code>. + * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>. + * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to + * <code>revision.getTimeStamp()</code>. + * </ul> + * + * @since 4.0 + */ + public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp, + boolean exactTime, CDORevisionHandler handler); + + /** + * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges. + * DetachedCDORevisions must also be considered! + * + * @see IStoreAccessor#readChangeSet(OMMonitor, CDOChangeSetSegment...) + * @since 4.0 + */ + public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments); + + /** + * @since 3.0 + */ + public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int lastReplicatedBranchID, int lastBranchID, + long lastReplicatedCommitTime, long lastCommitTime) throws IOException; + + /** + * @since 4.0 + */ + public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException; + + /** + * @since 4.0 + */ + public String getListJoin(String attrTable, String listTable); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java new file mode 100644 index 0000000000..c6d463c15e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingRegistry; +import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingUtil; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.factory.IFactory; + +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +/** + * Mapping of single values to and from the database. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 2.0 + */ +public interface ITypeMapping +{ + /** + * @return The feature which is associated with this mapping. + */ + public EStructuralFeature getFeature(); + + /** + * @return The db field which is associated with this mapping. + */ + public IDBField getField(); + + /** + * @return The db type which is associated with this mapping. + * @since 3.0 + */ + public DBType getDBType(); + + /** + * @since 4.0 + */ + public void setMappingStrategy(IMappingStrategy mappingStrategy); + + /** + * @since 4.0 + */ + public void setFeature(EStructuralFeature feature); + + /** + * @since 4.0 + */ + public void setDBType(DBType dbType); + + /** + * Creates the DBField and adds it to the given table. The name of the DBField is derived from the feature. + * + * @param table + * the table to add this field to. + */ + public void createDBField(IDBTable table); + + /** + * Creates the DBField and adds it to the given table. The name of the DBField is explicitly determined by the + * corresponding parameter. + * + * @param table + * the table to add this field to. + * @param fieldName + * the name for the DBField. + */ + public void createDBField(IDBTable table, String fieldName); + + /** + * Sets the DBField. The name of the DBField is explicitly determined by the corresponding parameter. + * + * @param table + * the table to add this field to. + * @param fieldName + * the name for the DBField. + * @since 3.0 + */ + public void setDBField(IDBTable table, String fieldName); + + /** + * Set the given value to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code> method. + * + * @param stmt + * the prepared statement to set the value + * @param index + * the index to use for the <code>setXxx</code> method. + * @param value + * the value to set. + * @throws SQLException + * if the <code>setXxx</code> throws it. + */ + public void setValue(PreparedStatement stmt, int index, Object value) throws SQLException; + + /** + * Set the feature's default value to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code> + * method. + * + * @param stmt + * the prepared statement to set the value + * @param index + * the index to use for the <code>setXxx</code> method. + * @throws SQLException + * if the <code>setXxx</code> throws it. + * @since 3.0 + */ + public void setDefaultValue(PreparedStatement stmt, int index) throws SQLException; + + /** + * Set a value of the given revision to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code> + * method. The feature from which the value is taken is determined by {@link #getFeature()}. + * + * @param stmt + * the prepared statement to set the value + * @param index + * the index to use for the <code>setXxx</code> method. + * @param value + * the revision to get the value to set from. + * @throws SQLException + * if the <code>setXxx</code> throws it. + */ + public void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision value) throws SQLException; + + /** + * Read the value from a {@link ResultSet} and convert it from the DB to the CDO representation. The resultSet field + * to read from is determined automatically by the internal {@link #getField()} name. + * + * @param resultSet + * the result set to read from + * @return the read value + * @throws SQLException + * if reading the value throws an SQLException + * @since 3.0 + */ + public Object readValue(ResultSet resultSet) throws SQLException; + + /** + * Read a value from a {@link ResultSet}, convert it from the DB to the CDO representation and set it to the feature + * of the revision. The feature is determined by getFeature() The resultSet field to read from is determined + * automatically by the internal {@link #getField()} name. + * + * @param resultSet + * the result set to read from + * @param revision + * the revision to which the value should be set. + * @throws SQLException + * if reading the value throws an SQLException + * @since 3.0 + */ + public void readValueToRevision(ResultSet resultSet, InternalCDORevision revision) throws SQLException; + + /** + * A descriptor which describes one type mapping class. The descriptor is encoded in the factoryType which is used as + * a string description for the extension point mechanism. Translations and instantiations can be done using the + * methods in {@link TypeMappingUtil}. + * + * @author Stefan Winkler + * @since 4.0 + */ + public interface Descriptor + { + /** + * The factoryType of the factory which can create the type mapping + */ + public String getFactoryType(); + + /** + * The ID of the described type mapping. + */ + public String getID(); + + /** + * The source (i.e., model) type that can be mapped by the type mapping. + */ + public EClassifier getEClassifier(); + + /** + * The target (i.e., db) type that can be mapped by the type mapping. + */ + public DBType getDBType(); + + } + + /** + * A global (singleton) registry which collects all available type mappings which are either available in the CDO + * core, as declared extensions, or registered manually. + * + * @author Stefan Winkler + * @since 4.0 + */ + public interface Registry + { + /** + * The one global (singleton) registry instance. + */ + public static Registry INSTANCE = new TypeMappingRegistry(); + + /** + * Register a type mapping by descriptor. + */ + public void registerTypeMapping(ITypeMapping.Descriptor descriptor); + + /** + * Provides a list of all DBTypes for which type mappings exist in the registry. This is used in feature map tables + * to create columns for all of these types. + */ + public Collection<DBType> getDefaultFeatureMapDBTypes(); + } + + /** + * A provider for type mapping information. This provider is used by the {@link TypeMappingRegistry} to create an + * {@link ITypeMapping} instance suitable for a given feature and DB field. Usually, one factory is responsible for + * one type mapping. + * + * @author Stefan Winkler + * @since 4.0 + */ + public interface Provider + { + /** + * The one global (singleton) provider instance. + */ + public static Provider INSTANCE = (Provider)Registry.INSTANCE; + + /** + * Create an {@link ITypeMapping} implementation. + * + * @param mappingStrategy + * the mapping strategy + * @param feature + * the feature the new type mapping shall be responsible for + * @return the newly created {@link ITypeMapping} instance + */ + public ITypeMapping createTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature); + } + + /** + * A factory for typeMappings. This is a regular Net4j factory registered by the respective extension point. It + * enhances the regular factory using a descriptor which is translated from and to the factoryType by the methods in + * {@link TypeMappingUtil}. + * + * @author Stefan Winkler + * @since 4.0 + */ + public interface Factory extends IFactory + { + /** + * The Net4j factory product group for type mappings + */ + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.db.typeMappings"; + + /** + * Return the descriptor of the kind of type mapping created by this factory. + */ + public ITypeMapping.Descriptor getDescriptor(); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java new file mode 100644 index 0000000000..cec7a9382a --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java @@ -0,0 +1,15 @@ +/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with mapping strategies and mappings for classes, lists and types.
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java new file mode 100644 index 0000000000..53e53996bb --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java @@ -0,0 +1,15 @@ +/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with DB stores and accessors.
+ */
+package org.eclipse.emf.cdo.server.db;
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java new file mode 100644 index 0000000000..2ffd031698 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; + +import org.eclipse.net4j.util.lifecycle.Lifecycle; + +import java.sql.Connection; + +/** + * @author Stefan Winkler + * @since 2.0 + */ +public abstract class AbstractPreparedStatementCache extends Lifecycle implements IPreparedStatementCache +{ + private Connection connection; + + public AbstractPreparedStatementCache() + { + } + + public final Connection getConnection() + { + return connection; + } + + public final void setConnection(Connection connection) + { + checkInactive(); + this.connection = connection; + } + + @Override + protected void doBeforeActivate() + { + checkState(connection, "Must have valid connection to start"); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java new file mode 100644 index 0000000000..dc3e19b6db --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + * Andre Dietisheim - bug 256649 + * + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.spi.db.DBSchema; + +/** + * @author Eike Stepper + */ +public class CDODBSchema extends DBSchema +{ + public static final CDODBSchema INSTANCE = new CDODBSchema(); + + /** + * DBTable cdo_properties + */ + public static final IDBTable PROPERTIES = INSTANCE.addTable("cdo_properties"); //$NON-NLS-1$ + + public static final IDBField PROPERTIES_NAME = // + PROPERTIES.addField("name", DBType.VARCHAR, 255); //$NON-NLS-1$ + + public static final IDBField PROPERTIES_VALUE = // + PROPERTIES.addField("value", DBType.LONGVARCHAR); //$NON-NLS-1$ + + public static final IDBIndex INDEX_PROPERTIES_PK = // + PROPERTIES.addIndex(IDBIndex.Type.PRIMARY_KEY, PROPERTIES_NAME); + + public static final String SQL_DELETE_PROPERTIES = "DELETE FROM " + PROPERTIES + " WHERE " + PROPERTIES_NAME + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + public static final String SQL_INSERT_PROPERTIES = "INSERT INTO " + PROPERTIES + " (" + PROPERTIES_NAME + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + PROPERTIES_VALUE + ") VALUES (?, ?)"; //$NON-NLS-1$ + + public static final String SQL_SELECT_PROPERTIES = "SELECT " + PROPERTIES_VALUE + " FROM " + PROPERTIES + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + PROPERTIES_NAME + "=?"; //$NON-NLS-1$ + + public static final String SQL_SELECT_ALL_PROPERTIES = "SELECT " + PROPERTIES_NAME + ", " + PROPERTIES_VALUE //$NON-NLS-1$ //$NON-NLS-2$ + + " FROM " + PROPERTIES; //$NON-NLS-1$ + + /** + * DBTable cdo_package_units + */ + public static final IDBTable PACKAGE_UNITS = INSTANCE.addTable("cdo_package_units"); //$NON-NLS-1$ + + public static final IDBField PACKAGE_UNITS_ID = // + PACKAGE_UNITS.addField("id", DBType.VARCHAR, 255); //$NON-NLS-1$ + + public static final IDBField PACKAGE_UNITS_ORIGINAL_TYPE = // + PACKAGE_UNITS.addField("original_type", DBType.INTEGER); //$NON-NLS-1$ + + public static final IDBField PACKAGE_UNITS_TIME_STAMP = // + PACKAGE_UNITS.addField("time_stamp", DBType.BIGINT); //$NON-NLS-1$ + + public static final IDBField PACKAGE_UNITS_PACKAGE_DATA = // + PACKAGE_UNITS.addField("package_data", DBType.BLOB); //$NON-NLS-1$ + + public static final IDBIndex INDEX_PACKAGE_UNITS_PK = // + PACKAGE_UNITS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_UNITS_ID); + + /** + * DBTable cdo_packages + */ + public static final IDBTable PACKAGE_INFOS = INSTANCE.addTable("cdo_package_infos"); //$NON-NLS-1$ + + public static final IDBField PACKAGE_INFOS_URI = // + PACKAGE_INFOS.addField("uri", DBType.VARCHAR, 255); //$NON-NLS-1$ + + public static final IDBField PACKAGE_INFOS_PARENT = // + PACKAGE_INFOS.addField("parent", DBType.VARCHAR, 255); //$NON-NLS-1$ + + public static final IDBField PACKAGE_INFOS_UNIT = // + PACKAGE_INFOS.addField("unit", DBType.VARCHAR, 255); //$NON-NLS-1$ + + public static final IDBIndex INDEX_PACKAGE_INFOS_PK = // + PACKAGE_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_INFOS_URI); + + public static final IDBIndex INDEX_PACKAGE_INFOS_PARENT = // + PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_PARENT); + + public static final IDBIndex INDEX_PACKAGE_INFOS_UNIT = // + PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_UNIT); + + /** + * DBTable cdo_branches + */ + public static final IDBTable BRANCHES = INSTANCE.addTable("cdo_branches"); //$NON-NLS-1$ + + public static final IDBField BRANCHES_ID = // + BRANCHES.addField("id", DBType.INTEGER); //$NON-NLS-1$ + + public static final IDBField BRANCHES_NAME = // + BRANCHES.addField("name", DBType.VARCHAR); //$NON-NLS-1$ + + public static final IDBField BRANCHES_BASE_BRANCH_ID = // + BRANCHES.addField("base_id", DBType.INTEGER); //$NON-NLS-1$ + + public static final IDBField BRANCHES_BASE_TIMESTAMP = // + BRANCHES.addField("base_time", DBType.BIGINT); //$NON-NLS-1$ + + public static final IDBIndex INDEX_BRANCHES_ID = // + BRANCHES.addIndex(IDBIndex.Type.PRIMARY_KEY, BRANCHES_ID); + + public static final String SQL_CREATE_BRANCH = "INSERT INTO " + BRANCHES + " (" + BRANCHES_ID + ", " + BRANCHES_NAME //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + ", " + BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP + ") VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + public static final String SQL_LOAD_BRANCH = "SELECT " + BRANCHES_NAME + ", " + BRANCHES_BASE_BRANCH_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + BRANCHES_BASE_TIMESTAMP + " FROM " + BRANCHES + " WHERE " + BRANCHES_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + public static final String SQL_LOAD_SUB_BRANCHES = "SELECT " + BRANCHES_ID + ", " + BRANCHES_NAME + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + BRANCHES_BASE_TIMESTAMP + " FROM " + BRANCHES + " WHERE " + BRANCHES_BASE_BRANCH_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + public static final String SQL_LOAD_BRANCHES = "SELECT " + BRANCHES_ID + ", " + BRANCHES_NAME + ", " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP //$NON-NLS-1$ + + " FROM " + BRANCHES + " WHERE " + BRANCHES_ID + " BETWEEN ? AND ? ORDER BY " + BRANCHES_ID; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** + * DBTable cdo_commit_infos + */ + public static final IDBTable COMMIT_INFOS = INSTANCE.addTable("cdo_commit_infos"); //$NON-NLS-1$ + + public static final IDBField COMMIT_INFOS_TIMESTAMP = // + COMMIT_INFOS.addField("commit_time", DBType.BIGINT); //$NON-NLS-1$ + + public static final IDBField COMMIT_INFOS_PREVIOUS_TIMESTAMP = // + COMMIT_INFOS.addField("previous_time", DBType.BIGINT); //$NON-NLS-1$ + + public static final IDBField COMMIT_INFOS_BRANCH = // + COMMIT_INFOS.addField("branch_id", DBType.INTEGER); //$NON-NLS-1$ + + public static final IDBField COMMIT_INFOS_USER = // + COMMIT_INFOS.addField("user_id", DBType.VARCHAR); //$NON-NLS-1$ + + public static final IDBField COMMIT_INFOS_COMMENT = // + COMMIT_INFOS.addField("commit_comment", DBType.VARCHAR); //$NON-NLS-1$ + + public static final IDBIndex INDEX_COMMIT_INFOS_PK = // + COMMIT_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, COMMIT_INFOS_TIMESTAMP); + + public static final IDBIndex INDEX_COMMIT_INFOS_BRANCH = // + COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_BRANCH); + + public static final IDBIndex INDEX_COMMIT_INFOS_USER = // + COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_USER); + + public static final String SQL_CREATE_COMMIT_INFO = "INSERT INTO " + COMMIT_INFOS + "(" + COMMIT_INFOS_TIMESTAMP //$NON-NLS-1$ //$NON-NLS-2$ + + ", " + COMMIT_INFOS_PREVIOUS_TIMESTAMP + ", " + COMMIT_INFOS_BRANCH + ", " + COMMIT_INFOS_USER + ", " + COMMIT_INFOS_COMMENT + ") " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + "VALUES (?, ?, ?, ?, ?)"; //$NON-NLS-1$ + + /** + * DBTable cdo_lobs + */ + public static final IDBTable LOBS = INSTANCE.addTable("cdo_lobs"); //$NON-NLS-1$ + + public static final IDBField LOBS_ID = // + LOBS.addField("id", DBType.VARCHAR, 64); //$NON-NLS-1$ + + public static final IDBField LOBS_SIZE = // + LOBS.addField("size", DBType.BIGINT); //$NON-NLS-1$ + + public static final IDBField LOBS_BDATA = // + LOBS.addField("bdata", DBType.BLOB); //$NON-NLS-1$ + + public static final IDBField LOBS_CDATA = // + LOBS.addField("cdata", DBType.CLOB); //$NON-NLS-1$ + + public static final IDBIndex INDEX_LOBS_ID = // + LOBS.addIndex(IDBIndex.Type.PRIMARY_KEY, LOBS_ID); + + public static final String SQL_QUERY_LOBS = "SELECT 1 FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + public static final String SQL_HANDLE_LOBS = "SELECT " + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ", " + CDODBSchema.LOBS_CDATA + " FROM " + CDODBSchema.LOBS; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + public static final String SQL_LOAD_LOB = "SELECT " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ", " + CDODBSchema.LOBS_CDATA + " FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + + public static final String SQL_WRITE_BLOB = "INSERT INTO " + CDODBSchema.LOBS + "(" + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + public static final String SQL_WRITE_CLOB = "INSERT INTO " + CDODBSchema.LOBS + "(" + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_CDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + /** + * Name of object table + */ + public static final String CDO_OBJECTS = "cdo_objects"; //$NON-NLS-1$ + + /** + * Field names of attribute tables + */ + public static final String ATTRIBUTES_ID = "cdo_id"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_BRANCH = "cdo_branch"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_VERSION = "cdo_version"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_CLASS = "cdo_class"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_CREATED = "cdo_created"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_REVISED = "cdo_revised"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_RESOURCE = "cdo_resource"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_CONTAINER = "cdo_container"; //$NON-NLS-1$ + + public static final String ATTRIBUTES_FEATURE = "cdo_feature"; //$NON-NLS-1$ + + /** + * Field names of list tables + */ + public static final String LIST_FEATURE = "cdo_feature"; //$NON-NLS-1$ + + public static final String LIST_REVISION_ID = "cdo_source"; //$NON-NLS-1$ + + public static final String LIST_REVISION_VERSION = "cdo_version"; //$NON-NLS-1$ + + public static final String LIST_REVISION_VERSION_ADDED = "cdo_version_added"; //$NON-NLS-1$ + + public static final String LIST_REVISION_VERSION_REMOVED = "cdo_version_removed"; //$NON-NLS-1$ + + public static final String LIST_REVISION_BRANCH = "cdo_branch"; //$NON-NLS-1$ + + public static final String LIST_IDX = "cdo_idx"; //$NON-NLS-1$ + + public static final String LIST_VALUE = "cdo_value"; //$NON-NLS-1$ + + /** + * Field names of featuremap tables + */ + public static final String FEATUREMAP_REVISION_ID = "cdo_id"; //$NON-NLS-1$ + + public static final String FEATUREMAP_VERSION = "cdo_version"; //$NON-NLS-1$ + + public static final String FEATUREMAP_VERSION_ADDED = "cdo_version_added"; //$NON-NLS-1$ + + public static final String FEATUREMAP_VERSION_REMOVED = "cdo_version_removed"; //$NON-NLS-1$ + + public static final String FEATUREMAP_BRANCH = "cdo_branch"; //$NON-NLS-1$ + + public static final String FEATUREMAP_IDX = "cdo_idx"; //$NON-NLS-1$ + + public static final String FEATUREMAP_TAG = "cdo_tag"; //$NON-NLS-1$ + + public static final String FEATUREMAP_VALUE = "cdo_value"; //$NON-NLS-1$ + + private CDODBSchema() + { + super("CDO"); //$NON-NLS-1$ + } + + static + { + INSTANCE.lock(); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java new file mode 100644 index 0000000000..148115658f --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kai Schlamp - initial API and implementation + * Eike Stepper - maintenance + * Kai Schlamp - Bug 284680 - [DB] Provide annotation to bypass ClassMapping + * Stefan Winkler - maintenance + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.util.EcoreUtil; + +/** + * @author Kai Schlamp + */ +public enum DBAnnotation +{ + TABLE_MAPPING("tableMapping"), // + TABLE_NAME("tableName"), // + COLUMN_NAME("columnName"), // + COLUMN_TYPE("columnType"), // + COLUMN_LENGTH("columnLength"), // + TYPE_MAPPING("typeMapping"); + + public final static String SOURCE_URI = "http://www.eclipse.org/CDO/DBStore"; + + public final static String TABLE_MAPPING_NONE = "NONE"; + + private String keyword; + + private DBAnnotation(String keyword) + { + this.keyword = keyword; + } + + public String getKeyword() + { + return keyword == null ? super.toString() : keyword; + } + + /** + * @return A non-empty string or <code>null</code>. + */ + public String getValue(EModelElement element) + { + String value = EcoreUtil.getAnnotation(element, SOURCE_URI, keyword); + if (value != null && value.length() == 0) + { + return null; + } + + return value; + } + + @Override + public String toString() + { + return getKeyword(); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java new file mode 100644 index 0000000000..c81aafe6ab --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.server.CDOServerBrowser; +import org.eclipse.emf.cdo.server.CDOServerBrowser.AbstractPage; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.util.factory.ProductCreationException; + +import java.io.PrintStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class DBBrowserPage extends AbstractPage +{ + public DBBrowserPage() + { + super("tables", "Database Tables"); + } + + public boolean canDisplay(InternalRepository repository) + { + return repository.getStore() instanceof IDBConnectionProvider; + } + + public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out) + { + IDBConnectionProvider connectionProvider = (IDBConnectionProvider)repository.getStore(); + Connection connection = null; + + try + { + connection = connectionProvider.getConnection(); + + out.print("<table border=\"0\">\r\n"); + out.print("<tr>\r\n"); + + out.print("<td valign=\"top\">\r\n"); + String table = showTables(browser, out, connection, repository.getName()); + out.print("</td>\r\n"); + + if (table != null) + { + out.print("<td valign=\"top\">\r\n"); + showTable(browser, out, connection, table); + out.print("</td>\r\n"); + } + + out.print("</tr>\r\n"); + out.print("</table>\r\n"); + } + catch (DBException ex) + { + ex.printStackTrace(); + } + finally + { + DBUtil.close(connection); + } + } + + /** + * @since 4.0 + */ + protected String showTables(CDOServerBrowser browser, PrintStream pout, Connection connection, String repo) + { + String table = browser.getParam("table"); + + List<String> allTableNames = DBUtil.getAllTableNames(connection, repo); + for (String tableName : allTableNames) + { + if (table == null) + { + table = tableName; + } + + String label = browser.escape(tableName)/* .toLowerCase() */; + if (tableName.equals(table)) + { + pout.print("<b>" + label + "</b><br>\r\n"); + } + else + { + pout.print(browser.href(label, getName(), "table", tableName, "order", null, "direction", null) + "<br>\r\n"); + } + } + + return table; + } + + /** + * @since 4.0 + */ + protected void showTable(CDOServerBrowser browser, PrintStream pout, Connection connection, String table) + { + try + { + String order = browser.getParam("order"); + executeQuery(browser, pout, connection, "SELECT * FROM " + table + + (order == null ? "" : " ORDER BY " + order + " " + browser.getParam("direction"))); + } + catch (Exception ex) + { + browser.removeParam("order"); + browser.removeParam("direction"); + executeQuery(browser, pout, connection, "SELECT * FROM " + table); + } + } + + protected void executeQuery(CDOServerBrowser browser, PrintStream pout, Connection connection, String sql) + { + String order = browser.getParam("order"); + String direction = browser.getParam("direction"); + String highlight = browser.getParam("highlight"); + + Statement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = connection.createStatement(); + resultSet = stmt.executeQuery(sql); + + ResultSetMetaData metaData = resultSet.getMetaData(); + int columns = metaData.getColumnCount(); + + pout.print("<table border=\"1\" cellpadding=\"2\">\r\n"); + pout.print("<tr>\r\n"); + pout.print("<td> </td>\r\n"); + for (int i = 0; i < columns; i++) + { + String column = metaData.getColumnLabel(1 + i); + String type = metaData.getColumnTypeName(1 + i).toLowerCase(); + + String dir = column.equals(order) && "ASC".equals(direction) ? "DESC" : "ASC"; + pout.print("<td align=\"center\"><b>" + browser.href(column, getName(), "order", column, "direction", dir)); + pout.print("</b><br>" + type + "</td>\r\n"); + } + + pout.print("</tr>\r\n"); + + int row = 0; + while (resultSet.next()) + { + ++row; + pout.print("<tr>\r\n"); + pout.print("<td><b>" + row + "</b></td>\r\n"); + for (int i = 0; i < columns; i++) + { + String value = resultSet.getString(1 + i); + String bgcolor = highlight != null && highlight.equals(value) ? " bgcolor=\"#fffca6\"" : ""; + pout.print("<td" + bgcolor + ">" + browser.href(value, getName(), "highlight", value) + "</td>\r\n"); + } + + pout.print("</tr>\r\n"); + } + + pout.print("</table>\r\n"); + } + catch (SQLException ex) + { + ex.printStackTrace(); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + /** + * @author Eike Stepper + */ + public static class Factory extends org.eclipse.net4j.util.factory.Factory + { + public static final String TYPE = "default"; + + public Factory() + { + super(PRODUCT_GROUP, TYPE); + } + + public DBBrowserPage create(String description) throws ProductCreationException + { + return new DBBrowserPage(); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java new file mode 100644 index 0000000000..d47db43967 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; + +/** + * @author Eike Stepper + */ +public class DBRevisionHandler implements CDORevisionHandler +{ + private CDORevisionHandler delegate; + + public DBRevisionHandler(CDORevisionHandler delegate) + { + this.delegate = delegate; + } + + public boolean handleRevision(CDORevision revision) + { + if (revision.getVersion() < CDOBranchVersion.FIRST_VERSION - 1) + { + revision = new DetachedCDORevision(revision.getEClass(), revision.getID(), revision.getBranch(), + -revision.getVersion(), revision.getTimeStamp(), revision.getRevised()); + } + + return delegate.handleRevision(revision); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java new file mode 100644 index 0000000000..3917333db8 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java @@ -0,0 +1,727 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 259402 + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - Bug 249610: [DB] Support external references (Implementation) + * Stefan Winkler - Bug 289056: [DB] Exception "ERROR: relation "cdo_external_refs" does not exist" while executing test-suite for PostgreSQL + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOAllRevisionsProvider; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.messages.Messages; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor; +import org.eclipse.emf.cdo.spi.server.Store; +import org.eclipse.emf.cdo.spi.server.StoreAccessorPool; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnectionProvider; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.spi.db.DBSchema; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Timer; + +/** + * @author Eike Stepper + */ +public class DBStore extends Store implements IDBStore, CDOAllRevisionsProvider +{ + public static final String TYPE = "db"; //$NON-NLS-1$ + + private static final String PROP_REPOSITORY_CREATED = "org.eclipse.emf.cdo.server.db.repositoryCreated"; //$NON-NLS-1$ + + private static final String PROP_REPOSITORY_STOPPED = "org.eclipse.emf.cdo.server.db.repositoryStopped"; //$NON-NLS-1$ + + private static final String PROP_NEXT_LOCAL_CDOID = "org.eclipse.emf.cdo.server.db.nextLocalCDOID"; //$NON-NLS-1$ + + private static final String PROP_LAST_CDOID = "org.eclipse.emf.cdo.server.db.lastCDOID"; //$NON-NLS-1$ + + private static final String PROP_LAST_BRANCHID = "org.eclipse.emf.cdo.server.db.lastBranchID"; //$NON-NLS-1$ + + private static final String PROP_LAST_LOCAL_BRANCHID = "org.eclipse.emf.cdo.server.db.lastLocalBranchID"; //$NON-NLS-1$ + + private static final String PROP_LAST_COMMITTIME = "org.eclipse.emf.cdo.server.db.lastCommitTime"; //$NON-NLS-1$ + + private static final String PROP_LAST_NONLOCAL_COMMITTIME = "org.eclipse.emf.cdo.server.db.lastNonLocalCommitTime"; //$NON-NLS-1$ + + private static final String PROP_GRACEFULLY_SHUT_DOWN = "org.eclipse.emf.cdo.server.db.gracefullyShutDown"; //$NON-NLS-1$ + + private long creationTime; + + private boolean firstTime; + + private Map<String, String> properties; + + private IIDHandler idHandler; + + private IMetaDataManager metaDataManager = new MetaDataManager(this); + + private DurableLockingManager durableLockingManager = new DurableLockingManager(this); + + private IMappingStrategy mappingStrategy; + + private IDBSchema dbSchema; + + private IDBAdapter dbAdapter; + + private IDBConnectionProvider dbConnectionProvider; + + @ExcludeFromDump + private transient ProgressDistributor accessorWriteDistributor = new ProgressDistributor.Geometric() + { + @Override + public String toString() + { + String result = "accessorWriteDistributor"; //$NON-NLS-1$ + if (getRepository() != null) + { + result += ": " + getRepository().getName(); //$NON-NLS-1$ + } + + return result; + } + }; + + @ExcludeFromDump + private transient StoreAccessorPool readerPool = new StoreAccessorPool(this, null); + + @ExcludeFromDump + private transient StoreAccessorPool writerPool = new StoreAccessorPool(this, null); + + @ExcludeFromDump + private transient Timer connectionKeepAliveTimer; + + public DBStore() + { + super(TYPE, null, set(ChangeFormat.REVISION, ChangeFormat.DELTA), // + set(RevisionTemporality.AUDITING, RevisionTemporality.NONE), // + set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING)); + } + + public IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public void setMappingStrategy(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + mappingStrategy.setStore(this); + } + + public IDBAdapter getDBAdapter() + { + return dbAdapter; + } + + public void setDBAdapter(IDBAdapter dbAdapter) + { + this.dbAdapter = dbAdapter; + } + + public void setProperties(Map<String, String> properties) + { + this.properties = properties; + } + + public Map<String, String> getProperties() + { + return properties; + } + + public IIDHandler getIDHandler() + { + return idHandler; + } + + public Connection getConnection() + { + Connection connection = dbConnectionProvider.getConnection(); + if (connection == null) + { + throw new DBException("No connection from connection provider: " + dbConnectionProvider); //$NON-NLS-1$ + } + + try + { + connection.setAutoCommit(false); + } + catch (SQLException ex) + { + throw new DBException(ex, "SET AUTO COMMIT = false"); + } + + return connection; + } + + public void setDbConnectionProvider(IDBConnectionProvider dbConnectionProvider) + { + this.dbConnectionProvider = dbConnectionProvider; + } + + public void setDataSource(DataSource dataSource) + { + dbConnectionProvider = DBUtil.createConnectionProvider(dataSource); + } + + public IMetaDataManager getMetaDataManager() + { + return metaDataManager; + } + + public DurableLockingManager getDurableLockingManager() + { + return durableLockingManager; + } + + public Timer getConnectionKeepAliveTimer() + { + return connectionKeepAliveTimer; + } + + @Override + public Set<ChangeFormat> getSupportedChangeFormats() + { + if (mappingStrategy.hasDeltaSupport()) + { + return set(ChangeFormat.DELTA); + } + + return set(ChangeFormat.REVISION); + } + + public ProgressDistributor getAccessorWriteDistributor() + { + return accessorWriteDistributor; + } + + public IDBSchema getDBSchema() + { + return dbSchema; + } + + public Map<String, String> getPersistentProperties(Set<String> names) + { + Connection connection = null; + PreparedStatement selectStmt = null; + String sql = null; + + try + { + connection = getConnection(); + Map<String, String> result = new HashMap<String, String>(); + boolean allProperties = names == null || names.isEmpty(); + if (allProperties) + { + sql = CDODBSchema.SQL_SELECT_ALL_PROPERTIES; + selectStmt = connection.prepareStatement(sql); + ResultSet resultSet = null; + + try + { + resultSet = selectStmt.executeQuery(); + while (resultSet.next()) + { + String key = resultSet.getString(1); + String value = resultSet.getString(2); + result.put(key, value); + } + } + finally + { + DBUtil.close(resultSet); + } + } + else + { + sql = CDODBSchema.SQL_SELECT_PROPERTIES; + selectStmt = connection.prepareStatement(sql); + for (String name : names) + { + selectStmt.setString(1, name); + ResultSet resultSet = null; + + try + { + resultSet = selectStmt.executeQuery(); + if (resultSet.next()) + { + String value = resultSet.getString(1); + result.put(name, value); + } + } + finally + { + DBUtil.close(resultSet); + } + } + } + + return result; + } + catch (SQLException ex) + { + throw new DBException(ex, sql); + } + finally + { + DBUtil.close(selectStmt); + DBUtil.close(connection); + } + } + + public void setPersistentProperties(Map<String, String> properties) + { + Connection connection = null; + PreparedStatement deleteStmt = null; + PreparedStatement insertStmt = null; + String sql = null; + + try + { + connection = getConnection(); + deleteStmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES); + insertStmt = connection.prepareStatement(CDODBSchema.SQL_INSERT_PROPERTIES); + + for (Entry<String, String> entry : properties.entrySet()) + { + String name = entry.getKey(); + String value = entry.getValue(); + + sql = CDODBSchema.SQL_DELETE_PROPERTIES; + deleteStmt.setString(1, name); + deleteStmt.executeUpdate(); + + sql = CDODBSchema.SQL_INSERT_PROPERTIES; + insertStmt.setString(1, name); + insertStmt.setString(2, value); + insertStmt.executeUpdate(); + } + + sql = null; + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex, sql); + } + finally + { + DBUtil.close(insertStmt); + DBUtil.close(deleteStmt); + DBUtil.close(connection); + } + } + + public void removePersistentProperties(Set<String> names) + { + Connection connection = null; + PreparedStatement deleteStmt = null; + + try + { + connection = getConnection(); + deleteStmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES); + + for (String name : names) + { + deleteStmt.setString(1, name); + deleteStmt.executeUpdate(); + } + + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex, CDODBSchema.SQL_DELETE_PROPERTIES); + } + finally + { + DBUtil.close(deleteStmt); + DBUtil.close(connection); + } + } + + @Override + public DBStoreAccessor getReader(ISession session) + { + return (DBStoreAccessor)super.getReader(session); + } + + @Override + public DBStoreAccessor getWriter(ITransaction transaction) + { + return (DBStoreAccessor)super.getWriter(transaction); + } + + @Override + protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing) + { + return readerPool; + } + + @Override + protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing) + { + return writerPool; + } + + @Override + protected DBStoreAccessor createReader(ISession session) throws DBException + { + return new DBStoreAccessor(this, session); + } + + @Override + protected DBStoreAccessor createWriter(ITransaction transaction) throws DBException + { + return new DBStoreAccessor(this, transaction); + } + + public Map<CDOBranch, List<CDORevision>> getAllRevisions() + { + final Map<CDOBranch, List<CDORevision>> result = new HashMap<CDOBranch, List<CDORevision>>(); + IDBStoreAccessor accessor = getReader(null); + StoreThreadLocal.setAccessor(accessor); + + try + { + accessor.handleRevisions(null, null, CDOBranchPoint.UNSPECIFIED_DATE, true, + new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + CDOBranch branch = revision.getBranch(); + List<CDORevision> list = result.get(branch); + if (list == null) + { + list = new ArrayList<CDORevision>(); + result.put(branch, list); + } + + list.add(revision); + return true; + } + })); + } + finally + { + StoreThreadLocal.release(); + } + + return result; + } + + public CDOID createObjectID(String val) + { + return idHandler.createCDOID(val); + } + + public boolean isLocal(CDOID id) + { + return idHandler.isLocalCDOID(id); + } + + public CDOID getNextCDOID(LongIDStoreAccessor accessor, CDORevision revision) + { + return idHandler.getNextCDOID(revision); + } + + public long getCreationTime() + { + return creationTime; + } + + public void setCreationTime(long creationTime) + { + this.creationTime = creationTime; + + Map<String, String> map = new HashMap<String, String>(); + map.put(PROP_REPOSITORY_CREATED, Long.toString(creationTime)); + setPersistentProperties(map); + } + + public boolean isFirstStart() + { + return firstTime; + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + checkNull(mappingStrategy, Messages.getString("DBStore.2")); //$NON-NLS-1$ + checkNull(dbAdapter, Messages.getString("DBStore.1")); //$NON-NLS-1$ + checkNull(dbConnectionProvider, Messages.getString("DBStore.0")); //$NON-NLS-1$ + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + idHandler = new UUIDHandler(this); + } + else + { + idHandler = new LongIDHandler(this); + } + + setObjectIDTypes(idHandler.getObjectIDTypes()); + connectionKeepAliveTimer = new Timer("Connection-Keep-Alive-" + this); //$NON-NLS-1$ + + Set<IDBTable> createdTables = null; + Connection connection = getConnection(); + + try + { + if (isDropAllDataOnActivate()) + { + OM.LOG.info("Dropping all tables from repository " + getRepository().getName() + "..."); + DBUtil.dropAllTables(connection, null); + connection.commit(); + } + + createdTables = CDODBSchema.INSTANCE.create(dbAdapter, connection); + connection.commit(); + } + finally + { + DBUtil.close(connection); + } + + dbSchema = createSchema(); + + LifecycleUtil.activate(idHandler); + LifecycleUtil.activate(metaDataManager); + LifecycleUtil.activate(durableLockingManager); + LifecycleUtil.activate(mappingStrategy); + + setRevisionTemporality(mappingStrategy.hasAuditSupport() ? RevisionTemporality.AUDITING : RevisionTemporality.NONE); + setRevisionParallelism(mappingStrategy.hasBranchingSupport() ? RevisionParallelism.BRANCHING + : RevisionParallelism.NONE); + + if (isFirstStart(createdTables)) + { + firstStart(); + } + else + { + reStart(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(mappingStrategy); + LifecycleUtil.deactivate(durableLockingManager); + LifecycleUtil.deactivate(metaDataManager); + LifecycleUtil.deactivate(idHandler); + + Map<String, String> map = new HashMap<String, String>(); + map.put(PROP_GRACEFULLY_SHUT_DOWN, Boolean.TRUE.toString()); + map.put(PROP_REPOSITORY_STOPPED, Long.toString(getRepository().getTimeStamp())); + + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + map.put(PROP_NEXT_LOCAL_CDOID, Store.idToString(idHandler.getNextLocalObjectID())); + map.put(PROP_LAST_CDOID, Store.idToString(idHandler.getLastObjectID())); + } + + map.put(PROP_LAST_BRANCHID, Integer.toString(getLastBranchID())); + map.put(PROP_LAST_LOCAL_BRANCHID, Integer.toString(getLastLocalBranchID())); + map.put(PROP_LAST_COMMITTIME, Long.toString(getLastCommitTime())); + map.put(PROP_LAST_NONLOCAL_COMMITTIME, Long.toString(getLastNonLocalCommitTime())); + setPersistentProperties(map); + + if (readerPool != null) + { + readerPool.dispose(); + } + + if (writerPool != null) + { + writerPool.dispose(); + } + + connectionKeepAliveTimer.cancel(); + connectionKeepAliveTimer = null; + + super.doDeactivate(); + } + + protected boolean isFirstStart(Set<IDBTable> createdTables) + { + if (createdTables.contains(CDODBSchema.PROPERTIES)) + { + return true; + } + + Set<String> names = new HashSet<String>(); + names.add(PROP_REPOSITORY_CREATED); + + Map<String, String> map = getPersistentProperties(names); + return map.get(PROP_REPOSITORY_CREATED) == null; + } + + protected void firstStart() + { + InternalRepository repository = getRepository(); + setCreationTime(repository.getTimeStamp()); + firstTime = true; + } + + protected void reStart() + { + Set<String> names = new HashSet<String>(); + names.add(PROP_REPOSITORY_CREATED); + names.add(PROP_GRACEFULLY_SHUT_DOWN); + + Map<String, String> map = getPersistentProperties(names); + creationTime = Long.valueOf(map.get(PROP_REPOSITORY_CREATED)); + + if (map.containsKey(PROP_GRACEFULLY_SHUT_DOWN)) + { + names.clear(); + + InternalRepository repository = getRepository(); + boolean generatingIDs = repository.getIDGenerationLocation() == IDGenerationLocation.STORE; + if (generatingIDs) + { + names.add(PROP_NEXT_LOCAL_CDOID); + names.add(PROP_LAST_CDOID); + } + + names.add(PROP_LAST_BRANCHID); + names.add(PROP_LAST_LOCAL_BRANCHID); + names.add(PROP_LAST_COMMITTIME); + names.add(PROP_LAST_NONLOCAL_COMMITTIME); + map = getPersistentProperties(names); + + if (generatingIDs) + { + idHandler.setNextLocalObjectID(Store.stringToID(map.get(PROP_NEXT_LOCAL_CDOID))); + idHandler.setLastObjectID(Store.stringToID(map.get(PROP_LAST_CDOID))); + } + + setLastBranchID(Integer.valueOf(map.get(PROP_LAST_BRANCHID))); + setLastLocalBranchID(Integer.valueOf(map.get(PROP_LAST_LOCAL_BRANCHID))); + setLastCommitTime(Long.valueOf(map.get(PROP_LAST_COMMITTIME))); + setLastNonLocalCommitTime(Long.valueOf(map.get(PROP_LAST_NONLOCAL_COMMITTIME))); + } + else + { + repairAfterCrash(); + } + + removePersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN)); + } + + protected void repairAfterCrash() + { + String name = getRepository().getName(); + OM.LOG.warn(MessageFormat.format(Messages.getString("DBStore.9"), name)); //$NON-NLS-1$ + + Connection connection = getConnection(); + + try + { + connection.setAutoCommit(false); + connection.setReadOnly(true); + + mappingStrategy.repairAfterCrash(dbAdapter, connection); // Must update the idHandler + + boolean storeIDs = getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; + CDOID lastObjectID = storeIDs ? idHandler.getLastObjectID() : CDOID.NULL; + CDOID nextLocalObjectID = storeIDs ? idHandler.getNextLocalObjectID() : CDOID.NULL; + + int branchID = DBUtil.selectMaximumInt(connection, CDODBSchema.BRANCHES_ID); + setLastBranchID(branchID > 0 ? branchID : 0); + + int localBranchID = DBUtil.selectMinimumInt(connection, CDODBSchema.BRANCHES_ID); + setLastLocalBranchID(localBranchID < 0 ? localBranchID : 0); + + long lastCommitTime = DBUtil.selectMaximumLong(connection, CDODBSchema.COMMIT_INFOS_TIMESTAMP); + setLastCommitTime(lastCommitTime); + + long lastNonLocalCommitTime = DBUtil.selectMaximumLong(connection, CDODBSchema.COMMIT_INFOS_TIMESTAMP, + CDOBranch.MAIN_BRANCH_ID + "<=" + CDODBSchema.COMMIT_INFOS_BRANCH); + setLastNonLocalCommitTime(lastNonLocalCommitTime); + + if (storeIDs) + { + OM.LOG + .info(MessageFormat.format( + Messages.getString("DBStore.10"), name, lastObjectID, nextLocalObjectID, getLastBranchID(), getLastCommitTime(), getLastNonLocalCommitTime())); //$NON-NLS-1$ + } + else + { + OM.LOG + .info(MessageFormat.format( + Messages.getString("DBStore.10b"), name, getLastBranchID(), getLastCommitTime(), getLastNonLocalCommitTime())); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + OM.LOG.error(MessageFormat.format(Messages.getString("DBStore.11"), name), e); //$NON-NLS-1$ + throw new DBException(e); + } + finally + { + DBUtil.close(connection); + } + } + + protected IDBSchema createSchema() + { + String name = getRepository().getName(); + return new DBSchema(name); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java new file mode 100644 index 0000000000..42aca92d88 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java @@ -0,0 +1,1393 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 259402 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Andre Dietisheim - bug 256649 + * Caspar De Groot - maintenance + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.db.CDODBUtil; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.StoreAccessor; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimerTask; + +/** + * @author Eike Stepper + */ +public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DurableLocking2 +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, DBStoreAccessor.class); + + private Connection connection; + + private ConnectionKeepAliveTask connectionKeepAliveTask; + + private IPreparedStatementCache statementCache; + + private Set<CDOID> newObjects = new HashSet<CDOID>(); + + private CDOID maxID = CDOID.NULL; + + public DBStoreAccessor(DBStore store, ISession session) throws DBException + { + super(store, session); + } + + public DBStoreAccessor(DBStore store, ITransaction transaction) throws DBException + { + super(store, transaction); + } + + @Override + public DBStore getStore() + { + return (DBStore)super.getStore(); + } + + public IPreparedStatementCache getStatementCache() + { + return statementCache; + } + + public DBStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + return new DBStoreChunkReader(this, revision, feature); + } + + /** + * Returns an iterator that iterates over all objects in the store and makes their CDOIDs available for processing. + * This method is supposed to be called very infrequently, for example during the recovery from a crash. + * + * @since 2.0 + * @deprecated Not used by the framework anymore. + */ + @Deprecated + public CloseableIterator<CDOID> readObjectIDs() + { + if (TRACER.isEnabled()) + { + TRACER.trace("Selecting object IDs"); //$NON-NLS-1$ + } + + return getStore().getMappingStrategy().readObjectIDs(this); + } + + public CDOClassifierRef readObjectType(CDOID id) + { + if (TRACER.isEnabled()) + { + TRACER.format("Selecting object type: {0}", id); //$NON-NLS-1$ + } + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + return mappingStrategy.readObjectType(this, id); + } + + protected EClass getObjectType(CDOID id) + { + IRepository repository = getStore().getRepository(); + if (repository.getRootResourceID().equals(id)) + { + return EresourcePackage.Literals.CDO_RESOURCE; + } + + EClass result = repository.getRevisionManager().getObjectType(id); + if (result != null) + { + return result; + } + + CDOClassifierRef type = readObjectType(id); + if (type != null) + { + CDOPackageRegistry packageRegistry = repository.getPackageRegistry(); + return (EClass)type.resolve(packageRegistry); + } + + return null; + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache) + { + if (TRACER.isEnabled()) + { + TRACER.format("Selecting revision {0} from {1}", id, branchPoint); //$NON-NLS-1$ + } + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + + EClass eClass = getObjectType(id); + if (eClass != null) + { + InternalCDORevision revision = getStore().createRevision(eClass, id); + revision.setBranchPoint(branchPoint); + + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + if (mapping.readRevision(this, revision, listChunk)) + { + int version = revision.getVersion(); + if (version < CDOBranchVersion.FIRST_VERSION - 1) + { + return new DetachedCDORevision(eClass, id, revision.getBranch(), -version, revision.getTimeStamp(), + revision.getRevised()); + } + + return revision; + } + } + + // Reading failed - revision does not exist. + return null; + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache) + { + DBStore store = getStore(); + EClass eClass = getObjectType(id); + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + + InternalCDORevision revision = store.createRevision(eClass, id); + revision.setVersion(branchVersion.getVersion()); + revision.setBranchPoint(branchVersion.getBranch().getHead()); + + boolean success = false; + + if (mappingStrategy.hasAuditSupport()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Selecting revision {0} from {1}", id, branchVersion); //$NON-NLS-1$ + } + + // if audit support is present, just use the audit method + success = ((IClassMappingAuditSupport)mapping).readRevisionByVersion(this, revision, listChunk); + if (success && revision.getVersion() < CDOBranchVersion.FIRST_VERSION - 1) + { + // it is detached revision + revision = new DetachedCDORevision(eClass, id, revision.getBranch(), -revision.getVersion(), + revision.getTimeStamp(), revision.getRevised()); + + } + } + else + { + // if audit support is not present, we still have to provide a method + // to readRevisionByVersion because TransactionCommitContext.computeDirtyObject + // needs to lookup the base revision for a change. Hence we emulate this + // behavior by getting the current revision and asserting that the version + // has not changed. This is valid because if the version has changed, + // we are in trouble because of a conflict anyways. + if (TRACER.isEnabled()) + { + TRACER.format("Selecting current base revision: {0}", id); //$NON-NLS-1$ + } + + success = mapping.readRevision(this, revision, listChunk); + + if (success && revision.getVersion() != branchVersion.getVersion()) + { + throw new IllegalStateException("Can only retrieve current version " + revision.getVersion() + " for " + id //$NON-NLS-1$ //$NON-NLS-2$ + + " - version requested was " + branchVersion); //$NON-NLS-1$ + } + } + + return success ? revision : null; + } + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + mappingStrategy.queryResources(this, context); + } + + public void queryXRefs(QueryXRefsContext context) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + mappingStrategy.queryXRefs(this, context); + } + + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + String queryLanguage = info.getQueryLanguage(); + if (StringUtil.equalsUpperOrLowerCase(queryLanguage, SQLQueryHandler.QUERY_LANGUAGE)) + { + return new SQLQueryHandler(this); + } + + return null; + } + + public void queryLobs(List<byte[]> ids) + { + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_QUERY_LOBS, ReuseProbability.MEDIUM); + + for (Iterator<byte[]> it = ids.iterator(); it.hasNext();) + { + byte[] id = it.next(); + stmt.setString(1, HexUtil.bytesToHex(id)); + + try + { + resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + it.remove(); + } + } + finally + { + DBUtil.close(resultSet); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_LOB, ReuseProbability.MEDIUM); + stmt.setString(1, HexUtil.bytesToHex(id)); + + try + { + resultSet = stmt.executeQuery(); + resultSet.next(); + + long size = resultSet.getLong(1); + Blob blob = resultSet.getBlob(2); + if (resultSet.wasNull()) + { + Clob clob = resultSet.getClob(3); + Reader in = clob.getCharacterStream(); + IOUtil.copyCharacter(in, new OutputStreamWriter(out), size); + } + else + { + InputStream in = blob.getBinaryStream(); + IOUtil.copyBinary(in, out, size); + } + } + finally + { + DBUtil.close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_HANDLE_LOBS, ReuseProbability.LOW); + + try + { + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + byte[] id = HexUtil.hexToBytes(resultSet.getString(1)); + long size = resultSet.getLong(2); + Blob blob = resultSet.getBlob(3); + if (resultSet.wasNull()) + { + Clob clob = resultSet.getClob(4); + Reader in = clob.getCharacterStream(); + Writer out = handler.handleClob(id, size); + if (out != null) + { + try + { + IOUtil.copyCharacter(in, out, size); + } + finally + { + IOUtil.close(out); + } + } + } + else + { + InputStream in = blob.getBinaryStream(); + OutputStream out = handler.handleBlob(id, size); + if (out != null) + { + try + { + IOUtil.copyBinary(in, out, size); + } + finally + { + IOUtil.close(out); + } + } + } + } + } + finally + { + DBUtil.close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor) + { + super.applyIDMappings(context, monitor); + + // Remember maxID because it may have to be adjusted if the repository is BACKUP or CLONE. See bug 325097. + boolean adjustMaxID = !context.getBranchPoint().getBranch().isLocal() + && getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; + + IIDHandler idHandler = getStore().getIDHandler(); + + // Remember CDOIDs of new objects. They are cleared after writeRevisions() + for (InternalCDORevision revision : context.getNewObjects()) + { + CDOID id = revision.getID(); + newObjects.add(id); + + if (adjustMaxID && idHandler.compare(id, maxID) > 0) + { + maxID = id; + } + } + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment, OMMonitor monitor) + { + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_COMMIT_INFO, ReuseProbability.HIGH); + stmt.setLong(1, timeStamp); + stmt.setLong(2, previousTimeStamp); + stmt.setInt(3, branch.getID()); + stmt.setString(4, userID); + stmt.setString(5, comment); + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created, + OMMonitor monitor) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + + if (!mappingStrategy.hasDeltaSupport()) + { + throw new UnsupportedOperationException("Mapping strategy does not support revision deltas"); //$NON-NLS-1$ + } + + monitor.begin(revisionDeltas.length); + try + { + for (InternalCDORevisionDelta delta : revisionDeltas) + { + writeRevisionDelta(delta, created, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + protected void writeRevisionDelta(InternalCDORevisionDelta delta, long created, OMMonitor monitor) + { + CDOID id = delta.getID(); + EClass eClass = getObjectType(id); + IClassMappingDeltaSupport mapping = (IClassMappingDeltaSupport)getStore().getMappingStrategy().getClassMapping( + eClass); + mapping.writeRevisionDelta(this, delta, created, monitor); + } + + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + for (InternalCDORevision revision : revisions) + { + writeRevision(revision, newObjects.contains(revision.getID()), true, monitor.fork()); + } + } + finally + { + newObjects.clear(); + monitor.done(); + } + } + + protected void writeRevision(InternalCDORevision revision, boolean mapType, boolean revise, OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing revision: {0}", revision); //$NON-NLS-1$ + } + + EClass eClass = revision.getEClass(); + + IClassMapping mapping = getStore().getMappingStrategy().getClassMapping(eClass); + mapping.writeRevision(this, revision, mapType, revise, monitor); + } + + /* + * XXX Eike: change API from CDOID[] to CDOIDAndVersion[] + */ + @Override + protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + monitor.begin(detachedObjects.length); + + try + { + InternalCDORevisionManager revisionManager = getStore().getRepository().getRevisionManager(); + for (CDOID id : detachedObjects) + { + // TODO when CDOIDAndVersion is available: + // CDOID id = idAndVersion.getID(); // + // int version = idAndVersion.getVersion(); // + + // but for now: + + InternalCDORevision revision = revisionManager.getRevision(id, branch.getHead(), CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + int version = ObjectUtil.equals(branch, revision.getBranch()) ? revision.getVersion() + : CDOBranchVersion.FIRST_VERSION; + + if (TRACER.isEnabled()) + { + TRACER.format("Detaching object: {0}", id); //$NON-NLS-1$ + } + + EClass eClass = getObjectType(id); + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + mapping.detachObject(this, id, version, branch, timeStamp, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + public Connection getConnection() + { + return connection; + } + + @Override + protected CDOID getNextCDOID(CDORevision revision) + { + return getStore().getIDHandler().getNextCDOID(revision); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_BLOB, ReuseProbability.MEDIUM); + stmt.setString(1, HexUtil.bytesToHex(id)); + stmt.setLong(2, size); + stmt.setBinaryStream(3, inputStream, (int)size); + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_CLOB, ReuseProbability.MEDIUM); + stmt.setString(1, HexUtil.bytesToHex(id)); + stmt.setLong(2, size); + stmt.setCharacterStream(3, reader, (int)size); + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected final void doCommit(OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("--- DB COMMIT ---"); //$NON-NLS-1$ + } + + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + getConnection().commit(); + + if (maxID != CDOID.NULL) + { + // See bug 325097 + getStore().getIDHandler().adjustLastObjectID(maxID); + maxID = CDOID.NULL; + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + monitor.done(); + } + } + + @Override + protected final void doRollback(IStoreAccessor.CommitContext commitContext) + { + getStore().getMetaDataManager().clearMetaIDMappings(); + + if (TRACER.isEnabled()) + { + TRACER.format("--- DB ROLLBACK ---"); //$NON-NLS-1$ + } + + try + { + getConnection().rollback(); + + // Bugzilla 298632: Must rollback DBSchema to its prior state and drop the tables + getStore().getMappingStrategy().removeMapping(getConnection(), commitContext.getNewPackageUnits()); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + @Override + protected void doActivate() throws Exception + { + DBStore store = getStore(); + connection = store.getConnection(); + connectionKeepAliveTask = new ConnectionKeepAliveTask(); + + long keepAlivePeriod = ConnectionKeepAliveTask.EXECUTION_PERIOD; + Map<String, String> storeProps = store.getProperties(); + if (storeProps != null) + { + String value = storeProps.get(IDBStore.Props.CONNECTION_KEEPALIVE_PERIOD); + if (value != null) + { + keepAlivePeriod = Long.parseLong(value) * 60L * 1000L; + } + } + + store.getConnectionKeepAliveTimer().schedule(connectionKeepAliveTask, keepAlivePeriod, keepAlivePeriod); + + // TODO - make this configurable? + statementCache = CDODBUtil.createStatementCache(); + statementCache.setConnection(connection); + LifecycleUtil.activate(statementCache); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(statementCache); + connectionKeepAliveTask.cancel(); + DBUtil.close(connection); + connection = null; + } + + @Override + protected void doPassivate() throws Exception + { + // this is called when the accessor is put back into the pool + // we want to make sure that no DB lock is held (see Bug 276926) + connection.rollback(); + } + + @Override + protected void doUnpassivate() throws Exception + { + // do nothing + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + return getStore().getMetaDataManager().loadPackageUnit(getConnection(), packageUnit); + } + + public Collection<InternalCDOPackageUnit> readPackageUnits() + { + return getStore().getMetaDataManager().readPackageUnits(getConnection()); + } + + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + monitor.begin(2); + + try + { + DBStore store = getStore(); + Connection connection = getConnection(); + + IMetaDataManager metaDataManager = store.getMetaDataManager(); + metaDataManager.writePackageUnits(connection, packageUnits, monitor.fork()); + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + mappingStrategy.createMapping(connection, packageUnits, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + checkBranchingSupport(); + if (branchID == NEW_BRANCH) + { + branchID = getStore().getNextBranchID(); + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = getStore().getNextLocalBranchID(); + } + + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_BRANCH, ReuseProbability.LOW); + stmt.setInt(1, branchID); + stmt.setString(2, branchInfo.getName()); + stmt.setInt(3, branchInfo.getBaseBranchID()); + stmt.setLong(4, branchInfo.getBaseTimeStamp()); + + DBUtil.update(stmt, true); + getConnection().commit(); + return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp()); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public BranchInfo loadBranch(int branchID) + { + checkBranchingSupport(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCH, ReuseProbability.HIGH); + stmt.setInt(1, branchID); + + resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("Branch with ID " + branchID + " does not exist"); + } + + String name = resultSet.getString(1); + int baseBranchID = resultSet.getInt(2); + long baseTimeStamp = resultSet.getLong(3); + return new BranchInfo(name, baseBranchID, baseTimeStamp); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public SubBranchInfo[] loadSubBranches(int baseID) + { + checkBranchingSupport(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_SUB_BRANCHES, ReuseProbability.HIGH); + stmt.setInt(1, baseID); + + resultSet = stmt.executeQuery(); + List<SubBranchInfo> result = new ArrayList<SubBranchInfo>(); + while (resultSet.next()) + { + int id = resultSet.getInt(1); + String name = resultSet.getString(2); + long baseTimeStamp = resultSet.getLong(3); + result.add(new SubBranchInfo(id, name, baseTimeStamp)); + } + + return result.toArray(new SubBranchInfo[result.size()]); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + private void checkBranchingSupport() + { + if (!getStore().getMappingStrategy().hasBranchingSupport()) + { + throw new UnsupportedOperationException("Mapping strategy does not support branching"); //$NON-NLS-1$ + } + } + + public int loadBranches(int startID, int endID, CDOBranchHandler handler) + { + int count = 0; + PreparedStatement stmt = null; + ResultSet resultSet = null; + + InternalRepository repository = getSession().getManager().getRepository(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCHES, ReuseProbability.HIGH); + stmt.setInt(1, startID); + stmt.setInt(2, endID > 0 ? endID : Integer.MAX_VALUE); + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + int branchID = resultSet.getInt(1); + String name = resultSet.getString(2); + int baseBranchID = resultSet.getInt(3); + long baseTimeStamp = resultSet.getLong(4); + + InternalCDOBranch branch = branchManager.getBranch(branchID, new BranchInfo(name, baseBranchID, baseTimeStamp)); + handler.handleBranch(branch); + ++count; + } + + return count; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_PREVIOUS_TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_USER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_COMMENT); + if (branch == null) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_BRANCH); + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS); + boolean where = false; + + if (branch != null) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.COMMIT_INFOS_BRANCH); + builder.append("="); //$NON-NLS-1$ + builder.append(branch.getID()); + where = true; + } + + if (startTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + builder.append(">="); //$NON-NLS-1$ + builder.append(startTime); + where = true; + } + + if (endTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + builder.append("<="); //$NON-NLS-1$ + builder.append(endTime); + where = true; + } + + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + String sql = builder.toString(); + + PreparedStatement stmt = null; + ResultSet resultSet = null; + + InternalRepository repository = getStore().getRepository(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + + try + { + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.MEDIUM); + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + long timeStamp = resultSet.getLong(1); + long previousTimeStamp = resultSet.getLong(2); + String userID = resultSet.getString(3); + String comment = resultSet.getString(4); + CDOBranch infoBranch = branch; + if (infoBranch == null) + { + int id = resultSet.getInt(5); + infoBranch = branchManager.getBranch(id); + } + + CDOCommitInfo commitInfo = commitInfoManager.createCommitInfo(infoBranch, timeStamp, previousTimeStamp, userID, + comment, null); + handler.handleCommitInfo(commitInfo); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + return mappingStrategy.readChangeSet(this, monitor, segments); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + mappingStrategy.handleRevisions(this, eClass, branch, timeStamp, exactTime, new DBRevisionHandler(handler)); + } + + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException + { + DBStore store = getStore(); + if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + out.writeCDOID(store.getIDHandler().getLastObjectID()); // See bug 325097 + } + + String where = " WHERE " + CDODBSchema.BRANCHES_ID + " BETWEEN " + fromBranchID + " AND " + toBranchID; + DBUtil.serializeTable(out, connection, CDODBSchema.BRANCHES, null, where); + + where = " WHERE " + CDODBSchema.COMMIT_INFOS_TIMESTAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime; + DBUtil.serializeTable(out, connection, CDODBSchema.COMMIT_INFOS, null, where); + + IIDHandler idHandler = store.getIDHandler(); + idHandler.rawExport(connection, out, fromCommitTime, toCommitTime); + + IMetaDataManager metaDataManager = store.getMetaDataManager(); + metaDataManager.rawExport(connection, out, fromCommitTime, toCommitTime); + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + mappingStrategy.rawExport(this, out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + DBStore store = getStore(); + if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + store.getIDHandler().setLastObjectID(in.readCDOID()); // See bug 325097 + } + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + int size = mappingStrategy.getClassMappings().size(); + int commitWork = 4; + monitor.begin(commitWork + size + commitWork); + + Collection<InternalCDOPackageUnit> packageUnits = new HashSet<InternalCDOPackageUnit>(); + + try + { + DBUtil.deserializeTable(in, connection, CDODBSchema.BRANCHES, monitor.fork()); + DBUtil.deserializeTable(in, connection, CDODBSchema.COMMIT_INFOS, monitor.fork()); + store.getIDHandler().rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + rawImportPackageUnits(in, fromCommitTime, toCommitTime, packageUnits, monitor.fork()); + mappingStrategy.rawImport(this, in, fromCommitTime, toCommitTime, monitor.fork(size)); + rawCommit(commitWork, monitor); + } + catch (RuntimeException ex) + { + rawRollback(packageUnits); + throw ex; + } + catch (IOException ex) + { + rawRollback(packageUnits); + throw ex; + } + finally + { + monitor.done(); + } + } + + private void rawRollback(Collection<InternalCDOPackageUnit> packageUnits) + { + try + { + connection.rollback(); + } + catch (SQLException ex) + { + OM.LOG.error(ex); + } + + getStore().getMappingStrategy().removeMapping(getConnection(), + packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()])); + } + + protected void rawImportPackageUnits(CDODataInput in, long fromCommitTime, long toCommitTime, + Collection<InternalCDOPackageUnit> packageUnits, OMMonitor monitor) throws IOException + { + monitor.begin(2); + + try + { + DBStore store = getStore(); + IMetaDataManager metaDataManager = store.getMetaDataManager(); + + packageUnits.addAll(metaDataManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork())); + + InternalRepository repository = store.getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + packageRegistry.putPackageUnit(packageUnit); + } + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + + // Using another connection because CREATE TABLE (which is called in createMapping) on H2 databases does a commit. + Connection connection2 = null; + try + { + connection2 = store.getConnection(); + + mappingStrategy.createMapping(connection2, + packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]), monitor.fork()); + } + finally + { + DBUtil.close(connection2); + } + } + finally + { + monitor.done(); + } + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + CDOID id = revision.getID(); + + CDOClassifierRef classifierRef = getStore().getMappingStrategy().readObjectType(this, id); + boolean isFirstRevision = classifierRef == null; + + if (!isFirstRevision) + { + EClass eClass = revision.getEClass(); + boolean namesMatch = classifierRef.getClassifierName().equals(eClass.getName()); + boolean packagesMatch = classifierRef.getPackageURI().equals(eClass.getEPackage().getNsURI()); + if (!namesMatch || !packagesMatch) + { + throw new IllegalStateException(); + } + } + + writeRevision(revision, isFirstRevision, false, monitor); + getStore().getIDHandler().adjustLastObjectID(id); + } + + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException + { + writeBlob(id, size, inputStream); + } + + public void rawStore(byte[] id, long size, Reader reader) throws IOException + { + writeClob(id, size, reader); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + @Deprecated + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + + // IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + // if (eClass == null) + // { + // eClass = getObjectType(id); + // } + // + // IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + // mapping.detachObject(this, id, version, branch, CDOBranchPoint.UNSPECIFIED_DATE, monitor); + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + async.stop(); + monitor.done(); + } + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + return manager.createLockArea(this, durableLockingID, userID, branchPoint, readOnly, locks); + } + + public void updateLockArea(LockArea area) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.updateLockArea(this, area); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + return manager.getLockArea(this, durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.getLockAreas(this, userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.deleteLockArea(this, durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.lock(this, durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID); + } + + /** + * @author Stefan Winkler + */ + private class ConnectionKeepAliveTask extends TimerTask + { + public static final long EXECUTION_PERIOD = 1000 * 60 * 60 * 4; // 4 hours + + @Override + public void run() + { + Statement stmt = null; + + try + { + if (TRACER.isEnabled()) + { + TRACER.trace("DB connection keep-alive task activated"); //$NON-NLS-1$ + } + + stmt = connection.createStatement(); + stmt.executeQuery("SELECT 1 FROM " + CDODBSchema.PROPERTIES); //$NON-NLS-1$ + } + catch (java.sql.SQLException ex) + { + OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$ + + // Assume the connection has failed. + try + { + LifecycleUtil.deactivate(DBStoreAccessor.this); + LifecycleUtil.activate(DBStoreAccessor.this); + } + catch (Exception ex2) + { + OM.LOG.error("DB connection reconnect failed", ex2); //$NON-NLS-1$ + } + } + catch (Exception ex) // Important: Do not throw any unchecked exceptions to the TimerThread!!! + { + OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$ + } + finally + { + DBUtil.close(stmt); + } + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java new file mode 100644 index 0000000000..59287fa3ee --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 283998: [DB] Chunk reading for multiple chunks fails + * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.spi.server.StoreChunkReader; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.List; + +/** + * @author Eike Stepper + */ +public class DBStoreChunkReader extends StoreChunkReader implements IDBStoreChunkReader +{ + private IListMapping referenceMapping; + + private StringBuilder builder = new StringBuilder(); + + public DBStoreChunkReader(DBStoreAccessor accessor, CDORevision revision, EStructuralFeature feature) + { + super(accessor, revision, feature); + IMappingStrategy mappingStrategy = accessor.getStore().getMappingStrategy(); + IClassMapping mapping = mappingStrategy.getClassMapping(revision.getEClass()); + referenceMapping = mapping.getListMapping(feature); + } + + @Override + public DBStoreAccessor getAccessor() + { + return (DBStoreAccessor)super.getAccessor(); + } + + @Override + public void addSimpleChunk(int index) + { + super.addSimpleChunk(index); + prepareAddition(); + + builder.append(CDODBSchema.LIST_IDX); + builder.append('='); + builder.append(index); + } + + @Override + public void addRangedChunk(int fromIndex, int toIndex) + { + super.addRangedChunk(fromIndex, toIndex); + prepareAddition(); + + builder.append(CDODBSchema.LIST_IDX); + builder.append(" BETWEEN "); //$NON-NLS-1$ + builder.append(fromIndex); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(toIndex - 1); + } + + public List<Chunk> executeRead() + { + List<Chunk> chunks = getChunks(); + if (chunks.size() > 1) + { + builder.insert(0, '('); + builder.append(')'); + } + + referenceMapping.readChunks(this, chunks, builder.toString()); + return chunks; + } + + private void prepareAddition() + { + // If not empty, a chunk has been already added, and the next condition needs to be OR-ed + if (builder.length() > 0) + { + builder.append(" OR "); //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java new file mode 100644 index 0000000000..229028902b --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Caspar De Groot - maintenance + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStoreFactory; +import org.eclipse.emf.cdo.server.db.CDODBUtil; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.spi.server.RepositoryConfigurator; + +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.IDBConnectionProvider; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import javax.sql.DataSource; + +import java.util.Map; +import java.util.Properties; + +/** + * @author Eike Stepper + */ +public class DBStoreFactory implements IStoreFactory +{ + public DBStoreFactory() + { + } + + public String getStoreType() + { + return DBStore.TYPE; + } + + public IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig) + { + IMappingStrategy mappingStrategy = getMappingStrategy(repositoryName, repositoryProperties, storeConfig); + IDBAdapter dbAdapter = getDBAdapter(storeConfig); + DataSource dataSource = getDataSource(storeConfig); + IDBConnectionProvider connectionProvider = DBUtil.createConnectionProvider(dataSource); + + DBStore store = new DBStore(); + store.setMappingStrategy(mappingStrategy); + store.setDBAdapter(dbAdapter); + store.setDbConnectionProvider(connectionProvider); + + Map<String, String> storeProperties = RepositoryConfigurator.getProperties(storeConfig, 1); + store.setProperties(storeProperties); + + return store; + } + + private IMappingStrategy getMappingStrategy(String repositoryName, Map<String, String> repositoryProperties, + Element storeConfig) + { + NodeList mappingStrategyConfigs = storeConfig.getElementsByTagName("mappingStrategy"); //$NON-NLS-1$ + if (mappingStrategyConfigs.getLength() != 1) + { + throw new IllegalStateException("Exactly one mapping strategy must be configured for DB store"); //$NON-NLS-1$ + } + + Element mappingStrategyConfig = (Element)mappingStrategyConfigs.item(0); + String mappingStrategyType = mappingStrategyConfig.getAttribute("type"); //$NON-NLS-1$ + IMappingStrategy mappingStrategy = CDODBUtil.createMappingStrategy(mappingStrategyType); + if (mappingStrategy == null) + { + throw new IllegalArgumentException("Unknown mapping strategy: " + mappingStrategyType); //$NON-NLS-1$ + } + + Map<String, String> properties = RepositoryConfigurator.getProperties(mappingStrategyConfig, 1); + properties.put("repositoryName", repositoryName); + properties.putAll(repositoryProperties); + mappingStrategy.setProperties(properties); + + return mappingStrategy; + } + + private IDBAdapter getDBAdapter(Element storeConfig) + { + NodeList dbAdapterConfigs = storeConfig.getElementsByTagName("dbAdapter"); //$NON-NLS-1$ + if (dbAdapterConfigs.getLength() != 1) + { + throw new IllegalStateException("Exactly one dbAdapter must be configured for DB store"); //$NON-NLS-1$ + } + + Element dbAdapterConfig = (Element)dbAdapterConfigs.item(0); + String dbAdapterName = dbAdapterConfig.getAttribute("name"); //$NON-NLS-1$ + IDBAdapter dbAdapter = DBUtil.getDBAdapter(dbAdapterName); + if (dbAdapter == null) + { + throw new IllegalArgumentException("Unknown DB adapter: " + dbAdapterName); //$NON-NLS-1$ + } + + return dbAdapter; + } + + private DataSource getDataSource(Element storeConfig) + { + NodeList dataSourceConfigs = storeConfig.getElementsByTagName("dataSource"); //$NON-NLS-1$ + if (dataSourceConfigs.getLength() != 1) + { + throw new IllegalStateException("Exactly one dataSource must be configured for DB store"); //$NON-NLS-1$ + } + + Properties properties = new Properties(); + Element dataSourceConfig = (Element)dataSourceConfigs.item(0); + NamedNodeMap attributes = dataSourceConfig.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr)attributes.item(i); + properties.put(attribute.getName(), attribute.getValue()); + } + + return DBUtil.createDataSource(properties); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java new file mode 100644 index 0000000000..7fbd726665 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java @@ -0,0 +1,687 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaAlreadyExistsException;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * @author Eike Stepper
+ */
+public class DurableLockingManager extends Lifecycle
+{
+ private DBStore store;
+
+ private InternalCDOBranchManager branchManager;
+
+ private IIDHandler idHandler;
+
+ private IDBTable lockAreas;
+
+ private IDBField lockAreasID;
+
+ private IDBField lockAreasUser;
+
+ private IDBField lockAreasBranch;
+
+ private IDBField lockAreasTime;
+
+ private IDBField lockAreasReadOnly;
+
+ private IDBTable locks;
+
+ private IDBField locksArea;
+
+ private IDBField locksObject;
+
+ private IDBField locksGrade;
+
+ private String sqlInsertLockArea;
+
+ private String sqlSelectLockArea;
+
+ private String sqlSelectAllLockAreas;
+
+ private String sqlSelectLockAreas;
+
+ private String sqlDeleteLockArea;
+
+ private String sqlSelectLocks;
+
+ private String sqlSelectLock;
+
+ private String sqlInsertLock;
+
+ private String sqlUpdateLock;
+
+ private String sqlDeleteLock;
+
+ private String sqlDeleteLocks;
+
+ public DurableLockingManager(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public synchronized LockArea createLockArea(DBStoreAccessor accessor, String durableLockingID, String userID,
+ CDOBranchPoint branchPoint, boolean readOnly, Map<CDOID, LockGrade> locks)
+ {
+ try
+ {
+ if (durableLockingID == null)
+ {
+ durableLockingID = getNextDurableLockingID(accessor);
+ }
+ else
+ {
+ // If the caller is specifying the ID, make sure there is no area with this ID yet
+ //
+ try
+ {
+ getLockArea(accessor, durableLockingID);
+ throw new LockAreaAlreadyExistsException(durableLockingID);
+ }
+ catch (LockAreaNotFoundException good)
+ {
+ }
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertLockArea, ReuseProbability.LOW);
+ stmt.setString(1, durableLockingID);
+ stmt.setString(2, userID);
+ stmt.setInt(3, branchPoint.getBranch().getID());
+ stmt.setLong(4, branchPoint.getTimeStamp());
+ stmt.setBoolean(5, readOnly);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (!locks.isEmpty())
+ {
+ insertLocks(accessor, durableLockingID, locks);
+ }
+
+ accessor.getConnection().commit();
+
+ return CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+
+ private void insertLocks(DBStoreAccessor accessor, String durableLockingID, Map<CDOID, LockGrade> locks)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertLock, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+
+ for (Entry<CDOID, LockGrade> entry : locks.entrySet())
+ {
+ CDOID id = entry.getKey();
+ int grade = entry.getValue().getValue();
+
+ idHandler.setCDOID(stmt, 2, id);
+ stmt.setInt(3, grade);
+
+ DBUtil.update(stmt, true);
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public LockArea getLockArea(DBStoreAccessor accessor, String durableLockingID) throws LockAreaNotFoundException
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectLockArea, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+ resultSet = stmt.executeQuery();
+
+ if (!resultSet.next())
+ {
+ throw new LockAreaNotFoundException(durableLockingID);
+ }
+
+ String userID = resultSet.getString(1);
+ int branchID = resultSet.getInt(2);
+ long timeStamp = resultSet.getLong(3);
+ boolean readOnly = resultSet.getBoolean(4);
+
+ return makeLockArea(accessor, durableLockingID, userID, branchID, timeStamp, readOnly);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void getLockAreas(DBStoreAccessor accessor, String userIDPrefix, Handler handler)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ if (userIDPrefix.length() == 0)
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectAllLockAreas, ReuseProbability.MEDIUM);
+ }
+ else
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectLockAreas, ReuseProbability.MEDIUM);
+ stmt.setString(1, userIDPrefix + "%");
+ }
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ String durableLockingID = resultSet.getString(1);
+ String userID = resultSet.getString(2);
+ int branchID = resultSet.getInt(3);
+ long timeStamp = resultSet.getLong(4);
+ boolean readOnly = resultSet.getBoolean(5);
+
+ LockArea area = makeLockArea(accessor, durableLockingID, userID, branchID, timeStamp, readOnly);
+ if (!handler.handleLockArea(area))
+ {
+ break;
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void deleteLockArea(DBStoreAccessor accessor, String durableLockingID)
+ {
+ try
+ {
+ unlockWithoutCommit(accessor, durableLockingID);
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlDeleteLockArea, ReuseProbability.LOW);
+ stmt.setString(1, durableLockingID);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ }
+
+ public void updateLockArea(DBStoreAccessor accessor, LockArea area)
+ {
+ try
+ {
+ String areaID = area.getDurableLockingID();
+ unlockWithoutCommit(accessor, areaID);
+ insertLocks(accessor, areaID, area.getLocks());
+
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ }
+
+ public void lock(DBStoreAccessor accessor, String durableLockingID, LockType type,
+ Collection<? extends Object> objectsToLock)
+ {
+ changeLocks(accessor, durableLockingID, type, objectsToLock, true);
+ }
+
+ public void unlock(DBStoreAccessor accessor, String durableLockingID, LockType type,
+ Collection<? extends Object> objectsToUnlock)
+ {
+ changeLocks(accessor, durableLockingID, type, objectsToUnlock, false);
+ }
+
+ public void unlock(DBStoreAccessor accessor, String durableLockingID)
+ {
+ try
+ {
+ unlockWithoutCommit(accessor, durableLockingID);
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ }
+
+ private void unlockWithoutCommit(DBStoreAccessor accessor, String durableLockingID)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlDeleteLocks, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ branchManager = store.getRepository().getBranchManager();
+ idHandler = store.getIDHandler();
+
+ IDBSchema schema = store.getDBSchema();
+
+ // Lock areas
+ lockAreas = schema.addTable("cdo_lock_areas");
+ lockAreasID = lockAreas.addField("id", DBType.VARCHAR);
+ lockAreasUser = lockAreas.addField("user_id", DBType.VARCHAR);
+ lockAreasBranch = lockAreas.addField("view_branch", DBType.INTEGER);
+ lockAreasTime = lockAreas.addField("view_time", DBType.BIGINT);
+ lockAreasReadOnly = lockAreas.addField("read_only", DBType.BOOLEAN);
+
+ lockAreas.addIndex(IDBIndex.Type.PRIMARY_KEY, lockAreasID);
+ lockAreas.addIndex(IDBIndex.Type.NON_UNIQUE, lockAreasUser);
+
+ // Locks
+ locks = schema.addTable("cdo_locks");
+ locksArea = locks.addField("area_id", DBType.VARCHAR);
+ locksObject = locks.addField("object_id", idHandler.getDBType());
+ locksGrade = locks.addField("lock_grade", DBType.INTEGER);
+
+ locks.addIndex(IDBIndex.Type.PRIMARY_KEY, locksArea, locksObject);
+ locks.addIndex(IDBIndex.Type.NON_UNIQUE, locksArea);
+
+ IDBStoreAccessor writer = store.getWriter(null);
+ Connection connection = writer.getConnection();
+ Statement statement = null;
+
+ try
+ {
+ statement = connection.createStatement();
+ store.getDBAdapter().createTable(lockAreas, statement);
+ store.getDBAdapter().createTable(locks, statement);
+ connection.commit();
+ }
+ catch (SQLException ex)
+ {
+ connection.rollback();
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(statement);
+ writer.release();
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ builder.append("("); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasBranch);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasTime);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasReadOnly);
+ builder.append(") VALUES (?, ?, ?, ?, ?)"); //$NON-NLS-1$
+ sqlInsertLockArea = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasBranch);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasTime);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasReadOnly);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectLockArea = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasBranch);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasTime);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasReadOnly);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ sqlSelectAllLockAreas = builder.toString();
+
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(" LIKE ?"); //$NON-NLS-1$
+ sqlSelectLockAreas = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteLockArea = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectLocks = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append("("); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append(") VALUES (?, ?, ?)"); //$NON-NLS-1$
+ sqlInsertLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlUpdateLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteLocks = builder.toString();
+ }
+
+ private String getNextDurableLockingID(DBStoreAccessor accessor)
+ {
+ for (;;)
+ {
+ String durableLockingID = CDOLockUtil.createDurableLockingID();
+
+ try
+ {
+ getLockArea(accessor, durableLockingID); // Check uniqueness
+ // Not unique; try once more...
+ }
+ catch (LockAreaNotFoundException ex)
+ {
+ return durableLockingID;
+ }
+ }
+ }
+
+ private LockArea makeLockArea(DBStoreAccessor accessor, String durableLockingID, String userID, int branchID,
+ long timeStamp, boolean readOnly)
+ {
+ CDOBranchPoint branchPoint = branchManager.getBranch(branchID).getPoint(timeStamp);
+ Map<CDOID, LockGrade> lockMap = getLockMap(accessor, durableLockingID);
+ return CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, lockMap);
+ }
+
+ private Map<CDOID, LockGrade> getLockMap(DBStoreAccessor accessor, String durableLockingID)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectLocks, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+ resultSet = stmt.executeQuery();
+
+ Map<CDOID, LockGrade> lockMap = new HashMap<CDOID, LockGrade>();
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ int grade = resultSet.getInt(2);
+
+ lockMap.put(id, LockGrade.get(grade));
+ }
+
+ return lockMap;
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void changeLocks(DBStoreAccessor accessor, String durableLockingID, LockType type,
+ Collection<? extends Object> keys, boolean on)
+ {
+ if (keys.isEmpty())
+ {
+ return;
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmtSelect = null;
+ PreparedStatement stmtInsertOrDelete = null;
+ PreparedStatement stmtUpdate = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmtSelect = statementCache.getPreparedStatement(sqlSelectLock, ReuseProbability.MEDIUM);
+ stmtSelect.setString(1, durableLockingID);
+
+ String sql = on ? sqlInsertLock : sqlDeleteLock;
+ stmtInsertOrDelete = statementCache.getPreparedStatement(sql, ReuseProbability.MEDIUM);
+ stmtInsertOrDelete.setString(1, durableLockingID);
+
+ stmtUpdate = statementCache.getPreparedStatement(sqlUpdateLock, ReuseProbability.MEDIUM);
+ stmtUpdate.setString(2, durableLockingID);
+
+ InternalLockManager lockManager = accessor.getStore().getRepository().getLockManager();
+ for (Object key : keys)
+ {
+ CDOID id = lockManager.getLockKeyID(key);
+ idHandler.setCDOID(stmtSelect, 2, id);
+ resultSet = stmtSelect.executeQuery();
+
+ LockGrade oldGrade = LockGrade.NONE;
+ if (resultSet.next())
+ {
+ oldGrade = LockGrade.get(resultSet.getInt(1));
+ }
+
+ LockGrade newGrade = oldGrade.getUpdated(type, on);
+ if (on && oldGrade == LockGrade.NONE)
+ {
+ idHandler.setCDOID(stmtInsertOrDelete, 2, id);
+ stmtInsertOrDelete.setInt(3, newGrade.getValue());
+ DBUtil.update(stmtInsertOrDelete, true);
+ }
+ else if (!on && newGrade == LockGrade.NONE)
+ {
+ idHandler.setCDOID(stmtInsertOrDelete, 2, id);
+ DBUtil.update(stmtInsertOrDelete, true);
+ }
+ else
+ {
+ stmtUpdate.setInt(1, newGrade.getValue());
+ idHandler.setCDOID(stmtUpdate, 3, id);
+ DBUtil.update(stmtUpdate, true);
+ }
+ }
+
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmtUpdate);
+ statementCache.releasePreparedStatement(stmtInsertOrDelete);
+ statementCache.releasePreparedStatement(stmtSelect);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java new file mode 100644 index 0000000000..34b5f895d3 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java @@ -0,0 +1,302 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation + * Stefan Winkler - bug 249610: [DB] Support external references (Implementation) + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.id.CDOIDExternal; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author Stefan Winkler + */ +public class ExternalReferenceManager extends Lifecycle +{ + private static final int NULL = 0; + + private IDBTable table; + + private IDBField idField; + + private IDBField uriField; + + private IDBField timestampField; + + private final IIDHandler idHandler; + + private AtomicLong lastMappedID = new AtomicLong(NULL); + + @ExcludeFromDump + private transient String sqlSelectByLongID; + + @ExcludeFromDump + private transient String sqlSelectByURI; + + @ExcludeFromDump + private transient String sqlInsert; + + public ExternalReferenceManager(IIDHandler idHandler) + { + this.idHandler = idHandler; + } + + public IIDHandler getIDHandler() + { + return idHandler; + } + + public long mapExternalReference(CDOIDExternal id, long commitTime) + { + IDBStoreAccessor accessor = getAccessor(); + return mapURI(accessor, id.getURI(), commitTime); + } + + public CDOIDExternal unmapExternalReference(long mappedId) + { + IDBStoreAccessor accessor = getAccessor(); + return CDOIDUtil.createExternal(unmapURI(accessor, mappedId)); + } + + public long mapURI(IDBStoreAccessor accessor, String uri, long commitTime) + { + long result = lookupByURI(accessor, uri); + if (result < NULL) + { + // mapping found + return result; + } + + return insertNew(accessor, uri, commitTime); + } + + public String unmapURI(IDBStoreAccessor accessor, long mappedId) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectByLongID, ReuseProbability.HIGH); + stmt.setLong(1, mappedId); + resultSet = stmt.executeQuery(); + + if (!resultSet.next()) + { + OM.LOG.error("External ID " + mappedId + " not found. Database inconsistent!"); + throw new IllegalStateException("External ID " + mappedId + " not found. Database inconsistent!"); + } + + return resultSet.getString(1); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) + throws IOException + { + String where = " WHERE " + timestampField + " BETWEEN " + fromCommitTime + " AND " + toCommitTime; + DBUtil.serializeTable(out, connection, table, null, where); + } + + public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + DBUtil.deserializeTable(in, connection, table, monitor); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + IDBStore store = idHandler.getStore(); + table = store.getDBSchema().addTable("cdo_external_refs"); //$NON-NLS-1$ + idField = table.addField("id", idHandler.getDBType()); //$NON-NLS-1$ + uriField = table.addField("uri", DBType.VARCHAR, 1024); //$NON-NLS-1$ + timestampField = table.addField("committime", DBType.BIGINT); //$NON-NLS-1$ + + table.addIndex(IDBIndex.Type.PRIMARY_KEY, idField); + table.addIndex(IDBIndex.Type.NON_UNIQUE, uriField); + + IDBStoreAccessor writer = store.getWriter(null); + Connection connection = writer.getConnection(); + Statement statement = null; + ResultSet resultSet = null; + + try + { + statement = connection.createStatement(); + store.getDBAdapter().createTable(table, statement); + connection.commit(); + + String sql = "SELECT MIN(" + idField + ") FROM " + table; + resultSet = statement.executeQuery(sql); + + if (resultSet.next()) + { + lastMappedID.set(resultSet.getLong(1)); + } + + // else: resultSet is empty => table is empty + // and lastMappedId stays 0 - as initialized. + } + catch (SQLException ex) + { + connection.rollback(); + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(statement); + writer.release(); + } + + StringBuilder builder = new StringBuilder(); + builder.append("INSERT INTO "); + builder.append(table); + builder.append("("); + builder.append(idField); + builder.append(","); + builder.append(uriField); + builder.append(","); + builder.append(timestampField); + builder.append(") VALUES (?, ?, ?)"); + sqlInsert = builder.toString(); + + builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(idField); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(uriField); + builder.append("=?"); //$NON-NLS-1$ + sqlSelectByURI = builder.toString(); + + builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(uriField); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(table); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(idField); + builder.append("=?"); //$NON-NLS-1$ + sqlSelectByLongID = builder.toString(); + } + + private long insertNew(IDBStoreAccessor accessor, String uri, long commitTime) + { + long newMappedID = lastMappedID.decrementAndGet(); + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.MEDIUM); + stmt.setLong(1, newMappedID); + stmt.setString(2, uri); + stmt.setLong(3, commitTime); + + DBUtil.update(stmt, true); + return newMappedID; + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private long lookupByURI(IDBStoreAccessor accessor, String uri) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectByURI, ReuseProbability.HIGH); + stmt.setString(1, uri); + + resultSet = stmt.executeQuery(); + + if (resultSet.next()) + { + return resultSet.getLong(1); + } + + // Not found ... + return NULL; + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + private static IDBStoreAccessor getAccessor() + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + if (accessor == null) + { + throw new IllegalStateException("Can only be called from within a valid IDBStoreAccessor context"); + } + + return (IDBStoreAccessor)accessor; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java new file mode 100644 index 0000000000..49344d311c --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; + +import java.io.IOException; +import java.sql.Connection; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public interface IObjectTypeMapper +{ + public CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id); + + public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type); + + public void removeObjectType(IDBStoreAccessor accessor, CDOID id); + + /** + * Return the maximum object id managed by this cache. + * + * @param connection + * the DB connection to use. + * @return the maximum object ID. + */ + public CDOID getMaxID(Connection connection, IIDHandler idHandler); + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) + throws IOException; + + public void rawImport(Connection connection, CDODataInput in, OMMonitor monitor) throws IOException; +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java new file mode 100644 index 0000000000..79d06158c7 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java @@ -0,0 +1,265 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDExternal;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+import org.eclipse.emf.cdo.spi.server.LongIDStore;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class LongIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final CDOID MIN = CDOID.NULL;
+
+ public static final CDOID MAX = create(Long.MAX_VALUE);
+
+ private DBStore store;
+
+ private ExternalReferenceManager externalReferenceManager;
+
+ private CDOID lastObjectID = MIN;
+
+ private CDOID nextLocalObjectID = MAX;
+
+ public LongIDHandler(DBStore store)
+ {
+ this.store = store;
+ externalReferenceManager = new ExternalReferenceManager(this);
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return LongIDStore.OBJECT_ID_TYPES;
+ }
+
+ public CDOID getMinCDOID()
+ {
+ return MIN;
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ return MAX;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ return id1.compareTo(id2);
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ Long id = Long.valueOf(val);
+ return create(id);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ return lastObjectID;
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ this.lastObjectID = lastObjectID;
+ }
+
+ public synchronized void adjustLastObjectID(CDOID maxID)
+ {
+ if (compare(maxID, lastObjectID) > 0)
+ {
+ lastObjectID = maxID;
+ }
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ return nextLocalObjectID;
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ this.nextLocalObjectID = nextLocalObjectID;
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ if (revision.getBranch().isLocal())
+ {
+ CDOID result = nextLocalObjectID;
+ nextLocalObjectID = create(value(result) - 1);
+ return result;
+ }
+
+ lastObjectID = create(value(lastObjectID) + 1);
+ return lastObjectID;
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ return compare(id, nextLocalObjectID) > 0;
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.BIGINT;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ long value = value(id);
+ builder.append(value);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ long value;
+ if (id.getType() == CDOID.Type.EXTERNAL_OBJECT)
+ {
+ if (commitTime == CDOBranchPoint.INVALID_DATE)
+ {
+ CommitContext commitContext = StoreThreadLocal.getCommitContext();
+ commitTime = commitContext != null ? commitContext.getBranchPoint().getTimeStamp()
+ : CDOBranchPoint.UNSPECIFIED_DATE; // Happens on rawStore for workspace checkouts
+ }
+
+ value = externalReferenceManager.mapExternalReference((CDOIDExternal)id, commitTime);
+ }
+ else
+ {
+ value = value(id);
+ }
+
+ stmt.setLong(column, value);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ long id = resultSet.getLong(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return unmapExternalReference(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ long id = resultSet.getLong(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return unmapExternalReference(id);
+ }
+
+ private CDOID unmapExternalReference(long id)
+ {
+ if (id < 0)
+ {
+ return externalReferenceManager.unmapExternalReference(id);
+ }
+
+ return create(id);
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return create(externalReferenceManager.mapURI(accessor, uri, commitTime));
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ if (id.getType() == CDOID.Type.EXTERNAL_OBJECT)
+ {
+ return ((CDOIDExternal)id).getURI();
+ }
+
+ return externalReferenceManager.unmapURI(accessor, value(id));
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ externalReferenceManager.rawExport(connection, out, fromCommitTime, toCommitTime);
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ externalReferenceManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ externalReferenceManager.activate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ externalReferenceManager.deactivate();
+ super.doDeactivate();
+ }
+
+ private static CDOID create(long id)
+ {
+ return CDOIDUtil.createLong(id);
+ }
+
+ private static long value(CDOID id)
+ {
+ return CDOIDUtil.getLong(id);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java new file mode 100644 index 0000000000..8882555ac7 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java @@ -0,0 +1,429 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 271444: [DB] Multiple refactorings + * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations + * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOModelConstants; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBRowHandler; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author Eike Stepper + */ +public class MetaDataManager extends Lifecycle implements IMetaDataManager +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, MetaDataManager.class); + + private static final boolean ZIP_PACKAGE_BYTES = true; + + private IDBStore store; + + private Map<EModelElement, CDOID> modelElementToMetaID = new HashMap<EModelElement, CDOID>(); + + private Map<CDOID, EModelElement> metaIDToModelElement = new HashMap<CDOID, EModelElement>(); + + public MetaDataManager(IDBStore store) + { + this.store = store; + } + + public synchronized CDOID getMetaID(EModelElement modelElement, long commitTime) + { + CDOID metaID = modelElementToMetaID.get(modelElement); + if (metaID != null) + { + return metaID; + } + + IDBStoreAccessor accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + String uri = EcoreUtil.getURI(modelElement).toString(); + metaID = store.getIDHandler().mapURI(accessor, uri, commitTime); + cacheMetaIDMapping(modelElement, metaID); + + return metaID; + } + + public synchronized EModelElement getMetaInstance(CDOID id) + { + EModelElement modelElement = metaIDToModelElement.get(id); + if (modelElement != null) + { + return modelElement; + } + + IDBStoreAccessor accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor(); + String uri = store.getIDHandler().unmapURI(accessor, id); + + ResourceSet resourceSet = new ResourceSetImpl(); + resourceSet.setPackageRegistry(getStore().getRepository().getPackageRegistry()); + + return (EModelElement)resourceSet.getEObject(URI.createURI(uri), true); + } + + public synchronized void clearMetaIDMappings() + { + modelElementToMetaID.clear(); + metaIDToModelElement.clear(); + } + + public final EPackage[] loadPackageUnit(Connection connection, InternalCDOPackageUnit packageUnit) + { + String where = CDODBSchema.PACKAGE_UNITS_ID.getName() + "='" + packageUnit.getID() + "'"; //$NON-NLS-1$ //$NON-NLS-2$ + Object[] values = DBUtil.select(connection, where, CDODBSchema.PACKAGE_UNITS_PACKAGE_DATA); + byte[] bytes = (byte[])values[0]; + EPackage ePackage = createEPackage(packageUnit, bytes); + return EMFUtil.getAllPackages(ePackage); + } + + public Collection<InternalCDOPackageUnit> readPackageUnits(Connection connection) + { + return readPackageUnits(connection, CDOBranchPoint.UNSPECIFIED_DATE, CDOBranchPoint.UNSPECIFIED_DATE, new Monitor()); + } + + public final void writePackageUnits(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + try + { + monitor.begin(2); + fillSystemTables(connection, packageUnits, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) + throws IOException + { + // Export package units + String where = " WHERE p_u." + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.CORE_PACKAGE_URI + // + "' AND p_u." + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.RESOURCE_PACKAGE_URI + // + "' AND p_u." + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.TYPES_PACKAGE_URI + // + "' AND p_u." + CDODBSchema.PACKAGE_UNITS_TIME_STAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime; + DBUtil.serializeTable(out, connection, CDODBSchema.PACKAGE_UNITS, "p_u", where); + + // Export package infos + String join = ", " + CDODBSchema.PACKAGE_UNITS + " p_u" + where + " AND p_i." + CDODBSchema.PACKAGE_INFOS_UNIT + + "=p_u." + CDODBSchema.PACKAGE_UNITS_ID; + DBUtil.serializeTable(out, connection, CDODBSchema.PACKAGE_INFOS, "p_i", join); + } + + public Collection<InternalCDOPackageUnit> rawImport(Connection connection, CDODataInput in, long fromCommitTime, + long toCommitTime, OMMonitor monitor) throws IOException + { + monitor.begin(3); + + try + { + DBUtil.deserializeTable(in, connection, CDODBSchema.PACKAGE_UNITS, monitor.fork()); + DBUtil.deserializeTable(in, connection, CDODBSchema.PACKAGE_INFOS, monitor.fork()); + return readPackageUnits(connection, fromCommitTime, toCommitTime, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + protected IDBStore getStore() + { + return store; + } + + @Override + protected void doBeforeActivate() throws Exception + { + checkState(store, "Store is not set"); //$NON-NLS-1$ + } + + @Override + protected void doDeactivate() throws Exception + { + clearMetaIDMappings(); + super.doDeactivate(); + } + + protected InternalCDOPackageInfo createPackageInfo() + { + return (InternalCDOPackageInfo)CDOModelUtil.createPackageInfo(); + } + + protected InternalCDOPackageUnit createPackageUnit() + { + return (InternalCDOPackageUnit)CDOModelUtil.createPackageUnit(); + } + + private InternalCDOPackageRegistry getPackageRegistry() + { + return (InternalCDOPackageRegistry)store.getRepository().getPackageRegistry(); + } + + private EPackage createEPackage(InternalCDOPackageUnit packageUnit, byte[] bytes) + { + ResourceSet resourceSet = EMFUtil.newEcoreResourceSet(getPackageRegistry()); + return EMFUtil.createEPackage(packageUnit.getID(), bytes, ZIP_PACKAGE_BYTES, resourceSet, false); + } + + private byte[] getEPackageBytes(InternalCDOPackageUnit packageUnit) + { + EPackage ePackage = packageUnit.getTopLevelPackageInfo().getEPackage(); + return EMFUtil.getEPackageBytes(ePackage, ZIP_PACKAGE_BYTES, getPackageRegistry()); + } + + private void fillSystemTables(Connection connection, InternalCDOPackageUnit packageUnit, OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing package unit: {0}", packageUnit); //$NON-NLS-1$ + } + + InternalCDOPackageInfo[] packageInfos = packageUnit.getPackageInfos(); + Async async = null; + monitor.begin(1 + packageInfos.length); + + try + { + String sql = "INSERT INTO " + CDODBSchema.PACKAGE_UNITS + " VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + DBUtil.trace(sql); + PreparedStatement stmt = null; + + try + { + async = monitor.forkAsync(); + stmt = connection.prepareStatement(sql); + stmt.setString(1, packageUnit.getID()); + stmt.setInt(2, packageUnit.getOriginalType().ordinal()); + stmt.setLong(3, packageUnit.getTimeStamp()); + stmt.setBytes(4, getEPackageBytes(packageUnit)); + + if (stmt.execute()) + { + throw new DBException("No result set expected"); //$NON-NLS-1$ + } + + if (stmt.getUpdateCount() == 0) + { + throw new DBException("No row inserted into table " + CDODBSchema.PACKAGE_UNITS); //$NON-NLS-1$ + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + if (async != null) + { + async.stop(); + } + } + + for (InternalCDOPackageInfo packageInfo : packageInfos) + { + fillSystemTables(connection, packageInfo, monitor); // Don't fork monitor + } + } + finally + { + monitor.done(); + } + } + + private void fillSystemTables(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + try + { + monitor.begin(packageUnits.length); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + fillSystemTables(connection, packageUnit, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + private void fillSystemTables(Connection connection, InternalCDOPackageInfo packageInfo, OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing package info: {0}", packageInfo); //$NON-NLS-1$ + } + + String packageURI = packageInfo.getPackageURI(); + String parentURI = packageInfo.getParentURI(); + String unitID = packageInfo.getPackageUnit().getID(); + + String sql = "INSERT INTO " + CDODBSchema.PACKAGE_INFOS + " VALUES (?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + DBUtil.trace(sql); + PreparedStatement stmt = null; + Async async = monitor.forkAsync(); + + try + { + stmt = connection.prepareStatement(sql); + stmt.setString(1, packageURI); + stmt.setString(2, parentURI); + stmt.setString(3, unitID); + + if (stmt.execute()) + { + throw new DBException("No result set expected"); //$NON-NLS-1$ + } + + if (stmt.getUpdateCount() == 0) + { + throw new DBException("No row inserted into table " + CDODBSchema.PACKAGE_INFOS); //$NON-NLS-1$ + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + if (async != null) + { + async.stop(); + } + } + } + + private Collection<InternalCDOPackageUnit> readPackageUnits(Connection connection, long fromCommitTime, + long toCommitTime, OMMonitor monitor) + { + final Map<String, InternalCDOPackageUnit> packageUnits = new HashMap<String, InternalCDOPackageUnit>(); + IDBRowHandler unitRowHandler = new IDBRowHandler() + { + public boolean handle(int row, final Object... values) + { + InternalCDOPackageUnit packageUnit = createPackageUnit(); + packageUnit.setOriginalType(CDOPackageUnit.Type.values()[(Integer)values[1]]); + packageUnit.setTimeStamp((Long)values[2]); + packageUnits.put((String)values[0], packageUnit); + return true; + } + }; + + String where = null; + if (fromCommitTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + where = CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.CORE_PACKAGE_URI + "' AND " + + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.RESOURCE_PACKAGE_URI + "' AND " + + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.TYPES_PACKAGE_URI + "' AND " + + CDODBSchema.PACKAGE_UNITS_TIME_STAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime; + } + + DBUtil.select(connection, unitRowHandler, where, CDODBSchema.PACKAGE_UNITS_ID, + CDODBSchema.PACKAGE_UNITS_ORIGINAL_TYPE, CDODBSchema.PACKAGE_UNITS_TIME_STAMP); + + final Map<String, List<InternalCDOPackageInfo>> packageInfos = new HashMap<String, List<InternalCDOPackageInfo>>(); + IDBRowHandler infoRowHandler = new IDBRowHandler() + { + public boolean handle(int row, final Object... values) + { + InternalCDOPackageInfo packageInfo = createPackageInfo(); + packageInfo.setPackageURI((String)values[1]); + packageInfo.setParentURI((String)values[2]); + + String unit = (String)values[0]; + List<InternalCDOPackageInfo> list = packageInfos.get(unit); + if (list == null) + { + list = new ArrayList<InternalCDOPackageInfo>(); + packageInfos.put(unit, list); + } + + list.add(packageInfo); + return true; + } + }; + + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + DBUtil.select(connection, infoRowHandler, CDODBSchema.PACKAGE_INFOS_UNIT, CDODBSchema.PACKAGE_INFOS_URI, + CDODBSchema.PACKAGE_INFOS_PARENT); + } + finally + { + async.stop(); + monitor.done(); + } + + for (Entry<String, InternalCDOPackageUnit> entry : packageUnits.entrySet()) + { + String id = entry.getKey(); + InternalCDOPackageUnit packageUnit = entry.getValue(); + + List<InternalCDOPackageInfo> list = packageInfos.get(id); + InternalCDOPackageInfo[] array = list.toArray(new InternalCDOPackageInfo[list.size()]); + packageUnit.setPackageInfos(array); + } + + return packageUnits.values(); + } + + private void cacheMetaIDMapping(EModelElement modelElement, CDOID metaID) + { + modelElementToMetaID.put(modelElement, metaID); + metaIDToModelElement.put(metaID, modelElement); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java new file mode 100644 index 0000000000..86f189482b --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.HashSet; + +/** + * @author Stefan Winkler + * @since 2.0 + */ +public class NullPreparedStatementCache extends AbstractPreparedStatementCache +{ + private HashSet<PreparedStatement> allocatedStatements = new HashSet<PreparedStatement>(); + + public NullPreparedStatementCache() + { + } + + public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability) + { + try + { + PreparedStatement result = getConnection().prepareStatement(sql); + allocatedStatements.add(result); + return result; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + public void releasePreparedStatement(PreparedStatement ps) + { + allocatedStatements.remove(ps); + DBUtil.close(ps); + } + + @Override + protected void doBeforeDeactivate() throws Exception + { + if (!allocatedStatements.isEmpty()) + { + OM.LOG.warn("Possible Leak Detected:"); //$NON-NLS-1$ + for (PreparedStatement ps : allocatedStatements) + { + OM.LOG.warn("- " + ps.toString()); //$NON-NLS-1$ + } + + assert false; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java new file mode 100644 index 0000000000..9328e5c633 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Victor Roldan Betancort - bug 208689 + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.collection.CloseableIterator; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.NoSuchElementException; + +/** + * @author Eike Stepper + */ +public abstract class ObjectIDIterator implements CloseableIterator<CDOID> +{ + private IMappingStrategy mappingStrategy; + + private IIDHandler idHandler; + + private IDBStoreAccessor accessor; + + private ResultSet currentResultSet; + + private CDOID nextID; + + private boolean closed; + + /** + * Creates an iterator over all objects in a store. It is important to {@link #close()} of this iterator after usage + * to properly close internal result sets. + */ + public ObjectIDIterator(IMappingStrategy mappingStrategy, IDBStoreAccessor accessor) + { + this.mappingStrategy = mappingStrategy; + this.accessor = accessor; + idHandler = getMappingStrategy().getStore().getIDHandler(); + + } + + public void close() + { + closeCurrentResultSet(); + nextID = null; + closed = true; + } + + public boolean isClosed() + { + return closed; + } + + public IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public IDBStoreAccessor getAccessor() + { + return accessor; + } + + public boolean hasNext() + { + if (closed) + { + return false; + } + + nextID = null; + for (;;) + { + if (currentResultSet == null) + { + currentResultSet = getNextResultSet(); + if (currentResultSet == null) + { + return false; + } + } + + try + { + if (currentResultSet.next()) + { + nextID = idHandler.getCDOID(currentResultSet, 1); + return true; + } + + closeCurrentResultSet(); + + currentResultSet = null; + return false; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + } + + protected void closeCurrentResultSet() + { + DBUtil.close(currentResultSet); + } + + public CDOID next() + { + if (nextID == null) + { + throw new NoSuchElementException(); + } + + return nextID; + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + + protected abstract ResultSet getNextResultSet(); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java new file mode 100644 index 0000000000..589e7a1ed7 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java @@ -0,0 +1,364 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kai Schlamp - initial API and implementation + * Eike Stepper - maintenance + * Kai Schlamp - Bug 284812: [DB] Query non CDO object fails + * Erdal Karaca - added cdoObjectResultAsMap parameter to return Map<String,Object> in result + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryContext; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.db.IIDHandler; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; + +import java.sql.Clob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Implements server side SQL query execution. + * + * @author Kai Schlamp + */ +public class SQLQueryHandler implements IQueryHandler +{ + public static final String QUERY_LANGUAGE = "sql"; + + public static final String FIRST_RESULT = "firstResult"; + + public static final String CDO_OBJECT_QUERY = "cdoObjectQuery"; + + public static final String MAP_QUERY = "mapQuery"; + + public static final String QUERY_STATEMENT = "queryStatement"; + + private DBStoreAccessor storeAccessor; + + public SQLQueryHandler(DBStoreAccessor storeAccessor) + { + this.storeAccessor = storeAccessor; + } + + public DBStoreAccessor getStoreAccessor() + { + return storeAccessor; + } + + /** + * Executes SQL queries. Gets the connection from {@link DBStoreAccessor}, creates a SQL query and sets the parameters + * taken from the {@link CDOQueryInfo#getParameters()}. + * <p> + * Takes into account the {@link CDOQueryInfo#getMaxResults()} and the {@link SQLQueryHandler#FIRST_RESULT} (numbered + * from 0) values for paging. + * <p> + * By default (parameter {@link SQLQueryHandler#CDO_OBJECT_QUERY} == true) a query for CDO Objects is exectued. The + * SQL query must return the CDO ID in the first column for this to work. If you set + * {@link SQLQueryHandler#CDO_OBJECT_QUERY} parameter to false, the value of the first column of a row itself is + * returned. + * <p> + * By default (parameter {@link SQLQueryHandler#QUERY_STATEMENT} == true) query statements are executed. Set this + * parameter to false for update/DDL statements. + * <p> + * It is possible to use variables inside the SQL string with ":" as prefix. E.g. + * "SELECT cdo_id FROM Company WHERE name LIKE :name". The value must then be set by using a parameter. E.g. + * query.setParameter(":name", "Foo%"); + * + * @param info + * the object containing the query and parameters + * @param context + * the query results are placed in the context + * @see IQueryHandler#executeQuery(CDOQueryInfo, IQueryContext) + */ + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + String language = info.getQueryLanguage(); + if (!QUERY_LANGUAGE.equals(language)) + { + throw new IllegalArgumentException("Unsupported query language: " + language); + } + + IIDHandler idHandler = storeAccessor.getStore().getIDHandler(); + Connection connection = storeAccessor.getConnection(); + PreparedStatement statement = null; + ResultSet resultSet = null; + String query = info.getQueryString(); + + try + { + int firstResult = -1; + boolean queryStatement = true; + boolean objectQuery = true; + boolean mapQuery = false; + + HashMap<String, List<Integer>> paramMap = new HashMap<String, List<Integer>>(); + query = parse(query, paramMap); + statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + + for (String key : info.getParameters().keySet()) + { + if (FIRST_RESULT.equalsIgnoreCase(key)) + { + final Object o = info.getParameters().get(key); + if (o != null) + { + try + { + firstResult = (Integer)o; + } + catch (ClassCastException ex) + { + throw new IllegalArgumentException("Parameter " + FIRST_RESULT + " must be an integer but it is a " + o + + " class " + o.getClass().getName(), ex); + } + } + } + else if (QUERY_STATEMENT.equalsIgnoreCase(key)) + { + final Object o = info.getParameters().get(key); + if (o != null) + { + try + { + queryStatement = (Boolean)o; + } + catch (ClassCastException ex) + { + throw new IllegalArgumentException("Parameter " + QUERY_STATEMENT + " must be an boolean but it is a " + + o + " class " + o.getClass().getName(), ex); + } + } + } + else if (CDO_OBJECT_QUERY.equalsIgnoreCase(key)) + { + final Object o = info.getParameters().get(key); + if (o != null) + { + try + { + objectQuery = (Boolean)o; + } + catch (ClassCastException ex) + { + throw new IllegalArgumentException("Parameter " + CDO_OBJECT_QUERY + " must be a boolean but it is a " + + o + " class " + o.getClass().getName(), ex); + } + } + } + else if (MAP_QUERY.equalsIgnoreCase(key)) + { + final Object o = info.getParameters().get(key); + if (o != null) + { + try + { + mapQuery = (Boolean)o; + } + catch (ClassCastException ex) + { + throw new IllegalArgumentException("Parameter " + MAP_QUERY + " must be a boolean but it is a " + o + + " class " + o.getClass().getName(), ex); + } + } + } + else + { + if (!paramMap.containsKey(key) || paramMap.get(key) == null) + { + throw new IllegalArgumentException("No parameter value found for named parameter " + key); + } + + Integer[] indexes = paramMap.get(key).toArray(new Integer[0]); + for (int i = 0; i < indexes.length; i++) + { + Object parameter = info.getParameters().get(key); + statement.setObject(indexes[i], parameter); + } + } + } + + if (queryStatement) + { + resultSet = statement.executeQuery(); + if (firstResult > -1) + { + resultSet.absolute(firstResult); + } + + String[] columnNames = null; + if (mapQuery) + { + columnNames = new String[resultSet.getMetaData().getColumnCount()]; + for (int i = 1; i <= columnNames.length; i++) + { + columnNames[i - 1] = resultSet.getMetaData().getColumnName(i); + } + } + + int maxResults = info.getMaxResults(); + int counter = 0; + + while (resultSet.next()) + { + if (maxResults != CDOQueryInfo.UNLIMITED_RESULTS && counter++ >= maxResults) + { + break; + } + + if (objectQuery) + { + CDOID result = idHandler.getCDOID(resultSet, 1); + context.addResult(result); + } + else + { + int columnCount = resultSet.getMetaData().getColumnCount(); + if (columnCount == 1) + { + Object result = convertValue(resultSet.getObject(1)); + context.addResult(mapQuery ? toMap(columnNames, new Object[] { result }) : result); + } + else + { + Object[] results = new Object[columnCount]; + for (int i = 0; i < columnCount; i++) + { + results[i] = convertValue(resultSet.getObject(i + 1)); + } + + context.addResult(mapQuery ? toMap(columnNames, results) : results); + } + } + } + } + else + { + int result = statement.executeUpdate(); + context.addResult(result); + } + } + catch (SQLException ex) + { + throw new DBException("Problem while executing SQL query: " + query, ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(statement); + } + } + + private Object convertValue(Object value) + { + if (value instanceof Clob) + { + Clob clob = (Clob)value; + + try + { + value = clob.getSubString(1, (int)clob.length()); + } + catch (SQLException ex) + { + throw new DBException("Could not extract CLOB value", ex); + } + } + + return value; + } + + private Map<String, Object> toMap(String[] columnNames, Object[] results) + { + Map<String, Object> ret = new HashMap<String, Object>(); + + for (int i = 0; i < columnNames.length; i++) + { + String columnName = columnNames[i]; + ret.put(columnName, results[i]); + } + + return ret; + } + + private String parse(String query, Map<String, List<Integer>> paramMap) + { + int length = query.length(); + StringBuilder builder = new StringBuilder(length); + + boolean inSingleQuote = false; + boolean inDoubleQuote = false; + int index = 1; + + for (int i = 0; i < length; i++) + { + char c = query.charAt(i); + if (inSingleQuote) + { + if (c == '\'') + { + inSingleQuote = false; + } + } + else if (inDoubleQuote) + { + if (c == '"') + { + inDoubleQuote = false; + } + } + else + { + if (c == '\'') + { + inSingleQuote = true; + } + else if (c == '"') + { + inDoubleQuote = true; + } + else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1))) + { + int j = i + 2; + while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) + { + j++; + } + + String name = query.substring(i + 1, j); + c = '?'; + i += name.length(); + + List<Integer> indexList = paramMap.get(name); + if (indexList == null) + { + indexList = new ArrayList<Integer>(); + paramMap.put(name, indexList); + } + + indexList.add(new Integer(index)); + index++; + } + } + + builder.append(c); + } + + return builder.toString(); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java new file mode 100644 index 0000000000..0ea0e9109b --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java @@ -0,0 +1,291 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.util.ImplementationError; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.HashMap; + +/** + * @author Stefan Winkler + * @since 2.0 + */ +public class SmartPreparedStatementCache extends AbstractPreparedStatementCache +{ + private Cache cache; + + private HashMap<PreparedStatement, CachedPreparedStatement> checkedOut = new HashMap<PreparedStatement, CachedPreparedStatement>(); + + public SmartPreparedStatementCache(int capacity) + { + cache = new Cache(capacity); + } + + public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability) + { + CachedPreparedStatement cachedStatement = cache.remove(sql); + if (cachedStatement == null) + { + cachedStatement = createCachedPreparedStatement(sql, reuseProbability); + } + + PreparedStatement result = cachedStatement.getPreparedStatement(); + checkedOut.put(result, cachedStatement); + + return result; + } + + /** + * @param ps + * the prepared statement to be released to the cache, or <code>null</code>. + */ + public void releasePreparedStatement(PreparedStatement ps) + { + if (ps != null) // Bug 276926: Silently accept ps == null and do nothing. + { + CachedPreparedStatement cachedStatement = checkedOut.remove(ps); + cache.put(cachedStatement); + } + } + + @Override + protected void doBeforeDeactivate() throws Exception + { + if (!checkedOut.isEmpty()) + { + OM.LOG.warn("Statement leak detected"); //$NON-NLS-1$ + } + } + + private CachedPreparedStatement createCachedPreparedStatement(String sql, ReuseProbability reuseProbability) + { + try + { + Connection connection = getConnection(); + PreparedStatement stmt = connection.prepareStatement(sql); + return new CachedPreparedStatement(sql, reuseProbability, stmt); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + /** + * @author Stefan Winkler + */ + private static final class Cache + { + private CacheList lists[]; + + private HashMap<String, CachedPreparedStatement> lookup; + + private int capacity; + + public Cache(int capacity) + { + this.capacity = capacity; + + lookup = new HashMap<String, CachedPreparedStatement>(capacity); + + lists = new CacheList[ReuseProbability.values().length]; + for (ReuseProbability prob : ReuseProbability.values()) + { + lists[prob.ordinal()] = new CacheList(); + } + } + + public void put(CachedPreparedStatement cachedStatement) + { + // refresh age + cachedStatement.touch(); + + // put into appripriate list + lists[cachedStatement.getProbability().ordinal()].add(cachedStatement); + + // put into lookup table + if (lookup.put(cachedStatement.getSQL(), cachedStatement) != null) + { + throw new ImplementationError(cachedStatement.getSQL() + " already in cache"); //$NON-NLS-1$ + } + + // handle capacity overflow + if (lookup.size() > capacity) + { + evictOne(); + } + } + + private void evictOne() + { + long maxAge = -1; + int ordinal = -1; + + for (ReuseProbability prob : ReuseProbability.values()) + { + if (!lists[prob.ordinal()].isEmpty()) + { + long age = lists[prob.ordinal()].tail().getAge(); + if (maxAge < age) + { + maxAge = age; + ordinal = prob.ordinal(); + } + } + } + + remove(lists[ordinal].tail().getSQL()); + } + + public CachedPreparedStatement remove(String sql) + { + CachedPreparedStatement result = lookup.remove(sql); + if (result == null) + { + return null; + } + + lists[result.getProbability().ordinal()].remove(result); + return result; + } + + /** + * @author Stefan Winkler + */ + private class CacheList + { + private CachedPreparedStatement first; + + private CachedPreparedStatement last; + + public CacheList() + { + } + + public void add(CachedPreparedStatement s) + { + if (first == null) + { + first = s; + last = s; + s.previous = null; + s.next = null; + } + else + { + first.previous = s; + s.next = first; + first = s; + } + } + + public void remove(CachedPreparedStatement s) + { + if (s == first) + { + first = s.next; + } + + if (s.next != null) + { + s.next.previous = s.previous; + } + + if (s == last) + { + last = s.previous; + } + + if (s.previous != null) + { + s.previous.next = s.next; + } + + s.previous = null; + s.next = null; + } + + public CachedPreparedStatement tail() + { + return last; + } + + public boolean isEmpty() + { + return first == null; + } + } + } + + /** + * @author Stefan Winkler + */ + private static final class CachedPreparedStatement + { + private long timeStamp; + + private String sql; + + private ReuseProbability probability; + + private PreparedStatement statement; + + /** + * DL field + */ + private CachedPreparedStatement previous; + + /** + * DL field + */ + private CachedPreparedStatement next; + + public CachedPreparedStatement(String sql, ReuseProbability prob, PreparedStatement stmt) + { + this.sql = sql; + probability = prob; + statement = stmt; + timeStamp = System.currentTimeMillis(); + } + + public PreparedStatement getPreparedStatement() + { + return statement; + } + + public long getAge() + { + long currentTime = System.currentTimeMillis(); + return (currentTime - timeStamp) * probability.ordinal(); + } + + public void touch() + { + timeStamp = System.currentTimeMillis(); + } + + public String getSQL() + { + return sql; + } + + public ReuseProbability getProbability() + { + return probability; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java new file mode 100644 index 0000000000..803958428d --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java @@ -0,0 +1,249 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class StringIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final Set<ObjectType> OBJECT_ID_TYPES = Collections.singleton(CDOID.ObjectType.STRING);
+
+ public static final CDOID MIN = CDOID.NULL;
+
+ public static final CDOID MAX = create(Long.toString(Long.MAX_VALUE));
+
+ private DBStore store;
+
+ private long lastObjectID = 0;
+
+ private long nextLocalObjectID = Long.MAX_VALUE;
+
+ public StringIDHandler(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ if (id1.getType() == CDOID.Type.OBJECT && id2.getType() == CDOID.Type.OBJECT)
+ {
+ return Long.valueOf(value(id1)).compareTo(Long.valueOf(value(id2)));
+ }
+
+ return id1.compareTo(id2);
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.VARCHAR;
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return OBJECT_ID_TYPES;
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ return create(val);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ return CDOIDUtil.createString("" + lastObjectID);
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ this.lastObjectID = Long.parseLong(value(lastObjectID));
+ }
+
+ public void adjustLastObjectID(CDOID maxID)
+ {
+ // TODO: implement StringIDHandler.adjustLastObjectID(maxID)
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ return CDOIDUtil.createString("" + nextLocalObjectID);
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ this.nextLocalObjectID = Long.parseLong(value(nextLocalObjectID));
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ if (revision.getBranch().isLocal())
+ {
+ return CDOIDUtil.createString("" + nextLocalObjectID--);
+ }
+
+ return CDOIDUtil.createString("" + ++lastObjectID);
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ if (id.getType() == CDOID.Type.OBJECT)
+ {
+ return Long.parseLong(value(id)) > nextLocalObjectID;
+ }
+
+ return false;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ builder.append("'");
+ builder.append(value(id));
+ builder.append("'");
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ String value = value(id);
+ stmt.setString(column, value == null || value.length() == 0 ? "0" : value);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ String id = resultSet.getString(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ String id = resultSet.getString(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getMinCDOID()
+ {
+ return MIN;
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ return MAX;
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return create(uri);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ return value(id);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Do nothing
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ // Do nothing
+ }
+
+ private static CDOID create(String id)
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ int length = id.length();
+ if (length == 0)
+ {
+ return null;
+ }
+
+ char firstChar = id.charAt(0);
+ if (length == 1 && firstChar == '0')
+ {
+ return null;
+ }
+
+ if (Character.isDigit(firstChar))
+ {
+ long value = Long.parseLong(id);
+ if (value < 0)
+ {
+ throw new IllegalArgumentException("Illegal ID value: " + id);
+ }
+
+ return CDOIDUtil.createString(id);
+ }
+
+ return CDOIDUtil.createExternal(id);
+ }
+
+ private static String value(CDOID id)
+ {
+ return CDOIDUtil.getString(id);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java new file mode 100644 index 0000000000..088cf83c62 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java @@ -0,0 +1,237 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class UUIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final Set<ObjectType> OBJECT_ID_TYPES = Collections.singleton(ObjectType.UUID);
+
+ private static final char NULL_CHAR = '0';
+
+ private static final String NULL_STRING = Character.toString(NULL_CHAR);
+
+ private static final char INTERNAL_CHAR = '@';
+
+ private static final String INTERNAL_STRING = Character.toString(INTERNAL_CHAR);
+
+ private DBStore store;
+
+ public UUIDHandler(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.VARCHAR;
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return OBJECT_ID_TYPES;
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ return create(INTERNAL_STRING + val);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ // Do nothing
+ }
+
+ public void adjustLastObjectID(CDOID maxID)
+ {
+ // Do nothing
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ // Do nothing
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ return false;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ builder.append("'");
+ builder.append(value(id));
+ builder.append("'");
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ stmt.setString(column, value(id));
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ String id = resultSet.getString(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ String id = resultSet.getString(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getMinCDOID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return CDOIDUtil.createExternal(uri);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ return CDOIDUtil.getString(id);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Do nothing
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ // Do nothing
+ }
+
+ private static CDOID create(String id)
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ int length = id.length();
+ if (length == 0)
+ {
+ return null;
+ }
+
+ char firstChar = id.charAt(0);
+ if (length == 1 && firstChar == NULL_CHAR)
+ {
+ return null;
+ }
+
+ if (firstChar == INTERNAL_CHAR)
+ {
+ byte[] bytes = CDOIDUtil.decodeUUID(id.substring(1));
+ return CDOIDUtil.createUUID(bytes);
+ }
+
+ return CDOIDUtil.createExternal(id);
+ }
+
+ private static String value(CDOID id)
+ {
+ if (CDOIDUtil.isNull(id))
+ {
+ return NULL_STRING;
+ }
+
+ if (id.isExternal())
+ {
+ return CDOIDUtil.getString(id);
+ }
+
+ return INTERNAL_STRING + id.toURIFragment();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java new file mode 100644 index 0000000000..d45d95c8f7 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + */ +package org.eclipse.emf.cdo.server.internal.db.bundle; + +import org.eclipse.net4j.util.om.OMBundle; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.om.OSGiActivator; +import org.eclipse.net4j.util.om.log.OMLogger; +import org.eclipse.net4j.util.om.trace.OMTracer; + +/** + * The <em>Operations & Maintenance</em> class of this bundle. + * + * @author Eike Stepper + */ +public abstract class OM +{ + public static final String BUNDLE_ID = "org.eclipse.emf.cdo.server.db"; //$NON-NLS-1$ + + public static final OMBundle BUNDLE = OMPlatform.INSTANCE.bundle(BUNDLE_ID, OM.class); + + public static final OMTracer DEBUG = BUNDLE.tracer("debug"); //$NON-NLS-1$ + + public static final OMLogger LOG = BUNDLE.logger(); + + /** + * @author Eike Stepper + */ + public static final class Activator extends OSGiActivator + { + public Activator() + { + super(BUNDLE); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java new file mode 100644 index 0000000000..d4781d312a --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.jdbc; + +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; + +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.sql.PreparedStatement; +import java.text.MessageFormat; + +/** + * Wrapper for a prepared statement that is cleaned up when it is cached in a WeakReferenceCache and gc'd. Note that + * this is just a wrapper with access to its wrapped object. There's no interface delegation, because the interface + * delegation would also put the necessity to wrap resultSets and maybe even more, which seems to much overkill for a + * simple internal implementation. + * + * @author Stefan Winkler + */ +public class WrappedPreparedStatement +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, WrappedPreparedStatement.class); + + private PreparedStatement wrappedStatement; + + public WrappedPreparedStatement(PreparedStatement ps) + { + wrappedStatement = ps; + if (TRACER.isEnabled()) + { + TRACER.format("Wrapping Statement: {0}", wrappedStatement); //$NON-NLS-1$ + } + } + + public PreparedStatement getWrappedStatement() + { + return wrappedStatement; + } + + public PreparedStatement unwrapStatement() + { + if (TRACER.isEnabled()) + { + TRACER.format("UnWrapping Statement: {0}", wrappedStatement); //$NON-NLS-1$ + } + + PreparedStatement result = wrappedStatement; + wrappedStatement = null; + return result; + } + + @Override + public String toString() + { + return MessageFormat.format("Wrapped[{0}]", wrappedStatement); //$NON-NLS-1$ + } + + @Override + protected void finalize() throws Throwable + { + if (wrappedStatement != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("Closing statement: {0}", wrappedStatement); //$NON-NLS-1$ + } + + DBUtil.close(wrappedStatement); + wrappedStatement = null; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java new file mode 100644 index 0000000000..d9c76b325f --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java @@ -0,0 +1,641 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - Bug 282976: [DB] Influence Mappings through EAnnotations + * Kai Schlamp - Bug 284680 - [DB] Provide annotation to bypass ClassMapping + * Stefan Winkler - maintenance + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + */ +package org.eclipse.emf.cdo.server.internal.db.mapping; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.DBAnnotation; +import org.eclipse.emf.cdo.server.internal.db.ObjectIDIterator; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMapUtil; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * This abstract base class implements those methods which are most likely common to most mapping strategies. It can be + * used to derive custom mapping strategy implementation. + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractMappingStrategy extends Lifecycle implements IMappingStrategy +{ + // --------- database name generation strings -------------- + protected static final String NAME_SEPARATOR = "_"; //$NON-NLS-1$ + + protected static final String TYPE_PREFIX_FEATURE = "F"; //$NON-NLS-1$ + + protected static final String TYPE_PREFIX_CLASS = "C"; //$NON-NLS-1$ + + protected static final String TYPE_PREFIX_PACKAGE = "P"; //$NON-NLS-1$ + + protected static final String GENERAL_PREFIX = "X"; //$NON-NLS-1$ + + protected static final String GENERAL_SUFFIX = "0"; //$NON-NLS-1$ + + /** + * Prefix for unsettable feature helper columns + */ + protected static final String CDO_SET_PREFIX = "cdo_set_"; //$NON-NLS-1$ + + protected static final String FEATURE_TABLE_SUFFIX = "_list"; //$NON-NLS-1$ + + private IDBStore store; + + private Map<String, String> properties; + + private ConcurrentMap<EClass, IClassMapping> classMappings; + + private boolean allClassMappingsCreated; + + public AbstractMappingStrategy() + { + classMappings = new ConcurrentHashMap<EClass, IClassMapping>(); + } + + // -- property related methods ----------------------------------------- + + public synchronized Map<String, String> getProperties() + { + if (properties == null) + { + properties = new HashMap<String, String>(); + } + + return properties; + } + + public synchronized void setProperties(Map<String, String> properties) + { + this.properties = properties; + } + + private int getMaxTableNameLength() + { + String value = getProperties().get(PROP_MAX_TABLE_NAME_LENGTH); + return value == null ? store.getDBAdapter().getMaxTableNameLength() : Integer.valueOf(value); + } + + private int getMaxFieldNameLength() + { + String value = getProperties().get(PROP_MAX_FIELD_NAME_LENGTH); + return value == null ? store.getDBAdapter().getMaxFieldNameLength() : Integer.valueOf(value); + } + + private boolean isQualifiedNames() + { + String value = getProperties().get(PROP_QUALIFIED_NAMES); + return value == null ? false : Boolean.valueOf(value); + } + + private boolean isForceNamesWithID() + { + String value = getProperties().get(PROP_FORCE_NAMES_WITH_ID); + return value == null ? false : Boolean.valueOf(value); + } + + private String getTableNamePrefix() + { + String value = getProperties().get(PROP_TABLE_NAME_PREFIX); + return StringUtil.safe(value); + } + + // -- getters and setters ---------------------------------------------- + + public final IDBStore getStore() + { + return store; + } + + public final void setStore(IDBStore dbStore) + { + checkInactive(); + store = dbStore; + } + + protected final IMetaDataManager getMetaDataManager() + { + return getStore().getMetaDataManager(); + } + + // -- object id related methods ---------------------------------------- + + public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp, + boolean exactTime, CDORevisionHandler handler) + { + if (eClass == null) + { + Collection<IClassMapping> values = getClassMappings().values(); + for (IClassMapping mapping : values) + { + mapping.handleRevisions(accessor, branch, timeStamp, exactTime, handler); + } + } + else + { + IClassMapping classMapping = getClassMapping(eClass); + classMapping.handleRevisions(accessor, branch, timeStamp, exactTime, handler); + } + } + + public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments) + { + Set<CDOID> result = new HashSet<CDOID>(); + Collection<IClassMapping> classMappings = getClassMappings().values(); + + monitor.begin(classMappings.size()); + + try + { + for (IClassMapping mapping : classMappings) + { + Async async = monitor.forkAsync(); + + try + { + Set<CDOID> ids = mapping.readChangeSet(accessor, segments); + result.addAll(ids); + } + finally + { + async.stop(); + } + } + + return result; + } + finally + { + monitor.done(); + } + } + + public CloseableIterator<CDOID> readObjectIDs(IDBStoreAccessor accessor) + { + Collection<EClass> classes = getClassesWithObjectInfo(); + final Iterator<EClass> classIt = classes.iterator(); + + return new ObjectIDIterator(this, accessor) + { + private PreparedStatement currentStatement; + + @Override + protected ResultSet getNextResultSet() + { + while (classIt.hasNext()) + { + EClass eClass = classIt.next(); + IClassMapping mapping = getClassMapping(eClass); + currentStatement = mapping.createObjectIDStatement(getAccessor()); + + ResultSet resultSet = null; + + try + { + resultSet = currentStatement.executeQuery(); + return resultSet; + } + catch (Exception ex) + { + DBUtil.close(resultSet); // only on error + releaseCurrentStatement(); + throw new DBException(ex); + } + } + + return null; + } + + @Override + protected void closeCurrentResultSet() + { + super.closeCurrentResultSet(); + releaseCurrentStatement(); + } + + private void releaseCurrentStatement() + { + IPreparedStatementCache statementCache = getAccessor().getStatementCache(); + statementCache.releasePreparedStatement(currentStatement); + currentStatement = null; + } + }; + } + + protected abstract Collection<EClass> getClassesWithObjectInfo(); + + // -- database name demangling methods --------------------------------- + + public String getTableName(ENamedElement element) + { + String name = null; + String typePrefix = null; + + if (element instanceof EClass) + { + typePrefix = TYPE_PREFIX_CLASS; + name = DBAnnotation.TABLE_NAME.getValue(element); + if (name == null) + { + name = isQualifiedNames() ? EMFUtil.getQualifiedName((EClass)element, NAME_SEPARATOR) : element.getName(); + } + } + else if (element instanceof EPackage) + { + typePrefix = TYPE_PREFIX_PACKAGE; + name = DBAnnotation.TABLE_NAME.getValue(element); + if (name == null) + { + name = isQualifiedNames() ? EMFUtil.getQualifiedName((EPackage)element, NAME_SEPARATOR) : element.getName(); + } + } + else + { + throw new ImplementationError("Unknown element: " + element); //$NON-NLS-1$ + } + + String prefix = getTableNamePrefix(); + if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR)) + { + prefix += NAME_SEPARATOR; + } + + return getName(prefix + name, typePrefix + getUniqueID(element), getMaxTableNameLength()); + } + + public String getTableName(EClass eClass, EStructuralFeature feature) + { + String name = DBAnnotation.TABLE_NAME.getValue(eClass); + if (name == null) + { + name = isQualifiedNames() ? EMFUtil.getQualifiedName(eClass, NAME_SEPARATOR) : eClass.getName(); + } + + name += NAME_SEPARATOR; + name += feature.getName(); + name += FEATURE_TABLE_SUFFIX; + + String prefix = getTableNamePrefix(); + if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR)) + { + prefix += NAME_SEPARATOR; + } + + return getName(prefix + name, TYPE_PREFIX_FEATURE + getUniqueID(feature), getMaxTableNameLength()); + } + + public String getFieldName(EStructuralFeature feature) + { + String name = DBAnnotation.COLUMN_NAME.getValue(feature); + if (name == null) + { + name = getName(feature.getName(), TYPE_PREFIX_FEATURE + getUniqueID(feature), getMaxFieldNameLength()); + } + + return name; + } + + public String getUnsettableFieldName(EStructuralFeature feature) + { + String name = DBAnnotation.COLUMN_NAME.getValue(feature); + if (name != null) + { + return CDO_SET_PREFIX + name; + } + + return getName(CDO_SET_PREFIX + feature.getName(), TYPE_PREFIX_FEATURE + getUniqueID(feature), + getMaxFieldNameLength()); + } + + private String getName(String name, String suffix, int maxLength) + { + if (!store.getDBAdapter().isValidFirstChar(name.charAt(0))) + { + name = GENERAL_PREFIX + name; + } + + boolean forceNamesWithID = isForceNamesWithID(); + if (!forceNamesWithID && store.getDBAdapter().isReservedWord(name)) + { + name = name + GENERAL_SUFFIX; + } + + if (name.length() > maxLength || forceNamesWithID) + { + suffix = NAME_SEPARATOR + suffix.replace('-', 'S'); + int length = Math.min(name.length(), maxLength - suffix.length()); + name = name.substring(0, length) + suffix; + } + + return name; + } + + private String getUniqueID(ENamedElement element) + { + long timeStamp; + CommitContext commitContext = StoreThreadLocal.getCommitContext(); + if (commitContext != null) + { + timeStamp = commitContext.getBranchPoint().getTimeStamp(); + } + else + { + // This happens outside a commit, i.e. at system init time. + // Ensure that resulting ext refs are not replicated! + timeStamp = CDOBranchPoint.INVALID_DATE; + + // timeStamp = getStore().getRepository().getTimeStamp(); + } + + CDOID result = getMetaDataManager().getMetaID(element, timeStamp); + + StringBuilder builder = new StringBuilder(); + CDOIDUtil.write(builder, result); + return builder.toString(); + } + + // -- factories for mapping of classes, values, lists ------------------ + + public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + async = monitor.forkAsync(); + + try + { + mapPackageUnits(packageUnits, connection, false); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits) + { + mapPackageUnits(packageUnits, connection, true); + } + + private void mapPackageUnits(InternalCDOPackageUnit[] packageUnits, Connection connection, boolean unmap) + { + if (packageUnits != null && packageUnits.length != 0) + { + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + mapPackageInfos(packageUnit.getPackageInfos(), connection, unmap); + } + } + } + + private void mapPackageInfos(InternalCDOPackageInfo[] packageInfos, Connection connection, boolean unmap) + { + boolean supportingEcore = getStore().getRepository().isSupportingEcore(); + for (InternalCDOPackageInfo packageInfo : packageInfos) + { + EPackage ePackage = packageInfo.getEPackage(); + if (!CDOModelUtil.isCorePackage(ePackage) || supportingEcore) + { + mapClasses(connection, unmap, EMFUtil.getPersistentClasses(ePackage)); + } + } + } + + private void mapClasses(Connection connection, boolean unmap, EClass... eClasses) + { + for (EClass eClass : eClasses) + { + if (!(eClass.isInterface() || eClass.isAbstract())) + { + String mappingAnnotation = DBAnnotation.TABLE_MAPPING.getValue(eClass); + + // TODO Maybe we should explicitly report unknown values of the annotation + if (mappingAnnotation != null && mappingAnnotation.equalsIgnoreCase(DBAnnotation.TABLE_MAPPING_NONE)) + { + continue; + } + + if (!unmap) + { + // TODO Bugzilla 296087: Before we go ahead with creation, we should check if it's already there + IClassMapping mapping = createClassMapping(eClass); + getStore().getDBAdapter().createTables(mapping.getDBTables(), connection); + } + else + { + IClassMapping mapping = removeClassMapping(eClass); + getStore().getDBAdapter().dropTables(mapping.getDBTables(), connection); + } + } + } + } + + private IClassMapping createClassMapping(EClass eClass) + { + IClassMapping mapping = doCreateClassMapping(eClass); + if (mapping != null) + { + classMappings.put(eClass, mapping); + } + + return mapping; + } + + private IClassMapping removeClassMapping(EClass eClass) + { + IClassMapping mapping = classMappings.get(eClass); + if (mapping != null) + { + IDBSchema schema = getStore().getDBSchema(); + for (IDBTable table : mapping.getDBTables()) + { + schema.removeTable(table.getName()); + } + classMappings.remove(eClass); + } + return mapping; + } + + protected abstract IClassMapping doCreateClassMapping(EClass eClass); + + public final IClassMapping getClassMapping(EClass eClass) + { + if (!isMapped(eClass)) + { + throw new IllegalArgumentException("Class is not mapped: " + eClass); + } + + // Try without synchronization first; this will almost always succeed, so it avoids the + // performance penalty of syncing in the majority of cases + IClassMapping result = classMappings.get(eClass); + if (result == null) + { + // Synchronize on the classMappings to prevent concurrent invocations of createClassMapping + // (Synchronizing on the eClass allows for more concurrency, but is risky because application + // code may be syncing on the eClass also.) + synchronized (classMappings) + { + // Check again, because other thread may have just added the mapping + result = classMappings.get(eClass); + if (result == null) + { + result = createClassMapping(eClass); + } + } + } + + return result; + } + + public final Map<EClass, IClassMapping> getClassMappings() + { + return getClassMappings(true); + } + + public final Map<EClass, IClassMapping> getClassMappings(boolean createOnDemand) + { + return doGetClassMappings(createOnDemand); + } + + public final Map<EClass, IClassMapping> doGetClassMappings(boolean createOnDemand) + { + if (createOnDemand) + { + synchronized (classMappings) + { + if (!allClassMappingsCreated) + { + createAllClassMappings(); + allClassMappingsCreated = true; + } + } + } + + return classMappings; + } + + private void createAllClassMappings() + { + InternalRepository repository = (InternalRepository)getStore().getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (InternalCDOPackageInfo packageInfo : packageRegistry.getPackageInfos()) + { + if (!packageInfo.isSystemPackage()) + { + for (EClassifier eClassifier : packageInfo.getEPackage().getEClassifiers()) + { + if (eClassifier instanceof EClass) + { + EClass eClass = (EClass)eClassifier; + if (isMapped(eClass)) + { + getClassMapping(eClass); // Get or create it + } + } + } + } + } + } + + protected abstract boolean isMapped(EClass eClass); + + public ITypeMapping createValueMapping(EStructuralFeature feature) + { + ITypeMapping.Provider provider = getTypeMappingProvider(); + return provider.createTypeMapping(this, feature); + } + + protected ITypeMapping.Provider getTypeMappingProvider() + { + return ITypeMapping.Provider.INSTANCE; + } + + public final IListMapping createListMapping(EClass containingClass, EStructuralFeature feature) + { + checkArg(feature.isMany(), "Only many-valued features allowed"); //$NON-NLS-1$ + IListMapping mapping = doCreateListMapping(containingClass, feature); + return mapping; + } + + public final IListMapping createFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + checkArg(FeatureMapUtil.isFeatureMap(feature), "Only FeatureMaps allowed"); //$NON-NLS-1$ + IListMapping mapping = doCreateFeatureMapMapping(containingClass, feature); + return mapping; + } + + public abstract IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature); + + public abstract IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java new file mode 100644 index 0000000000..f5baf6a4c6 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java @@ -0,0 +1,882 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 271444: [DB] Multiple refactorings + * Stefan Winkler - bug 275303: [DB] DBStore does not handle BIG_INTEGER and BIG_DECIMAL + * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations + * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations + * Stefan Winkler - bug 285270: [DB] Support XSD based models + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + */ +package org.eclipse.emf.cdo.server.internal.db.mapping; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOBlob; +import org.eclipse.emf.cdo.common.lob.CDOClob; +import org.eclipse.emf.cdo.common.lob.CDOLobUtil; +import org.eclipse.emf.cdo.common.revision.CDORevisionData; +import org.eclipse.emf.cdo.etypes.EtypesPackage; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.AbstractTypeMapping; +import org.eclipse.emf.cdo.server.db.mapping.AbstractTypeMappingFactory; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.factory.ProductCreationException; + +import org.eclipse.emf.common.util.Enumerator; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EEnumLiteral; +import org.eclipse.emf.ecore.EFactory; +import org.eclipse.emf.ecore.EcorePackage; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Date; + +/** + * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common + * types. + * + * @author Eike Stepper + */ +public class CoreTypeMappings +{ + public static final String ID_PREFIX = "org.eclipse.emf.cdo.server.db.CoreTypeMappings"; + + /** + * @author Eike Stepper + */ + public static class TMEnum extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Enum", + EcorePackage.eINSTANCE.getEEnum(), DBType.INTEGER)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + // see Bug 271941 + return resultSet.getInt(getField().getName()); + // EEnum type = (EEnum)getFeature().getEType(); + // int value = resultSet.getInt(column); + // return type.getEEnumLiteral(value); + } + + @Override + protected Object getDefaultValue() + { + EEnum eenum = (EEnum)getFeature().getEType(); + + String defaultValueLiteral = getFeature().getDefaultValueLiteral(); + if (defaultValueLiteral != null) + { + EEnumLiteral literal = eenum.getEEnumLiteralByLiteral(defaultValueLiteral); + return literal.getValue(); + } + + Enumerator enumerator = (Enumerator)eenum.getDefaultValue(); + return enumerator.getValue(); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMEnum(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMString extends AbstractTypeMapping + { + public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".StringVarchar", EcorePackage.eINSTANCE.getEString(), DBType.VARCHAR)); + + public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".StringLongVarchar", EcorePackage.eINSTANCE.getEString(), DBType.LONGVARCHAR)); + + public static final Factory FACTORY_CLOB = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".StringClob", + EcorePackage.eINSTANCE.getEString(), DBType.CLOB)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getString(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMString(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMBlob extends AbstractTypeMapping + { + public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".BlobStream", EtypesPackage.eINSTANCE.getBlob(), DBType.VARCHAR)); + + public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".BlobStreamLongVarchar", EtypesPackage.eINSTANCE.getBlob(), DBType.LONGVARCHAR)); + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + CDOBlob blob = (CDOBlob)value; + stmt.setString(index, HexUtil.bytesToHex(blob.getID()) + "-" + blob.getSize()); + } + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + String str = resultSet.getString(getField().getName()); + if (str == null) + { + return null; + } + + int pos = str.indexOf('-'); + + byte[] id = HexUtil.hexToBytes(str.substring(0, pos)); + long size = Long.parseLong(str.substring(pos + 1)); + return CDOLobUtil.createBlob(id, size); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMBlob(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMClob extends AbstractTypeMapping + { + public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".ClobStream", EtypesPackage.eINSTANCE.getClob(), DBType.VARCHAR)); + + public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".ClobStreamLongVarchar", EtypesPackage.eINSTANCE.getClob(), DBType.LONGVARCHAR)); + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + CDOClob clob = (CDOClob)value; + stmt.setString(index, HexUtil.bytesToHex(clob.getID()) + "-" + clob.getSize()); + } + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + String str = resultSet.getString(getField().getName()); + if (str == null) + { + return null; + } + + int pos = str.indexOf('-'); + + byte[] id = HexUtil.hexToBytes(str.substring(0, pos)); + long size = Long.parseLong(str.substring(pos + 1)); + return CDOLobUtil.createClob(id, size); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMClob(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMShort extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Short", + EcorePackage.eINSTANCE.getEShort(), DBType.SMALLINT)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".ShortObject", EcorePackage.eINSTANCE.getEShortObject(), DBType.SMALLINT)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getShort(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMShort(); + } + } + } + + /** + * @author Eike Stepper <br> + */ + public static class TMObject extends AbstractTypeMapping + { + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + CDOID id = idHandler.getCDOID(resultSet, getField().getName()); + + if (id == null && getFeature().isUnsettable()) + { + return CDORevisionData.NIL; + } + + return id; + } + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + idHandler.setCDOID(stmt, index, (CDOID)value); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMObject(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMLong extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Long", + EcorePackage.eINSTANCE.getELong(), DBType.BIGINT)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor( + ID_PREFIX + ".LongObject", EcorePackage.eINSTANCE.getELongObject(), DBType.BIGINT)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getLong(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMLong(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMInteger extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Integer", + EcorePackage.eINSTANCE.getEInt(), DBType.INTEGER)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".IntegerObject", EcorePackage.eINSTANCE.getEIntegerObject(), DBType.INTEGER)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getInt(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMInteger(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMFloat extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Float", + EcorePackage.eINSTANCE.getEFloat(), DBType.FLOAT)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".FloatObject", EcorePackage.eINSTANCE.getEFloatObject(), DBType.FLOAT)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getFloat(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMFloat(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMDouble extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Double", + EcorePackage.eINSTANCE.getEDouble(), DBType.DOUBLE)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".DoubleObject", EcorePackage.eINSTANCE.getEDoubleObject(), DBType.DOUBLE)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getDouble(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMDouble(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMDate2Timestamp extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Timestamp", + EcorePackage.eINSTANCE.getEDate(), DBType.TIMESTAMP)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getTimestamp(getField().getName()); + } + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + stmt.setTimestamp(index, new Timestamp(((Date)value).getTime())); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMDate2Timestamp(); + } + } + } + + /** + * @author Heiko Ahlig + */ + public static class TMDate2Date extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Date", + EcorePackage.eINSTANCE.getEDate(), DBType.DATE)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getDate(getField().getName(), Calendar.getInstance()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMDate2Date(); + } + } + } + + /** + * @author Heiko Ahlig + */ + public static class TMDate2Time extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Time", + EcorePackage.eINSTANCE.getEDate(), DBType.TIME)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getTime(getField().getName(), Calendar.getInstance()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMDate2Time(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMCharacter extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Character", + EcorePackage.eINSTANCE.getEChar(), DBType.CHAR)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".CharacterObject", EcorePackage.eINSTANCE.getECharacterObject(), DBType.CHAR)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + String str = resultSet.getString(getField().getName()); + if (resultSet.wasNull()) + { + return getFeature().isUnsettable() ? CDORevisionData.NIL : null; + } + + return str.charAt(0); + } + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + stmt.setString(index, ((Character)value).toString()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMCharacter(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMByte extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Byte", + EcorePackage.eINSTANCE.getEByte(), DBType.SMALLINT)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor( + ID_PREFIX + ".ByteObject", EcorePackage.eINSTANCE.getEByteObject(), DBType.SMALLINT)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getByte(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMByte(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMBytes extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".ByteArray", + EcorePackage.eINSTANCE.getEByteArray(), DBType.BLOB)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getBytes(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMBytes(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class TMBoolean extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Boolean", + EcorePackage.eINSTANCE.getEBoolean(), DBType.BOOLEAN)); + + public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".BooleanObject", EcorePackage.eINSTANCE.getEBooleanObject(), DBType.BOOLEAN)); + + public static final Factory FACTORY_SMALLINT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".Boolean_SMALLINT", EcorePackage.eINSTANCE.getEBoolean(), DBType.SMALLINT)); + + public static final Factory FACTORY_OBJECT_SMALLINT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".BooleanObject_SMALLINT", EcorePackage.eINSTANCE.getEBooleanObject(), DBType.SMALLINT)); + + @Override + public Object getResultSetValue(ResultSet resultSet) throws SQLException + { + return resultSet.getBoolean(getField().getName()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMBoolean(); + } + } + } + + /** + * @author Stefan Winkler + */ + public static class TMBigInteger extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".BigInteger", + EcorePackage.eINSTANCE.getEBigInteger(), DBType.VARCHAR)); + + public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".BigIntegerLongVarChar", EcorePackage.eINSTANCE.getEBigInteger(), DBType.LONGVARCHAR)); + + @Override + protected Object getResultSetValue(ResultSet resultSet) throws SQLException + { + String val = resultSet.getString(getField().getName()); + + if (resultSet.wasNull()) + { + return getFeature().isUnsettable() ? CDORevisionData.NIL : null; + } + + return new BigInteger(val); + } + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + stmt.setString(index, ((BigInteger)value).toString()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMBigInteger(); + } + } + } + + /** + * @author Stefan Winkler + */ + public static class TMBigDecimal extends AbstractTypeMapping + { + public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".BigDecimal", + EcorePackage.eINSTANCE.getEBigDecimal(), DBType.VARCHAR)); + + public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".BigDecimalLongVarchar", EcorePackage.eINSTANCE.getEBigDecimal(), DBType.LONGVARCHAR)); + + @Override + protected Object getResultSetValue(ResultSet resultSet) throws SQLException + { + String val = resultSet.getString(getField().getName()); + + if (resultSet.wasNull()) + { + return getFeature().isUnsettable() ? CDORevisionData.NIL : null; + } + + return new BigDecimal(val); + } + + @Override + protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException + { + stmt.setString(index, ((BigDecimal)value).toPlainString()); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMBigDecimal(); + } + } + } + + /** + * @author Stefan Winkler + */ + public static class TMCustom extends AbstractTypeMapping + { + public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".CustomVarchar", EcorePackage.eINSTANCE.getEDataType(), DBType.VARCHAR)); + + public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + + ".CustomLongVarchar", EcorePackage.eINSTANCE.getEDataType(), DBType.LONGVARCHAR)); + + public static final Factory FACTORY_CLOB = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".CustomClob", + EcorePackage.eINSTANCE.getEDataType(), DBType.CLOB)); + + @Override + protected Object getResultSetValue(ResultSet resultSet) throws SQLException + { + String val = resultSet.getString(getField().getName()); + if (resultSet.wasNull()) + { + return getFeature().isUnsettable() ? CDORevisionData.NIL : null; + } + + return val; + } + + @Override + protected Object getDefaultValue() + { + Object defaultValue = getFeature().getDefaultValue(); + if (defaultValue == null) + { + return null; + } + + EFactory factory = getFeature().getEType().getEPackage().getEFactoryInstance(); + return factory.convertToString((EDataType)getFeature().getEType(), defaultValue); + } + + /** + * @author Eike Stepper + */ + public static class Factory extends AbstractTypeMappingFactory + { + public Factory(Descriptor descriptor) + { + super(descriptor); + } + + @Override + public ITypeMapping create(String description) throws ProductCreationException + { + return new TMCustom(); + } + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java new file mode 100644 index 0000000000..f47075d0d2 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.mapping; + +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; + +import org.eclipse.net4j.db.DBType; + +import org.eclipse.emf.ecore.EClassifier; + +/** + * @author Stefan Winkler + */ +public class TypeMappingDescriptor implements ITypeMapping.Descriptor +{ + private String id; + + private String factoryType; + + private EClassifier eClassifier; + + private DBType dbType; + + public TypeMappingDescriptor(String id, String factoryType, EClassifier eClassifier, DBType dbType) + { + this.id = id; + this.factoryType = factoryType; + this.eClassifier = eClassifier; + this.dbType = dbType; + } + + public String getID() + { + return id; + } + + public String getFactoryType() + { + return factoryType; + } + + public EClassifier getEClassifier() + { + return eClassifier; + } + + public DBType getDBType() + { + return dbType; + } + + @Override + public String toString() + { + return "TypeMappingDescriptor [" + factoryType + "]"; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java new file mode 100644 index 0000000000..096e2c97a6 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java @@ -0,0 +1,447 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + */ +package org.eclipse.emf.cdo.server.internal.db.mapping; + +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.etypes.EtypesPackage; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.DBAnnotation; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingUtil.FactoryTypeParserException; +import org.eclipse.emf.cdo.server.internal.db.messages.Messages; + +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.container.ContainerEvent; +import org.eclipse.net4j.util.container.IContainerDelta; +import org.eclipse.net4j.util.container.IContainerDelta.Kind; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.factory.IFactory; +import org.eclipse.net4j.util.factory.IFactoryKey; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * An implementation of both the Registry and Provider interfaces for type mappings. This class is a singleton which + * keeps itself in sync with the global factory registry. It reads the available factoryTypes for the type mappings + * product type and populates indexes which make it easier to determine and look up the correct factories for a needed + * type mapping. + * + * @author Stefan Winkler + */ +public class TypeMappingRegistry implements ITypeMapping.Registry, ITypeMapping.Provider +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, TypeMappingRegistry.class); + + /** + * Contains a map from model types to db types which represent default mappings. (I.e., if a model element without db + * type annotation is encountered, this map is consulted to retrieve the default type mapping. This map is populated + * on a come-first basis. The first mapping for a particular {@link EClassifier} is set as default. + */ + private Map<EClassifier, DBType> classifierDefaultMapping; + + /** + * The main TypeMapping index. For any known pair of model and db types the {@link ITypeMapping.Descriptor} is + * registered here. + */ + private Map<Pair<EClassifier, DBType>, ITypeMapping.Descriptor> typeMappingByTypes; + + /** + * ID-based index. Can be used to lookup an {@link ITypeMapping.Descriptor} for a given ID. + */ + private Map<String, ITypeMapping.Descriptor> typeMappingsById; + + /** + * A set of all known mapped DBTypes. This is needed for the feature map mappings. + */ + private Set<DBType> defaultFeatureMapDBTypes; + + /** + * A populator which is used to keep the registry in sync with the registered factories of the + * {@link IManagedContainer}. + */ + private RegistryPopulator populator = new RegistryPopulator(); + + public TypeMappingRegistry() + { + init(); + } + + public void init() + { + populator.disconnect(); + + defaultFeatureMapDBTypes = new HashSet<DBType>(); + typeMappingsById = new HashMap<String, ITypeMapping.Descriptor>(); + typeMappingByTypes = new HashMap<Pair<EClassifier, DBType>, ITypeMapping.Descriptor>(); + classifierDefaultMapping = new HashMap<EClassifier, DBType>(); + + registerCoreTypeMappings(); + populator.connect(); + } + + /** + * Register builtin type mapping factories + */ + private void registerCoreTypeMappings() + { + IManagedContainer container = getContainer(); + container.registerFactory(CoreTypeMappings.TMBigDecimal.FACTORY); + container.registerFactory(CoreTypeMappings.TMBigDecimal.FACTORY_LONG_VARCHAR); + container.registerFactory(CoreTypeMappings.TMBigInteger.FACTORY); + container.registerFactory(CoreTypeMappings.TMBigInteger.FACTORY_LONG_VARCHAR); + container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY); + container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_SMALLINT); + container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_OBJECT_SMALLINT); + container.registerFactory(CoreTypeMappings.TMByte.FACTORY); + container.registerFactory(CoreTypeMappings.TMByte.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMBytes.FACTORY); + container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY); + container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_VARCHAR); + container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_CLOB); + container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_LONG_VARCHAR); + container.registerFactory(CoreTypeMappings.TMDate2Date.FACTORY); + container.registerFactory(CoreTypeMappings.TMDate2Time.FACTORY); + container.registerFactory(CoreTypeMappings.TMDate2Timestamp.FACTORY); + container.registerFactory(CoreTypeMappings.TMDouble.FACTORY); + container.registerFactory(CoreTypeMappings.TMDouble.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMEnum.FACTORY); + container.registerFactory(CoreTypeMappings.TMFloat.FACTORY); + container.registerFactory(CoreTypeMappings.TMFloat.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMInteger.FACTORY); + container.registerFactory(CoreTypeMappings.TMInteger.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMLong.FACTORY); + container.registerFactory(CoreTypeMappings.TMLong.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMShort.FACTORY); + container.registerFactory(CoreTypeMappings.TMShort.FACTORY_OBJECT); + container.registerFactory(CoreTypeMappings.TMString.FACTORY_VARCHAR); + container.registerFactory(CoreTypeMappings.TMString.FACTORY_CLOB); + container.registerFactory(CoreTypeMappings.TMString.FACTORY_LONG_VARCHAR); + container.registerFactory(CoreTypeMappings.TMBlob.FACTORY_VARCHAR); + container.registerFactory(CoreTypeMappings.TMBlob.FACTORY_LONG_VARCHAR); + container.registerFactory(CoreTypeMappings.TMClob.FACTORY_VARCHAR); + container.registerFactory(CoreTypeMappings.TMClob.FACTORY_LONG_VARCHAR); + + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDataType(), DBType.VARCHAR); + + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBigDecimal(), DBType.VARCHAR); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBigInteger(), DBType.VARCHAR); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBoolean(), DBType.BOOLEAN); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBooleanObject(), DBType.BOOLEAN); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByte(), DBType.SMALLINT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByteObject(), DBType.SMALLINT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByteArray(), DBType.BLOB); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEChar(), DBType.CHAR); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getECharacterObject(), DBType.CHAR); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDate(), DBType.TIMESTAMP); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDouble(), DBType.DOUBLE); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDoubleObject(), DBType.DOUBLE); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEEnum(), DBType.INTEGER); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEFloat(), DBType.FLOAT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEFloatObject(), DBType.FLOAT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEInt(), DBType.INTEGER); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEIntegerObject(), DBType.INTEGER); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getELong(), DBType.BIGINT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getELongObject(), DBType.BIGINT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEShort(), DBType.SMALLINT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEShortObject(), DBType.SMALLINT); + classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEString(), DBType.VARCHAR); + + classifierDefaultMapping.put(EtypesPackage.eINSTANCE.getBlob(), DBType.VARCHAR); // TODO Should be DBType.BLOB? + classifierDefaultMapping.put(EtypesPackage.eINSTANCE.getClob(), DBType.VARCHAR); // TODO Should be DBType.CLOB? + } + + protected IManagedContainer getContainer() + { + return IPluginContainer.INSTANCE; + } + + public void registerTypeMapping(ITypeMapping.Descriptor descriptor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Registering {0}", descriptor); + } + + EClassifier eClassifier = descriptor.getEClassifier(); + DBType dbType = descriptor.getDBType(); + Pair<EClassifier, DBType> sourceTargetPair = new Pair<EClassifier, DBType>(eClassifier, dbType); + + // currently we do not support more than one typeMapping per source-target type pair + if (typeMappingByTypes.containsKey(sourceTargetPair)) + { + OM.LOG.error(Messages.getString("TypeMappingRegistry.4")); + return; + } + + if (typeMappingsById.containsKey(descriptor.getID())) + { + OM.LOG.error(MessageFormat.format(Messages.getString("TypeMappingRegistry.5"), descriptor.getID())); + return; + } + + typeMappingsById.put(descriptor.getID(), descriptor); + + // register first dbType for classifier as default + if (!classifierDefaultMapping.containsKey(eClassifier)) + { + classifierDefaultMapping.put(eClassifier, dbType); + } + + defaultFeatureMapDBTypes.add(dbType); + + typeMappingByTypes.put(sourceTargetPair, descriptor); + } + + public ITypeMapping createTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature) + { + ITypeMapping typeMapping = null; + if (feature instanceof EReference) + { + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + typeMapping = idHandler.getObjectTypeMapping(); + typeMapping.setDBType(idHandler.getDBType()); + } + else + { + IDBAdapter dbAdapter = mappingStrategy.getStore().getDBAdapter(); + DBType dbType = getDBType(feature, dbAdapter); + + ITypeMapping.Descriptor descriptor = null; + + String typeMappingID = DBAnnotation.TYPE_MAPPING.getValue(feature); + if (typeMappingID != null) + { + // lookup annotated mapping + descriptor = typeMappingsById.get(typeMappingID); + + if (descriptor == null) + { + OM.LOG.warn(MessageFormat.format(Messages.getString("TypeMappingRegistry.2"), // + typeMappingID, feature.toString())); + } + } + + if (descriptor == null) + { + // try to find suitable mapping by type + descriptor = getMappingByType(feature, dbType); + } + + if (descriptor == null) + { + EClassifier type = getEType(feature); + throw new IllegalStateException(MessageFormat.format(Messages.getString("TypeMappingRegistry.1"), feature + .getEContainingClass().getName() + "." + feature.getName(), + type.getEPackage().getName() + "." + type.getName(), dbType.getKeyword())); + } + + IFactory factory = getContainer().getFactory(ITypeMapping.Factory.PRODUCT_GROUP, descriptor.getFactoryType()); + typeMapping = (ITypeMapping)factory.create(null); + typeMapping.setDBType(dbType); + } + + typeMapping.setMappingStrategy(mappingStrategy); + typeMapping.setFeature(feature); + return typeMapping; + } + + private EClassifier getEType(EStructuralFeature feature) + { + EClassifier classifier = feature.getEType(); + if (classifier instanceof EEnum) + { + return EcorePackage.eINSTANCE.getEEnum(); + } + + if (classifier instanceof EClass) + { + return EcorePackage.eINSTANCE.getEClass(); + } + + EPackage ePackage = classifier.getEPackage(); + if (CDOModelUtil.isCorePackage(ePackage)) + { + return classifier; + } + + if (CDOModelUtil.isTypesPackage(ePackage)) + { + return classifier; + } + + return EcorePackage.eINSTANCE.getEDataType(); + } + + private DBType getDBType(EStructuralFeature feature, IDBAdapter dbAdapter) + { + String typeKeyword = DBAnnotation.COLUMN_TYPE.getValue(feature); + if (typeKeyword != null) + { + DBType dbType = DBType.getTypeByKeyword(typeKeyword); + if (dbType == null) + { + throw new IllegalArgumentException("Unsupported columnType (" + typeKeyword + ") annotation of feature " + + feature.getName()); + } + + return dbType; + } + + // No annotation present - lookup default DB type. + return getDefaultDBType(getEType(feature), dbAdapter); + } + + private DBType getDefaultDBType(EClassifier type, IDBAdapter dbAdapter) + { + DBType result = classifierDefaultMapping.get(type); + + if (result == null) + { + result = DBType.VARCHAR; + } + + // Give the DBAdapter a chance to override the default type, if it's not supported + return dbAdapter.adaptType(result); + } + + private ITypeMapping.Descriptor getMappingByType(EStructuralFeature feature, DBType dbType) + { + // First try: lookup specific mapping for the immediate type. + ITypeMapping.Descriptor descriptor = typeMappingByTypes.get(new Pair<EClassifier, DBType>(feature.getEType(), + dbType)); + + if (descriptor == null) + { + // Second try: lookup general mapping + descriptor = typeMappingByTypes.get(new Pair<EClassifier, DBType>(getEType(feature), dbType)); + if (descriptor == null) + { + // Lookup failed. Give up + return null; + } + } + + return descriptor; + } + + public Collection<DBType> getDefaultFeatureMapDBTypes() + { + return defaultFeatureMapDBTypes; + } + + /** + * Keeps the {@link TypeMappingRegistry} in sync with {@link IManagedContainer#getFactoryRegistry()}. + * + * @author Stefan Winkler + */ + private class RegistryPopulator implements IListener + { + private IManagedContainer container = getContainer(); + + public RegistryPopulator() + { + } + + /** + * Connect to the factory registry. + */ + public void connect() + { + populateTypeMappingRegistry(); + container.getFactoryRegistry().addListener(this); + } + + public void disconnect() + { + container.getFactoryRegistry().removeListener(this); + } + + private void populateTypeMappingRegistry() + { + // get available factory types + Set<String> factoryTypes = container.getFactoryTypes(ITypeMapping.Factory.PRODUCT_GROUP); + + // parse the descriptor of each factory type + for (String factoryType : factoryTypes) + { + registerFactoryType(factoryType); + } + } + + private void registerFactoryType(String factoryType) + { + ITypeMapping.Descriptor desc; + + try + { + desc = TypeMappingUtil.descriptorFromFactoryType(factoryType); + registerTypeMapping(desc); + } + catch (FactoryTypeParserException ex) + { + OM.LOG.warn(ex); + } + } + + public void notifyEvent(IEvent event) + { + if (event instanceof ContainerEvent<?>) + { + @SuppressWarnings("unchecked") + ContainerEvent<Map.Entry<IFactoryKey, IFactory>> ev = (ContainerEvent<Entry<IFactoryKey, IFactory>>)event; + + for (IContainerDelta<Map.Entry<IFactoryKey, IFactory>> delta : ev.getDeltas()) + { + IFactoryKey key = delta.getElement().getKey(); + if (key.getProductGroup().equals(ITypeMapping.Factory.PRODUCT_GROUP)) + { + if (delta.getKind() == Kind.ADDED) + { + String factoryType = delta.getElement().getKey().getType(); + registerFactoryType(factoryType); + } + else + // delta.getKind() == Kind.REMOVED + { + // XXX Runtime removal of typeMappingFactories removal of type mappings is currently not supported. + OM.LOG.warn(Messages.getString("TypeMappingRegistry.3")); + } + } + } + } + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java new file mode 100644 index 0000000000..1bdbaa1a3f --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.mapping; + +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.messages.Messages; + +import org.eclipse.net4j.db.DBType; + +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EPackage; + +import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Stefan Winkler + */ +public class TypeMappingUtil +{ + private static final Pattern FACTORY_DESCRIPTOR_PATTERN = Pattern.compile("(.+);(.+)#(.+)->(.+)"); + + /** + * Utility class - no instantiation. + */ + private TypeMappingUtil() + { + } + + public static ITypeMapping.Descriptor createDescriptor(String id, EClassifier eClassifier, DBType dbType) + { + String factoryType = createFactoryType(id, eClassifier, dbType); + return new TypeMappingDescriptor(id, factoryType, eClassifier, dbType); + } + + public static String createFactoryType(String id, EClassifier eClassifier, DBType dbType) + { + StringBuilder builder = new StringBuilder(); + + // id + builder.append(id); + builder.append(";"); + + // classifier + builder.append(eClassifier.getEPackage().getNsURI()); + builder.append("#"); + builder.append(eClassifier.getName()); + builder.append("->"); + + // dbtype + builder.append(dbType.getKeyword()); + + return builder.toString(); + } + + public static ITypeMapping.Descriptor descriptorFromFactoryType(String factoryType) throws FactoryTypeParserException + { + Matcher matcher = FACTORY_DESCRIPTOR_PATTERN.matcher(factoryType); + + if (!matcher.matches()) + { + throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.1"), + factoryType)); + } + + String id = matcher.group(1); + String packageUri = matcher.group(2); + String classifierName = matcher.group(3); + String typeKeyword = matcher.group(4); + + EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(packageUri); + if (ePackage == null) + { + throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.2"), + packageUri, factoryType)); + } + + EClassifier eClassifier = ePackage.getEClassifier(classifierName); + if (eClassifier == null) + { + throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.3"), + classifierName, factoryType)); + } + + DBType dbType = DBType.getTypeByKeyword(typeKeyword); + if (dbType == null) + { + throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.4"), + dbType, factoryType)); + } + + return new TypeMappingDescriptor(id, factoryType, eClassifier, dbType); + } + + public static class FactoryTypeParserException extends Exception + { + private static final long serialVersionUID = 1L; + + public FactoryTypeParserException(String desc) + { + super(desc); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java new file mode 100644 index 0000000000..83493b4896 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java @@ -0,0 +1,588 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables. + * + * @author Eike Stepper + * @since 3.0 + */ +public abstract class AbstractFeatureMapTableMapping extends BasicAbstractListTableMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractFeatureMapTableMapping.class); + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The tags mapped to column names + */ + private HashMap<CDOID, String> tagMap; + + /** + * Column name Set + */ + private List<String> columnNames; + + /** + * The type mappings for the value fields. + */ + private Map<CDOID, ITypeMapping> typeMappings; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + protected String sqlInsert; + + private List<DBType> dbTypes; + + public AbstractFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initDBTypes(); + initTable(); + initSQLStrings(); + } + + private void initDBTypes() + { + // TODO add annotation processing here ... + ITypeMapping.Registry registry = getTypeMappingRegistry(); + dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes()); + } + + protected ITypeMapping.Registry getTypeMappingRegistry() + { + return ITypeMapping.Registry.INSTANCE; + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + // add fields for keys (cdo_id, version, feature_id) + FieldInfo[] fields = getKeyFields(); + IDBField[] dbFields = new IDBField[fields.length]; + + for (int i = 0; i < fields.length; i++) + { + dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType()); + } + + // add field for list index + IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER); + + // add field for FeatureMap tag (MetaID for Feature in CDO registry) + IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType()); + + tagMap = new HashMap<CDOID, String>(); + typeMappings = new HashMap<CDOID, ITypeMapping>(); + columnNames = new ArrayList<String>(); + + // create columns for all DBTypes + for (DBType type : getDBTypes()) + { + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name(); + table.addField(column, type); + columnNames.add(column); + } + + table.addIndex(Type.NON_UNIQUE, dbFields); + table.addIndex(Type.NON_UNIQUE, idxField); + table.addIndex(Type.NON_UNIQUE, tagField); + } + + protected abstract FieldInfo[] getKeyFields(); + + protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException; + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + FieldInfo[] fields = getKeyFields(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); + + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); + + Iterator<String> iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); + } + } + + builder.append(" FROM "); + builder.append(tableName); + builder.append(" WHERE "); + + for (int i = 0; i < fields.length; i++) + { + builder.append(fields[i].getName()); + if (i + 1 < fields.length) + { + // more to come + builder.append("=? AND "); + } + else + { + // last one + builder.append("=? "); + } + } + + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$ + + // INSERT with dynamic field name + // TODO: Better: universal INSERT-Statement, because of stmt caching! + + // ----------------- INSERT - prefix ----------------- + builder = new StringBuilder("INSERT INTO "); + builder.append(tableName); + builder.append(" ("); //$NON-NLS-1$ + for (int i = 0; i < fields.length; i++) + { + builder.append(fields[i].getName()); + builder.append(", "); //$NON-NLS-1$ + } + + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(columnNames.get(i)); + builder.append(", "); //$NON-NLS-1$ + } + + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(") VALUES ("); //$NON-NLS-1$ + for (int i = 0; i < fields.length + columnNames.size(); i++) + { + builder.append("?, "); + } + + builder.append("?, ?)"); + sqlInsert = builder.toString(); + } + + protected List<DBType> getDBTypes() + { + return dbTypes; + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List<String> getColumnNames() + { + return columnNames; + } + + protected final Map<CDOID, ITypeMapping> getTypeMappings() + { + return typeMappings; + } + + protected final Map<CDOID, String> getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), getFeature() + .getName(), revision.getID(), revision.getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + setKeyFields(stmt, revision); + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + int currentIndex = 0; + + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), + getFeature().getName(), revision.getID(), revision.getVersion()); + } + } + + private void addFeature(CDOID tag) + { + EStructuralFeature modelFeature = getFeatureByTag(tag); + + ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature); + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); + + tagMap.put(tag, column); + typeMapping.setDBField(table, column); + typeMappings.put(tag, typeMapping); + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + setKeyFields(stmt, chunkReader.getRevision()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); + } + + chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), + getFeature(), chunkReader.getRevision()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER + .format( + "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$ + } + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, revision.getTimeStamp()); + String columnName = getColumnName(tag); + + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH); + setKeyFields(stmt, revision); + int column = getKeyFields().length + 1; + + for (int i = 0; i < columnNames.size(); i++) + { + if (columnNames.get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + stmt.setInt(column++, idx); + idHandler.setCDOID(stmt, column++, tag); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Get column name (lazy) + * + * @param tag + * The feature's MetaID in CDO + * @return the column name where the values are stored + */ + protected String getColumnName(CDOID tag) + { + String column = tagMap.get(tag); + if (column == null) + { + addFeature(tag); + column = tagMap.get(tag); + } + + return column; + } + + /** + * Get type mapping (lazy) + * + * @param tag + * The feature's MetaID in CDO + * @return the corresponding type mapping + */ + protected ITypeMapping getTypeMapping(CDOID tag) + { + ITypeMapping typeMapping = typeMappings.get(tag); + if (typeMapping == null) + { + addFeature(tag); + typeMapping = typeMappings.get(tag); + } + + return typeMapping; + } + + /** + * @param metaID + * @return the column name where the values are stored + */ + private EStructuralFeature getFeatureByTag(CDOID tag) + { + return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag); + } + + /** + * @param feature + * The EStructuralFeature + * @return The feature's MetaID in CDO + */ + protected CDOID getTagByFeature(EStructuralFeature feature, long timeStamp) + { + return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, timeStamp); + } + + /** + * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ... + * + * @author Stefan Winkler + */ + protected static class FieldInfo + { + private String name; + + private DBType dbType; + + public FieldInfo(String name, DBType dbType) + { + this.name = name; + this.dbType = dbType; + } + + public String getName() + { + return name; + } + + public DBType getDbType() + { + return dbType; + } + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + /* + * must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported + * here) + */ + throw new ImplementationError("Should never be called!"); + } + +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java new file mode 100644 index 0000000000..bb500d3e4a --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java @@ -0,0 +1,870 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMapUtil; + +import org.eclipse.core.runtime.Assert; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractHorizontalClassMapping implements IClassMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class); + + private EClass eClass; + + private IDBTable table; + + private AbstractHorizontalMappingStrategy mappingStrategy; + + private List<ITypeMapping> valueMappings; + + private List<IListMapping> listMappings; + + private Map<EStructuralFeature, String> listSizeFields; + + private Map<EStructuralFeature, String> unsettableFields; + + private String sqlSelectForHandle; + + private String sqlSelectForChangeSet; + + public AbstractHorizontalClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + this.mappingStrategy = mappingStrategy; + this.eClass = eClass; + + initTable(); + initFeatures(); + initSQLStrings(); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + DBType idType = store.getIDHandler().getDBType(); + + String name = getMappingStrategy().getTableName(eClass); + table = store.getDBSchema().addTable(name); + + IDBField idField = table.addField(CDODBSchema.ATTRIBUTES_ID, idType, true); + IDBField versionField = table.addField(CDODBSchema.ATTRIBUTES_VERSION, DBType.INTEGER, true); + + IDBField branchField = addBranchingField(table); + + table.addField(CDODBSchema.ATTRIBUTES_CREATED, DBType.BIGINT, true); + IDBField revisedField = table.addField(CDODBSchema.ATTRIBUTES_REVISED, DBType.BIGINT, true); + table.addField(CDODBSchema.ATTRIBUTES_RESOURCE, idType, true); + table.addField(CDODBSchema.ATTRIBUTES_CONTAINER, idType, true); + table.addField(CDODBSchema.ATTRIBUTES_FEATURE, DBType.INTEGER, true); + + if (branchField != null) + { + table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField, branchField); + } + else + { + table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField); + } + + table.addIndex(IDBIndex.Type.NON_UNIQUE, idField, revisedField); + } + + protected IDBField addBranchingField(IDBTable table) + { + return null; + } + + private void initFeatures() + { + EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass); + + if (features == null) + { + valueMappings = Collections.emptyList(); + listMappings = Collections.emptyList(); + } + else + { + valueMappings = createValueMappings(features); + listMappings = createListMappings(features); + } + } + + private void initSQLStrings() + { + // ----------- Select all revisions (for handleRevisions) --- + StringBuilder builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + sqlSelectForHandle = builder.toString(); + + // ----------- Select all revisions (for readChangeSet) --- + builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + sqlSelectForChangeSet = builder.toString(); + } + + private List<ITypeMapping> createValueMappings(EStructuralFeature[] features) + { + List<ITypeMapping> mappings = new ArrayList<ITypeMapping>(); + for (EStructuralFeature feature : features) + { + if (!feature.isMany()) + { + ITypeMapping mapping = mappingStrategy.createValueMapping(feature); + mapping.createDBField(getTable()); + mappings.add(mapping); + + if (feature.isUnsettable()) + { + String fieldName = mappingStrategy.getUnsettableFieldName(feature); + if (unsettableFields == null) + { + unsettableFields = new LinkedHashMap<EStructuralFeature, String>(); + } + + unsettableFields.put(feature, fieldName); + } + } + } + + // add unsettable fields to end of table + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + table.addField(fieldName, DBType.BOOLEAN, 1); + } + } + + return mappings; + } + + private List<IListMapping> createListMappings(EStructuralFeature[] features) + { + List<IListMapping> listMappings = new ArrayList<IListMapping>(); + for (EStructuralFeature feature : features) + { + if (feature.isMany()) + { + IListMapping mapping = null; + if (FeatureMapUtil.isFeatureMap(feature)) + { + mapping = mappingStrategy.createFeatureMapMapping(eClass, feature); + } + else + { + mapping = mappingStrategy.createListMapping(eClass, feature); + } + + listMappings.add(mapping); + + // add field for list sizes + createListSizeField(feature); + } + } + + return listMappings; + } + + /** + * Create an integer field in the attribute tabel for the list size of the associated list mapping. + */ + private void createListSizeField(EStructuralFeature feature) + { + if (listSizeFields == null) + { + listSizeFields = new LinkedHashMap<EStructuralFeature, String>(); + } + + String fieldName = mappingStrategy.getFieldName(feature); + table.addField(fieldName, DBType.INTEGER); + + listSizeFields.put(feature, fieldName); + } + + /** + * Read the revision's values from the DB. + * + * @return <code>true</code> if the revision has been read successfully.<br> + * <code>false</code> if the revision does not exist in the DB. + */ + protected final boolean readValuesFromStatement(PreparedStatement stmt, InternalCDORevision revision, + IDBStoreAccessor accessor) + { + ResultSet resultSet = null; + + try + { + if (TRACER.isEnabled()) + { + TRACER.format("Executing Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + stmt.setMaxRows(1); // Optimization: only 1 row + + resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Resultset was empty"); //$NON-NLS-1$ + } + + return false; + } + + revision.setVersion(resultSet.getInt(CDODBSchema.ATTRIBUTES_VERSION)); + + long timeStamp = resultSet.getLong(CDODBSchema.ATTRIBUTES_CREATED); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp); + + revision.setBranchPoint(branchPoint); + revision.setRevised(resultSet.getLong(CDODBSchema.ATTRIBUTES_REVISED)); + revision.setResourceID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_RESOURCE)); + revision.setContainerID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_CONTAINER)); + revision.setContainingFeatureID(resultSet.getInt(CDODBSchema.ATTRIBUTES_FEATURE)); + + for (ITypeMapping mapping : valueMappings) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (!resultSet.getBoolean(unsettableFields.get(feature))) + { + // isSet==false -- setValue: null + revision.setValue(feature, null); + continue; + } + } + + mapping.readValueToRevision(resultSet, revision); + } + + if (listSizeFields != null) + { + for (Map.Entry<EStructuralFeature, String> listSizeEntry : listSizeFields.entrySet()) + { + EStructuralFeature feature = listSizeEntry.getKey(); + String fieldName = listSizeEntry.getValue(); + int size = resultSet.getInt(fieldName); + + // ensure the listSize (TODO: remove assertion) + CDOList list = revision.getList(feature, size); + + for (int i = 0; i < size; i++) + { + list.add(InternalCDOList.UNINITIALIZED); + } + + if (list.size() != size) + { + Assert.isTrue(false); + } + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + } + } + + protected final void readLists(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + for (IListMapping listMapping : listMappings) + { + listMapping.readValues(accessor, revision, listChunk); + } + } + + protected final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final EClass getEClass() + { + return eClass; + } + + protected final Map<EStructuralFeature, String> getUnsettableFields() + { + return unsettableFields; + } + + protected final Map<EStructuralFeature, String> getListSizeFields() + { + return listSizeFields; + } + + public final List<ITypeMapping> getValueMappings() + { + return valueMappings; + } + + public final ITypeMapping getValueMapping(EStructuralFeature feature) + { + for (ITypeMapping mapping : valueMappings) + { + if (mapping.getFeature() == feature) + { + return mapping; + } + } + + return null; + } + + public final List<IListMapping> getListMappings() + { + return listMappings; + } + + public final IListMapping getListMapping(EStructuralFeature feature) + { + for (IListMapping mapping : listMappings) + { + if (mapping.getFeature() == feature) + { + return mapping; + } + } + + throw new IllegalArgumentException("List mapping for feature " + feature + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + protected final IDBTable getTable() + { + return table; + } + + public List<IDBTable> getDBTables() + { + List<IDBTable> tables = new ArrayList<IDBTable>(); + tables.add(table); + + for (IListMapping listMapping : listMappings) + { + tables.addAll(listMapping.getDBTables()); + } + + return tables; + } + + protected void checkDuplicateResources(IDBStoreAccessor accessor, CDORevision revision) throws IllegalStateException + { + CDOID folderID = (CDOID)revision.data().getContainerID(); + String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0); + CDOID existingID = accessor.readResourceID(folderID, name, revision.getBranch().getHead()); + if (existingID != null && !existingID.equals(revision.getID())) + { + throw new IllegalStateException("Duplicate resource or folder: " + name + " in folder " + folderID); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + protected void writeLists(IDBStoreAccessor accessor, InternalCDORevision revision) + { + for (IListMapping listMapping : listMappings) + { + listMapping.writeValues(accessor, revision); + } + } + + public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise, + OMMonitor monitor) + { + Async async = null; + monitor.begin(10); + + try + { + try + { + async = monitor.forkAsync(); + CDOID id = revision.getID(); + if (mapType) + { + long timeStamp = revision.getTimeStamp(); + mappingStrategy.putObjectType(accessor, timeStamp, id, eClass); + } + else if (revise) + { + long revised = revision.getTimeStamp() - 1; + reviseOldRevision(accessor, id, revision.getBranch(), revised); + for (IListMapping mapping : getListMappings()) + { + mapping.objectDetached(accessor, id, revised); + } + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + async = monitor.forkAsync(); + if (revision.isResourceFolder() || revision.isResource()) + { + checkDuplicateResources(accessor, revision); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + // Write attribute table always (even without modeled attributes!) + async = monitor.forkAsync(); + writeValues(accessor, revision); + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + // Write list tables only if they exist + if (listMappings != null) + { + async = monitor.forkAsync(7); + writeLists(accessor, revision); + } + else + { + monitor.worked(7); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + // branch parameter is ignored, because either it is null or main branch. + // this does not make any difference for non-branching store. + // see #handleRevisions() implementation in HorizontalBranchingClassMapping + // for branch handling. + + IRepository repository = accessor.getStore().getRepository(); + CDORevisionManager revisionManager = repository.getRevisionManager(); + CDOBranchManager branchManager = repository.getBranchManager(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + // TODO: test for timeStamp == INVALID_TIME and encode revision.isValid() as WHERE instead of fetching all revisions + // in order to increase performance + + StringBuilder builder = new StringBuilder(sqlSelectForHandle); + + int timeParameters = 0; + if (timeStamp != CDOBranchPoint.INVALID_DATE) + { + if (exactTime) + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("=?"); //$NON-NLS-1$ + timeParameters = 1; + } + } + else + { + builder.append(" WHERE "); //$NON-NLS-1$ + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + builder.append(")"); //$NON-NLS-1$ + timeParameters = 2; + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + } + } + } + + try + { + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW); + for (int i = 0; i < timeParameters; i++) + { + stmt.setLong(i + 1, timeStamp); + } + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + int version = resultSet.getInt(2); + + if (version >= CDOBranchVersion.FIRST_VERSION) + { + InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchManager + .getMainBranch().getVersion(version), CDORevision.UNCHUNKED, true); + + if (!handler.handleRevision(revision)) + { + break; + } + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments) + { + StringBuilder builder = new StringBuilder(sqlSelectForChangeSet); + boolean isFirst = true; + + for (int i = 0; i < segments.length; i++) + { + if (isFirst) + { + isFirst = false; + } + else + { + builder.append(" OR "); //$NON-NLS-1$ + } + + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + builder.append(")"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + Set<CDOID> result = new HashSet<CDOID>(); + + try + { + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW); + int column = 1; + for (CDOChangeSetSegment segment : segments) + { + stmt.setLong(column++, segment.getTimeStamp()); + stmt.setLong(column++, segment.getEndTime()); + } + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + result.add(id); + } + + return result; + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor monitor) + { + Async async = null; + monitor.begin(1 + listMappings.size()); + + try + { + if (version >= CDOBranchVersion.FIRST_VERSION) + { + reviseOldRevision(accessor, id, branch, timeStamp - 1); + } + + detachAttributes(accessor, id, version + 1, branch, timeStamp, monitor.fork()); + + // notify list mappings so they can clean up + for (IListMapping mapping : getListMappings()) + { + try + { + async = monitor.forkAsync(); + mapping.objectDetached(accessor, id, timeStamp); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + } + finally + { + monitor.done(); + } + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString) + { + String tableName = getTable().getName(); + EClass eClass = getEClass(); + List<EReference> refs = context.getSourceCandidates().get(eClass); + List<EReference> scalarRefs = new ArrayList<EReference>(); + + for (EReference ref : refs) + { + if (ref.isMany()) + { + IListMapping listMapping = getListMapping(ref); + String where = getListXRefsWhere(context); + + boolean more = listMapping.queryXRefs(accessor, tableName, where, context, idString); + if (!more) + { + return false; + } + } + else + { + scalarRefs.add(ref); + } + } + + if (!scalarRefs.isEmpty()) + { + boolean more = queryScalarXRefs(accessor, scalarRefs, context, idString); + if (!more) + { + return false; + } + } + + return true; + } + + protected final boolean queryScalarXRefs(IDBStoreAccessor accessor, List<EReference> scalarRefs, + QueryXRefsContext context, String idString) + { + String tableName = getTable().getName(); + String where = getListXRefsWhere(context); + + for (EReference ref : scalarRefs) + { + ITypeMapping valueMapping = getValueMapping(ref); + String valueField = valueMapping.getField().getName(); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); + builder.append(valueField); + builder.append(" FROM "); + builder.append(tableName); + builder.append(" WHERE "); + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); + builder.append(where); + builder.append(" AND "); + builder.append(valueField); + builder.append(" IN "); + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (attributes): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID sourceID = idHandler.getCDOID(resultSet, 1); + CDOID targetID = idHandler.getCDOID(resultSet, 2); + + boolean more = context.addXRef(targetID, sourceID, ref, 0); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx=0", sourceID, targetID); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + return true; + } + + protected abstract String getListXRefsWhere(QueryXRefsContext context); + + protected abstract void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, + long timeStamp, OMMonitor fork); + + protected abstract void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp); + + protected abstract void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java new file mode 100644 index 0000000000..7a9cd33b45 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java @@ -0,0 +1,480 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.eresource.CDOResourceNode; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.IObjectTypeMapper; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.AbstractMappingStrategy; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collection; +import java.util.List; + +/** + * * This abstract base class refines {@link AbstractMappingStrategy} by implementing aspects common to horizontal + * mapping strategies -- namely: + * <ul> + * <li>object type cache (table cdo_objects) + * <li>resource query handling + * </ul> + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractHorizontalMappingStrategy extends AbstractMappingStrategy +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalMappingStrategy.class); + + /** + * The associated object type mapper. + */ + private IObjectTypeMapper objectTypeMapper; + + public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id) + { + return objectTypeMapper.getObjectType(accessor, id); + } + + public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type) + { + objectTypeMapper.putObjectType(accessor, timeStamp, id, type); + } + + public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection) + { + IDBStore store = getStore(); + if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + IIDHandler idHandler = store.getIDHandler(); + + CDOID minLocalID = getMinLocalID(connection); + idHandler.setNextLocalObjectID(minLocalID); + + CDOID maxID = objectTypeMapper.getMaxID(connection, idHandler); + idHandler.setLastObjectID(maxID); + } + } + + public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context) + { + // only support timestamp in audit mode + if (context.getTimeStamp() != CDORevision.UNSPECIFIED_DATE && !hasAuditSupport()) + { + throw new IllegalArgumentException("Mapping Strategy does not support audits"); //$NON-NLS-1$ + } + + EresourcePackage resourcesPackage = EresourcePackage.eINSTANCE; + + // first query folders + IClassMapping resourceFolder = getClassMapping(resourcesPackage.getCDOResourceFolder()); + boolean shallContinue = queryResources(accessor, resourceFolder, context); + + // not enough results? -> query resources + if (shallContinue) + { + IClassMapping resource = getClassMapping(resourcesPackage.getCDOResource()); + queryResources(accessor, resource, context); + } + } + + public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context) + { + IIDHandler idHandler = getStore().getIDHandler(); + StringBuilder builder = null; + + // create a string containing "(id1,id2,...)" + // NOTE: this might not scale infinitely, because of dbms-dependent + // max size for SQL strings. But for now, it's the easiest way... + for (CDOID targetID : context.getTargetObjects().keySet()) + { + // NOTE: currently no support for external references! + if (builder == null) + { + builder = new StringBuilder("("); + } + else + { + builder.append(","); + } + + idHandler.appendCDOID(builder, targetID); + } + + builder.append(")"); + String idString = builder.toString(); + + for (EClass eClass : context.getSourceCandidates().keySet()) + { + IClassMapping classMapping = getClassMapping(eClass); + boolean more = classMapping.queryXRefs(accessor, context, idString); + if (!more) + { + // cancel query (max results reached or user canceled) + return; + } + } + } + + public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int fromBranchID, int toBranchID, + long fromCommitTime, long toCommitTime) throws IOException + { + StringBuilder builder = new StringBuilder(); + builder.append(" WHERE a_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(" BETWEEN "); //$NON-NLS-1$ + builder.append(fromCommitTime); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(toCommitTime); + + String attrSuffix = builder.toString(); + Connection connection = accessor.getConnection(); + + Collection<IClassMapping> classMappings = getClassMappings(true).values(); + out.writeInt(classMappings.size()); + + for (IClassMapping classMapping : classMappings) + { + EClass eClass = classMapping.getEClass(); + out.writeCDOClassifierRef(eClass); + + IDBTable table = classMapping.getDBTables().get(0); + DBUtil.serializeTable(out, connection, table, "a_t", attrSuffix); + + for (IListMapping listMapping : classMapping.getListMappings()) + { + rawExportList(out, connection, listMapping, table, attrSuffix); + } + } + + objectTypeMapper.rawExport(connection, out, fromCommitTime, toCommitTime); + } + + protected void rawExportList(CDODataOutput out, Connection connection, IListMapping listMapping, IDBTable attrTable, + String attrSuffix) throws IOException + { + for (IDBTable table : listMapping.getDBTables()) + { + String listSuffix = ", " + attrTable + " a_t" + attrSuffix; + String listJoin = getListJoin("a_t", "l_t"); + if (listJoin != null) + { + listSuffix += listJoin; + } + + DBUtil.serializeTable(out, connection, table, "l_t", listSuffix); + } + } + + public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + int size = in.readInt(); + if (size == 0) + { + return; + } + + int objectTypeMapperWork = 10; + monitor.begin(3 * size + objectTypeMapperWork); + + try + { + Connection connection = accessor.getConnection(); + for (int i = 0; i < size; i++) + { + EClass eClass = (EClass)in.readCDOClassifierRefAndResolve(); + IClassMapping classMapping = getClassMapping(eClass); + + IDBTable table = classMapping.getDBTables().get(0); + DBUtil.deserializeTable(in, connection, table, monitor.fork()); + rawImportReviseOldRevisions(connection, table, monitor.fork()); + rawImportUnreviseNewRevisions(connection, table, fromCommitTime, toCommitTime, monitor.fork()); + + List<IListMapping> listMappings = classMapping.getListMappings(); + int listSize = listMappings.size(); + if (listSize == 0) + { + monitor.worked(); + } + else + { + OMMonitor listMonitor = monitor.fork(); + listMonitor.begin(listSize); + + try + { + for (IListMapping listMapping : listMappings) + { + rawImportList(in, connection, listMapping, listMonitor.fork()); + } + } + finally + { + listMonitor.done(); + } + } + } + + objectTypeMapper.rawImport(connection, in, monitor.fork(objectTypeMapperWork)); + } + finally + { + monitor.done(); + } + } + + protected void rawImportUnreviseNewRevisions(Connection connection, IDBTable table, long fromCommitTime, + long toCommitTime, OMMonitor monitor) + { + throw new UnsupportedOperationException("Must be overridden"); + } + + protected void rawImportReviseOldRevisions(Connection connection, IDBTable table, OMMonitor monitor) + { + throw new UnsupportedOperationException("Must be overridden"); + } + + protected void rawImportList(CDODataInput in, Connection connection, IListMapping listMapping, OMMonitor monitor) + throws IOException + { + Collection<IDBTable> tables = listMapping.getDBTables(); + int size = tables.size(); + if (size == 0) + { + return; + } + + monitor.begin(size); + + try + { + for (IDBTable table : tables) + { + DBUtil.deserializeTable(in, connection, table, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + public String getListJoin(String attrTable, String listTable) + { + return " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_ID + "=" + listTable + "." + CDODBSchema.LIST_REVISION_ID; + } + + @Override + protected boolean isMapped(EClass eClass) + { + return !eClass.isAbstract() && !eClass.isInterface(); + } + + @Override + protected Collection<EClass> getClassesWithObjectInfo() + { + return getClassMappings().keySet(); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + if (objectTypeMapper == null) + { + objectTypeMapper = createObjectTypeMapper(); + } + + LifecycleUtil.activate(objectTypeMapper); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(objectTypeMapper); + super.doDeactivate(); + } + + private IObjectTypeMapper createObjectTypeMapper() + { + ObjectTypeTable table = new ObjectTypeTable(); + table.setMappingStrategy(this); + + int cacheSize = getObjectTypeCacheSize(); + if (cacheSize == 0) + { + return table; + } + + ObjectTypeCache cache = new ObjectTypeCache(cacheSize); + cache.setMappingStrategy(this); + cache.setDelegate(table); + return cache; + } + + private int getObjectTypeCacheSize() + { + int objectTypeCacheSize = ObjectTypeCache.DEFAULT_CACHE_CAPACITY; + + Object value = getProperties().get(PROP_OBJECT_TYPE_CACHE_SIZE); + if (value != null) + { + try + { + int intValue = Integer.parseInt((String)value); + objectTypeCacheSize = intValue; + } + catch (NumberFormatException e) + { + OM.LOG.warn("Malformed configuration option for object type cache size. Using default."); + } + } + + return objectTypeCacheSize; + } + + /** + * This is an intermediate implementation. It should be changed after classmappings support a general way to implement + * queries ... + * + * @param accessor + * the accessor to use. + * @param classMapping + * the class mapping of a class instanceof {@link CDOResourceNode} which should be queried. + * @param context + * the query context containing the parameters and the result. + * @return <code>true</code> if result context is not yet full and query should continue false, if result context is + * full and query should stop. + */ + private boolean queryResources(IDBStoreAccessor accessor, IClassMapping classMapping, QueryResourcesContext context) + { + IIDHandler idHandler = getStore().getIDHandler(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + CDOID folderID = context.getFolderID(); + String name = context.getName(); + boolean exactMatch = context.exactMatch(); + + try + { + stmt = classMapping.createResourceQueryStatement(accessor, folderID, name, exactMatch, context); + resultSet = stmt.executeQuery(); + + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + if (TRACER.isEnabled()) + { + TRACER.trace("Resource query returned ID " + id); //$NON-NLS-1$ + } + + if (!context.addResource(id)) + { + // No more results allowed + return false; // don't continue + } + } + + return true; // continue with other results + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + accessor.getStatementCache().releasePreparedStatement(stmt); + } + } + + private CDOID getMinLocalID(Connection connection) + { + IIDHandler idHandler = getStore().getIDHandler(); + CDOID min = idHandler.getMaxCDOID(); + + // Do not call getClassMappings() at this point, as the package registry is not yet initialized! + String dbName = getStore().getRepository().getName(); + List<String> names = DBUtil.getAllTableNames(connection, dbName); + + String prefix = "SELECT MIN(t." + CDODBSchema.ATTRIBUTES_ID + ") FROM " + dbName + "." + CDODBSchema.CDO_OBJECTS + + " AS o, " + dbName + "."; + + String suffix = " AS t WHERE t." + CDODBSchema.ATTRIBUTES_BRANCH + "<0 AND t." + CDODBSchema.ATTRIBUTES_ID + "=o." + + CDODBSchema.ATTRIBUTES_ID + " AND t." + CDODBSchema.ATTRIBUTES_CREATED + "=o." + + CDODBSchema.ATTRIBUTES_CREATED; + + for (String name : names) + { + Statement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = connection.createStatement(); + resultSet = stmt.executeQuery(prefix + name + suffix); + + if (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + if (id != null && idHandler.compare(id, min) < 0) + { + min = id; + } + } + } + catch (SQLException ex) + { + //$FALL-THROUGH$ + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + return min; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java new file mode 100644 index 0000000000..9ca17b94ad --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java @@ -0,0 +1,485 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings + * Stefan Winkler - Bug 283998: [DB] Chunk reading for multiple chunks fails + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables. + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractListTableMapping extends BasicAbstractListTableMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractListTableMapping.class); + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The type mapping for the value field. + */ + private ITypeMapping typeMapping; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsertEntry; + + public AbstractListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initTable(); + initSQLStrings(); + } + + private void initTable() + { + IMappingStrategy mappingStrategy = getMappingStrategy(); + String tableName = mappingStrategy.getTableName(getContainingClass(), getFeature()); + table = mappingStrategy.getStore().getDBSchema().addTable(tableName); + + // add fields for keys (cdo_id, version, feature_id) + FieldInfo[] fields = getKeyFields(); + IDBField[] dbFields = new IDBField[fields.length + 1]; + + for (int i = 0; i < fields.length; i++) + { + dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType()); + } + + // add field for list index + dbFields[dbFields.length - 1] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER); + + // add field for value + typeMapping = mappingStrategy.createValueMapping(getFeature()); + typeMapping.createDBField(table, CDODBSchema.LIST_VALUE); + + // add table indexes + table.addIndex(Type.UNIQUE, dbFields); + } + + protected abstract FieldInfo[] getKeyFields(); + + protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException; + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + FieldInfo[] fields = getKeyFields(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + + for (int i = 0; i < fields.length; i++) + { + builder.append(fields[i].getName()); + if (i + 1 < fields.length) + { + // more to come + builder.append("=? AND "); //$NON-NLS-1$ + } + else + { + // last one + builder.append("=? "); //$NON-NLS-1$ + } + } + + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$ + + // ----------------- INSERT - reference entry ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + + for (int i = 0; i < fields.length; i++) + { + builder.append(fields[i].getName()); + builder.append(", "); //$NON-NLS-1$ + } + + builder.append(CDODBSchema.LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES ("); //$NON-NLS-1$ + for (int i = 0; i < fields.length; i++) + { + builder.append("?, "); //$NON-NLS-1$ + } + + builder.append(" ?, ?)"); //$NON-NLS-1$ + sqlInsertEntry = builder.toString(); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + setKeyFields(stmt, revision); + + if (TRACER.isEnabled()) + { + TRACER.trace(stmt.toString()); + } + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + setKeyFields(stmt, chunkReader.getRevision()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$ + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$ + } + + chunk.add(indexInChunk++, value); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); //$NON-NLS-1$ + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", getContainingClass().getName(), + getFeature().getName(), idx, revision.getID(), revision.getVersion(), value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + setKeyFields(stmt, revision); + int column = getKeyFields().length + 1; + stmt.setInt(column++, idx); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + String tableName = getTable().getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" AS l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" AS a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t." + mainTableWhere);//$NON-NLS-1$ + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID srcId = idHandler.getCDOID(resultSet, 1); + CDOID targetId = idHandler.getCDOID(resultSet, 2); + int idx = resultSet.getInt(3); + + boolean more = context.addXRef(targetId, srcId, (EReference)getFeature(), idx); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", srcId, targetId, idx); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + /** + * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ... + * + * @author Stefan Winkler + */ + protected static class FieldInfo + { + private String name; + + private DBType dbType; + + public FieldInfo(String name, DBType dbType) + { + this.name = name; + this.dbType = dbType; + } + + public String getName() + { + return name; + } + + public DBType getDbType() + { + return dbType; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java new file mode 100644 index 0000000000..d24e2239c0 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 259402 + * Stefan Winkler - redesign (prepared statements) + * Stefan Winkler - bug 276926 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.IObjectTypeMapper; + +import org.eclipse.net4j.util.lifecycle.Lifecycle; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public abstract class AbstractObjectTypeMapper extends Lifecycle implements IObjectTypeMapper +{ + private IMappingStrategy mappingStrategy; + + private IMetaDataManager metaDataManager; + + public AbstractObjectTypeMapper() + { + } + + public IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public void setMappingStrategy(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + } + + public IMetaDataManager getMetaDataManager() + { + return metaDataManager; + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + checkState(mappingStrategy, "mappingStrategy"); //$NON-NLS-1$ + } + + @Override + protected void doActivate() throws Exception + { + metaDataManager = getMappingStrategy().getStore().getMetaDataManager(); + } + + @Override + protected void doDeactivate() throws Exception + { + metaDataManager = null; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java new file mode 100644 index 0000000000..a9c7adce32 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBType; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @since 3.0 + */ +public class AuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping +{ + private FieldInfo[] keyFields; + + public AuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java new file mode 100644 index 0000000000..150fc06e63 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java @@ -0,0 +1,1212 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455 + * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails + * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode + * Stefan Winkler - cleanup, merge and maintenance + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This is a featuremap-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, + * which causes just 1 DB row to be changed. This is achieved by introducing a version range (columns + * {@link CDODBSchema#LIST_REVISION_VERSION_ADDED cdo_version_added} and + * {@link CDODBSchema#LIST_REVISION_VERSION_REMOVED cdo_version_removed}) which records for which revisions a particular + * entry existed. Also, this mapping is mainly optimized for potentially very large lists: the need for having the + * complete list stored in memory to do in-the-middle-moved and inserts is traded in for a few more DB access + * operations. + * + * @author Eike Stepper + * @author Stefan Winkler + * @author Lothar Werzinger + * @since 3.0 + */ +public class AuditFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements + IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditFeatureMapTableMappingWithRanges.class); + + /** + * Used to clean up lists for detached objects. + */ + private static final int FINAL_VERSION = Integer.MAX_VALUE; + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The tags mapped to column names + */ + private HashMap<CDOID, String> tagMap; + + /** + * Column name Set + */ + private List<String> columnNames; + + /** + * The type mappings for the value fields. + */ + private Map<CDOID, ITypeMapping> typeMappings; + + private List<DBType> dbTypes; + + // --------- SQL strings - see initSQLStrings() ----------------- + + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsert; + + private String sqlRemoveEntry; + + private String sqlDeleteEntry; + + private String sqlUpdateIndex; + + private String sqlGetValue; + + private String sqlClearList; + + private String sqlDeleteList; + + public AuditFeatureMapTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, + EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initDBTypes(); + initTable(); + initSQLStrings(); + } + + private void initDBTypes() + { + // TODO add annotation processing here ... + ITypeMapping.Registry registry = ITypeMapping.Registry.INSTANCE; + dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes()); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + // add fields for CDOID + IDBField idField = table.addField(CDODBSchema.FEATUREMAP_REVISION_ID, store.getIDHandler().getDBType()); + + // add fields for version range + IDBField versionAddedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_ADDED, DBType.INTEGER); + IDBField versionRemovedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_REMOVED, DBType.INTEGER); + + // add field for list index + IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER); + + // add field for FeatureMap tag (MetaID for Feature in CDO registry) + IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType()); + + tagMap = new HashMap<CDOID, String>(); + typeMappings = new HashMap<CDOID, ITypeMapping>(); + columnNames = new ArrayList<String>(); + + // create columns for all DBTypes + for (DBType type : getDBTypes()) + { + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name(); + table.addField(column, type); + columnNames.add(column); + } + + // TODO think about indices + table.addIndex(Type.NON_UNIQUE, idField); + table.addIndex(Type.NON_UNIQUE, versionAddedField); + table.addIndex(Type.NON_UNIQUE, versionRemovedField); + table.addIndex(Type.NON_UNIQUE, idxField); + table.addIndex(Type.NON_UNIQUE, tagField); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + Iterator<String> iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$ + + // ----------------- INSERT - prefix ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(columnNames.get(i)); + } + + builder.append(") VALUES (?, ?, ?, ?, ?"); //$NON-NLS-1$ + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsert = builder.toString(); + + // ----------------- remove current entry ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlRemoveEntry = builder.toString(); + + // ----------------- delete temporary entry ----------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=?"); //$NON-NLS-1$ + sqlDeleteEntry = builder.toString(); + + // ----------------- update index ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlGetValue = builder.toString(); + + // ----------- clear list items ------------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + + // ----------- delete temporary list items ------------------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlDeleteList = builder.toString(); + } + + protected List<DBType> getDBTypes() + { + return dbTypes; + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List<String> getColumnNames() + { + return columnNames; + } + + protected final Map<CDOID, ITypeMapping> getTypeMappings() + { + return typeMappings; + } + + protected final Map<CDOID, String> getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), getFeature() //$NON-NLS-1$ + .getName(), revision.getID(), revision.getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + stmt.setInt(3, revision.getVersion()); + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$ + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + } + + private void addFeature(CDOID tag) + { + EStructuralFeature modelFeature = getFeatureByTag(tag); + + ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature); + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); //$NON-NLS-1$ + + tagMap.put(tag, column); + typeMapping.setDBField(table, column); + typeMappings.put(tag, typeMapping); + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID()); + stmt.setInt(2, chunkReader.getRevision().getVersion()); + stmt.setInt(3, chunkReader.getRevision().getVersion()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$ + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$ + } + + chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); //$NON-NLS-1$ + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature(), chunkReader.getRevision()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Writing done"); //$NON-NLS-1$ + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value) + { + if (TRACER.isEnabled()) + { + TRACER + .format( + "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$ + } + + addEntry(accessor, revision.getID(), revision.getVersion(), idx, value, revision.getTimeStamp()); + } + + /** + * Get column name (lazy). + * + * @param tag + * The feature's MetaID in CDO + * @return the column name where the values are stored + */ + protected String getColumnName(CDOID tag) + { + String column = tagMap.get(tag); + if (column == null) + { + addFeature(tag); + column = tagMap.get(tag); + } + + return column; + } + + /** + * Get type mapping (lazy). + * + * @param tag + * The feature's MetaID in CDO + * @return the corresponding type mapping + */ + protected ITypeMapping getTypeMapping(CDOID tag) + { + ITypeMapping typeMapping = typeMappings.get(tag); + if (typeMapping == null) + { + addFeature(tag); + typeMapping = typeMappings.get(tag); + } + + return typeMapping; + } + + /** + * @param metaID + * @return the column name where the values are stored + */ + private EStructuralFeature getFeatureByTag(CDOID tag) + { + return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag); + } + + /** + * @param feature + * The EStructuralFeature + * @return The feature's MetaID in CDO + */ + protected CDOID getTagByFeature(EStructuralFeature feature, long timestamp) + { + return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, timestamp); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmtDeleteTemp = null; + PreparedStatement stmtClear = null; + + try + { + // delete temporary entries + stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH); + idHandler.setCDOID(stmtDeleteTemp, 1, id); + stmtDeleteTemp.setInt(2, newVersion); + + int result = DBUtil.update(stmtDeleteTemp, false); + if (TRACER.isEnabled()) + { + TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$ + } + + // clear rest of the list + stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmtClear.setInt(1, newVersion); + idHandler.setCDOID(stmtClear, 2, id); + + result = DBUtil.update(stmtClear, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmtDeleteTemp); + statementCache.releasePreparedStatement(stmtClear); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + if (TRACER.isEnabled()) + { + TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$ + } + + CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch(); + + // get revision from cache to find out version number + CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager() + .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + // set cdo_revision_removed for all list items (so we have no NULL values) + clearList(accessor, id, revision.getVersion(), FINAL_VERSION); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + IRepository repo = accessor.getStore().getRepository(); + InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id, + repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion, created); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : delta.getListChanges()) + { + listDelta.accept(visitor); + } + } + + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private InternalCDORevision originalRevision; + + private CDOID id; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private long timestamp; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion, + int newVersion, long timestamp) + { + this.accessor = accessor; + this.originalRevision = originalRevision; + id = this.originalRevision.getID(); + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + this.timestamp = timestamp; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + Object value = getValue(accessor, id, fromIdx); + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1); + } + + // create the item + addEntry(accessor, id, newVersion, toIdx, value, timestamp); + } + + public void visit(CDOAddFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$ + } + + if (startIndex <= endIndex) + { + // make room for the new item + moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, newVersion, startIndex, delta.getValue(), timestamp); + + ++lastIndex; + } + + public void visit(CDORemoveFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, startIndex); + + // make room for the new item + moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + } + + public void visit(CDOSetFeatureDelta delta) + { + int index = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, index); + + // create the item + addEntry(accessor, id, newVersion, index, delta.getValue(), timestamp); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = startIndex; index <= endIndex; ++index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index - 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index - 1, value, timestamp); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + for (int index = endIndex; index >= startIndex; --index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index + 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index + 1, value, timestamp); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, version, value); + } + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, version); + stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + for (int i = 0; i < columnNames.size(); i++) + { + if (columnNames.get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion); + } + + try + { + // try to delete a temporary entry first + stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + statementCache.releasePreparedStatement(stmt); + stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + DBUtil.update(stmt, true); + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + FeatureMap.Entry result = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("getValue expects exactly one result"); + } + + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value); + + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + // must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported + // here) + throw new ImplementationError("Should never be called!"); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java new file mode 100644 index 0000000000..9918525de5 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBType; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a list-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @since 2.0 + */ +public class AuditListTableMapping extends AbstractListTableMapping +{ + private FieldInfo[] keyFields; + + public AuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java new file mode 100644 index 0000000000..a3f0b83b24 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java @@ -0,0 +1,1084 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This class has been derived from AbstractListTableMapping + * + * Contributors: + * Eike Stepper - initial API and implementation + * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode + * Stefan Winkler - cleanup, merge and maintenance + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * This is a list-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, which + * causes just 1 DB row to be changed. This is achieved by introducing a version range (columns cdo_version_added and + * cdo_version_removed) which records for which revisions a particular entry existed. Also, this mapping is mainly + * optimized for potentially very large lists: the need for having the complete list stored in memopy to do + * in-the-middle-moved and inserts is traded in for a few more DB access operations. + * + * @author Eike Stepper + * @author Stefan Winkler + * @author Lothar Werzinger + */ +public class AuditListTableMappingWithRanges extends BasicAbstractListTableMapping implements IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditListTableMappingWithRanges.class); + + /** + * Used to clean up lists for detached objects. + */ + private static final int FINAL_VERSION = Integer.MAX_VALUE; + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The type mapping for the value field. + */ + private ITypeMapping typeMapping; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsertEntry; + + private String sqlDeleteEntry; + + private String sqlRemoveEntry; + + private String sqlUpdateIndex; + + private String sqlGetValue; + + private String sqlClearList; + + private String sqlDeleteList; + + public AuditListTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initTable(); + initSQLStrings(); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + IDBField[] dbFields = new IDBField[4]; + + dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType()); + dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER); + dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER); + dbFields[3] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER); + + // add field for value + typeMapping = getMappingStrategy().createValueMapping(getFeature()); + typeMapping.createDBField(table, CDODBSchema.LIST_VALUE); + + // TODO think about indexes + // add table indexes + table.addIndex(Type.UNIQUE, dbFields); + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$ + + // ----------------- insert entry ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES (?, ?, NULL, ?, ?)"); //$NON-NLS-1$ + sqlInsertEntry = builder.toString(); + + // ----------------- remove current entry ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlRemoveEntry = builder.toString(); + + // ----------------- delete temporary entry ----------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=?"); //$NON-NLS-1$ + sqlDeleteEntry = builder.toString(); + + // ----------------- update index ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlGetValue = builder.toString(); + + // ----------- clear list items ------------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + + // ----------- delete temporary list items ------------------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlDeleteList = builder.toString(); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + stmt.setInt(3, revision.getVersion()); + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading {4} list values done for feature {0}.{1} of {2}v{3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision.getID(), revision.getVersion(), list.size()); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature() {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID()); + stmt.setInt(2, chunkReader.getRevision().getVersion()); + stmt.setInt(3, chunkReader.getRevision().getVersion()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$ + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$ + } + + chunk.add(indexInChunk++, value); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); //$NON-NLS-1$ + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature() {0}.{1} of {2}v{3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), chunkReader.getRevision().getID(), chunkReader + .getRevision().getVersion()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Writing done"); //$NON-NLS-1$ + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int index, Object value) + { + if (TRACER.isEnabled()) + { + TRACER + .format( + "Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, revision.getID(), revision.getVersion(), + value); + } + + addEntry(accessor, revision.getID(), revision.getVersion(), index, value); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmtDeleteTemp = null; + PreparedStatement stmtClear = null; + + try + { + // delete temporary entries + stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH); + idHandler.setCDOID(stmtDeleteTemp, 1, id); + stmtDeleteTemp.setInt(2, newVersion); + + int result = DBUtil.update(stmtDeleteTemp, false); + if (TRACER.isEnabled()) + { + TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$ + } + + // clear rest of the list + stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmtClear.setInt(1, newVersion); + idHandler.setCDOID(stmtClear, 2, id); + + result = DBUtil.update(stmtClear, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmtDeleteTemp); + statementCache.releasePreparedStatement(stmtClear); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + if (TRACER.isEnabled()) + { + TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$ + } + + CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch(); + + // get revision from cache to find out version number + CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager() + .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + // set cdo_revision_removed for all list items (so we have no NULL values) + clearList(accessor, id, revision.getVersion(), FINAL_VERSION); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + IRepository repo = accessor.getStore().getRepository(); + InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id, + repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : delta.getListChanges()) + { + listDelta.accept(visitor); + } + } + + /** + * @author Stefan Winkler + */ + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private CDOID id; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion, + int newVersion) + { + this.accessor = accessor; + id = originalRevision.getID(); + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + Object value = getValue(accessor, id, fromIdx); + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1); + } + + // create the item + addEntry(accessor, id, newVersion, toIdx, value); + } + + public void visit(CDOAddFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$ + } + + if (startIndex <= endIndex) + { + // make room for the new item + moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, newVersion, startIndex, delta.getValue()); + + ++lastIndex; + } + + public void visit(CDORemoveFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, startIndex); + + // make room for the new item + moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + } + + public void visit(CDOSetFeatureDelta delta) + { + int index = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, index); + + // create the item + addEntry(accessor, id, newVersion, index, delta.getValue()); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = startIndex; index <= endIndex; ++index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index - 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index - 1, value); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = endIndex; index >= startIndex; --index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index + 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index + 1, value); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, version, value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, version); + stmt.setInt(column++, index); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion); + } + + try + { + // try to delete a temporary entry first + stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + statementCache.releasePreparedStatement(stmt); + stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + DBUtil.update(stmt, true); + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private Object getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + Object result = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("getValue() expects exactly one result"); + } + + result = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + + String tableName = getTable().getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" AS l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" AS a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t." + mainTableWhere);//$NON-NLS-1$ + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID sourceID = idHandler.getCDOID(resultSet, 1); + CDOID targetID = idHandler.getCDOID(resultSet, 2); + int idx = resultSet.getInt(3); + + boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, targetID, idx); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java new file mode 100644 index 0000000000..6e2e1b60e0 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Stefan Winkler + */ +public abstract class BasicAbstractListTableMapping implements IListMapping +{ + private IMappingStrategy mappingStrategy; + + private EClass containingClass; + + private EStructuralFeature feature; + + public BasicAbstractListTableMapping(IMappingStrategy mappingStrategy, EClass containingClass, + EStructuralFeature feature) + { + this.mappingStrategy = mappingStrategy; + this.containingClass = containingClass; + this.feature = feature; + } + + public final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final EClass getContainingClass() + { + return containingClass; + } + + public final EStructuralFeature getFeature() + { + return feature; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java new file mode 100644 index 0000000000..056ccc3e8a --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455 + * Stefan Winkler - derived branch mapping from audit mapping + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBType; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 3.0 + */ +public class BranchingFeatureMapTableMapping extends AbstractFeatureMapTableMapping +{ + private FieldInfo[] keyFields; + + public BranchingFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.FEATUREMAP_BRANCH, DBType.INTEGER), + new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java new file mode 100644 index 0000000000..b1458ae679 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java @@ -0,0 +1,1502 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Winkler - initial API and implementation taken from AuditFeatureMapTableMappingWithRanges + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This is a featuremap-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, + * which causes just 1 DB row to be changed. This is achieved by introducing a version range (columns + * {@link CDODBSchema#LIST_REVISION_VERSION_ADDED cdo_version_added} and + * {@link CDODBSchema#LIST_REVISION_VERSION_REMOVED cdo_version_removed}) which records for which revisions a particular + * entry existed. Also, this mapping is mainly optimized for potentially very large lists: the need for having the + * complete list stored in memory to do in-the-middle-moved and inserts is traded in for a few more DB access + * operations. + * + * @author Eike Stepper + * @author Stefan Winkler + * @author Lothar Werzinger + * @since 3.0 + */ +public class BranchingFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements + IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, + BranchingFeatureMapTableMappingWithRanges.class); + + /** + * Used to clean up lists for detached objects. + */ + private static final int FINAL_VERSION = Integer.MAX_VALUE; + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The tags mapped to column names + */ + private HashMap<CDOID, String> tagMap; + + /** + * Column name Set + */ + private List<String> columnNames; + + /** + * The type mappings for the value fields. + */ + private Map<CDOID, ITypeMapping> typeMappings; + + private List<DBType> dbTypes; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsert; + + private String sqlRemoveEntry; + + private String sqlDeleteEntry; + + private String sqlUpdateIndex; + + private String sqlGetValue; + + private String sqlClearList; + + public BranchingFeatureMapTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, + EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initDBTypes(); + initTable(); + initSQLStrings(); + } + + private void initDBTypes() + { + // TODO add annotation processing here ... + ITypeMapping.Registry registry = ITypeMapping.Registry.INSTANCE; + dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes()); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + // add fields for CDOID + IDBField idField = table.addField(CDODBSchema.FEATUREMAP_REVISION_ID, store.getIDHandler().getDBType()); + + IDBField branchField = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER); + + // add fields for version range + IDBField versionAddedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_ADDED, DBType.INTEGER); + IDBField versionRemovedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_REMOVED, DBType.INTEGER); + + // add field for list index + IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER); + + // add field for FeatureMap tag (MetaID for Feature in CDO registry) + IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType()); + + tagMap = new HashMap<CDOID, String>(); + typeMappings = new HashMap<CDOID, ITypeMapping>(); + columnNames = new ArrayList<String>(); + + // create columns for all DBTypes + for (DBType type : getDBTypes()) + { + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name(); + table.addField(column, type); + columnNames.add(column); + } + + table.addIndex(Type.NON_UNIQUE, idField); + table.addIndex(Type.NON_UNIQUE, branchField); + table.addIndex(Type.NON_UNIQUE, versionAddedField); + table.addIndex(Type.NON_UNIQUE, versionRemovedField); + table.addIndex(Type.NON_UNIQUE, idxField); + table.addIndex(Type.NON_UNIQUE, tagField); + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + Iterator<String> iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$ + + // ----------------- INSERT - prefix ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(columnNames.get(i)); + } + + builder.append(") VALUES (?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsert = builder.toString(); + + // ----------------- remove current entry ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlRemoveEntry = builder.toString(); + + // ----------------- delete temporary entry ----------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=?"); //$NON-NLS-1$ + sqlDeleteEntry = builder.toString(); + + // ----------------- update index ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlGetValue = builder.toString(); + + // ----------- clear list items ------------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + } + + protected List<DBType> getDBTypes() + { + return dbTypes; + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List<String> getColumnNames() + { + return columnNames; + } + + protected final Map<CDOID, ITypeMapping> getTypeMappings() + { + return typeMappings; + } + + protected final Map<CDOID, String> getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + int valuesToRead = list.size(); + + if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead) + { + valuesToRead = listChunk; + } + + if (valuesToRead == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), getFeature() //$NON-NLS-1$ + .getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branchID); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + + stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows. + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + + while (valuesToRead > 0 && resultSet.next()) + { + int index = resultSet.getInt(1); + if (index > currentIndex) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, index); + if (TRACER.isEnabled()) + { + TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$ + } + + valuesToRead -= index - currentIndex; + currentIndex = index; + } + + CDOID tag = idHandler.getCDOID(resultSet, 2); + Object value = getTypeMapping(tag).readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + valuesToRead--; + } + + if (valuesToRead > 0) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (baseReader != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading base revision chunks for featureMap {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision()); + } + + List<Chunk> baseChunks = baseReader.executeRead(); + for (Chunk chunk : baseChunks) + { + int startIndex = chunk.getStartIndex(); + for (int i = 0; i < chunk.size(); i++) + { + list.set(startIndex + i, chunk.get(i)); + } + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + } + + private void addFeature(CDOID tag) + { + EStructuralFeature modelFeature = getFeatureByTag(tag); + + ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature); + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); //$NON-NLS-1$ + + tagMap.put(tag, column); + typeMapping.setDBField(table, column); + typeMappings.put(tag, typeMapping); + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + CDORevision revision = chunkReader.getRevision(); + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + + resultSet = stmt.executeQuery(); + + int nextDBIndex = Integer.MAX_VALUE; // next available DB index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + + for (Chunk chunk : chunks) + { + int startIndex = chunk.getStartIndex(); + int missingValueStartIndex = -1; + + for (int i = 0; i < chunk.size(); i++) + { + int nextListIndex = startIndex + i; // next expected list index + + if (nextDBIndex == nextListIndex) + { + // DB value is available. check first if missing indexes were present before. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to currentIndex + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(), + chunkReader.getRevision().getBranch().getID()); + } + + if (TRACER.isEnabled()) + { + TRACER.format( + "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$ + } + + baseReader.addRangedChunk(missingValueStartIndex, nextListIndex); + + // reset missingValueStartIndex + missingValueStartIndex = -1; + } + + // now read value and set to chunk + CDOID tag = idHandler.getCDOID(resultSet, 2); + Object value = getTypeMapping(tag).readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$ + } + + chunk.add(i, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + + // advance DB cursor and read next available index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + else + { + // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE + nextDBIndex = Integer.MAX_VALUE; + } + } + else + { + // gap between next DB index and next list index detected. + // skip until end of chunk or until DB value becomes available + if (missingValueStartIndex == -1) + { + missingValueStartIndex = nextListIndex; + } + } + } + + // chunk complete. check for missing values at the end of the chunk. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to last chunk index + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(), + chunkReader.getRevision().getBranch().getID()); + } + baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size()); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + // now read missing values from base revision. + if (baseReader != null) + { + List<Chunk> baseChunks = baseReader.executeRead(); + + Iterator<Chunk> thisIterator = chunks.iterator(); + Chunk thisChunk = thisIterator.next(); + + for (Chunk baseChunk : baseChunks) + { + int baseStartIndex = baseChunk.getStartIndex(); + + while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size()) + { + // advance thisChunk, because it does not match baseChunk + thisChunk = thisIterator.next(); + } + + // baseChunk now corresponds to this chunk, but startIndex of baseChunk may be higher. + // therefore calculate offset + int offset = thisChunk.getStartIndex() - baseStartIndex; + + // and copy values. + for (int i = 0; i < baseChunk.size(); i++) + { + thisChunk.add(i + offset, baseChunk.get(i)); + } + } // finally, continue with the next baseChunk + + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature(), revision); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Writing done"); //$NON-NLS-1$ + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value) + { + if (TRACER.isEnabled()) + { + TRACER + .format( + "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$ + } + + addEntry(accessor, revision.getID(), revision.getBranch().getID(), revision.getVersion(), idx, value, + revision.getTimeStamp()); + } + + /** + * Get column name (lazy). + * + * @param tag + * The feature's MetaID in CDO + * @return the column name where the values are stored + */ + protected String getColumnName(CDOID tag) + { + String column = tagMap.get(tag); + if (column == null) + { + addFeature(tag); + column = tagMap.get(tag); + } + + return column; + } + + /** + * Get type mapping (lazy). + * + * @param tag + * The feature's MetaID in CDO + * @return the corresponding type mapping + */ + protected ITypeMapping getTypeMapping(CDOID tag) + { + ITypeMapping typeMapping = typeMappings.get(tag); + if (typeMapping == null) + { + addFeature(tag); + typeMapping = typeMappings.get(tag); + } + + return typeMapping; + } + + /** + * @param metaID + * @return the column name where the values are stored + */ + private EStructuralFeature getFeatureByTag(CDOID tag) + { + return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag); + } + + /** + * @param feature + * The EStructuralFeature + * @return The feature's MetaID in CDO + */ + protected CDOID getTagByFeature(EStructuralFeature feature, long created) + { + return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, created); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + int lastIndex, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmtDeleteTemp = null; + PreparedStatement stmtClear = null; + + try + { + // check for each index if the value exists in the current branch + for (int i = 0; i <= lastIndex; i++) + { + if (getValue(accessor, id, branchId, i, false) == null) + { + // if not, add a historic entry for missing ones. + addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i), + timestamp); + } + } + + // clear rest of the list + stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmtClear.setInt(1, newVersion); + idHandler.setCDOID(stmtClear, 2, id); + stmtClear.setInt(3, branchId); + + int result = DBUtil.update(stmtClear, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmtDeleteTemp); + statementCache.releasePreparedStatement(stmtClear); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + InternalCDORevision revision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + int branchId = accessor.getTransaction().getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$ + } + + clearList(accessor, id, branchId, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1, + revised); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + List<CDOFeatureDelta> listChanges = delta.getListChanges(); + if (listChanges.size() == 0) + { + // nothing to do. + return; + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, branchId, oldVersion, newVersion, + created); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + // optimization: it's only necessary to process deltas + // starting with the last feature delta which clears the list + // (any operation before the clear is cascaded by it anyway) + int index = listChanges.size() - 1; + while (index > 0) + { + CDOFeatureDelta listDelta = listChanges.get(index); + if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta) + { + break; + } + index--; + } + while (index < listChanges.size()) + { + listChanges.get(index++).accept(visitor); + } + } + + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private InternalCDORevision originalRevision; + + private CDOID id; + + private int branchID; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private long timestamp; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID, + int oldVersion, int newVersion, long timestamp) + { + this.accessor = accessor; + this.originalRevision = originalRevision; + id = this.originalRevision.getID(); + branchID = targetBranchID; + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + this.timestamp = timestamp; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + Object value = getValue(accessor, id, branchID, fromIdx, true); + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx, timestamp); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1); + } + + // create the item + addEntry(accessor, id, branchID, newVersion, toIdx, value, timestamp); + } + + public void visit(CDOAddFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$ + } + + if (startIndex <= endIndex) + { + // make room for the new item + moveOneDown(accessor, id, branchID, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, branchID, newVersion, startIndex, delta.getValue(), timestamp); + + ++lastIndex; + } + + public void visit(CDORemoveFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, startIndex, timestamp); + + // make room for the new item + moveOneUp(accessor, id, branchID, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + } + + public void visit(CDOSetFeatureDelta delta) + { + int index = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, index, timestamp); + + // create the item + addEntry(accessor, id, branchID, newVersion, index, delta.getValue(), timestamp); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex, timestamp); + lastIndex = -1; + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex, timestamp); + lastIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = startIndex; index <= endIndex; ++index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index - 1); + idHandler.setCDOID(stmt, startIndex++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + case 0: + Object value = getValue(accessor, id, branchId, index, false); + + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp); + } + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, branchId, newVersion, index - 1, value, timestamp); + break; + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + for (int index = endIndex; index >= startIndex; --index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index + 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + case 0: + Object value = getValue(accessor, id, branchId, index, false); + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp); + } + + addEntry(accessor, id, branchId, newVersion, index + 1, value, timestamp); + break; + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int version, int index, Object value, + long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, version, value); + } + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, version); + stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + for (int i = 0; i < columnNames.size(); i++) + { + if (columnNames.get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded, + int versionRemoved, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format( + "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved, + value); + } + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, versionAdded); + stmt.setNull(column++, versionRemoved); + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + for (int i = 0; i < columnNames.size(); i++) + { + if (columnNames.get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + int index, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion); + } + + try + { + // try to delete a temporary entry first + stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + statementCache.releasePreparedStatement(stmt); + stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, index); + result = DBUtil.update(stmt, false); + + if (result == 0) + { + // no entry removed -> this means that we are in a branch and + // the entry has not been modified since the branch fork. + // therefore, we have to copy the base value and mark it as removed + Object value = getValueFromBase(accessor, id, branchId, index); + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp); + } + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + FeatureMap.Entry result = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value); + } + else + { + // value is not in this branch. + // -> read from base revision + if (getFromBase) + { + result = getValueFromBase(accessor, id, branchId, index); + } // else: result remains null + } + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + /** + * Read a single value (at a given index) from the base revision + * + * @param accessor + * the DBStoreAccessor + * @param id + * the ID of the revision + * @param branchID + * the ID of the current (child) branch + * @param index + * the index to read the value from + * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at + * the base of this branch (indicated by <code>branchID</code>). + */ + private FeatureMap.Entry getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index) + { + IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID); + chunkReader.addSimpleChunk(index); + List<Chunk> chunks = chunkReader.executeRead(); + return (FeatureMap.Entry)chunks.get(0).get(0); + } + + private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID) + { + CDOBranchPoint base = accessor.getStore().getRepository().getBranchManager().getBranch(branchID).getBase(); + InternalCDORevision baseRevision = (InternalCDORevision)accessor.getStore().getRepository().getRevisionManager() + .getRevision(id, base, /* referenceChunk = */0, /* prefetchDepth = */CDORevision.DEPTH_NONE, true); + IStoreChunkReader chunkReader = accessor.createChunkReader(baseRevision, getFeature()); + return chunkReader; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + // must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported + // here) + throw new ImplementationError("Should never be called!"); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java new file mode 100644 index 0000000000..858c34a0c3 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - derived branch mapping from audit mapping + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBType; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a list-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 3.0 + */ +public class BranchingListTableMapping extends AbstractListTableMapping +{ + private FieldInfo[] keyFields; + + public BranchingListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER), + new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java new file mode 100644 index 0000000000..f7b056a00b --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java @@ -0,0 +1,1411 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This class has been derived from AbstractListTableMapping + * + * Contributors: + * Stefan Winkler - initial API and implementation taken from AuditListTableMappingWithRanges + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * This is a list-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, which + * causes just 1 DB row to be changed. This is achieved by introducing a version range (columns cdo_version_added and + * cdo_version_removed) which records for which revisions a particular entry existed. Also, this mapping is mainly + * optimized for potentially very large lists: the need for having the complete list stored in memopy to do + * in-the-middle-moved and inserts is traded in for a few more DB access operations. + * + * @author Eike Stepper + * @author Stefan Winkler + * @author Lothar Werzinger + */ +public class BranchingListTableMappingWithRanges extends BasicAbstractListTableMapping implements + IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, BranchingListTableMappingWithRanges.class); + + /** + * Used to clean up lists for detached objects. + */ + private static final int FINAL_VERSION = Integer.MAX_VALUE; + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The type mapping for the value field. + */ + private ITypeMapping typeMapping; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsertEntry; + + private String sqlDeleteEntry; + + private String sqlRemoveEntry; + + private String sqlUpdateIndex; + + private String sqlGetValue; + + private String sqlClearList; + + public BranchingListTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initTable(); + initSQLStrings(); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + IDBField[] dbFields = new IDBField[5]; + + dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType()); + dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER); + dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER); + dbFields[3] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER); + dbFields[4] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER); + + // add field for value + typeMapping = getMappingStrategy().createValueMapping(getFeature()); + typeMapping.createDBField(table, CDODBSchema.LIST_VALUE); + + // add table indexes + for (IDBField dbField : dbFields) + { + table.addIndex(Type.NON_UNIQUE, dbField); + } + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$ + + // ----------------- insert entry ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES (?, ?, ?, ?, ?, ?)"); //$NON-NLS-1$ + sqlInsertEntry = builder.toString(); + + // ----------------- remove current entry ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlRemoveEntry = builder.toString(); + + // ----------------- delete temporary entry ----------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=?"); //$NON-NLS-1$ + sqlDeleteEntry = builder.toString(); + + // ----------------- update index ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlGetValue = builder.toString(); + + // ----------- clear list items ------------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, final int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + int valuesToRead = list.size(); + if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead) + { + valuesToRead = listChunk; + } + + if (valuesToRead == 0) + { + // nothing to read take shortcut + return; + } + + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branchID); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows. + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + + while (valuesToRead > 0 && resultSet.next()) + { + int index = resultSet.getInt(1); + if (index > currentIndex) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, index); + if (TRACER.isEnabled()) + { + TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$ + } + + valuesToRead -= index - currentIndex; + currentIndex = index; + } + + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + valuesToRead--; + } + + if (valuesToRead > 0) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead); + if (TRACER.isEnabled()) + { + TRACER.format( + "Scheduling range {0}-{1} to be read from base revision", currentIndex, currentIndex + valuesToRead); //$NON-NLS-1$ + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (baseReader != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading base revision chunks for feature {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision()); + } + + List<Chunk> baseChunks = baseReader.executeRead(); + for (Chunk chunk : baseChunks) + { + int startIndex = chunk.getStartIndex(); + for (int i = 0; i < chunk.size(); i++) + { + if (TRACER.isEnabled()) + { + TRACER.format("Copying value {0} at chunk index {1}+{2} to index {3}", //$NON-NLS-1$ + chunk.get(i), startIndex, i, startIndex + i); + } + + list.set(startIndex + i, chunk.get(i)); + } + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading {3} list values done for feature {0}.{1} of {2}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision, list.size()); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID()); + stmt.setInt(2, chunkReader.getRevision().getBranch().getID()); + stmt.setInt(3, chunkReader.getRevision().getVersion()); + stmt.setInt(4, chunkReader.getRevision().getVersion()); + + if (TRACER.isEnabled()) + { + TRACER.format("Readung Chunks: {0}", stmt); //$NON-NLS-1$ + } + + resultSet = stmt.executeQuery(); + + int nextDBIndex = Integer.MAX_VALUE; // next available DB index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + + for (Chunk chunk : chunks) + { + int startIndex = chunk.getStartIndex(); + int missingValueStartIndex = -1; + + for (int i = 0; i < chunk.size(); i++) + { + int nextListIndex = startIndex + i; // next expected list index + + if (nextDBIndex == nextListIndex) + { + // DB value is available. check first if missing indexes were present before. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to currentIndex + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(), + chunkReader.getRevision().getBranch().getID()); + } + if (TRACER.isEnabled()) + { + TRACER.format( + "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$ + } + + baseReader.addRangedChunk(missingValueStartIndex, nextListIndex); + + // reset missingValueStartIndex + missingValueStartIndex = -1; + } + + // now read value and set to chunk + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$ + } + chunk.add(i, value); + + // advance DB cursor and read next available index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + else + { + // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE + nextDBIndex = Integer.MAX_VALUE; + } + } + else + { + // gap between next DB index and next list index detected. + // skip until end of chunk or until DB value becomes available + if (missingValueStartIndex == -1) + { + missingValueStartIndex = nextListIndex; + } + } + } + + // chunk complete. check for missing values at the end of the chunk. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to last chunk index + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(), + chunkReader.getRevision().getBranch().getID()); + } + + if (TRACER.isEnabled()) + { + TRACER + .format( + "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, chunk.getStartIndex() + chunk.size()); //$NON-NLS-1$ + } + baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size()); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + // now read missing values from base revision. + if (baseReader != null) + { + List<Chunk> baseChunks = baseReader.executeRead(); + + Iterator<Chunk> thisIterator = chunks.iterator(); + Chunk thisChunk = thisIterator.next(); + + for (Chunk baseChunk : baseChunks) + { + int baseStartIndex = baseChunk.getStartIndex(); + + while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size()) + { + // advance thisChunk, because it does not match baseChunk + thisChunk = thisIterator.next(); + } + + // baseChunk now corresponds to thisChunk, but startIndex of baseChunk may be higher. + // therefore calculate offset + int offset = baseStartIndex - thisChunk.getStartIndex(); + + // and copy values. + for (int i = 0; i < baseChunk.size(); i++) + { + if (TRACER.isEnabled()) + { + TRACER.format("Copying base chunk reader value {0} at index {1} to current chunk reader at index {2}.", + baseChunk.get(i), baseChunk.getStartIndex() + i, thisChunk.getStartIndex() + i + offset); + } + + thisChunk.add(i + offset, baseChunk.get(i)); + } // finally, continue with the next baseChunk + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), chunkReader.getRevision()); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Writing done"); //$NON-NLS-1$ + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int index, Object value) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing value for feature {0}.{1} index {2} of {3} : {4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, revision, value); + } + + addEntry(accessor, revision.getID(), revision.getBranch().getID(), revision.getVersion(), index, value); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + * @param lastIndex + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int lastIndex) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + // check for each index if the value exists in the current branch + for (int i = 0; i <= lastIndex; i++) + { + if (getValue(accessor, id, branchId, i, false) == null) + { + // if not, add a historic entry for missing ones. + addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i)); + } + } + + // clear rest of the list + stmt = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmt.setInt(1, newVersion); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 2, id); + stmt.setInt(3, branchId); + + int result = DBUtil.update(stmt, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + ITransaction transaction = accessor.getTransaction(); + InternalCDORevision revision = (InternalCDORevision)transaction.getRevision(id); + int branchID = transaction.getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, final int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + List<CDOFeatureDelta> listChanges = delta.getListChanges(); + if (listChanges.size() == 0) + { + // nothing to do. + return; + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, branchId, oldVersion, newVersion); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + // optimization: it's only necessary to process deltas + // starting with the last feature delta which clears the list + // (any operation before the clear is cascaded by it anyway) + int index = listChanges.size() - 1; + while (index > 0) + { + CDOFeatureDelta listDelta = listChanges.get(index); + if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta) + { + break; + } + index--; + } + while (index < listChanges.size()) + { + listChanges.get(index++).accept(visitor); + } + } + + /** + * @author Stefan Winkler + */ + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private CDOID id; + + private int branchID; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID, + int oldVersion, int newVersion) + { + this.accessor = accessor; + id = originalRevision.getID(); + branchID = targetBranchID; + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + Object value = getValue(accessor, id, branchID, fromIdx, true); + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1); + } + + // create the item + addEntry(accessor, id, branchID, newVersion, toIdx, value); + } + + public void visit(CDOAddFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$ + } + + if (startIndex <= endIndex) + { + // make room for the new item + moveOneDown(accessor, id, branchID, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, branchID, newVersion, startIndex, delta.getValue()); + + ++lastIndex; + } + + public void visit(CDORemoveFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, startIndex); + + // make room for the new item + moveOneUp(accessor, id, branchID, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + } + + public void visit(CDOSetFeatureDelta delta) + { + int index = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, index); + + // create the item + addEntry(accessor, id, branchID, newVersion, index, delta.getValue()); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex); + lastIndex = -1; + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex); + lastIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = startIndex; index <= endIndex; ++index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index - 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + // no entry for current revision there. + case 0: + Object value = getValue(accessor, id, branchId, index, false); + + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value); + } + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, branchId, newVersion, index - 1, value); + break; + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, + int startIndex, int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = endIndex; index >= startIndex; --index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index + 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + case 0: + Object value = getValue(accessor, id, branchId, index, false); + + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value); + } + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, branchId, newVersion, index + 1, value); + break; + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int version, int index, Object value) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Adding value for feature {0}.{1} index {2} of {3}:{4}v{5} : {6}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, version, value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, version); // versionAdded + stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved + stmt.setInt(column++, index); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded, + int versionRemoved, int index, Object value) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format( + "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved, + value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, versionAdded); // versionAdded + stmt.setInt(column++, versionRemoved); // versionRemoved + stmt.setInt(column++, index); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature {0}.{1} index {2} of {3}:{4}v{5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, newVersion); + } + + try + { + // try to delete a temporary entry first + stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + statementCache.releasePreparedStatement(stmt); + stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, index); + + result = DBUtil.update(stmt, false); + + if (result == 0) + { + // no entry removed -> this means that we are in a branch and + // the entry has not been modified since the branch fork. + // therefore, we have to copy the base value and mark it as removed + Object value = getValueFromBase(accessor, id, branchId, index); + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value); + } + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature {0}.{1} index {2} of {3}:{4}v{5} FAILED {6}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature {0}.{1} index {2} of {3}:{4}v{5} FAILED {6}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Read a single value from the current revision's list. + * + * @param accessor + * the store accessor + * @param id + * the revision's ID + * @param branchId + * the revision's branch ID + * @param index + * the index from which to get the value + * @param getFromBase + * if <code>true</code>, the value is recursively loaded from the base revision of a branch, if it is not + * present in the current branch (because it has not been changed since the branch fork). If + * <code>false</code>, <code>null</code> is returned in the former case. + */ + private Object getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + Object result = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH); + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (resultSet.next()) + { + result = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + else + { + // value is not in this branch. + // -> read from base revision + if (getFromBase) + { + result = getValueFromBase(accessor, id, branchId, index); + } // else: result remains null + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + /** + * Read a single value (at a given index) from the base revision + * + * @param accessor + * the DBStoreAccessor + * @param id + * the ID of the revision + * @param branchID + * the ID of the current (child) branch + * @param index + * the index to read the value from + * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at + * the base of this branch (indicated by <code>branchID</code>). + */ + private Object getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index) + { + IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID); + chunkReader.addSimpleChunk(index); + List<Chunk> chunks = chunkReader.executeRead(); + return chunks.get(0).get(0); + } + + private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID) + { + IRepository repository = accessor.getStore().getRepository(); + CDOBranchPoint base = repository.getBranchManager().getBranch(branchID).getBase(); + InternalCDORevision baseRevision = (InternalCDORevision)repository.getRevisionManager().getRevision(id, base, /* + * referenceChunk + * = + */0, /* + * prefetchDepth + * = + */ + CDORevision.DEPTH_NONE, true); + IStoreChunkReader chunkReader = accessor.createChunkReader(baseRevision, getFeature()); + return chunkReader; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + + String tableName = getTable().getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" AS l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" AS a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t." + mainTableWhere);//$NON-NLS-1$ + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID sourceID = idHandler.getCDOID(resultSet, 1); + CDOID targetID = idHandler.getCDOID(resultSet, 2); + int idx = resultSet.getInt(3); + + boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, targetID, idx); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java new file mode 100644 index 0000000000..3a1400d18c --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.internal.db.IObjectTypeMapper; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; + +import java.io.IOException; +import java.sql.Connection; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public abstract class DelegatingObjectTypeMapper extends AbstractObjectTypeMapper +{ + private IObjectTypeMapper delegate; + + public DelegatingObjectTypeMapper() + { + } + + public IObjectTypeMapper getDelegate() + { + return delegate; + } + + public void setDelegate(IObjectTypeMapper delegate) + { + this.delegate = delegate; + } + + public CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id) + { + CDOID type = doGetObjectType(accessor, id); + if (type != null) + { + EClass eClass = (EClass)getMetaDataManager().getMetaInstance(type); + return new CDOClassifierRef(eClass); + } + + return delegate.getObjectType(accessor, id); + } + + public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type) + { + CDOID classID = getMetaDataManager().getMetaID(type, timeStamp); + doPutObjectType(accessor, id, classID); + + delegate.putObjectType(accessor, timeStamp, id, type); + } + + public void removeObjectType(IDBStoreAccessor accessor, CDOID id) + { + doRemoveObjectType(accessor, id); + delegate.removeObjectType(accessor, id); + } + + public CDOID getMaxID(Connection connection, IIDHandler idHandler) + { + CDOID maxID = doGetMaxID(connection, idHandler); + if (maxID != null) + { + return maxID; + } + + return delegate.getMaxID(connection, idHandler); + } + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) + throws IOException + { + delegate.rawExport(connection, out, fromCommitTime, toCommitTime); + } + + public void rawImport(Connection connection, CDODataInput in, OMMonitor monitor) throws IOException + { + delegate.rawImport(connection, in, monitor); + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + checkState(delegate, "delegate"); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + LifecycleUtil.activate(delegate); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(delegate); + super.doDeactivate(); + } + + protected abstract CDOID doGetObjectType(IDBStoreAccessor accessor, CDOID id); + + protected abstract void doPutObjectType(IDBStoreAccessor accessor, CDOID id, CDOID type); + + protected abstract void doRemoveObjectType(IDBStoreAccessor accessor, CDOID id); + + protected abstract CDOID doGetMaxID(Connection connection, IIDHandler idHandler); +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java new file mode 100644 index 0000000000..d1aee6eb42 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java @@ -0,0 +1,742 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Stefan Winkler - Bug 249610: [DB] Support external references (Implementation) + * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingAuditSupport, + IClassMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalAuditClassMapping.class); + + private String sqlInsertAttributes; + + private String sqlSelectCurrentAttributes; + + private String sqlSelectAllObjectIDs; + + private String sqlSelectAttributesByTime; + + private String sqlSelectAttributesByVersion; + + private String sqlReviseAttributes; + + private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>() + { + @Override + protected FeatureDeltaWriter initialValue() + { + return new FeatureDeltaWriter(); + } + }; + + public HorizontalAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + + initSQLStrings(); + } + + private void initSQLStrings() + { + Map<EStructuralFeature, String> unsettableFields = getUnsettableFields(); + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND ("); //$NON-NLS-1$ + + String sqlSelectAttributesPrefix = builder.toString(); + + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + + sqlSelectCurrentAttributes = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + + sqlSelectAttributesByTime = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + + builder.append("ABS("); + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(")=?)"); //$NON-NLS-1$ + + sqlSelectAttributesByVersion = builder.toString(); + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + + for (int i = 0; i < getValueMappings().size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + if (unsettableFields != null) + { + for (int i = 0; i < unsettableFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + if (listSizeFields != null) + { + for (int i = 0; i < listSizeFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsertAttributes = builder.toString(); + + // ----------- Update to set revised ---------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlReviseAttributes = builder.toString(); + + // ----------- Select all unrevised Object IDs ------ + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlSelectAllObjectIDs = builder.toString(); + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + long timeStamp = revision.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + stmt = statementCache.getPreparedStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setLong(2, timeStamp); + stmt.setLong(3, timeStamp); + } + else + { + stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + } + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, + boolean exactMatch, CDOBranchPoint branchPoint) + { + EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name(); + long timeStamp = branchPoint.getTimeStamp(); + + ITypeMapping nameValueMapping = getValueMapping(nameFeature); + if (nameValueMapping == null) + { + throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$ + } + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(nameValueMapping.getField()); + if (name == null) + { + builder.append(" IS NULL"); //$NON-NLS-1$ + } + else + { + builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$ + } + + builder.append(" AND ("); //$NON-NLS-1$ + + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, column++, folderId); + + if (name != null) + { + String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$ + nameValueMapping.setValue(stmt, column++, queryName); + } + + if (timeStamp != CDORevision.UNSPECIFIED_DATE) + { + stmt.setLong(column++, timeStamp); + stmt.setLong(column++, timeStamp); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + return stmt; + } + catch (SQLException ex) + { + statementCache.releasePreparedStatement(stmt); // only release on error + throw new DBException(ex); + } + } + + public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + @Override + protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, column++, revision.getID()); + stmt.setInt(column++, revision.getVersion()); + stmt.setLong(column++, revision.getTimeStamp()); + stmt.setLong(column++, revision.getRevised()); + idHandler.setCDOID(stmt, column++, revision.getResourceID()); + idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID()); + stmt.setInt(column++, revision.getContainingFeatureID()); + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (revision.getValue(feature) == null) + { + stmt.setBoolean(isSetCol++, false); + + // also set value column to default value + mapping.setDefaultValue(stmt, column++); + + continue; + } + + stmt.setBoolean(isSetCol++, true); + } + + mapping.setValueFromRevision(stmt, column++, revision); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getList(feature); + stmt.setInt(column++, list.size()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + int column = 1; + + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, -version); // cdo_version + stmt.setLong(column++, timeStamp); // cdo_created + stmt.setLong(column++, CDOBranchPoint.UNSPECIFIED_DATE); // cdo_revised + idHandler.setCDOID(stmt, column++, CDOID.NULL); // resource + idHandler.setCDOID(stmt, column++, CDOID.NULL); // container + stmt.setInt(column++, 0); // containing feature ID + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + stmt.setBoolean(isSetCol++, false); + } + + mapping.setDefaultValue(stmt, column++); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // list size columns begin after isSet-columns + column = isSetCol; + + for (int i = 0; i < listSizeFields.size(); i++) + { + stmt.setInt(column++, 0); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlReviseAttributes, ReuseProbability.HIGH); + + stmt.setLong(1, revised); + idHandler.setCDOID(stmt, 2, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, + OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + FeatureDeltaWriter writer = deltaWriter.get(); + writer.process(accessor, delta, created); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + @Override + protected String getListXRefsWhere(QueryXRefsContext context) + { + if (CDOBranch.MAIN_BRANCH_ID != context.getBranch().getID()) + { + throw new IllegalArgumentException("Non-audit mode does not support branch specification"); + } + + StringBuilder builder = new StringBuilder(); + long timeStamp = context.getTimeStamp(); + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<="); + builder.append(timeStamp); + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">="); + builder.append(timeStamp); + builder.append(")"); //$NON-NLS-1$ + } + + return builder.toString(); + } + + /** + * @author Stefan Winkler + */ + private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private long created; + + private CDOID id; + + private int oldVersion; + + private InternalCDORevision newRevision; + + private int branchId; + + public void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created) + { + this.accessor = accessor; + this.created = created; + id = delta.getID(); + branchId = delta.getBranch().getID(); + oldVersion = delta.getVersion(); + + if (TRACER.isEnabled()) + { + TRACER.format("FeatureDeltaWriter: old version: {0}, new version: {1}", oldVersion, oldVersion + 1); //$NON-NLS-1$ + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository() + .getRevisionManager().getRevisionByVersion(id, delta, 0, true); + + newRevision = originalRevision.copy(); + + newRevision.setVersion(oldVersion + 1); + newRevision.setBranchPoint(delta.getBranch().getPoint(created)); + + // process revision delta tree + delta.accept(this); + + long revised = newRevision.getTimeStamp() - 1; + reviseOldRevision(accessor, id, delta.getBranch(), revised); + + writeValues(accessor, newRevision); + } + + public void visit(CDOMoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOAddFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDORemoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOSetFeatureDelta delta) + { + delta.apply(newRevision); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + delta.apply(newRevision); + } + + public void visit(CDOListFeatureDelta delta) + { + delta.apply(newRevision); + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature()); + listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta); + } + + public void visit(CDOClearFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOContainerFeatureDelta delta) + { + delta.apply(newRevision); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java new file mode 100644 index 0000000000..c9eff1a73d --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalAuditMappingStrategy extends AbstractHorizontalMappingStrategy +{ + public HorizontalAuditMappingStrategy() + { + } + + public boolean hasAuditSupport() + { + return true; + } + + public boolean hasBranchingSupport() + { + return false; + } + + public boolean hasDeltaSupport() + { + return false; + } + + @Override + public IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalAuditClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditListTableMapping(this, containingClass, feature); + } + + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditFeatureMapTableMapping(this, containingClass, feature); + } + + @Override + public String getListJoin(String attrTable, String listTable) + { + String join = super.getListJoin(attrTable, listTable); + join += " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION; + join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION; + return join; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java new file mode 100644 index 0000000000..93de7d84bb --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalAuditMappingStrategyWithRanges extends AbstractHorizontalMappingStrategy +{ + public HorizontalAuditMappingStrategyWithRanges() + { + } + + public boolean hasAuditSupport() + { + return true; + } + + public boolean hasBranchingSupport() + { + return false; + } + + public boolean hasDeltaSupport() + { + return true; + } + + @Override + public IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalAuditClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditListTableMappingWithRanges(this, containingClass, feature); + } + + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new AuditFeatureMapTableMappingWithRanges(this, containingClass, feature); + } + + @Override + public String getListJoin(String attrTable, String listTable) + { + String join = super.getListJoin(attrTable, listTable); + join += " AND " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_ADDED; + join += "<=" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION; + join += " AND (" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED; + join += " IS NULL OR " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED; + join += ">" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION + ")"; + return join; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java new file mode 100644 index 0000000000..6f337b26e4 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java @@ -0,0 +1,1124 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + * Stefan Winkler - derived branch mapping from audit mapping + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + * @author Stefan Winkler + * @since 3.0 + */ +public class HorizontalBranchingClassMapping extends AbstractHorizontalClassMapping implements + IClassMappingAuditSupport, IClassMappingDeltaSupport +{ + /** + * @author Stefan Winkler + */ + private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private long created; + + private CDOID id; + + private CDOBranch targetBranch; + + private int oldVersion; + + private int newVersion; + + private InternalCDORevision newRevision; + + public void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created) + { + this.accessor = accessor; + this.created = created; + id = delta.getID(); + oldVersion = delta.getVersion(); + + if (TRACER.isEnabled()) + { + TRACER.format("FeatureDeltaWriter: old version: {0}, new version: {1}", oldVersion, oldVersion + 1); //$NON-NLS-1$ + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + newRevision = originalRevision.copy(); + targetBranch = accessor.getTransaction().getBranch(); + newRevision.adjustForCommit(targetBranch, created); + + newVersion = newRevision.getVersion(); + + // process revision delta tree + delta.accept(this); + + if (newVersion != CDORevision.FIRST_VERSION) + { + reviseOldRevision(accessor, id, delta.getBranch(), newRevision.getTimeStamp() - 1); + } + + writeValues(accessor, newRevision); + } + + public void visit(CDOMoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOAddFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDORemoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOSetFeatureDelta delta) + { + delta.apply(newRevision); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + delta.apply(newRevision); + } + + public void visit(CDOListFeatureDelta delta) + { + delta.apply(newRevision); + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature()); + listMapping.processDelta(accessor, id, targetBranch.getID(), oldVersion, newVersion, created, delta); + } + + public void visit(CDOClearFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOContainerFeatureDelta delta) + { + delta.apply(newRevision); + } + } + + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalBranchingClassMapping.class); + + private String sqlInsertAttributes; + + private String sqlSelectCurrentAttributes; + + private String sqlSelectAllObjectIDs; + + private String sqlSelectAttributesByTime; + + private String sqlSelectAttributesByVersion; + + private String sqlReviseAttributes; + + private String sqlSelectForHandle; + + private String sqlSelectForChangeSet; + + private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>() + { + @Override + protected FeatureDeltaWriter initialValue() + { + return new FeatureDeltaWriter(); + } + }; + + public HorizontalBranchingClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + + initSQLStrings(); + } + + @Override + protected IDBField addBranchingField(IDBTable table) + { + return table.addField(CDODBSchema.ATTRIBUTES_BRANCH, DBType.INTEGER, true); + } + + private void initSQLStrings() + { + Map<EStructuralFeature, String> unsettableFields = getUnsettableFields(); + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("=? AND ("); //$NON-NLS-1$ + String sqlSelectAttributesPrefix = builder.toString(); + + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + + sqlSelectCurrentAttributes = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + + sqlSelectAttributesByTime = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + + builder.append("ABS("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(")=?)"); //$NON-NLS-1$ + + sqlSelectAttributesByVersion = builder.toString(); + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + + for (int i = 0; i < getValueMappings().size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + if (unsettableFields != null) + { + for (int i = 0; i < unsettableFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + if (listSizeFields != null) + { + for (int i = 0; i < listSizeFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsertAttributes = builder.toString(); + + // ----------- Update to set revised ---------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlReviseAttributes = builder.toString(); + + // ----------- Select all unrevised Object IDs ------ + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlSelectAllObjectIDs = builder.toString(); + + // ----------- Select all revisions (for handleRevision) --- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + sqlSelectForHandle = builder.toString(); + + // ----------- Select all revisions (for handleRevision) --- + builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + sqlSelectForChangeSet = builder.toString(); + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + long timeStamp = revision.getTimeStamp(); + int branchID = revision.getBranch().getID(); + + try + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + stmt = statementCache.getPreparedStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, branchID); + stmt.setLong(3, timeStamp); + stmt.setLong(4, timeStamp); + } + else + { + stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, branchID); + } + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, + boolean exactMatch, CDOBranchPoint branchPoint) + { + EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name(); + + ITypeMapping nameValueMapping = getValueMapping(nameFeature); + if (nameValueMapping == null) + { + throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$ + } + + int branchID = branchPoint.getBranch().getID(); + long timeStamp = branchPoint.getTimeStamp(); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(nameValueMapping.getField()); + if (name == null) + { + builder.append(" IS NULL"); //$NON-NLS-1$ + } + else + { + builder.append(exactMatch ? " =?" : " LIKE ?"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + builder.append(" AND ("); //$NON-NLS-1$ + + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM); + stmt.setInt(column++, branchID); + idHandler.setCDOID(stmt, column++, folderId); + + if (name != null) + { + String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$ + nameValueMapping.setValue(stmt, column++, queryName); + } + + if (timeStamp != CDORevision.UNSPECIFIED_DATE) + { + stmt.setLong(column++, timeStamp); + stmt.setLong(column++, timeStamp); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + return stmt; + } + catch (SQLException ex) + { + statementCache.releasePreparedStatement(stmt); // only release on error + throw new DBException(ex); + } + } + + public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + @Override + protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, column++, revision.getID()); + stmt.setInt(column++, revision.getVersion()); + stmt.setInt(column++, revision.getBranch().getID()); + stmt.setLong(column++, revision.getTimeStamp()); + stmt.setLong(column++, revision.getRevised()); + idHandler.setCDOID(stmt, column++, revision.getResourceID()); + idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID()); + stmt.setInt(column++, revision.getContainingFeatureID()); + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (revision.getValue(feature) == null) + { + stmt.setBoolean(isSetCol++, false); + + // also set value column to default value + mapping.setDefaultValue(stmt, column++); + continue; + } + + stmt.setBoolean(isSetCol++, true); + } + + mapping.setValueFromRevision(stmt, column++, revision); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getList(feature); + stmt.setInt(column++, list.size()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, -version); // cdo_version + stmt.setInt(column++, branch.getID()); + stmt.setLong(column++, timeStamp); // cdo_created + stmt.setLong(column++, CDOBranchPoint.UNSPECIFIED_DATE); // cdo_revised + idHandler.setCDOID(stmt, column++, CDOID.NULL); // resource + idHandler.setCDOID(stmt, column++, CDOID.NULL); // container + stmt.setInt(column++, 0); // containing feature ID + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + stmt.setBoolean(isSetCol++, false); + } + + mapping.setDefaultValue(stmt, column++); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // list size columns begin after isSet-columns + column = isSetCol; + + for (int i = 0; i < listSizeFields.size(); i++) + { + stmt.setInt(column++, 0); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlReviseAttributes, ReuseProbability.HIGH); + + stmt.setLong(1, revised); + idHandler.setCDOID(stmt, 2, id); + stmt.setInt(3, branch.getID()); + + DBUtil.update(stmt, false); // No row affected if old revision from other branch! + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise, + OMMonitor monitor) + { + Async async = null; + monitor.begin(10); + + try + { + try + { + async = monitor.forkAsync(); + CDOID id = revision.getID(); + if (mapType) + { + // put new objects into objectTypeMapper + long timeStamp = revision.getTimeStamp(); + AbstractHorizontalMappingStrategy mappingStrategy = (AbstractHorizontalMappingStrategy)getMappingStrategy(); + mappingStrategy.putObjectType(accessor, timeStamp, id, getEClass()); + } + else if (revise && revision.getVersion() > CDOBranchVersion.FIRST_VERSION) + { + // if revision is not the first one, revise the old revision + long revised = revision.getTimeStamp() - 1; + reviseOldRevision(accessor, id, revision.getBranch(), revised); + for (IListMapping mapping : getListMappings()) + { + mapping.objectDetached(accessor, id, revised); + } + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + async = monitor.forkAsync(); + if (revision.isResourceFolder() || revision.isResource()) + { + checkDuplicateResources(accessor, revision); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + // Write attribute table always (even without modeled attributes!) + async = monitor.forkAsync(); + writeValues(accessor, revision); + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + // Write list tables only if they exist + async = monitor.forkAsync(7); + if (getListMappings() != null) + { + writeLists(accessor, revision); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + @Override + public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + StringBuilder builder = new StringBuilder(sqlSelectForHandle); + boolean whereAppend = false; + + if (branch != null) + { + // TODO: Prepare this string literal + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("=?"); //$NON-NLS-1$ + + whereAppend = true; + } + + int timeParameters = 0; + if (timeStamp != CDOBranchPoint.INVALID_DATE) + { + if (exactTime) + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(whereAppend ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("=?"); //$NON-NLS-1$ + timeParameters = 1; + } + } + else + { + builder.append(whereAppend ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?)"); //$NON-NLS-1$ + timeParameters = 2; + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + } + } + } + + IRepository repository = accessor.getStore().getRepository(); + CDORevisionManager revisionManager = repository.getRevisionManager(); + CDOBranchManager branchManager = repository.getBranchManager(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW); + + int column = 1; + if (branch != null) + { + stmt.setInt(column++, branch.getID()); + } + + for (int i = 0; i < timeParameters; i++) + { + stmt.setLong(column++, timeStamp); + } + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + int version = resultSet.getInt(2); + + if (version >= CDOBranchVersion.FIRST_VERSION) + { + int branchID = resultSet.getInt(3); + CDOBranchVersion branchVersion = branchManager.getBranch(branchID).getVersion(Math.abs(version)); + InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchVersion, + CDORevision.UNCHUNKED, true); + + if (!handler.handleRevision(revision)) + { + break; + } + } + else + { + // Tell handler about detached IDs + InternalCDORevision revision = new DetachedCDORevision(null, id, null, version, 0); + handler.handleRevision(revision); + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments) + { + StringBuilder builder = new StringBuilder(sqlSelectForChangeSet); + boolean isFirst = true; + + for (int i = 0; i < segments.length; i++) + { + if (isFirst) + { + isFirst = false; + } + else + { + builder.append(" OR "); //$NON-NLS-1$ + } + + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + builder.append(")"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + Set<CDOID> result = new HashSet<CDOID>(); + + try + { + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW); + int column = 1; + for (CDOChangeSetSegment segment : segments) + { + stmt.setInt(column++, segment.getBranch().getID()); + stmt.setLong(column++, segment.getTimeStamp()); + stmt.setLong(column++, segment.getEndTime()); + } + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + result.add(id); + } + + return result; + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected String getListXRefsWhere(QueryXRefsContext context) + { + StringBuilder builder = new StringBuilder(); + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("="); + builder.append(context.getBranch().getID()); + builder.append(" AND ("); + + long timeStamp = context.getTimeStamp(); + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<="); + builder.append(timeStamp); + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">="); + builder.append(timeStamp); + builder.append("))"); //$NON-NLS-1$ + } + + return builder.toString(); + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, + OMMonitor monitor) + { + monitor.begin(); + + try + { + if (accessor.getTransaction().getBranch() != delta.getBranch()) + { + // new branch -> decide, if branch should be copied + if (((HorizontalBranchingMappingStrategyWithRanges)getMappingStrategy()).shallCopyOnBranch()) + { + doCopyOnBranch(accessor, delta, created, monitor.fork()); + return; + } + } + + Async async = null; + + try + { + async = monitor.forkAsync(); + FeatureDeltaWriter writer = deltaWriter.get(); + writer.process(accessor, delta, created); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + private void doCopyOnBranch(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, OMMonitor monitor) + { + monitor.begin(2); + try + { + InternalRepository repository = (InternalRepository)accessor.getStore().getRepository(); + + InternalCDORevision oldRevision = (InternalCDORevision)accessor.getTransaction().getRevision(delta.getID()); + if (oldRevision == null) + { + throw new IllegalStateException("Origin revision not found for " + delta); + } + + // Make sure all chunks are loaded + for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(oldRevision.getEClass())) + { + if (feature.isMany()) + { + repository.ensureChunk(oldRevision, feature, 0, oldRevision.getList(feature).size()); + } + } + + InternalCDORevision newRevision = oldRevision.copy(); + newRevision.adjustForCommit(accessor.getTransaction().getBranch(), created); + delta.apply(newRevision); + monitor.worked(); + writeRevision(accessor, newRevision, false, true, monitor.fork()); + } + finally + { + monitor.done(); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java new file mode 100644 index 0000000000..473883b53e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalBranchingMappingStrategy extends AbstractHorizontalMappingStrategy +{ + public HorizontalBranchingMappingStrategy() + { + } + + public boolean hasAuditSupport() + { + return true; + } + + public boolean hasBranchingSupport() + { + return true; + } + + public boolean hasDeltaSupport() + { + return false; + } + + @Override + public IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalBranchingClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingListTableMapping(this, containingClass, feature); + } + + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingFeatureMapTableMapping(this, containingClass, feature); + } + + @Override + protected void rawImportReviseOldRevisions(Connection connection, IDBTable table, OMMonitor monitor) + { + String sqlUpdate = "UPDATE " + table + " SET " + CDODBSchema.ATTRIBUTES_REVISED + "=? WHERE " + + CDODBSchema.ATTRIBUTES_ID + "=? AND " + CDODBSchema.ATTRIBUTES_BRANCH + "=? AND " + + CDODBSchema.ATTRIBUTES_VERSION + "=?"; + + String sqlQuery = "SELECT cdo1." + CDODBSchema.ATTRIBUTES_ID + ", cdo1." + CDODBSchema.ATTRIBUTES_BRANCH + + ", cdo1." + CDODBSchema.ATTRIBUTES_VERSION + ", cdo2." + CDODBSchema.ATTRIBUTES_CREATED + " FROM " + table + + " cdo1, " + table + " cdo2 WHERE cdo1." + CDODBSchema.ATTRIBUTES_ID + "=cdo2." + CDODBSchema.ATTRIBUTES_ID + + " AND cdo1." + CDODBSchema.ATTRIBUTES_BRANCH + "=cdo2." + CDODBSchema.ATTRIBUTES_BRANCH + " AND (cdo1." + + CDODBSchema.ATTRIBUTES_VERSION + "=cdo2." + CDODBSchema.ATTRIBUTES_VERSION + "-1 OR (cdo1." + + CDODBSchema.ATTRIBUTES_VERSION + "+cdo2." + CDODBSchema.ATTRIBUTES_VERSION + "=-1 AND cdo1." + + CDODBSchema.ATTRIBUTES_VERSION + ">cdo2." + CDODBSchema.ATTRIBUTES_VERSION + ")) AND cdo1." + + CDODBSchema.ATTRIBUTES_REVISED + "=0"; + + IIDHandler idHandler = getStore().getIDHandler(); + PreparedStatement stmtUpdate = null; + PreparedStatement stmtQuery = null; + ResultSet resultSet = null; + + try + { + stmtUpdate = connection.prepareStatement(sqlUpdate); + stmtQuery = connection.prepareStatement(sqlQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + + resultSet = stmtQuery.executeQuery(); + int size = DBUtil.getRowCount(resultSet); + if (size == 0) + { + return; + } + + monitor.begin(2 * size); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + int branch = resultSet.getInt(2); + int version = resultSet.getInt(3); + long revised = resultSet.getLong(4) - 1L; + + stmtUpdate.setLong(1, revised); + idHandler.setCDOID(stmtUpdate, 2, id); + stmtUpdate.setInt(3, branch); + stmtUpdate.setInt(4, version); + stmtUpdate.addBatch(); + monitor.worked(); + } + + Async async = monitor.forkAsync(size); + try + { + stmtUpdate.executeBatch(); + } + finally + { + async.stop(); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmtQuery); + DBUtil.close(stmtUpdate); + monitor.done(); + } + } + + @Override + protected void rawImportUnreviseNewRevisions(Connection connection, IDBTable table, long fromCommitTime, + long toCommitTime, OMMonitor monitor) + { + String sqlUpdate = "UPDATE " + table + " SET " + CDODBSchema.ATTRIBUTES_REVISED + "=0 WHERE " + + CDODBSchema.ATTRIBUTES_BRANCH + ">=0 AND " + CDODBSchema.ATTRIBUTES_CREATED + "<=" + toCommitTime + " AND " + + CDODBSchema.ATTRIBUTES_REVISED + ">" + toCommitTime + " AND " + CDODBSchema.ATTRIBUTES_VERSION + ">0"; + + PreparedStatement stmtUpdate = null; + + try + { + stmtUpdate = connection.prepareStatement(sqlUpdate); + + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + stmtUpdate.executeUpdate(); + } + finally + { + async.stop(); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmtUpdate); + monitor.done(); + } + } + + @Override + public String getListJoin(String attrTable, String listTable) + { + String join = super.getListJoin(attrTable, listTable); + join += " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION; + join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION; + join += " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_BRANCH; + join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_BRANCH; + return join; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java new file mode 100644 index 0000000000..16508c6e0b --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.server.db.CDODBUtil; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalBranchingMappingStrategyWithRanges extends AbstractHorizontalMappingStrategy +{ + private boolean copyOnBranch; + + public HorizontalBranchingMappingStrategyWithRanges() + { + } + + public boolean hasAuditSupport() + { + return true; + } + + public boolean hasBranchingSupport() + { + return true; + } + + public boolean hasDeltaSupport() + { + return true; + } + + public boolean shallCopyOnBranch() + { + return copyOnBranch; + } + + @Override + public IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalBranchingClassMapping(this, eClass); + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingListTableMappingWithRanges(this, containingClass, feature); + } + + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new BranchingFeatureMapTableMappingWithRanges(this, containingClass, feature); + } + + @Override + public String getListJoin(String attrTable, String listTable) + { + String join = super.getListJoin(attrTable, listTable); + join += " AND " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_ADDED; + join += "<=" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION; + join += " AND (" + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED; + join += " IS NULL OR " + listTable + "." + CDODBSchema.LIST_REVISION_VERSION_REMOVED; + join += ">" + attrTable + "." + CDODBSchema.ATTRIBUTES_VERSION; + join += ") AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_BRANCH; + join += "=" + listTable + "." + CDODBSchema.LIST_REVISION_BRANCH; + return join; + } + + @Override + protected void doAfterActivate() throws Exception + { + super.doAfterActivate(); + + String value = getProperties().get(CDODBUtil.PROP_COPY_ON_BRANCH); + copyOnBranch = value == null ? false : Boolean.valueOf(value); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java new file mode 100644 index 0000000000..836014bb38 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java @@ -0,0 +1,272 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IRepository.Props;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.CDODBUtil;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class HorizontalMappingStrategy extends Lifecycle implements IMappingStrategy
+{
+ private Map<String, String> properties;
+
+ private IDBStore store;
+
+ private IMappingStrategy delegate;
+
+ public HorizontalMappingStrategy()
+ {
+ }
+
+ public IMappingStrategy getDelegate()
+ {
+ return delegate;
+ }
+
+ public Map<String, String> getProperties()
+ {
+ if (delegate != null)
+ {
+ return delegate.getProperties();
+ }
+
+ if (properties != null)
+ {
+ return properties;
+ }
+
+ return new HashMap<String, String>();
+ }
+
+ public void setProperties(Map<String, String> properties)
+ {
+ if (delegate != null)
+ {
+ delegate.setProperties(properties);
+ }
+ else
+ {
+ this.properties = properties;
+ }
+ }
+
+ public IDBStore getStore()
+ {
+ if (delegate != null)
+ {
+ return delegate.getStore();
+ }
+
+ return store;
+ }
+
+ public void setStore(IDBStore store)
+ {
+ if (delegate != null)
+ {
+ delegate.setStore(store);
+ }
+ else
+ {
+ this.store = store;
+ }
+ }
+
+ public ITypeMapping createValueMapping(EStructuralFeature feature)
+ {
+ return delegate.createValueMapping(feature);
+ }
+
+ public IListMapping createListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ return delegate.createListMapping(containingClass, feature);
+ }
+
+ public String getTableName(ENamedElement element)
+ {
+ return delegate.getTableName(element);
+ }
+
+ public String getTableName(EClass containingClass, EStructuralFeature feature)
+ {
+ return delegate.getTableName(containingClass, feature);
+ }
+
+ public String getFieldName(EStructuralFeature feature)
+ {
+ return delegate.getFieldName(feature);
+ }
+
+ public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ delegate.createMapping(connection, packageUnits, monitor);
+ }
+
+ public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits)
+ {
+ delegate.removeMapping(connection, packageUnits);
+ }
+
+ public IClassMapping getClassMapping(EClass eClass)
+ {
+ return delegate.getClassMapping(eClass);
+ }
+
+ public Map<EClass, IClassMapping> getClassMappings()
+ {
+ return delegate.getClassMappings();
+ }
+
+ public Map<EClass, IClassMapping> getClassMappings(boolean createOnDemand)
+ {
+ return delegate.getClassMappings(createOnDemand);
+ }
+
+ public boolean hasDeltaSupport()
+ {
+ return delegate.hasDeltaSupport();
+ }
+
+ public boolean hasAuditSupport()
+ {
+ return delegate.hasAuditSupport();
+ }
+
+ public boolean hasBranchingSupport()
+ {
+ return delegate.hasBranchingSupport();
+ }
+
+ public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context)
+ {
+ delegate.queryResources(accessor, context);
+ }
+
+ public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context)
+ {
+ delegate.queryXRefs(accessor, context);
+ }
+
+ public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ return delegate.readObjectType(accessor, id);
+ }
+
+ public CloseableIterator<CDOID> readObjectIDs(IDBStoreAccessor accessor)
+ {
+ return delegate.readObjectIDs(accessor);
+ }
+
+ public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection)
+ {
+ delegate.repairAfterCrash(dbAdapter, connection);
+ }
+
+ public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler)
+ {
+ delegate.handleRevisions(accessor, eClass, branch, timeStamp, exactTime, handler);
+ }
+
+ public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments)
+ {
+ return delegate.readChangeSet(accessor, monitor, segments);
+ }
+
+ public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int lastReplicatedBranchID, int lastBranchID,
+ long lastReplicatedCommitTime, long lastCommitTime) throws IOException
+ {
+ delegate.rawExport(accessor, out, lastReplicatedBranchID, lastBranchID, lastReplicatedCommitTime, lastCommitTime);
+ }
+
+ public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ delegate.rawImport(accessor, in, fromCommitTime, toCommitTime, monitor);
+ }
+
+ public String getListJoin(String attrTable, String listTable)
+ {
+ return delegate.getListJoin(attrTable, listTable);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ boolean auditing = getBooleanProperty(Props.SUPPORTING_AUDITS);
+ boolean branching = getBooleanProperty(Props.SUPPORTING_BRANCHES);
+
+ boolean withRanges = false;
+ if (auditing || branching)
+ {
+ withRanges = getBooleanProperty(CDODBUtil.PROP_WITH_RANGES);
+ }
+
+ delegate = CDODBUtil.createHorizontalMappingStrategy(auditing, branching, withRanges);
+ delegate.setStore(store);
+ delegate.setProperties(properties);
+ LifecycleUtil.activate(delegate);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(delegate);
+ super.doDeactivate();
+ }
+
+ private boolean getBooleanProperty(String prop)
+ {
+ String valueAudits = properties.get(prop);
+ if (valueAudits != null)
+ {
+ return Boolean.valueOf(valueAudits);
+ }
+
+ return false;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java new file mode 100644 index 0000000000..d37b7144f0 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java @@ -0,0 +1,764 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalNonAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalNonAuditClassMapping.class); + + private String sqlSelectAllObjectIDs; + + private String sqlSelectCurrentAttributes; + + private String sqlInsertAttributes; + + private String sqlUpdateAffix; + + private String sqlUpdatePrefix; + + private String sqlUpdateContainerPart; + + private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>() + { + @Override + protected FeatureDeltaWriter initialValue() + { + return new FeatureDeltaWriter(); + } + }; + + public HorizontalNonAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + + initSQLStrings(); + } + + private void initSQLStrings() + { + Map<EStructuralFeature, String> unsettableFields = getUnsettableFields(); + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=?"); //$NON-NLS-1$ + + sqlSelectCurrentAttributes = builder.toString(); + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + for (int i = 0; i < getValueMappings().size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + if (unsettableFields != null) + { + for (int i = 0; i < unsettableFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + if (listSizeFields != null) + { + for (int i = 0; i < listSizeFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsertAttributes = builder.toString(); + + // ----------- Select all unrevised Object IDs ------ + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + sqlSelectAllObjectIDs = builder.toString(); + + // ----------- Update attributes -------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdatePrefix = builder.toString(); + + builder = new StringBuilder(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateContainerPart = builder.toString(); + + builder = new StringBuilder(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateAffix = builder.toString(); + } + + @Override + protected void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, column++, revision.getID()); + stmt.setInt(column++, revision.getVersion()); + stmt.setLong(column++, revision.getTimeStamp()); + stmt.setLong(column++, revision.getRevised()); + idHandler.setCDOID(stmt, column++, revision.getResourceID()); + idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID()); + stmt.setInt(column++, revision.getContainingFeatureID()); + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (revision.getValue(feature) == null) + { + stmt.setBoolean(isSetCol++, false); + + // also set value column to default value + mapping.setDefaultValue(stmt, column++); + continue; + } + + stmt.setBoolean(isSetCol++, true); + } + + mapping.setValueFromRevision(stmt, column++, revision); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getList(feature); + stmt.setInt(column++, list.size()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, + boolean exactMatch, CDOBranchPoint branchPoint) + { + long timeStamp = branchPoint.getTimeStamp(); + if (timeStamp != CDORevision.UNSPECIFIED_DATE) + { + throw new IllegalArgumentException("Non-audit store does not support explicit timeStamp in resource query"); //$NON-NLS-1$ + } + + EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name(); + + ITypeMapping nameValueMapping = getValueMapping(nameFeature); + if (nameValueMapping == null) + { + throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$ + } + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(nameValueMapping.getField()); + if (name == null) + { + builder.append(" IS NULL"); //$NON-NLS-1$ + } + else + { + builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, column++, folderId); + + if (name != null) + { + String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$ + nameValueMapping.setValue(stmt, column++, queryName); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + return stmt; + } + catch (SQLException ex) + { + statementCache.releasePreparedStatement(stmt); // only release on error + throw new DBException(ex); + } + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + long timeStamp = revision.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + throw new UnsupportedOperationException("Mapping strategy does not support audits"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdatePrefix + sqlUpdateAffix, ReuseProbability.HIGH); + stmt.setInt(1, -version); + stmt.setLong(2, timeStamp); + idHandler.setCDOID(stmt, 3, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, + OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + FeatureDeltaWriter writer = deltaWriter.get(); + writer.process(accessor, delta, created); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + /** + * @author Eike Stepper + */ + private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor + { + private CDOID id; + + private int oldVersion; + + private long created; + + private IDBStoreAccessor accessor; + + private boolean updateContainer; + + private List<Pair<ITypeMapping, Object>> attributeChanges; + + private List<Pair<EStructuralFeature, Integer>> listSizeChanges; + + private int newContainingFeatureID; + + private CDOID newContainerID; + + private CDOID newResourceID; + + private int branchId; + + private int newVersion; + + /* + * this is a temporary copy of the revision to track list size changes... + */ + private InternalCDORevision tempRevision; + + public FeatureDeltaWriter() + { + attributeChanges = new ArrayList<Pair<ITypeMapping, Object>>(); + listSizeChanges = new ArrayList<Pair<EStructuralFeature, Integer>>(); + } + + protected void reset() + { + attributeChanges.clear(); + listSizeChanges.clear(); + updateContainer = false; + } + + public void process(IDBStoreAccessor a, CDORevisionDelta d, long c) + { + // set context + id = d.getID(); + + branchId = d.getBranch().getID(); + oldVersion = d.getVersion(); + newVersion = oldVersion + 1; + created = c; + accessor = a; + + tempRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id).copy(); + + // process revision delta tree + d.accept(this); + + updateAttributes(); + // clean up + reset(); + } + + public void visit(CDOMoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOSetFeatureDelta delta) + { + if (delta.getFeature().isMany()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + ITypeMapping am = getValueMapping(delta.getFeature()); + if (am == null) + { + throw new IllegalArgumentException("AttributeMapping for " + delta.getFeature() + " is null!"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + attributeChanges.add(new Pair<ITypeMapping, Object>(am, delta.getValue())); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + // TODO: correct this when DBStore implements unsettable features + // see Bugs 259868 and 263010 + ITypeMapping tm = getValueMapping(delta.getFeature()); + attributeChanges.add(new Pair<ITypeMapping, Object>(tm, null)); + } + + public void visit(CDOListFeatureDelta delta) + { + EStructuralFeature feature = delta.getFeature(); + + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(feature); + listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta); + + int oldSize = tempRevision.getList(feature).size(); + delta.apply(tempRevision); + int newSize = tempRevision.getList(feature).size(); + + if (oldSize != newSize) + { + listSizeChanges.add(new Pair<EStructuralFeature, Integer>(feature, newSize)); + } + } + + public void visit(CDOClearFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOAddFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDORemoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOContainerFeatureDelta delta) + { + newContainingFeatureID = delta.getContainerFeatureID(); + newContainerID = (CDOID)delta.getContainerID(); + newResourceID = delta.getResourceID(); + updateContainer = true; + } + + private void updateAttributes() + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(buildUpdateStatement(), ReuseProbability.MEDIUM); + stmt.setInt(column++, newVersion); + stmt.setLong(column++, created); + if (updateContainer) + { + idHandler.setCDOID(stmt, column++, newResourceID, created); + idHandler.setCDOID(stmt, column++, newContainerID, created); + stmt.setInt(column++, newContainingFeatureID); + } + + column = setUpdateAttributeValues(attributeChanges, stmt, column); + column = setUpdateListSizeChanges(listSizeChanges, stmt, column); + + idHandler.setCDOID(stmt, column++, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private String buildUpdateStatement() + { + StringBuilder builder = new StringBuilder(sqlUpdatePrefix); + if (updateContainer) + { + builder.append(sqlUpdateContainerPart); + } + + for (Pair<ITypeMapping, Object> change : attributeChanges) + { + builder.append(", "); //$NON-NLS-1$ + ITypeMapping typeMapping = change.getElement1(); + builder.append(typeMapping.getField()); + builder.append("=?"); //$NON-NLS-1$ + + if (typeMapping.getFeature().isUnsettable()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(getUnsettableFields().get(typeMapping.getFeature())); + builder.append("=?"); //$NON-NLS-1$ + } + } + + for (Pair<EStructuralFeature, Integer> change : listSizeChanges) + { + builder.append(", "); //$NON-NLS-1$ + EStructuralFeature feature = change.getElement1(); + builder.append(getListSizeFields().get(feature)); + builder.append("=?"); //$NON-NLS-1$ + } + + builder.append(sqlUpdateAffix); + return builder.toString(); + } + + private int setUpdateAttributeValues(List<Pair<ITypeMapping, Object>> attributeChanges, PreparedStatement stmt, + int col) throws SQLException + { + for (Pair<ITypeMapping, Object> change : attributeChanges) + { + ITypeMapping typeMapping = change.getElement1(); + Object value = change.getElement2(); + if (typeMapping.getFeature().isUnsettable()) + { + // feature is unsettable + if (value == null) + { + // feature is unset + typeMapping.setDefaultValue(stmt, col++); + stmt.setBoolean(col++, false); + } + else + { + // feature is set + typeMapping.setValue(stmt, col++, value); + stmt.setBoolean(col++, true); + } + } + else + { + typeMapping.setValue(stmt, col++, change.getElement2()); + } + } + + return col; + } + + private int setUpdateListSizeChanges(List<Pair<EStructuralFeature, Integer>> attributeChanges, + PreparedStatement stmt, int col) throws SQLException + { + for (Pair<EStructuralFeature, Integer> change : listSizeChanges) + { + stmt.setInt(col++, change.getElement2()); + } + + return col; + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp) + { + // do nothing + } + + @Override + protected String getListXRefsWhere(QueryXRefsContext context) + { + if (CDORevision.UNSPECIFIED_DATE != context.getTimeStamp()) + { + throw new IllegalArgumentException("Non-audit mode does not support timestamp specification"); + } + + if (!context.getBranch().isMainBranch()) + { + throw new IllegalArgumentException("Non-audit mode does not support branch specification"); + } + + return CDODBSchema.ATTRIBUTES_REVISED + "=0"; + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java new file mode 100644 index 0000000000..d8f7f6e09d --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - major refactoring + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalNonAuditMappingStrategy extends AbstractHorizontalMappingStrategy +{ + public HorizontalNonAuditMappingStrategy() + { + } + + public boolean hasAuditSupport() + { + return false; + } + + public boolean hasBranchingSupport() + { + return false; + } + + public boolean hasDeltaSupport() + { + return true; + } + + @Override + public IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature) + { + return new NonAuditListTableMapping(this, containingClass, feature); + } + + @Override + public IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature) + { + return new NonAuditFeatureMapTableMapping(this, containingClass, feature); + } + + @Override + protected IClassMapping doCreateClassMapping(EClass eClass) + { + return new HorizontalNonAuditClassMapping(this, eClass); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java new file mode 100644 index 0000000000..0f8c57ea80 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java @@ -0,0 +1,590 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Iterator; + +/** + * This is a featuremap-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta + * support. + * + * @author Eike Stepper + * @since 3.0 + */ +public class NonAuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping implements IListMappingDeltaSupport +{ + private FieldInfo[] keyFields; + + private static final int TEMP_INDEX = -1; + + private static final int UNBOUNDED_MOVE = -1; + + private String sqlClear; + + private String sqlUpdateIndex; + + private String sqlUpdateValue; + + private String sqlDeleteItem; + + private String sqlMoveDownWithLimit; + + private String sqlMoveDown; + + private String sqlMoveUpWithLimit; + + private String sqlMoveUp; + + public NonAuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // TODO: add key fields length support + + StringBuilder builder = new StringBuilder(); + + // ----------- clear list ------------------------- + + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? "); //$NON-NLS-1$ + + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + + sqlDeleteItem = builder.toString(); + + // ----------- update one item index -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------- update one item value -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append("=?,"); //$NON-NLS-1$ + + Iterator<String> iter = getColumnNames().iterator(); + while (iter.hasNext()) + { + String column = iter.next(); + builder.append(column); + builder.append("=?"); //$NON-NLS-1$ + + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateValue = builder.toString(); + + // ----------- move down -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("-1 WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(">? "); //$NON-NLS-1$ + sqlMoveDown = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("<=?"); //$NON-NLS-1$ + sqlMoveDownWithLimit = builder.toString(); + + // ----------- move up -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("+1 WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(">=? "); //$NON-NLS-1$ + sqlMoveUp = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("<?"); //$NON-NLS-1$ + sqlMoveUpWithLimit = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType(); + keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, dbType) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + clearList(accessor, id); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Insert a list item at a specified position. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision to insert the value + * @param index + * the index where to insert the element + * @param value + * the value to insert. + */ + public void insertListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + move1up(accessor, id, index, UNBOUNDED_MOVE); + insertValue(accessor, id, index, value, timestamp); + } + + private void insertValue(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + String sql = sqlInsert; + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, id); + int column = getKeyFields().length + 1; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Move a list item from one position to another. Indices between both positions are updated so that the list remains + * consistent. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision in which to move the item + * @param oldPosition + * the old position of the item. + * @param newPosition + * the new position of the item. + */ + public void moveListItem(IDBStoreAccessor accessor, CDOID id, int oldPosition, int newPosition) + { + if (oldPosition == newPosition) + { + return; + } + + // move element away temporarily + updateOneIndex(accessor, id, oldPosition, TEMP_INDEX); + + // move elements in between + if (oldPosition < newPosition) + { + move1down(accessor, id, oldPosition, newPosition); + } + else + { + // oldPosition > newPosition -- equal case is handled above + move1up(accessor, id, newPosition, oldPosition); + } + + // move temporary element to new position + updateOneIndex(accessor, id, TEMP_INDEX, newPosition); + } + + private void updateOneIndex(IDBStoreAccessor accessor, CDOID id, int oldIndex, int newIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + stmt.setInt(1, newIndex); + idHandler.setCDOID(stmt, 2, id); + stmt.setInt(3, oldIndex); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Remove a list item from a specified a position. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove the item + * @param index + * the index of the item to remove + */ + public void removeListItem(IDBStoreAccessor accessor, CDOID id, int index) + { + deleteItem(accessor, id, index); + move1down(accessor, id, index, UNBOUNDED_MOVE); + } + + /** + * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with + * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down. + */ + private void move1down(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit, + ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + if (upperIndex != UNBOUNDED_MOVE) + { + stmt.setInt(3, upperIndex); + } + + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with + * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down. + */ + private void move1up(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit, + ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + if (upperIndex != UNBOUNDED_MOVE) + { + stmt.setInt(3, upperIndex); + } + + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Set a value at a specified position to the given value. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision to set the value + * @param index + * the index of the item to set + * @param value + * the value to be set. + */ + public void setListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + ITypeMapping mapping = getTypeMapping(tag); + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, tag); + int column = 2; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + mapping.setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, final long created, CDOListFeatureDelta listDelta) + { + CDOFeatureDeltaVisitor visitor = new CDOFeatureDeltaVisitor() + { + public void visit(CDOMoveFeatureDelta delta) + { + moveListItem(accessor, id, delta.getOldPosition(), delta.getNewPosition()); + } + + public void visit(CDOAddFeatureDelta delta) + { + insertListItem(accessor, id, delta.getIndex(), delta.getValue(), created); + } + + public void visit(CDORemoveFeatureDelta delta) + { + removeListItem(accessor, id, delta.getIndex()); + } + + public void visit(CDOSetFeatureDelta delta) + { + setListItem(accessor, id, delta.getIndex(), delta.getValue(), created); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + clearList(accessor, id); + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + clearList(accessor, id); + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + }; + + for (CDOFeatureDelta delta : listDelta.getListChanges()) + { + delta.accept(visitor); + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java new file mode 100644 index 0000000000..92d96aad20 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java @@ -0,0 +1,825 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import org.eclipse.core.runtime.Assert; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; + +/** + * This is a list-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta support. + * + * @author Eike Stepper + * @since 2.0 + */ +public class NonAuditListTableMapping extends AbstractListTableMapping implements IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, NonAuditListTableMapping.class); + + private FieldInfo[] keyFields; + + private static final int UNBOUNDED_SHIFT = -1; + + /** + * The central data structure which is used to calculate the outcomes of the list deltas. + */ + private ArrayList<ManipulationElement> manipulations = new ArrayList<ManipulationElement>(); + + /** + * This is a flag to remember if a delta of type "clear" has been encountered. If so, the list in the DB has to be + * cleared before writing out the changes. + */ + private boolean clearFirst; + + private String sqlClear; + + private String sqlUpdateValue; + + private String sqlUpdateIndex; + + private String sqlInsertValue; + + private String sqlDeleteItem; + + public NonAuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? "); //$NON-NLS-1$ + + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + + sqlDeleteItem = builder.toString(); + + // ----------- update one item -------------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateValue = builder.toString(); + + // ----------- insert one item -------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" ("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES(?, ?, ?) "); //$NON-NLS-1$ + sqlInsertValue = builder.toString(); + + // ----------- update one item index -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType(); + keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.LIST_REVISION_ID, dbType) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + clearList(accessor, id); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id) + { + PreparedStatement stmt = null; + + try + { + stmt = accessor.getStatementCache().getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + accessor.getStatementCache().releasePreparedStatement(stmt); + } + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + CDOBranchPoint main = accessor.getStore().getRepository().getBranchManager().getMainBranch().getHead(); + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository() + .getRevisionManager().getRevision(id, main, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + int oldListSize = originalRevision.getList(getFeature()).size(); + + // reset the clear-flag + clearFirst = false; + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + if (manipulations == null) + { + manipulations = new ArrayList<ManipulationElement>(); + } + else + { + manipulations.clear(); + } + + // create list and initialize with original indexes + for (int i = 0; i < oldListSize; i++) + { + manipulations.add(createOriginalElement(i)); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(); + + if (TRACER.isEnabled()) + { + TRACER.format("Procssing deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : delta.getListChanges()) + { + listDelta.accept(visitor); + } + + // TODO: here, we could implement further optimizations. + // e.g., if more than 50% of the list's items are removed, + // it's better to clear the list and reinsert all values + // from scratch. + + // finally, write results to the database + writeResultToDatabase(accessor, id); + } + + /** + * Write calculated changes to the database + * + * @param accessor + * , + */ + private void writeResultToDatabase(IDBStoreAccessor accessor, CDOID id) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + PreparedStatement deleteStmt = null; + PreparedStatement moveStmt = null; + PreparedStatement setValueStmt = null; + PreparedStatement insertStmt = null; + + int deleteCounter = 0; + int moveCounter = 0; + int setValueCounter = 0; + int insertCounter = 0; + + int tempIndex = -1; + + if (TRACER.isEnabled()) + { + TRACER.format("Writing to database:"); //$NON-NLS-1$ + } + + if (clearFirst) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - clear list"); //$NON-NLS-1$ + } + + clearList(accessor, id); + } + + try + { + for (ManipulationElement element : manipulations) + { + if (element.is(ManipulationConstants.DELETE)) + { + /* + * Step 1: DELETE all elements e which have e.is(REMOVE) by e.sourceIdx + */ + + if (deleteStmt == null) + { + deleteStmt = accessor.getStatementCache().getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH); + idHandler.setCDOID(deleteStmt, 1, id); + } + + deleteStmt.setInt(2, element.sourceIndex); + deleteStmt.addBatch(); + deleteCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - delete at {0} ", element.sourceIndex); //$NON-NLS-1$ + } + } + + if (element.is(ManipulationConstants.MOVE)) + { + /* + * Step 2: MOVE all elements e (by e.sourceIdx) which have e.is(MOVE) to temporary idx (-1, -2, -3, -4, ...) + * and store temporary idx in e.tempIndex + */ + if (moveStmt == null) + { + moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + idHandler.setCDOID(moveStmt, 2, id); + } + + moveStmt.setInt(3, element.sourceIndex); // from index + moveStmt.setInt(1, --tempIndex); // to index + element.tempIndex = tempIndex; + moveStmt.addBatch(); + moveCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.tempIndex); //$NON-NLS-1$ + } + } + } + + /* + * Step 3: move all elements which have to be shifted up or down because of add, remove or move of other elements + * to their proper position. This has to be done in two phases to avoid collisions, as the index has to be unique + */ + int size = manipulations.size(); + + /* Step 3a: shift down */ + for (int i = 0; i < size; i++) + { + ManipulationElement element = manipulations.get(i); + + if ((element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE) + && element.sourceIndex > element.destinationIndex) + { + if (moveStmt == null) + { + moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + idHandler.setCDOID(moveStmt, 2, id); + } + + moveStmt.setInt(3, element.sourceIndex); // from index + moveStmt.setInt(1, element.destinationIndex); // to index + moveStmt.addBatch(); + moveCounter++; + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.destinationIndex); //$NON-NLS-1$ + } + } + } + + /* Step 3b: shift up */ + for (int i = size - 1; i >= 0; i--) + { + ManipulationElement element = manipulations.get(i); + + if ((element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE) + && element.sourceIndex < element.destinationIndex) + { + if (moveStmt == null) + { + moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + idHandler.setCDOID(moveStmt, 2, id); + } + + moveStmt.setInt(3, element.sourceIndex); // from index + moveStmt.setInt(1, element.destinationIndex); // to index + moveStmt.addBatch(); + moveCounter++; + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.destinationIndex); //$NON-NLS-1$ + } + } + } + + for (ManipulationElement element : manipulations) + { + if (element.is(ManipulationConstants.MOVE)) + { + /* + * Step 4: MOVE all elements e have e.is(MOVE) from e.tempIdx to e.destinationIdx (because we have moved them + * before, moveStmt is always initialized + */ + moveStmt.setInt(3, element.tempIndex); // from index + moveStmt.setInt(1, element.destinationIndex); // to index + element.tempIndex = tempIndex; + moveStmt.addBatch(); + moveCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", element.tempIndex, element.destinationIndex); //$NON-NLS-1$ + } + } + + if (element.is(ManipulationConstants.SET_VALUE)) + { + /* + * Step 5: SET all elements which have e.type == SET_VALUE by index == e.destinationIdx + */ + if (setValueStmt == null) + { + setValueStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH); + idHandler.setCDOID(setValueStmt, 2, id); + } + + setValueStmt.setInt(3, element.destinationIndex); + getTypeMapping().setValue(setValueStmt, 1, element.value); + setValueStmt.addBatch(); + setValueCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - set value at {0} to {1} ", element.destinationIndex, element.value); //$NON-NLS-1$ + } + } + + if (element.is(ManipulationConstants.INSERT)) + { + /* + * Step 6: INSERT all elements which have e.type == INSERT. + */ + if (insertStmt == null) + { + insertStmt = accessor.getStatementCache().getPreparedStatement(sqlInsertValue, ReuseProbability.HIGH); + idHandler.setCDOID(insertStmt, 1, id); + } + + insertStmt.setInt(2, element.destinationIndex); + getTypeMapping().setValue(insertStmt, 3, element.value); + insertStmt.addBatch(); + insertCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - insert value at {0} : value {1} ", element.destinationIndex, element.value); //$NON-NLS-1$ + } + } + } + + // finally perform all operations + if (deleteCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} delete operations", deleteCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(deleteStmt, deleteCounter); + } + + if (moveCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(moveStmt, moveCounter); + } + + if (insertCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} insert operations", insertCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(insertStmt, insertCounter); + } + + if (setValueCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} set operations", setValueCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(setValueStmt, setValueCounter); + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + releaseStatement(accessor, deleteStmt, moveStmt, insertStmt, setValueStmt); + } + } + + private void releaseStatement(IDBStoreAccessor accessor, PreparedStatement... stmts) + { + Throwable t = null; + + for (PreparedStatement stmt : stmts) + { + try + { + if (stmt != null) + { + try + { + stmt.clearBatch(); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + accessor.getStatementCache().releasePreparedStatement(stmt); + } + } + } + catch (Throwable th) + { + if (t == null) + { + // remember first exception + t = th; + } + + // more exceptions go to the log + OM.LOG.error(t); + } + } + + if (t != null) + { + throw new DBException(t); + } + } + + /** + * Helper method: shift all (destination) indexes in the interval [from,to] (inclusive at both ends) by offset + * (positive or negative). + */ + private void shiftIndexes(int from, int to, int offset) + { + for (ManipulationElement e : manipulations) + { + if (e.destinationIndex >= from && (to == UNBOUNDED_SHIFT || e.destinationIndex <= to)) + { + e.destinationIndex += offset; + } + } + } + + /** + * Find a manipulation item by destination index). + */ + private ManipulationElement findElement(int index) + { + for (ManipulationElement e : manipulations) + { + if (e.destinationIndex == index) + { + return e; + } + } + + // never reached + Assert.isTrue(false); + return null; + } + + /** + * Delete an element (used in remove and clear) + */ + private void deleteItem(ManipulationElement e) + { + if (e.is(ManipulationConstants.INSERT)) + { + // newly inserted items are simply removed, as + // removing inserted items is equal to no change at all. + manipulations.remove(e); + } + else + { + // mark the existing item as to be deleted. + // (previous MOVE and SET conditions are overridden by setting + // the exclusive DELETE type). + e.type = ManipulationConstants.DELETE; + e.destinationIndex = ManipulationConstants.NO_INDEX; + } + } + + /** + * Create a ManipulationElement which represents an element which already is in the list. + */ + private ManipulationElement createOriginalElement(int index) + { + return new ManipulationElement(index, index, ManipulationConstants.NIL, ManipulationConstants.NONE); + } + + /** + * Create a ManipulationElement which represents an element which is inserted in the list. + */ + private ManipulationElement createInsertedElement(int index, Object value) + { + return new ManipulationElement(ManipulationConstants.NONE, index, value, ManipulationConstants.INSERT); + } + + /** + * @author Eike Stepper + */ + private final class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + public void visit(CDOAddFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - insert at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$ + } + + // make room for the new item + shiftIndexes(delta.getIndex(), UNBOUNDED_SHIFT, +1); + + // create the item + manipulations.add(createInsertedElement(delta.getIndex(), delta.getValue())); + } + + public void visit(CDORemoveFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - remove at {0}", delta.getIndex()); //$NON-NLS-1$ + } + + ManipulationElement e = findElement(delta.getIndex()); + deleteItem(e); + + // fill the gap by shifting all subsequent items down + shiftIndexes(delta.getIndex() + 1, UNBOUNDED_SHIFT, -1); + } + + public void visit(CDOSetFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - set at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$ + } + + ManipulationElement e = findElement(delta.getIndex()); + // set the new value + e.value = delta.getValue(); + + // if the item is freshly inserted we do not set the SET-mark. + // setting the value of a new item results in inserting with the + // new value at once. + if (!e.is(ManipulationConstants.INSERT)) + { + // else mark the existing item to be set to a new value + e.addType(ManipulationConstants.SET_VALUE); + } + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - clear list"); //$NON-NLS-1$ + } + + // set the clear-flag + clearFirst = true; + + // and also clear all manipulation items + manipulations.clear(); + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + // ignore the trivial case + if (fromIdx == toIdx) + { + return; + } + + ManipulationElement e = findElement(fromIdx); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + shiftIndexes(fromIdx + 1, toIdx, -1); + } + else + { // fromIdx > toIdx here + shiftIndexes(toIdx, fromIdx - 1, +1); + } + + // set the new index + e.destinationIndex = toIdx; + + // if it is a new element, no MOVE mark needed, because we insert it + // at the new position + if (!e.is(ManipulationConstants.INSERT)) + { + // else we need to handle the move of an existing item + e.addType(ManipulationConstants.MOVE); + } + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + Assert.isTrue(false); + } + + if (TRACER.isEnabled()) + { + TRACER.format(" - unset list"); //$NON-NLS-1$ + } + + // set the clear-flag + clearFirst = true; + + // and also clear all manipulation items + manipulations.clear(); + } + + public void visit(CDOListFeatureDelta delta) + { + // never called + Assert.isTrue(false); + } + + public void visit(CDOContainerFeatureDelta delta) + { + // never called + Assert.isTrue(false); + } + } + + /** + * @author Eike Stepper + */ + private static final class ManipulationConstants + { + public static final int NO_INDEX = Integer.MIN_VALUE; + + public static final int DELETE = 1 << 4; + + public static final int INSERT = 1 << 3; + + public static final int MOVE = 1 << 2; + + public static final int SET_VALUE = 1 << 1; + + public static final Object NIL = new Object(); + + public static final int NONE = 0; + } + + /** + * @author Eike Stepper + */ + private static final class ManipulationElement + { + public int type; + + public int sourceIndex; + + public int tempIndex; + + public int destinationIndex; + + public Object value; + + public ManipulationElement(int srcIdx, int dstIdx, Object val, int t) + { + sourceIndex = srcIdx; + tempIndex = ManipulationConstants.NONE; + destinationIndex = dstIdx; + value = val; + type = t; + } + + public boolean is(int t) + { + return (type & t) > 0; + } + + public void addType(int t) + { + type |= t; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java new file mode 100644 index 0000000000..5f38c818ed --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; + +import java.sql.Connection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class ObjectTypeCache extends DelegatingObjectTypeMapper +{ + public static final int DEFAULT_CACHE_CAPACITY = 100000; + + private Map<CDOID, CDOID> memoryCache; + + private int cacheSize; + + public ObjectTypeCache(int cacheSize) + { + this.cacheSize = cacheSize; + } + + @Override + protected CDOID doGetObjectType(IDBStoreAccessor accessor, CDOID id) + { + return memoryCache.get(id); + } + + @Override + protected void doPutObjectType(IDBStoreAccessor accessor, CDOID id, CDOID type) + { + memoryCache.put(id, type); + } + + @Override + protected void doRemoveObjectType(IDBStoreAccessor accessor, CDOID id) + { + memoryCache.remove(id); + } + + @Override + protected CDOID doGetMaxID(Connection connection, IIDHandler idHandler) + { + return null; + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + memoryCache = Collections.synchronizedMap(new MemoryCache(cacheSize)); + } + + @Override + protected void doDeactivate() throws Exception + { + memoryCache = null; + super.doDeactivate(); + } + + /** + * @author Stefan Winkler + */ + private static final class MemoryCache extends LinkedHashMap<CDOID, CDOID> + { + private static final long serialVersionUID = 1L; + + private int capacity; + + public MemoryCache(int capacity) + { + super(capacity, 0.75f, true); + this.capacity = capacity; + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry<CDOID, CDOID> eldest) + { + return size() > capacity; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java new file mode 100644 index 0000000000..c1b575490e --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java @@ -0,0 +1,256 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 259402 + * Stefan Winkler - redesign (prepared statements) + * Stefan Winkler - bug 276926 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBAdapter; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class ObjectTypeTable extends AbstractObjectTypeMapper +{ + private IDBTable table; + + private IDBField idField; + + private IDBField typeField; + + private IDBField timeField; + + private String sqlDelete; + + private String sqlInsert; + + private String sqlSelect; + + public ObjectTypeTable() + { + } + + public final CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelect, ReuseProbability.MAX); + idHandler.setCDOID(stmt, 1, id); + DBUtil.trace(stmt.toString()); + ResultSet resultSet = stmt.executeQuery(); + + if (!resultSet.next()) + { + DBUtil.trace("ClassID for CDOID " + id + " not found"); //$NON-NLS-1$ //$NON-NLS-2$ + return null; + } + + CDOID classID = idHandler.getCDOID(resultSet, 1); + EClass eClass = (EClass)getMetaDataManager().getMetaInstance(classID); + return new CDOClassifierRef(eClass); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public final void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type) + { + IDBStore store = getMappingStrategy().getStore(); + IIDHandler idHandler = store.getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.MAX); + idHandler.setCDOID(stmt, 1, id); + idHandler.setCDOID(stmt, 2, getMetaDataManager().getMetaID(type, timeStamp)); + stmt.setLong(3, timeStamp); + DBUtil.trace(stmt.toString()); + int result = stmt.executeUpdate(); + + if (result != 1) + { + throw new DBException("Object type could not be inserted: " + id); //$NON-NLS-1$ + } + } + catch (SQLException ex) + { + // Unique key violation can occur in rare cases (merging new objects from other branches) + if (!store.getDBAdapter().isDuplicateKeyException(ex)) + { + throw new DBException(ex); + } + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public final void removeObjectType(IDBStoreAccessor accessor, CDOID id) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlDelete, ReuseProbability.MAX); + idHandler.setCDOID(stmt, 1, id); + DBUtil.trace(stmt.toString()); + int result = stmt.executeUpdate(); + + if (result != 1) + { + throw new DBException("Object type could not be deleted: " + id); //$NON-NLS-1$ + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public CDOID getMaxID(Connection connection, IIDHandler idHandler) + { + Statement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = connection.createStatement(); + resultSet = stmt.executeQuery("SELECT MAX(" + idField + ") FROM " + table); + + if (resultSet.next()) + { + return idHandler.getCDOID(resultSet, 1); + } + + return null; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime) + throws IOException + { + String where = " WHERE " + timeField + " BETWEEN " + fromCommitTime + " AND " + toCommitTime; + DBUtil.serializeTable(out, connection, table, null, where); + } + + public void rawImport(Connection connection, CDODataInput in, OMMonitor monitor) throws IOException + { + DBUtil.deserializeTable(in, connection, table, monitor); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + IDBStore store = getMappingStrategy().getStore(); + IDBSchema schema = store.getDBSchema(); + DBType dbType = store.getIDHandler().getDBType(); + + table = schema.addTable(CDODBSchema.CDO_OBJECTS); + idField = table.addField(CDODBSchema.ATTRIBUTES_ID, dbType); + typeField = table.addField(CDODBSchema.ATTRIBUTES_CLASS, dbType); + timeField = table.addField(CDODBSchema.ATTRIBUTES_CREATED, DBType.BIGINT); + table.addIndex(IDBIndex.Type.UNIQUE, idField); + + IDBAdapter dbAdapter = store.getDBAdapter(); + IDBStoreAccessor writer = store.getWriter(null); + Connection connection = writer.getConnection(); + Statement statement = null; + + try + { + statement = connection.createStatement(); + dbAdapter.createTable(table, statement); + connection.commit(); + } + catch (SQLException ex) + { + connection.rollback(); + throw new DBException(ex); + } + finally + { + DBUtil.close(statement); + writer.release(); + } + + sqlSelect = "SELECT " + typeField + " FROM " + table + " WHERE " + idField + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + sqlInsert = "INSERT INTO " + table + "(" + idField + "," + typeField + "," + timeField + ") VALUES (?, ?, ?)"; + sqlDelete = "DELETE FROM " + table + " WHERE " + idField + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + protected void doDeactivate() throws Exception + { + table = null; + idField = null; + typeField = null; + super.doDeactivate(); + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java new file mode 100644 index 0000000000..a60fb5db17 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Victor Roldan Betancort - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.server.internal.db.messages; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * @author Victor Roldan Betancort + */ +public class Messages +{ + private static final String BUNDLE_NAME = "org.eclipse.emf.cdo.server.internal.db.messages.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties new file mode 100644 index 0000000000..e6baaa8ee1 --- /dev/null +++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties @@ -0,0 +1,31 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Victor Roldan Betancort - initial API and implementation +# Eike Stepper - maintenance +# Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + +DBStore.0=dbConnectionProvider is null +DBStore.1=dbAdapter is null +DBStore.2=mappingStrategy is null +DBStore.3=idHandler is null +DBStore.9=Detected crash of repository {0} +DBStore.10=Repaired crash of repository {0}: lastObjectID={1}, nextLocalObjectID={2}, lastBranchID={3}, lastCommitTime={4}, lastNonLocalCommitTime={5} +DBStore.10b=Repaired crash of repository {0}: lastBranchID={1}, lastCommitTime={2}, lastNonLocalCommitTime={3} +DBStore.11=Repairing crash of repository {0} failed. + + +TypeMappingRegistry.1=No type mapping factory found for {0}: {1} --> DBType.{2} +TypeMappingRegistry.2=TypeMapping {0} annotated at feature {1} could not be found in registry. +TypeMappingRegistry.3=Runtime removal of ITypeMapping.Factory extensions is currently not supported. +TypeMappingRegistry.4=Duplicate source:target typeMapping pairs are currently not supported! +TypeMappingRegistry.5=Duplicate typeMapping ID: {0} + +FactoryTypeParserException.1=Invalid format for typeMapping factoryType {0} +FactoryTypeParserException.2=EPackage {0} could not be resolved while registering typeMapping factoryType {1} +FactoryTypeParserException.3=EClassifier {0} could not be resolved while registering typeMapping factoryType {1} +FactoryTypeParserException.4=DBType {0} could not be resolved while registering typeMapping factoryType {1} diff --git a/org.eclipse.emf.cdo.server/.classpath b/org.eclipse.emf.cdo.server/.classpath new file mode 100644 index 0000000000..64c5e31b7a --- /dev/null +++ b/org.eclipse.emf.cdo.server/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.emf.cdo.server/.options b/org.eclipse.emf.cdo.server/.options new file mode 100644 index 0000000000..83038cb0e0 --- /dev/null +++ b/org.eclipse.emf.cdo.server/.options @@ -0,0 +1,11 @@ +# Debugging and tracing options + +org.eclipse.emf.cdo.server/debug = true +org.eclipse.emf.cdo.server/debug.protocol = true +org.eclipse.emf.cdo.server/debug.repository = true +org.eclipse.emf.cdo.server/debug.session = true +org.eclipse.emf.cdo.server/debug.transaction = true +org.eclipse.emf.cdo.server/debug.revision = true +org.eclipse.emf.cdo.server/debug.resource = true +org.eclipse.emf.cdo.server/debug.store = true +org.eclipse.emf.cdo.server/debug.types = true diff --git a/org.eclipse.emf.cdo.server/.project b/org.eclipse.emf.cdo.server/.project new file mode 100644 index 0000000000..08924a883b --- /dev/null +++ b/org.eclipse.emf.cdo.server/.project @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.emf.cdo.server</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.emf.cdo.releng.version.VersionBuilder</name> + <arguments> + <dictionary> + <key>release.path</key> + <value>/org.eclipse.emf.cdo.releng/release.xml</value> + </dictionary> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + <nature>org.eclipse.emf.cdo.releng.version.VersionNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.emf.cdo.server/.settings/.api_filters b/org.eclipse.emf.cdo.server/.settings/.api_filters new file mode 100644 index 0000000000..833c77689b --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/.api_filters @@ -0,0 +1,324 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.emf.cdo.server" version="2">
+ <resource path="src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java" type="org.eclipse.emf.cdo.internal.server.DelegatingRepository">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalRepository"/>
+ <message_argument value="CDOCommonRepository"/>
+ <message_argument value="DelegatingRepository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/LockManager.java" type="org.eclipse.emf.cdo.internal.server.LockManager$DurableView">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="DurableView"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="IView"/>
+ <message_argument value="CDOCommonView"/>
+ <message_argument value="DurableView"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/QueryManager.java" type="org.eclipse.emf.cdo.internal.server.QueryManager$QueryContext">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="IQueryContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/Repository.java" type="org.eclipse.emf.cdo.internal.server.Repository">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOReplicationInfo"/>
+ <message_argument value="Repository"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalRepository"/>
+ <message_argument value="CDOCommonRepository"/>
+ <message_argument value="Repository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java" type="org.eclipse.emf.cdo.internal.server.ResourcesQueryHandler$QueryContext">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="QueryResourcesContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java" type="org.eclipse.emf.cdo.internal.server.ServerCDOView">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="ServerCDOView"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java" type="org.eclipse.emf.cdo.internal.server.ServerCDOView$ServerCDOSession">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDORepositoryInfo"/>
+ <message_argument value="ServerCDOSession"/>
+ </message_arguments>
+ </filter>
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="InternalCDOSession"/>
+ <message_argument value="ServerCDOSession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/Session.java" type="org.eclipse.emf.cdo.internal.server.Session">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalSession"/>
+ <message_argument value="CDOCommonSession"/>
+ <message_argument value="Session"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java" type="org.eclipse.emf.cdo.internal.server.TransactionCommitContext$DeltaLockWrapper">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOIDAndBranch"/>
+ <message_argument value="DeltaLockWrapper"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java" type="org.eclipse.emf.cdo.internal.server.TransactionCommitContext$XRefContext">
+ <filter id="572522506">
+ <message_arguments>
+ <message_argument value="CDOIDReference"/>
+ <message_argument value="XRefContext"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="QueryXRefsContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="XRefContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/View.java" type="org.eclipse.emf.cdo.internal.server.View">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="View"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="InternalView"/>
+ <message_argument value="CDOCommonView"/>
+ <message_argument value="View"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java" type="org.eclipse.emf.cdo.internal.server.XRefsQueryHandler$QueryContext">
+ <filter id="572522506">
+ <message_arguments>
+ <message_argument value="CDOIDReference"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="QueryXRefsContext"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java" type="org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration$RepositoryInfo">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDORepositoryInfo"/>
+ <message_argument value="RepositoryInfo"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java" type="org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionProtocol">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOSessionProtocol"/>
+ <message_argument value="EmbeddedClientSessionProtocol"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java" type="org.eclipse.emf.cdo.internal.server.embedded.EmbeddedServerSessionProtocol">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="ISessionProtocol"/>
+ <message_argument value="CDOProtocol"/>
+ <message_argument value="EmbeddedServerSessionProtocol"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java" type="org.eclipse.emf.cdo.internal.server.mem.MEMStore">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="DurableLocking"/>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="MEMStore"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java" type="org.eclipse.emf.cdo.internal.server.mem.MEMStoreAccessor">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="DurableLocking"/>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="MEMStoreAccessor"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java" type="org.eclipse.emf.cdo.internal.server.syncing.OfflineClone$CommitContextData">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOCommitData"/>
+ <message_argument value="CommitContextData"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IQueryContext.java" type="org.eclipse.emf.cdo.server.IQueryContext">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="IQueryContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IRepository.java" type="org.eclipse.emf.cdo.server.IRepository">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOCommonRepository"/>
+ <message_argument value="IRepository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/ISession.java" type="org.eclipse.emf.cdo.server.ISession">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="CDOCommonSession"/>
+ <message_argument value="ISession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IStoreAccessor.java" type="org.eclipse.emf.cdo.server.IStoreAccessor$DurableLocking">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="DurableLocking"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IStoreAccessor.java" type="org.eclipse.emf.cdo.server.IStoreAccessor$QueryResourcesContext">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryResourcesContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IStoreAccessor.java" type="org.eclipse.emf.cdo.server.IStoreAccessor$QueryXRefsContext">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="QueryXRefsContext"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/ITransaction.java" type="org.eclipse.emf.cdo.server.ITransaction">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOCommonTransaction"/>
+ <message_argument value="ITransaction"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/IView.java" type="org.eclipse.emf.cdo.server.IView">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOCommonView"/>
+ <message_argument value="IView"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/embedded/CDOSession.java" type="org.eclipse.emf.cdo.server.embedded.CDOSession">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOSession"/>
+ <message_argument value="CDOSession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java" type="org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOSessionConfiguration"/>
+ <message_argument value="CDOSessionConfiguration"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java" type="org.eclipse.emf.cdo.spi.server.DurableLockArea">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="LockArea"/>
+ <message_argument value="DurableLockArea"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java" type="org.eclipse.emf.cdo.spi.server.ISessionProtocol">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOProtocol"/>
+ <message_argument value="ISessionProtocol"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java" type="org.eclipse.emf.cdo.spi.server.InternalLockManager">
+ <filter id="574619656">
+ <message_arguments>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="InternalLockManager"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/InternalSession.java" type="org.eclipse.emf.cdo.spi.server.InternalSession">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="Options"/>
+ <message_argument value="InternalSession"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java" type="org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOReplicationContext"/>
+ <message_argument value="InternalSynchronizableRepository"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/emf/cdo/spi/server/Store.java" type="org.eclipse.emf.cdo.spi.server.Store">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="ExactMatch"/>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="Store"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..9d1e69b075 --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Mon Jul 04 13:03:48 CEST 2011
+eclipse.preferences.version=1
+encoding//model/org.eclipse.emf.cdo.defs.ecorediag=UTF-8
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..611d1a92fb --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,373 @@ +#Fri Sep 02 05:40:11 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,*.ucls,doc-files/,package.html,package-info.java
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled
+org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,HIGH,LOW,LOW,LOW,LOW,LOW
+org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX,PERF,MEM,POLISH,@generated NOT,@ADDED
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=2
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.launching.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.launching.prefs new file mode 100644 index 0000000000..4658ec1435 --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.launching.prefs @@ -0,0 +1,3 @@ +#Fri Sep 02 05:38:34 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.launching.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE=ignore
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..4277817dad --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,120 @@ +#Thu Feb 04 09:44:24 CET 2010 +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=false +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_parentheses_in_expressions=true +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_EMFT +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_EMFT +formatter_settings_version=11 +org.eclipse.jdt.ui.exception.name=ex +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=org.eclipse.emf.cdo;org.eclipse.emf.internal.cdo;org.eclipse.net4j;org.eclipse.internal.net4j;org.eclipse.emf;org.eclipse;com;org;javax;java; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.keywordthis=false +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.staticondemandthreshold=99 +org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="false" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment"/><template autoinsert\="false" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment"/><template autoinsert\="false" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment"/><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n * \r\n * Contributors\:\r\n * Eike Stepper - initial API and implementation\r\n */</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author Eike Stepper\r\n */</template><template autoinsert\="false" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment"/><template autoinsert\="false" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment"/><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="false" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment"/><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="false" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">${exception_var}.printStackTrace();</template><template autoinsert\="false" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">${body_statement}</template><template autoinsert\="false" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates> +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=true +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=true +sp_cleanup.use_this_for_non_static_field_access=true +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=true +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.ltk.core.refactoring.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000000..864e30fe5d --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,3 @@ +#Thu Feb 04 09:44:24 CET 2010 +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..b050639a54 --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,4 @@ +#Thu Feb 04 09:44:24 CET 2010 +eclipse.preferences.version=1 +project.repository.kind=bugzilla +project.repository.url=https\://bugs.eclipse.org/bugs diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..2f50f36c0c --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.team.ui.prefs @@ -0,0 +1,3 @@ +#Thu Feb 04 09:44:24 CET 2010 +commit.comment.template=[${task.id}] ${task.description} \r\n${task.url} +eclipse.preferences.version=1 diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 0000000000..b0e52183ad --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.api.tools.prefs @@ -0,0 +1,94 @@ +#Mon May 16 19:22:50 CEST 2011
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Ignore
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Ignore
+automatically_removed_unused_problem_filters=Disabled
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.prefs b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.prefs new file mode 100644 index 0000000000..c6b96bb45e --- /dev/null +++ b/org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.prefs @@ -0,0 +1,31 @@ +#Thu Feb 04 09:44:24 CET 2010 +compilers.f.unresolved-features=1 +compilers.f.unresolved-plugins=1 +compilers.incompatible-environment=1 +compilers.p.build=1 +compilers.p.build.bin.includes=1 +compilers.p.build.java.compliance=1 +compilers.p.build.missing.output=2 +compilers.p.build.output.library=1 +compilers.p.build.source.library=1 +compilers.p.build.src.includes=1 +compilers.p.deprecated=1 +compilers.p.discouraged-class=1 +compilers.p.internal=1 +compilers.p.missing-packages=1 +compilers.p.missing-version-export-package=1 +compilers.p.missing-version-import-package=1 +compilers.p.missing-version-require-bundle=1 +compilers.p.no-required-att=0 +compilers.p.not-externalized-att=2 +compilers.p.unknown-attribute=1 +compilers.p.unknown-class=1 +compilers.p.unknown-element=1 +compilers.p.unknown-identifier=1 +compilers.p.unknown-resource=1 +compilers.p.unresolved-ex-points=0 +compilers.p.unresolved-import=0 +compilers.s.create-docs=false +compilers.s.doc-folder=doc +compilers.s.open-tags=1 +eclipse.preferences.version=1 diff --git a/org.eclipse.emf.cdo.server/CDOServer.launch b/org.eclipse.emf.cdo.server/CDOServer.launch new file mode 100644 index 0000000000..bd3d8e91a1 --- /dev/null +++ b/org.eclipse.emf.cdo.server/CDOServer.launch @@ -0,0 +1,368 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.pde.ui.RuntimeWorkbench">
+<booleanAttribute key="append.args" value="true"/>
+<stringAttribute key="application" value="org.eclipse.emf.cdo.server.app"/>
+<booleanAttribute key="askclear" value="true"/>
+<booleanAttribute key="automaticAdd" value="false"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="org.eclipse.emf.cdo.server,org.eclipse.emf.cdo.server.db,org.eclipse.net4j,org.eclipse.net4j.db,org.eclipse.net4j.db.derby,org.eclipse.net4j.http.common,org.eclipse.net4j.http.server,org.eclipse.net4j.tcp,org.eclipse.net4j.util"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="false"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/CDOServer"/>
+<booleanAttribute key="default" value="false"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../cdo.server"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+<mapEntry key="[run]" value="org.eclipse.pde.ui.RuntimeWorkbench"/>
+</mapAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -debug -console"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx1024m -Ddebug=true -Dnet4j.config="${project_loc:/org.eclipse.emf.cdo.server.product}/config" -Dorg.eclipse.emf.cdo.server.browser.port=7777 -Dorg.osgi.service.http.port=8080"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.platform.ide"/>
+<stringAttribute key="selectedPlugin" value="org.eclipse.emf.cdo"/>
+<stringAttribute key="selected_target_plugins" value="com.mysql.jdbc@default:default,javax.servlet@default:default,org.apache.derby@default:default,org.eclipse.ant.core@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem.win32.x86_64@default:false,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime.compatibility.auth@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore.change@default:default,org.eclipse.emf.ecore.xmi@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.eclipse.team.core@default:default,org.h2@default:default,org.hsqldb@default:default,org.postgresql.jdbc3@default:default"/>
+<stringAttribute key="selected_workspace_plugins" value="com.mongodb@default:default,org.eclipse.emf.cdo.common@default:default,org.eclipse.emf.cdo.examples.company@default:default,org.eclipse.emf.cdo.server.db@default:default,org.eclipse.emf.cdo.server.mongodb@default:default,org.eclipse.emf.cdo.server.net4j@default:default,org.eclipse.emf.cdo.server@default:default,org.eclipse.emf.cdo@default:default,org.eclipse.net4j.db.derby@default:default,org.eclipse.net4j.db.h2@default:default,org.eclipse.net4j.db.hsqldb@default:default,org.eclipse.net4j.db.mysql@default:default,org.eclipse.net4j.db.postgresql@default:default,org.eclipse.net4j.db@default:default,org.eclipse.net4j.tcp@default:default,org.eclipse.net4j.util@default:default,org.eclipse.net4j@default:default"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<stringAttribute key="templateConfig" value="${target_home}\configuration\config.ini"/>
+<booleanAttribute key="tracing" value="true"/>
+<mapAttribute key="tracingOptions">
+<mapEntry key="org.eclipse.core.contenttype/debug" value="false"/>
+<mapEntry key="org.eclipse.core.expressions/tracePropertyResolving" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/beginend" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/errorondeadlock" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/locks" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/shutdown" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/timing" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/delta" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/failure" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/interrupt" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/invoking" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuild" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuildstack" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/stacktrace" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype/cache" value="false"/>
+<mapEntry key="org.eclipse.core.resources/debug" value="false"/>
+<mapEntry key="org.eclipse.core.resources/history" value="false"/>
+<mapEntry key="org.eclipse.core.resources/natures" value="false"/>
+<mapEntry key="org.eclipse.core.resources/perf/builders" value="10000"/>
+<mapEntry key="org.eclipse.core.resources/perf/listeners" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/save.participants" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/snapshot" value="1000"/>
+<mapEntry key="org.eclipse.core.resources/preferences" value="false"/>
+<mapEntry key="org.eclipse.core.resources/refresh" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/snapshots" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/strings" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/compatibility/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug/context" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf/success" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/preferences/plugin" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachecopy" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachelookup" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/connect" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/commands" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/events" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/contextlaunching" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/launchhistory" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/contentProvider" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/deltas" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/model" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/updateSequence" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/viewer" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.reading" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.writing" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server.db/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.types" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.adapter" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.object" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.transaction" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.util" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.view" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/perf" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision.loading" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/general" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/get" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/set" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug/events" value="false"/>
+<mapEntry key="org.eclipse.help.base/debug" value="true"/>
+<mapEntry key="org.eclipse.help.base/debug/search" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser/inprocess" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/infopop" value="false"/>
+<mapEntry key="org.eclipse.help.webapp/debug" value="true"/>
+<mapEntry key="org.eclipse.help.webapp/debug/workingsets" value="false"/>
+<mapEntry key="org.eclipse.help/debug" value="true"/>
+<mapEntry key="org.eclipse.help/debug/context" value="false"/>
+<mapEntry key="org.eclipse.help/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core.manipulation/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug/buffermanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/builder" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/compiler" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/completion" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution/advanced" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/hierarchy" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/indexmanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta/verbose" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel/cache" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/postaction" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/resolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/selection" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/sourcemapper" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/zipaccess" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/perf/completion" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/containerinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/javadeltalistener" value="500"/>
+<mapEntry key="org.eclipse.jdt.core/perf/reconcile" value="1000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/selection" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/variableinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ASTProvider" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ResultCollector" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/TypeConstraints" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist/extensions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist_sorters/extensions" value=""/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/RefactorActionGroup" value="150"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/createPartControl" value="1300"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/makeActions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/search/participants" value="300"/>
+<mapEntry key="org.eclipse.jface.text.source/debug/RevisionRulerColumn" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/AnnotationPainter" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/ContentAssistSubjectAdapters" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/FastPartitioner/PositionCache" value="false"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/checkConditions" value="300"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/createChanges" value="300"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.common/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.debug/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server.jdbc/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jvm/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.tcp/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.concurrency" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle.dump" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.om" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.registry" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.acceptor" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer.stream" value="false"/>
+<mapEntry key="org.eclipse.net4j/debug.channel" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.connector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.selector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.signal" value="true"/>
+<mapEntry key="org.eclipse.net4j/perf" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/bundleTime" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/events" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/filter" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/loader" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/manifest" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/messageBundles" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/packageadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/security" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/services" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/startlevel" value="false"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/buffersize" value="256"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logfilename" value=""/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logsynchronously" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/converter/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/location" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin/resolver" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/classes" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/resources" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/benchmark" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/impl" value="org.eclipse.osgi.internal.profile.DefaultProfileLogger"/>
+<mapEntry key="org.eclipse.osgi/profile/startup" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/cycles" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/generics" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/grouping" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/imports" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/requires" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/wiring" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/classLoading" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/filename" value="runtime.traces"/>
+<mapEntry key="org.eclipse.osgi/trace/filters" value="trace.properties"/>
+<mapEntry key="org.eclipse.pde.build/debug" value="false"/>
+<mapEntry key="org.eclipse.pde.core/cache" value="false"/>
+<mapEntry key="org.eclipse.pde.core/classpath" value="false"/>
+<mapEntry key="org.eclipse.pde.core/debug" value="true"/>
+<mapEntry key="org.eclipse.pde.core/validation" value="false"/>
+<mapEntry key="org.eclipse.team.core/backgroundevents" value="false"/>
+<mapEntry key="org.eclipse.team.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.core/refreshjob" value="false"/>
+<mapEntry key="org.eclipse.team.core/streams" value="false"/>
+<mapEntry key="org.eclipse.team.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/cvsprotocol" value="true"/>
+<mapEntry key="org.eclipse.team.cvs.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/dirtycaching" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/metafiles" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/syncchangeevents" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/ssh_protocol" value="false"/>
+<mapEntry key="org.eclipse.ui.browser/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/gc" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/internalerror/openDialog" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/undomonitor" value="false"/>
+<mapEntry key="org.eclipse.ui.intro.universal/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/flags/noBrowser" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/printHTML" value="false"/>
+<mapEntry key="org.eclipse.ui.workbench/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/contributions" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/declaredImages" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/job.stale" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/showAllJobs" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/swtdebug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/workingSets" value="false"/>
+<mapEntry key="org.eclipse.ui/experimental/menus" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPropertyChangeListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPartReference" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPageListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPerspectiveListener" value="false"/>
+<mapEntry key="org.eclipse.ui/perf/contentTypes" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/page.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.activate" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.control" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.init" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.input" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.switch" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/showHeapStatus" value="true"/>
+<mapEntry key="org.eclipse.ui/perf/uijob" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.restore" value="30000"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.start" value="45000"/>
+<mapEntry key="org.eclipse.ui/trace/commands" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/graphics" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose.commandId" value=""/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/multipageeditor" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/sources" value="false"/>
+<mapEntry key="org.eclipse.update.configurator/debug" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug" value="true"/>
+<mapEntry key="org.eclipse.update.core/debug/configuration" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/install" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/installhandler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/parsing" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/reconciler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/type" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/warning" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/web" value="false"/>
+</mapAttribute>
+<booleanAttribute key="useCustomFeatures" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<booleanAttribute key="useNamedJRE" value="true"/>
+<booleanAttribute key="useProduct" value="false"/>
+<booleanAttribute key="usefeatures" value="false"/>
+</launchConfiguration>
diff --git a/org.eclipse.emf.cdo.server/CDOServer_SSL.launch b/org.eclipse.emf.cdo.server/CDOServer_SSL.launch new file mode 100644 index 0000000000..a668fd5ffd --- /dev/null +++ b/org.eclipse.emf.cdo.server/CDOServer_SSL.launch @@ -0,0 +1,368 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.pde.ui.RuntimeWorkbench">
+<booleanAttribute key="append.args" value="true"/>
+<stringAttribute key="application" value="org.eclipse.emf.cdo.server.app"/>
+<booleanAttribute key="askclear" value="true"/>
+<booleanAttribute key="automaticAdd" value="false"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="org.eclipse.emf.cdo.server,org.eclipse.emf.cdo.server.db,org.eclipse.net4j,org.eclipse.net4j.db,org.eclipse.net4j.db.derby,org.eclipse.net4j.http.common,org.eclipse.net4j.http.server,org.eclipse.net4j.tcp,org.eclipse.net4j.util"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="false"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/CDOServer_SSL"/>
+<booleanAttribute key="default" value="false"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../cdo.server"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+<mapEntry key="[run]" value="org.eclipse.pde.ui.RuntimeWorkbench"/>
+</mapAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -debug -console"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx1024m -Ddebug=true -Dnet4j.config="${resource_loc:/org.eclipse.emf.cdo.server.product-feature/rootfiles/configuration-ssl}" -Dorg.eclipse.net4j.tcp.ssl.passphrase=ab987c -Dorg.eclipse.net4j.tcp.ssl.trust=file:///${workspace_loc:org.eclipse.emf.cdo.server}/sslKey/testTrust -Dorg.eclipse.net4j.tcp.ssl.key=file:///${workspace_loc:org.eclipse.emf.cdo.server}/sslKey/testKeys -Dorg.eclipse.emf.cdo.server.browser.port=7777 -Dorg.osgi.service.http.port=8080"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.platform.ide"/>
+<stringAttribute key="selectedPlugin" value="org.eclipse.emf.cdo"/>
+<stringAttribute key="selected_target_plugins" value="com.mysql.jdbc@default:default,javax.servlet@default:default,org.apache.derby@default:default,org.eclipse.ant.core@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem.win32.x86_64@default:false,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime.compatibility.auth@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore.change@default:default,org.eclipse.emf.ecore.xmi@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.h2@default:default,org.hsqldb@default:default,org.postgresql.jdbc3@default:default"/>
+<stringAttribute key="selected_workspace_plugins" value="org.eclipse.emf.cdo.common@default:default,org.eclipse.emf.cdo.server.db@default:default,org.eclipse.emf.cdo.server.net4j@default:default,org.eclipse.emf.cdo.server@default:default,org.eclipse.emf.cdo@default:default,org.eclipse.net4j.db.derby@default:default,org.eclipse.net4j.db.h2@default:default,org.eclipse.net4j.db.hsqldb@default:default,org.eclipse.net4j.db.mysql@default:default,org.eclipse.net4j.db.postgresql@default:default,org.eclipse.net4j.db@default:default,org.eclipse.net4j.tcp@default:default,org.eclipse.net4j.util@default:default,org.eclipse.net4j@default:default"/>
+<booleanAttribute key="show_selected_only" value="true"/>
+<stringAttribute key="templateConfig" value="${target_home}\configuration\config.ini"/>
+<booleanAttribute key="tracing" value="true"/>
+<mapAttribute key="tracingOptions">
+<mapEntry key="org.eclipse.core.contenttype/debug" value="false"/>
+<mapEntry key="org.eclipse.core.expressions/tracePropertyResolving" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/beginend" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/errorondeadlock" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/locks" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/shutdown" value="false"/>
+<mapEntry key="org.eclipse.core.jobs/jobs/timing" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/delta" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/failure" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/interrupt" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/invoking" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuild" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/needbuildstack" value="false"/>
+<mapEntry key="org.eclipse.core.resources/build/stacktrace" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype" value="false"/>
+<mapEntry key="org.eclipse.core.resources/contenttype/cache" value="false"/>
+<mapEntry key="org.eclipse.core.resources/debug" value="false"/>
+<mapEntry key="org.eclipse.core.resources/history" value="false"/>
+<mapEntry key="org.eclipse.core.resources/natures" value="false"/>
+<mapEntry key="org.eclipse.core.resources/perf/builders" value="10000"/>
+<mapEntry key="org.eclipse.core.resources/perf/listeners" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/save.participants" value="500"/>
+<mapEntry key="org.eclipse.core.resources/perf/snapshot" value="1000"/>
+<mapEntry key="org.eclipse.core.resources/preferences" value="false"/>
+<mapEntry key="org.eclipse.core.resources/refresh" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/snapshots" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/restore/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/markers" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/mastertable" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/metainfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.resources/save/tree" value="false"/>
+<mapEntry key="org.eclipse.core.resources/strings" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/compatibility/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/debug/context" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/perf/success" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/preferences/plugin" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachecopy" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/cachelookup" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/url/debug/connect" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/commands" value="false"/>
+<mapEntry key="org.eclipse.debug.core/debug/events" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/contextlaunching" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/launchhistory" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/contentProvider" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/deltas" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/model" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/updateSequence" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug/viewers/viewer" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.reading" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.common/perf.revision.writing" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server.db/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.server/debug.types" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.adapter" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.model" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.object" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.repository" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.resource" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.revision" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.session" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.transaction" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.util" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/debug.view" value="true"/>
+<mapEntry key="org.eclipse.emf.cdo/perf" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision" value="false"/>
+<mapEntry key="org.eclipse.emf.cdo/perf.revision.loading" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/general" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/get" value="false"/>
+<mapEntry key="org.eclipse.equinox.preferences/set" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug" value="false"/>
+<mapEntry key="org.eclipse.equinox.registry/debug/events" value="false"/>
+<mapEntry key="org.eclipse.help.base/debug" value="true"/>
+<mapEntry key="org.eclipse.help.base/debug/search" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/embeddedBrowser/inprocess" value="false"/>
+<mapEntry key="org.eclipse.help.ui/debug/infopop" value="false"/>
+<mapEntry key="org.eclipse.help.webapp/debug" value="true"/>
+<mapEntry key="org.eclipse.help.webapp/debug/workingsets" value="false"/>
+<mapEntry key="org.eclipse.help/debug" value="true"/>
+<mapEntry key="org.eclipse.help/debug/context" value="false"/>
+<mapEntry key="org.eclipse.help/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core.manipulation/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.core/debug/buffermanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/builder" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/compiler" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/completion" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/cpresolution/advanced" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/hierarchy" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/indexmanager" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javadelta/verbose" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/javamodel/cache" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/postaction" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/resolution" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/search" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/selection" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/sourcemapper" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/zipaccess" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/perf/completion" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/containerinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/javadeltalistener" value="500"/>
+<mapEntry key="org.eclipse.jdt.core/perf/reconcile" value="1000"/>
+<mapEntry key="org.eclipse.jdt.core/perf/selection" value="300"/>
+<mapEntry key="org.eclipse.jdt.core/perf/variableinitializer" value="5000"/>
+<mapEntry key="org.eclipse.jdt.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ASTProvider" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/ResultCollector" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/debug/TypeConstraints" value="false"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist/extensions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/content_assist_sorters/extensions" value=""/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/RefactorActionGroup" value="150"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/createPartControl" value="1300"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/explorer/makeActions" value="1000"/>
+<mapEntry key="org.eclipse.jdt.ui/perf/search/participants" value="300"/>
+<mapEntry key="org.eclipse.jface.text.source/debug/RevisionRulerColumn" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/AnnotationPainter" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/ContentAssistSubjectAdapters" value="false"/>
+<mapEntry key="org.eclipse.jface.text/debug/FastPartitioner/PositionCache" value="false"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/checkConditions" value="300"/>
+<mapEntry key="org.eclipse.ltk.core.refactoring/perf/participants/createChanges" value="300"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.chat/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.common/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.buddies/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.derby/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.hsqldb/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db.mysql/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.db/debug.sql" value="true"/>
+<mapEntry key="org.eclipse.net4j.debug/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.examples/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.fileshare/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.admin/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server.jdbc/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms.server/debug.store" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.jms/debug.protocol" value="true"/>
+<mapEntry key="org.eclipse.net4j.jvm/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.tcp/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util.ui/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.concurrency" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.lifecycle.dump" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.om" value="true"/>
+<mapEntry key="org.eclipse.net4j.util/debug.registry" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.acceptor" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.buffer.stream" value="false"/>
+<mapEntry key="org.eclipse.net4j/debug.channel" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.connector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.selector" value="true"/>
+<mapEntry key="org.eclipse.net4j/debug.signal" value="true"/>
+<mapEntry key="org.eclipse.net4j/perf" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/bundleTime" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/events" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/filter" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/loader" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/manifest" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/messageBundles" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/packageadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/security" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/services" value="false"/>
+<mapEntry key="org.eclipse.osgi/debug/startlevel" value="false"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/buffersize" value="256"/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logfilename" value=""/>
+<mapEntry key="org.eclipse.osgi/defaultprofile/logsynchronously" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/converter/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/location" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin" value="false"/>
+<mapEntry key="org.eclipse.osgi/eclipseadaptor/debug/platformadmin/resolver" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/classes" value="false"/>
+<mapEntry key="org.eclipse.osgi/monitor/resources" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/benchmark" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/profile/impl" value="org.eclipse.osgi.internal.profile.DefaultProfileLogger"/>
+<mapEntry key="org.eclipse.osgi/profile/startup" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/cycles" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/debug" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/generics" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/grouping" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/imports" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/requires" value="false"/>
+<mapEntry key="org.eclipse.osgi/resolver/wiring" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/activation" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/classLoading" value="false"/>
+<mapEntry key="org.eclipse.osgi/trace/filename" value="runtime.traces"/>
+<mapEntry key="org.eclipse.osgi/trace/filters" value="trace.properties"/>
+<mapEntry key="org.eclipse.pde.build/debug" value="false"/>
+<mapEntry key="org.eclipse.pde.core/cache" value="false"/>
+<mapEntry key="org.eclipse.pde.core/classpath" value="false"/>
+<mapEntry key="org.eclipse.pde.core/debug" value="true"/>
+<mapEntry key="org.eclipse.pde.core/validation" value="false"/>
+<mapEntry key="org.eclipse.team.core/backgroundevents" value="false"/>
+<mapEntry key="org.eclipse.team.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.core/refreshjob" value="false"/>
+<mapEntry key="org.eclipse.team.core/streams" value="false"/>
+<mapEntry key="org.eclipse.team.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/cvsprotocol" value="true"/>
+<mapEntry key="org.eclipse.team.cvs.core/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/dirtycaching" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/metafiles" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/syncchangeevents" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.core/threading" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/debug" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/ssh_protocol" value="false"/>
+<mapEntry key="org.eclipse.ui.browser/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/gc" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/internalerror/openDialog" value="false"/>
+<mapEntry key="org.eclipse.ui.ide/debug/undomonitor" value="false"/>
+<mapEntry key="org.eclipse.ui.intro.universal/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro.universal/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/debug" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/flags/noBrowser" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logInfo" value="true"/>
+<mapEntry key="org.eclipse.ui.intro/trace/logPerformance" value="false"/>
+<mapEntry key="org.eclipse.ui.intro/trace/printHTML" value="false"/>
+<mapEntry key="org.eclipse.ui.workbench/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/contributions" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/declaredImages" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/job.stale" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/showAllJobs" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/swtdebug" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/workingSets" value="false"/>
+<mapEntry key="org.eclipse.ui/experimental/menus" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPage.IPropertyChangeListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchPartReference" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPageListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPartListener2" value="false"/>
+<mapEntry key="org.eclipse.ui/listeners/IWorkbenchWindow.IPerspectiveListener" value="false"/>
+<mapEntry key="org.eclipse.ui/perf/contentTypes" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/page.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.activate" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.control" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.init" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/part.input" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/part.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.create" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.listeners" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/perspective.switch" value="800"/>
+<mapEntry key="org.eclipse.ui/perf/showHeapStatus" value="true"/>
+<mapEntry key="org.eclipse.ui/perf/uijob" value="200"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.restore" value="30000"/>
+<mapEntry key="org.eclipse.ui/perf/workbench.start" value="45000"/>
+<mapEntry key="org.eclipse.ui/trace/commands" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/contexts.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/graphics" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.performance" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/handlers.verbose.commandId" value=""/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/keyBindings.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/multipageeditor" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/operations.verbose" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/sources" value="false"/>
+<mapEntry key="org.eclipse.update.configurator/debug" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug" value="true"/>
+<mapEntry key="org.eclipse.update.core/debug/configuration" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/install" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/installhandler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/parsing" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/reconciler" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/type" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/warning" value="false"/>
+<mapEntry key="org.eclipse.update.core/debug/web" value="false"/>
+</mapAttribute>
+<booleanAttribute key="useCustomFeatures" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<booleanAttribute key="useNamedJRE" value="true"/>
+<booleanAttribute key="useProduct" value="false"/>
+<booleanAttribute key="usefeatures" value="false"/>
+</launchConfiguration>
diff --git a/org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF b/org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..b35cecd3ec --- /dev/null +++ b/org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF @@ -0,0 +1,29 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: org.eclipse.emf.cdo.server;singleton:=true +Bundle-Version: 4.1.0.qualifier +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Bundle-ActivationPolicy: lazy +Bundle-Activator: org.eclipse.emf.cdo.internal.server.bundle.OM$Activator +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-ClassPath: . +Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)";resolution:=optional, + org.eclipse.emf.cdo;bundle-version="[4.0.0,5.0.0)";visibility:=reexport, + org.eclipse.net4j.util;bundle-version="[3.1.0,4.0.0)";visibility:=reexport +Export-Package: org.eclipse.emf.cdo.internal.server;version="4.1.0"; + x-friends:="org.eclipse.emf.cdo.server.db, + org.eclipse.emf.cdo.server.net4j, + org.eclipse.emf.cdo.tests, + org.eclipse.emf.cdo.workspace, + org.eclipse.emf.cdo.server.hibernate", + org.eclipse.emf.cdo.internal.server.bundle;version="4.1.0";x-internal:=true, + org.eclipse.emf.cdo.internal.server.embedded;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests", + org.eclipse.emf.cdo.internal.server.mem;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests", + org.eclipse.emf.cdo.internal.server.messages;version="4.1.0";x-internal:=true, + org.eclipse.emf.cdo.internal.server.syncing;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests", + org.eclipse.emf.cdo.server;version="4.1.0", + org.eclipse.emf.cdo.server.embedded;version="4.1.0", + org.eclipse.emf.cdo.server.mem;version="4.1.0", + org.eclipse.emf.cdo.spi.server;version="4.1.0" diff --git a/org.eclipse.emf.cdo.server/about.html b/org.eclipse.emf.cdo.server/about.html new file mode 100644 index 0000000000..d35d5aed64 --- /dev/null +++ b/org.eclipse.emf.cdo.server/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 5, 2007</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("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 <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("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 <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html> diff --git a/org.eclipse.emf.cdo.server/about.ini b/org.eclipse.emf.cdo.server/about.ini new file mode 100644 index 0000000000..b7e87ca285 --- /dev/null +++ b/org.eclipse.emf.cdo.server/about.ini @@ -0,0 +1,15 @@ +# about.ini +# contains information about a feature +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# "%key" are externalized strings defined in about.properties +# This file does not need to be translated. + +# Property "aboutText" contains blurb for "About" dialog (translated) +aboutText=%featureText + +# Property "featureImage" contains path to feature image (32x32) +featureImage=modeling32.png + +# Property "appName" contains name of the application (translated) +appName=%featureName + diff --git a/org.eclipse.emf.cdo.server/about.mappings b/org.eclipse.emf.cdo.server/about.mappings new file mode 100644 index 0000000000..bddaab4310 --- /dev/null +++ b/org.eclipse.emf.cdo.server/about.mappings @@ -0,0 +1,6 @@ +# about.mappings +# contains fill-ins for about.properties +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# This file does not need to be translated. + +0=@build@
\ No newline at end of file diff --git a/org.eclipse.emf.cdo.server/about.properties b/org.eclipse.emf.cdo.server/about.properties new file mode 100644 index 0000000000..24af1ffb0c --- /dev/null +++ b/org.eclipse.emf.cdo.server/about.properties @@ -0,0 +1,31 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Eike Stepper - initial API and implementation + +# NLS_MESSAGEFORMAT_VAR + +# ============================================================================== +# Do not change the properties between this line and the last line containing: +# %%% END OF TRANSLATED PROPERTIES %%% +# Instead, either redefine an existing property, or create a new property, +# append it to the end of the file, and change the code to use the new name. +# ============================================================================== + +featureName = CDO Model Repository Server +featureText = CDO Model Repository Server\n\ +Version: {featureVersion}\n\ +Build id: {0}\n\ +\n\ +Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. All rights reserved.\n\ +\n\ +Visit http://www.eclipse.org/cdo + +# ============================================================================== +# %%% END OF TRANSLATED PROPERTIES %%% +# The above properties have been shipped for translation. +# ============================================================================== diff --git a/org.eclipse.emf.cdo.server/build.properties b/org.eclipse.emf.cdo.server/build.properties new file mode 100644 index 0000000000..2c3acd3537 --- /dev/null +++ b/org.eclipse.emf.cdo.server/build.properties @@ -0,0 +1,32 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Eike Stepper - initial API and implementation + +# NLS_MESSAGEFORMAT_VAR + +bin.includes = .,\ + META-INF/,\ + plugin.properties,\ + .options,\ + about.html,\ + copyright.txt,\ + plugin.xml,\ + schema/,\ + about.ini,\ + about.mappings,\ + about.properties,\ + modeling32.png +jars.compile.order = . +source.. = src/ +output.. = bin/ +src.includes = about.html,\ + copyright.txt,\ + CDOServer.launch,\ + CDOServer_SSL.launch + +org.eclipse.emf.cdo.releng.javadoc.project = org.eclipse.emf.cdo.doc diff --git a/org.eclipse.emf.cdo.server/copyright.txt b/org.eclipse.emf.cdo.server/copyright.txt new file mode 100644 index 0000000000..e921242cf0 --- /dev/null +++ b/org.eclipse.emf.cdo.server/copyright.txt @@ -0,0 +1,8 @@ +Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html + +Contributors: + Eike Stepper - initial API and implementation
\ No newline at end of file diff --git a/org.eclipse.emf.cdo.server/modeling32.png b/org.eclipse.emf.cdo.server/modeling32.png Binary files differnew file mode 100644 index 0000000000..6b08de2ada --- /dev/null +++ b/org.eclipse.emf.cdo.server/modeling32.png diff --git a/org.eclipse.emf.cdo.server/plugin.properties b/org.eclipse.emf.cdo.server/plugin.properties new file mode 100644 index 0000000000..9cc5cf8608 --- /dev/null +++ b/org.eclipse.emf.cdo.server/plugin.properties @@ -0,0 +1,17 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Eike Stepper - initial API and implementation + +pluginName = CDO Model Repository Server +providerName = Eclipse Modeling Project + +app.name = CDOServer + +extension-point.name = CDO Store Factories +extension-point.name.0 = CDO Repository Factories +extension-point.name.1 = CDO Application Extensions diff --git a/org.eclipse.emf.cdo.server/plugin.xml b/org.eclipse.emf.cdo.server/plugin.xml new file mode 100644 index 0000000000..124577ba01 --- /dev/null +++ b/org.eclipse.emf.cdo.server/plugin.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<!-- + Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + Contributors: + Eike Stepper - initial API and implementation +--> + +<plugin> + + <extension-point id="storeFactories" name="%extension-point.name" schema="schema/storeFactories.exsd"/> + <extension-point id="repositoryFactories" name="%extension-point.name.0" schema="schema/repositoryFactories.exsd"/> + <extension-point id="appExtensions" name="%extension-point.name.1" schema="schema/appExtensions.exsd"/> + + <extension point="org.eclipse.net4j.util.factories"> + <factory + productGroup="org.eclipse.net4j.Negotiators" + type="challenge" + class="org.eclipse.net4j.util.security.ChallengeNegotiatorFactory"/> + <factory + productGroup="org.eclipse.emf.cdo.server.browsers" + type="default" + class="org.eclipse.emf.cdo.server.CDOServerBrowser$ContainerBased$Factory"/> + </extension> + + <extension point="org.eclipse.net4j.util.elementProcessors"> + <elementProcessor class="org.eclipse.net4j.util.security.ChallengeNegotiatorConfigurer"/> + <elementProcessor class="org.eclipse.emf.cdo.spi.server.RepositoryUserManager$RepositoryInjector"/> + </extension> + + <extension point="org.eclipse.emf.cdo.server.repositoryFactories"> + <repositoryFactory + class="org.eclipse.emf.cdo.spi.server.RepositoryFactory" + repositoryType="default"/> + </extension> + + <extension point="org.eclipse.emf.cdo.server.storeFactories"> + <storeFactory + class="org.eclipse.emf.cdo.internal.server.mem.MEMStoreFactory" + storeType="mem"/> + </extension> + + <extension id="app" point="org.eclipse.core.runtime.applications" name="%app.name"> + <application cardinality="1" thread="any"> + <run class="org.eclipse.emf.cdo.internal.server.bundle.CDOServerApplication"/> + </application> + </extension> + +</plugin> diff --git a/org.eclipse.emf.cdo.server/schema/appExtensions.exsd b/org.eclipse.emf.cdo.server/schema/appExtensions.exsd new file mode 100644 index 0000000000..3b3f70e5a5 --- /dev/null +++ b/org.eclipse.emf.cdo.server/schema/appExtensions.exsd @@ -0,0 +1,103 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.emf.cdo.server" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.emf.cdo.server" id="appExtensions" name="Application Extensions"/> + </appInfo> + <documentation> + [Enter description of this extension point.] + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element /> + </appInfo> + </annotation> + <complexType> + <sequence> + <element ref="appExtension" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="appExtension"> + <complexType> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn=":org.eclipse.emf.cdo.spi.server.IAppExtension"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + [Enter the first release in which this extension point appears.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiinfo"/> + </appInfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.<br> +All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + + +</schema> diff --git a/org.eclipse.emf.cdo.server/schema/repositoryFactories.exsd b/org.eclipse.emf.cdo.server/schema/repositoryFactories.exsd new file mode 100644 index 0000000000..1cdabf2f3b --- /dev/null +++ b/org.eclipse.emf.cdo.server/schema/repositoryFactories.exsd @@ -0,0 +1,113 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.emf.cdo.server"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.emf.cdo.server" id="repositoryFactories" name="CDO Repository Factories"/> + </appInfo> + <documentation> + [Enter description of this extension point.] + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="repositoryFactory" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="repositoryFactory"> + <complexType> + <attribute name="repositoryType" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn=":org.eclipse.emf.cdo.server.IRepositoryFactory"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + [Enter the first release in which this extension point appears.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + [Enter information about supplied implementation of this extension point.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.<br> +All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema> diff --git a/org.eclipse.emf.cdo.server/schema/storeFactories.exsd b/org.eclipse.emf.cdo.server/schema/storeFactories.exsd new file mode 100644 index 0000000000..b6cd6bd4ec --- /dev/null +++ b/org.eclipse.emf.cdo.server/schema/storeFactories.exsd @@ -0,0 +1,113 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.emf.cdo.server"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.emf.cdo.server" id="storeFactories" name="CDO Store Factories"/> + </appInfo> + <documentation> + [Enter description of this extension point.] + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="storeFactory" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="storeFactory"> + <complexType> + <attribute name="storeType" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn=":org.eclipse.emf.cdo.server.IStoreFactory"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + [Enter the first release in which this extension point appears.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + [Enter information about supplied implementation of this extension point.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.<br> +All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema> diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java new file mode 100644 index 0000000000..9a0d7598b0 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalCommitManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class CommitManager extends Lifecycle implements InternalCommitManager +{ + private InternalRepository repository; + + @ExcludeFromDump + private transient ExecutorService executors; + + private boolean shutdownExecutorService; + + @ExcludeFromDump + private transient Map<InternalTransaction, TransactionCommitContextEntry> contextEntries = new ConcurrentHashMap<InternalTransaction, TransactionCommitContextEntry>(); + + public CommitManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + } + + public synchronized ExecutorService getExecutors() + { + if (executors == null) + { + shutdownExecutorService = true; + executors = Executors.newFixedThreadPool(10); + } + + return executors; + } + + public synchronized void setExecutors(ExecutorService executors) + { + if (shutdownExecutorService) + { + this.executors.shutdown(); + shutdownExecutorService = false; + } + + this.executors = executors; + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + setExecutors(null); + } + + /** + * Create a future to execute commitContext in a different thread. + */ + public void preCommit(InternalCommitContext commitContext, OMMonitor monitor) + { + TransactionCommitContextEntry contextEntry = new TransactionCommitContextEntry(monitor); + contextEntry.setContext(commitContext); + + Future<Object> future = getExecutors().submit(contextEntry.createCallable()); + contextEntry.setFuture(future); + + contextEntries.put(commitContext.getTransaction(), contextEntry); + } + + /** + * Called after a commitContext is done successfully or not. + */ + public void remove(InternalCommitContext commitContext) + { + contextEntries.remove(commitContext.getTransaction()); + } + + public void rollback(InternalCommitContext commitContext) + { + TransactionCommitContextEntry contextEntry = contextEntries.get(commitContext.getTransaction()); + if (contextEntry != null) + { + contextEntry.getFuture().cancel(true); + commitContext.rollback("Remote rollback"); //$NON-NLS-1$ + commitContext.postCommit(false); + } + } + + /** + * Waiting for a commit to be done. + */ + public void waitForTermination(InternalTransaction transaction) throws InterruptedException, ExecutionException + { + TransactionCommitContextEntry contextEntry = contextEntries.get(transaction); + contextEntry.getFuture().get(); + } + + public InternalCommitContext get(InternalTransaction transaction) + { + TransactionCommitContextEntry contextEntry = contextEntries.get(transaction); + if (contextEntry != null) + { + return contextEntry.getContext(); + } + + return null; + } + + /** + * @author Simon McDuff + */ + private static final class TransactionCommitContextEntry + { + private InternalCommitContext context; + + private Future<Object> future; + + private OMMonitor monitor; + + public TransactionCommitContextEntry(OMMonitor monitor) + { + this.monitor = monitor; + } + + public Callable<Object> createCallable() + { + return new Callable<Object>() + { + public Object call() throws Exception + { + context.write(monitor); + return null; + } + }; + } + + public InternalCommitContext getContext() + { + return context; + } + + public void setContext(InternalCommitContext context) + { + this.context = context; + } + + public Future<Object> getFuture() + { + return future; + } + + public void setFuture(Future<Object> future) + { + this.future = future; + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java new file mode 100644 index 0000000000..f724419603 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.emf.ecore.EClass; + +import java.util.Map; + +/** + * @author Eike Stepper + */ +public abstract class DelegatingCommitContext implements IStoreAccessor.CommitContext +{ + protected abstract CommitContext getDelegate(); + + public ITransaction getTransaction() + { + return getDelegate().getTransaction(); + } + + public CDOBranchPoint getBranchPoint() + { + return getDelegate().getBranchPoint(); + } + + public String getUserID() + { + return getDelegate().getUserID(); + } + + public String getCommitComment() + { + return getDelegate().getCommitComment(); + } + + public boolean isAutoReleaseLocksEnabled() + { + return getDelegate().isAutoReleaseLocksEnabled(); + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + return getDelegate().getPackageRegistry(); + } + + public InternalCDOPackageUnit[] getNewPackageUnits() + { + return getDelegate().getNewPackageUnits(); + } + + public InternalCDORevision[] getNewObjects() + { + return getDelegate().getNewObjects(); + } + + public InternalCDORevision[] getDirtyObjects() + { + return getDelegate().getDirtyObjects(); + } + + public InternalCDORevisionDelta[] getDirtyObjectDeltas() + { + return getDelegate().getDirtyObjectDeltas(); + } + + public CDOID[] getDetachedObjects() + { + return getDelegate().getDetachedObjects(); + } + + public Map<CDOID, EClass> getDetachedObjectTypes() + { + return getDelegate().getDetachedObjectTypes(); + } + + public CDORevision getRevision(CDOID id) + { + return getDelegate().getRevision(id); + } + + public Map<CDOID, CDOID> getIDMappings() + { + return getDelegate().getIDMappings(); + } + + public String getRollbackMessage() + { + return getDelegate().getRollbackMessage(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java new file mode 100644 index 0000000000..8c4fd91a3b --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java @@ -0,0 +1,286 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IQueryHandlerProvider; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo; +import org.eclipse.emf.cdo.spi.server.InternalCommitManager; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalQueryManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalSessionManager; +import org.eclipse.emf.cdo.spi.server.InternalStore; + +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public abstract class DelegatingRepository implements InternalRepository +{ + public DelegatingRepository() + { + } + + protected abstract InternalRepository getDelegate(); + + public void addHandler(Handler handler) + { + getDelegate().addHandler(handler); + } + + public void addListener(IListener listener) + { + getDelegate().addListener(listener); + } + + public long[] createCommitTimeStamp(OMMonitor monitor) + { + return getDelegate().createCommitTimeStamp(monitor); + } + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, + int chunkEnd) + { + return getDelegate().ensureChunk(revision, feature, chunkStart, chunkEnd); + } + + public InternalCommitManager getCommitManager() + { + return getDelegate().getCommitManager(); + } + + public long getCreationTime() + { + return getDelegate().getCreationTime(); + } + + public Object[] getElements() + { + return getDelegate().getElements(); + } + + public long getLastCommitTimeStamp() + { + return getDelegate().getLastCommitTimeStamp(); + } + + public IListener[] getListeners() + { + return getDelegate().getListeners(); + } + + public InternalLockManager getLockManager() + { + return getDelegate().getLockManager(); + } + + public String getName() + { + return getDelegate().getName(); + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + return getDelegate().getPackageRegistry(); + } + + public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext) + { + return getDelegate().getPackageRegistry(considerCommitContext); + } + + public Map<String, String> getProperties() + { + return getDelegate().getProperties(); + } + + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + return getDelegate().getQueryHandler(info); + } + + public IQueryHandlerProvider getQueryHandlerProvider() + { + return getDelegate().getQueryHandlerProvider(); + } + + public InternalQueryManager getQueryManager() + { + return getDelegate().getQueryManager(); + } + + public InternalCDORevisionManager getRevisionManager() + { + return getDelegate().getRevisionManager(); + } + + public InternalSessionManager getSessionManager() + { + return getDelegate().getSessionManager(); + } + + public InternalStore getStore() + { + return getDelegate().getStore(); + } + + public String getUUID() + { + return getDelegate().getUUID(); + } + + public boolean hasListeners() + { + return getDelegate().hasListeners(); + } + + public boolean isEmpty() + { + return getDelegate().isEmpty(); + } + + public boolean isSupportingAudits() + { + return getDelegate().isSupportingAudits(); + } + + public boolean isSupportingBranches() + { + return getDelegate().isSupportingBranches(); + } + + public EPackage[] loadPackages(CDOPackageUnit packageUnit) + { + return getDelegate().loadPackages(packageUnit); + } + + public InternalCDOBranchManager getBranchManager() + { + return getDelegate().getBranchManager(); + } + + public void setBranchManager(InternalCDOBranchManager branchManager) + { + getDelegate().setBranchManager(branchManager); + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + return getDelegate().createBranch(branchID, branchInfo); + } + + public BranchInfo loadBranch(int branchID) + { + return getDelegate().loadBranch(branchID); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + return getDelegate().loadSubBranches(branchID); + } + + public List<InternalCDORevision> loadRevisions(List<RevisionInfo> infos, CDOBranchPoint branchPoint, + int referenceChunk, int prefetchDepth) + { + return getDelegate().loadRevisions(infos, branchPoint, referenceChunk, prefetchDepth); + } + + public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk) + { + return getDelegate().loadRevisionByVersion(id, branchVersion, referenceChunk); + } + + public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions, + List<CDORevision> additionalRevisions) + { + getDelegate().notifyReadAccessHandlers(session, revisions, additionalRevisions); + } + + public void notifyWriteAccessHandlers(ITransaction transaction, CommitContext commitContext, boolean beforeCommit, + OMMonitor monitor) + { + getDelegate().notifyWriteAccessHandlers(transaction, commitContext, beforeCommit, monitor); + } + + public void removeHandler(Handler handler) + { + getDelegate().removeHandler(handler); + } + + public void removeListener(IListener listener) + { + getDelegate().removeListener(listener); + } + + public void setName(String name) + { + getDelegate().setName(name); + } + + public void setProperties(Map<String, String> properties) + { + getDelegate().setProperties(properties); + } + + public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider) + { + getDelegate().setQueryHandlerProvider(queryHandlerProvider); + } + + public void setRevisionManager(InternalCDORevisionManager revisionManager) + { + getDelegate().setRevisionManager(revisionManager); + } + + public void setSessionManager(InternalSessionManager sessionManager) + { + getDelegate().setSessionManager(sessionManager); + } + + public void setStore(InternalStore store) + { + getDelegate().setStore(store); + } + + public long getTimeStamp() + { + return getDelegate().getTimeStamp(); + } + + public void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + getDelegate().validateTimeStamp(timeStamp); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java new file mode 100644 index 0000000000..5db93a490f --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java @@ -0,0 +1,694 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + * Caspar De Groot - write options + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.CDOCommonView; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.ISessionManager; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalStore; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.RWOLockManager; +import org.eclipse.net4j.util.container.ContainerEventAdapter; +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.lifecycle.ILifecycle; +import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter; +import org.eclipse.net4j.util.options.IOptionsContainer; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author Simon McDuff + * @since 3.0 + */ +public class LockManager extends RWOLockManager<Object, IView> implements InternalLockManager +{ + private InternalRepository repository; + + private Map<String, InternalView> openViews = new HashMap<String, InternalView>(); + + private Map<String, DurableView> durableViews = new HashMap<String, DurableView>(); + + @ExcludeFromDump + private transient IListener sessionListener = new ContainerEventAdapter<IView>() + { + @Override + protected void onRemoved(IContainer<IView> container, IView view) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID == null) + { + unlock(view); + } + else + { + changeContext(view, new DurableView(durableLockingID)); + unregisterOpenView(durableLockingID); + } + } + }; + + @ExcludeFromDump + private transient IListener sessionManagerListener = new ContainerEventAdapter<ISession>() + { + @Override + protected void onAdded(IContainer<ISession> container, ISession session) + { + session.addListener(sessionListener); + } + + @Override + protected void onRemoved(IContainer<ISession> container, ISession session) + { + session.removeListener(sessionListener); + } + }; + + public LockManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + } + + public synchronized Object getLockEntryObject(Object key) + { + LockState<Object, IView> lockState = getObjectToLocksMap().get(key); + return lockState == null ? null : lockState.getLockedObject(); + } + + public Object getLockKey(CDOID id, CDOBranch branch) + { + if (repository.isSupportingBranches()) + { + return CDOIDUtil.createIDAndBranch(id, branch); + } + + return id; + } + + public synchronized Map<CDOID, LockGrade> getLocks(final IView view) + { + final Map<CDOID, LockGrade> result = new HashMap<CDOID, LockGrade>(); + + for (LockState<Object, IView> lockState : getObjectToLocksMap().values()) + { + LockGrade grade = LockGrade.NONE; + if (lockState.hasLock(LockType.READ, view, false)) + { + grade = grade.getUpdated(LockType.READ, true); + } + + if (lockState.hasLock(LockType.WRITE, view, false)) + { + grade = grade.getUpdated(LockType.WRITE, true); + } + + if (lockState.hasLock(LockType.OPTION, view, false)) + { + grade = grade.getUpdated(LockType.OPTION, true); + } + + if (grade != LockGrade.NONE) + { + CDOID id = getLockKeyID(lockState.getLockedObject()); + result.put(id, grade); + } + } + + return result; + } + + @Deprecated + public void lock(boolean explicit, LockType type, IView view, Collection<? extends Object> objectsToLock, long timeout) + throws InterruptedException + { + lock2(explicit, type, view, objectsToLock, timeout); + } + + public List<LockState<Object, IView>> lock2(boolean explicit, LockType type, IView view, + Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException + { + String durableLockingID = null; + DurableLocking accessor = null; + + if (explicit) + { + durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + accessor = getDurableLocking(); + } + } + + List<LockState<Object, IView>> newLockStates = super.lock2(type, view, objectsToLock, timeout); + + if (accessor != null) + { + accessor.lock(durableLockingID, type, objectsToLock); + } + + return newLockStates; + } + + @Deprecated + public synchronized void unlock(boolean explicit, LockType type, IView view, + Collection<? extends Object> objectsToUnlock) + { + unlock2(explicit, type, view, objectsToUnlock); + } + + public synchronized List<LockState<Object, IView>> unlock2(boolean explicit, LockType type, IView view, + Collection<? extends Object> objectsToUnlock) + { + List<LockState<Object, IView>> newLockStates = super.unlock2(type, view, objectsToUnlock); + + if (explicit) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + DurableLocking accessor = getDurableLocking(); + accessor.unlock(durableLockingID, type, objectsToUnlock); + } + } + + return newLockStates; + } + + @Deprecated + public synchronized void unlock(boolean explicit, IView view) + { + unlock(explicit, view); + } + + public synchronized List<LockState<Object, IView>> unlock2(boolean explicit, IView view) + { + if (explicit) + { + String durableLockingID = view.getDurableLockingID(); + if (durableLockingID != null) + { + DurableLocking accessor = getDurableLocking(); + accessor.unlock(durableLockingID); + } + } + + return super.unlock2(view); + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return createLockArea(userID, branchPoint, readOnly, locks, null); + } + + private LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks, String lockAreaID) + { + if (lockAreaID == null) + { + DurableLocking accessor = getDurableLocking(); + return accessor.createLockArea(userID, branchPoint, readOnly, locks); + } + + DurableLocking2 accessor = getDurableLocking2(); + return accessor.createLockArea(lockAreaID, userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(InternalView view) + { + return createLockArea(view, null); + } + + public LockArea createLockArea(InternalView view, String lockAreaID) + { + String userID = view.getSession().getUserID(); + CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); + boolean readOnly = view.isReadOnly(); + Map<CDOID, LockGrade> locks = getLocks(view); + + LockArea area = createLockArea(userID, branchPoint, readOnly, locks, lockAreaID); + synchronized (openViews) + { + openViews.put(area.getDurableLockingID(), view); + } + + return area; + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + DurableLocking accessor = getDurableLocking(); + return accessor.getLockArea(durableLockingID); + } + + public void getLockAreas(String userIDPrefix, LockArea.Handler handler) + { + if (userIDPrefix == null) + { + userIDPrefix = ""; + } + + DurableLocking accessor = getDurableLocking(); + accessor.getLockAreas(userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + DurableLocking accessor = getDurableLocking(); + accessor.deleteLockArea(durableLockingID); + unregisterOpenView(durableLockingID); + } + + public IView openView(ISession session, int viewID, boolean readOnly, final String durableLockingID) + { + synchronized (openViews) + { + InternalView view = openViews.get(durableLockingID); + if (view != null) + { + throw new IllegalStateException("Durable view is already open: " + view); + } + + LockArea area = getLockArea(durableLockingID); + if (area.isReadOnly() != readOnly) + { + throw new IllegalStateException("Durable read-only state does not match the request"); + } + + if (readOnly) + { + view = (InternalView)session.openView(viewID, area); + } + else + { + view = (InternalView)session.openTransaction(viewID, area); + } + + changeContext(new DurableView(durableLockingID), view); + view.setDurableLockingID(durableLockingID); + view.addListener(new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + synchronized (openViews) + { + openViews.remove(durableLockingID); + } + } + }); + + openViews.put(durableLockingID, view); + return view; + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + loadDurableLocks(); + getRepository().getSessionManager().addListener(sessionManagerListener); + } + + @Override + protected void doDeactivate() throws Exception + { + ISessionManager sessionManager = getRepository().getSessionManager(); + sessionManager.removeListener(sessionManagerListener); + for (ISession session : sessionManager.getSessions()) + { + session.removeListener(sessionListener); + } + + super.doDeactivate(); + } + + private DurableLocking getDurableLocking() + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + if (accessor instanceof DurableLocking) + { + return (DurableLocking)accessor; + } + + throw new IllegalStateException("Store does not implement " + DurableLocking.class.getSimpleName()); + } + + private DurableLocking2 getDurableLocking2() + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + if (accessor instanceof DurableLocking2) + { + return (DurableLocking2)accessor; + } + + throw new IllegalStateException("Store does not implement " + DurableLocking2.class.getSimpleName()); + } + + private void loadDurableLocks() + { + InternalStore store = repository.getStore(); + IStoreAccessor reader = null; + + try + { + reader = store.getReader(null); + if (reader instanceof DurableLocking) + { + StoreThreadLocal.setAccessor(reader); + + DurableLockLoader handler = new DurableLockLoader(); + getLockAreas(null, handler); + } + } + finally + { + StoreThreadLocal.release(); + } + } + + private void unregisterOpenView(String durableLockingID) + { + synchronized (openViews) + { + InternalView view = openViews.remove(durableLockingID); + if (view != null) + { + view.setDurableLockingID(null); + } + } + } + + public CDOID getLockKeyID(Object key) + { + if (key instanceof CDOID) + { + return (CDOID)key; + } + + if (key instanceof CDOIDAndBranch) + { + return ((CDOIDAndBranch)key).getID(); + } + + throw new ImplementationError("Unexpected lock object: " + key); + } + + /** + * @author Eike Stepper + */ + private final class DurableView implements IView, CDOCommonView.Options + { + private String durableLockingID; + + public DurableView(String durableLockingID) + { + this.durableLockingID = durableLockingID; + } + + public String getDurableLockingID() + { + return durableLockingID; + } + + public int getViewID() + { + throw new UnsupportedOperationException(); + } + + public boolean isReadOnly() + { + throw new UnsupportedOperationException(); + } + + public CDOBranch getBranch() + { + throw new UnsupportedOperationException(); + } + + public long getTimeStamp() + { + throw new UnsupportedOperationException(); + } + + public CDORevision getRevision(CDOID id) + { + throw new UnsupportedOperationException(); + } + + public void close() + { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() + { + throw new UnsupportedOperationException(); + } + + public IRepository getRepository() + { + throw new UnsupportedOperationException(); + } + + public ISession getSession() + { + return null; + } + + @Override + public int hashCode() + { + return durableLockingID.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + + if (obj instanceof DurableView) + { + DurableView that = (DurableView)obj; + return durableLockingID.equals(that.getDurableLockingID()); + } + + return false; + } + + @Override + public String toString() + { + return MessageFormat.format("DurableView[{0}]", durableLockingID); + } + + public IOptionsContainer getContainer() + { + return null; + } + + public void addListener(IListener listener) + { + } + + public void removeListener(IListener listener) + { + } + + public boolean hasListeners() + { + return false; + } + + public IListener[] getListeners() + { + return null; + } + + public Options options() + { + return this; + } + + public boolean isLockNotificationEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + } + } + + /** + * @author Eike Stepper + */ + private final class DurableLockLoader implements LockArea.Handler + { + public DurableLockLoader() + { + } + + public boolean handleLockArea(LockArea area) + { + String durableLockingID = area.getDurableLockingID(); + IView view = durableViews.get(durableLockingID); + if (view == null) + { + view = new DurableView(durableLockingID); + durableViews.put(durableLockingID, (DurableView)view); + } + + Collection<Object> readLocks = new ArrayList<Object>(); + Collection<Object> writeLocks = new ArrayList<Object>(); + Collection<Object> writeOptions = new ArrayList<Object>(); + for (Entry<CDOID, LockGrade> entry : area.getLocks().entrySet()) + { + Object key = getLockKey(entry.getKey(), area.getBranch()); + LockGrade grade = entry.getValue(); + if (grade.isRead()) + { + readLocks.add(key); + } + + if (grade.isWrite()) + { + writeLocks.add(key); + } + + if (grade.isOption()) + { + writeOptions.add(key); + } + } + + try + { + lock(LockType.READ, view, readLocks, 1000L); + lock(LockType.WRITE, view, writeLocks, 1000L); + lock(LockType.OPTION, view, writeOptions, 1000L); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + + return true; + } + } + + public LockGrade getLockGrade(Object key) + { + LockState<Object, IView> lockState = getObjectToLocksMap().get(key); + LockGrade grade = LockGrade.NONE; + if (lockState != null) + { + for (LockType type : LockType.values()) + { + if (lockState.hasLock(type)) + { + grade = grade.getUpdated(type, true); + } + } + } + return grade; + } + + private IView getView(String lockAreaID) + { + IView view = openViews.get(lockAreaID); + if (view == null) + { + view = durableViews.get(lockAreaID); + } + return view; + } + + private LockArea getLockAreaNoEx(String durableLockingID) + { + try + { + return getLockArea(durableLockingID); + } + catch (LockAreaNotFoundException e) + { + return null; + } + } + + public void updateLockArea(LockArea lockArea) + { + String durableLockingID = lockArea.getDurableLockingID(); + DurableLocking2 accessor = getDurableLocking2(); + + if (lockArea.isMissing()) + { + LockArea localLockArea = getLockAreaNoEx(durableLockingID); + if (localLockArea != null && localLockArea.getLocks().size() > 0) + { + accessor.deleteLockArea(durableLockingID); + DurableView deletedView = durableViews.remove(durableLockingID); + CheckUtil.checkNull(deletedView, "deletedView"); + } + } + else + { + accessor.updateLockArea(lockArea); + IView view = getView(durableLockingID); + if (view != null) + { + unlock2(view); + } + new DurableLockLoader().handleLockArea(lockArea); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java new file mode 100644 index 0000000000..963092d439 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java @@ -0,0 +1,319 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IQueryContext; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.server.InternalQueryManager; +import org.eclipse.emf.cdo.spi.server.InternalQueryResult; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.container.IContainerDelta.Kind; +import org.eclipse.net4j.util.container.SingleDeltaContainerEvent; +import org.eclipse.net4j.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class QueryManager extends Lifecycle implements InternalQueryManager +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SESSION, QueryManager.class); + + private InternalRepository repository; + + private Map<Integer, QueryContext> queryContexts = new ConcurrentHashMap<Integer, QueryContext>(); + + private ExecutorService executors; + + private boolean shutdownExecutorService; + + private int nextQuery; + + private boolean allowInterruptRunningQueries = true; + + public QueryManager() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(InternalRepository repository) + { + this.repository = repository; + + String value = repository.getProperties().get(IRepository.Props.ALLOW_INTERRUPT_RUNNING_QUERIES); + if (value != null) + { + allowInterruptRunningQueries = Boolean.parseBoolean(value); + } + } + + public synchronized ExecutorService getExecutors() + { + if (executors == null) + { + shutdownExecutorService = true; + executors = Executors.newFixedThreadPool(10); + } + + return executors; + } + + public synchronized void setExecutors(ExecutorService executors) + { + if (shutdownExecutorService) + { + this.executors.shutdown(); + shutdownExecutorService = false; + } + + this.executors = executors; + } + + public InternalQueryResult execute(InternalView view, CDOQueryInfo queryInfo) + { + InternalQueryResult queryResult = new QueryResult(view, queryInfo, nextQuery()); + QueryContext queryContext = new QueryContext(queryResult); + execute(queryContext); + return queryResult; + } + + public boolean isRunning(int queryID) + { + QueryContext queryContext = queryContexts.get(queryID); + return queryContext != null; + } + + public void cancel(int queryID) + { + QueryContext queryContext = queryContexts.get(queryID); + if (queryContext == null || queryContext.getFuture().isDone()) + { + throw new RuntimeException("Query " + queryID + " is not running anymore"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (TRACER.isEnabled()) + { + TRACER.trace("Cancelling query for context: " + queryContext); //$NON-NLS-1$ + } + + queryContext.cancel(); + } + + public synchronized void register(final QueryContext queryContext) + { + queryContexts.put(queryContext.getQueryResult().getQueryID(), queryContext); + queryContext.addListener(); + } + + public synchronized void unregister(final QueryContext queryContext) + { + if (queryContexts.remove(queryContext.getQueryResult().getQueryID()) != null) + { + queryContext.removeListener(); + } + } + + public synchronized int nextQuery() + { + return nextQuery++; + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + setExecutors(null); + } + + private Future<?> execute(QueryContext queryContext) + { + Future<?> future = getExecutors().submit(queryContext); + queryContext.setFuture(future); + register(queryContext); + return future; + } + + /** + * @author Simon McDuff + * @since 2.0 + */ + private class QueryContext implements IQueryContext, Runnable + { + private CDOBranchPoint branchPoint; + + private InternalQueryResult queryResult; + + private boolean started; + + private boolean cancelled; + + private int resultCount; + + private Future<?> future; + + private IListener sessionListener = new IListener() + { + public void notifyEvent(IEvent event) + { + if (event instanceof SingleDeltaContainerEvent<?>) + { + IView view = getQueryResult().getView(); + SingleDeltaContainerEvent<?> deltaEvent = (SingleDeltaContainerEvent<?>)event; + if (deltaEvent.getDeltaKind() == Kind.REMOVED && deltaEvent.getDeltaElement() == view) + { + // Cancel the query when view is closing + cancel(); + } + } + } + }; + + public QueryContext(InternalQueryResult queryResult) + { + this.queryResult = queryResult; + + // Remember the branchPoint because it can change + InternalView view = getView(); + + // long timeStamp = view.getTimeStamp(); + // if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE && repository.isSupportingAudits()) + // { + // timeStamp = repository.getTimeStamp(); + // } + // + // branchPoint = view.getBranch().getPoint(timeStamp); + + branchPoint = CDOBranchUtil.copyBranchPoint(view); + } + + public InternalQueryResult getQueryResult() + { + return queryResult; + } + + public InternalView getView() + { + return queryResult.getView(); + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public Future<?> getFuture() + { + return future; + } + + public void setFuture(Future<?> future) + { + this.future = future; + } + + public void cancel() + { + cancelled = true; + if (future != null) + { + future.cancel(allowInterruptRunningQueries); + } + + if (!started) + { + unregister(this); + } + } + + public int getResultCount() + { + return resultCount; + } + + public boolean addResult(Object object) + { + if (resultCount == 0) + { + throw new IllegalStateException("Maximum number of results exceeded"); //$NON-NLS-1$ + } + + queryResult.getQueue().add(object); + return !cancelled && --resultCount > 0; + } + + public void run() + { + InternalSession session = queryResult.getView().getSession(); + StoreThreadLocal.setSession(session); + + try + { + started = true; + CDOQueryInfo info = queryResult.getQueryInfo(); + resultCount = info.getMaxResults() < 0 ? Integer.MAX_VALUE : info.getMaxResults(); + IQueryHandler handler = repository.getQueryHandler(info); + handler.executeQuery(info, this); + } + catch (Throwable exception) + { + queryResult.getQueue().setException(exception); + } + finally + { + queryResult.getQueue().close(); + unregister(this); + StoreThreadLocal.release(); + } + } + + public void addListener() + { + InternalView view = getQueryResult().getView(); + view.getSession().addListener(sessionListener); + } + + public void removeListener() + { + InternalView view = getQueryResult().getView(); + view.getSession().removeListener(sessionListener); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java new file mode 100644 index 0000000000..c5c17b405e --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.spi.common.AbstractQueryResult; +import org.eclipse.emf.cdo.spi.server.InternalQueryResult; +import org.eclipse.emf.cdo.spi.server.InternalView; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class QueryResult extends AbstractQueryResult<Object> implements InternalQueryResult +{ + public QueryResult(InternalView view, CDOQueryInfo queryInfo, int queryID) + { + super(view, queryInfo, queryID); + } + + @Override + public InternalView getView() + { + return (InternalView)super.getView(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java new file mode 100644 index 0000000000..70dacf074b --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java @@ -0,0 +1,1923 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 201266 + * Simon McDuff - bug 233273 + * Simon McDuff - bug 233490 + * Stefan Winkler - changed order of determining audit and revision delta support. + * Andre Dietisheim - bug 256649 + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.CDOCommonView; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDGenerator; +import org.eclipse.emf.cdo.common.id.CDOIDTemp; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.common.util.RepositoryStateChangedEvent; +import org.eclipse.emf.cdo.common.util.RepositoryTypeChangedEvent; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.etypes.EtypesPackage; +import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IQueryHandlerProvider; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreChunkReader; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.CDOReplicationContext; +import org.eclipse.emf.cdo.spi.common.CDOReplicationInfo; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.CDOCommitInfoUtil; +import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo; +import org.eclipse.emf.cdo.spi.server.ContainerQueryHandlerProvider; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalCommitManager; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalQueryManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalSessionManager; +import org.eclipse.emf.cdo.spi.server.InternalStore; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.emf.internal.cdo.object.CDOFactoryImpl; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.transaction.TransactionException; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Semaphore; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class Repository extends Container<Object> implements InternalRepository +{ + private String name; + + private String uuid; + + private InternalStore store; + + private Type type = Type.MASTER; + + private State state = State.ONLINE; + + private Map<String, String> properties; + + private boolean supportingAudits; + + private boolean supportingBranches; + + private boolean supportingEcore; + + private boolean ensuringReferentialIntegrity; + + private IDGenerationLocation idGenerationLocation; + + /** + * Must not be thread-bound to support XA commits. + */ + private Semaphore packageRegistryCommitLock = new Semaphore(1); + + private InternalCDOPackageRegistry packageRegistry; + + private InternalCDOBranchManager branchManager; + + private InternalCDORevisionManager revisionManager; + + private InternalCDOCommitInfoManager commitInfoManager; + + private InternalSessionManager sessionManager; + + private InternalQueryManager queryManager; + + private InternalCommitManager commitManager; + + private InternalLockManager lockManager; + + private IQueryHandlerProvider queryHandlerProvider; + + private List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>(); + + private List<WriteAccessHandler> writeAccessHandlers = new ArrayList<WriteAccessHandler>(); + + private List<CDOCommitInfoHandler> commitInfoHandlers = new ArrayList<CDOCommitInfoHandler>(); + + private EPackage[] initialPackages; + + // Bugzilla 297940 + private TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this); + + @ExcludeFromDump + private transient Object createBranchLock = new Object(); + + private boolean skipInitialization; + + private CDOID rootResourceID; + + public Repository() + { + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getUUID() + { + if (uuid == null) + { + uuid = getProperties().get(Props.OVERRIDE_UUID); + if (uuid == null) + { + uuid = UUID.randomUUID().toString(); + } + else if (uuid.length() == 0) + { + uuid = getName(); + } + } + + return uuid; + } + + public InternalStore getStore() + { + return store; + } + + public void setStore(InternalStore store) + { + this.store = store; + } + + public Type getType() + { + return type; + } + + public void setType(Type type) + { + checkArg(type, "type"); //$NON-NLS-1$ + if (this.type != type) + { + changingType(this.type, type); + } + } + + protected void changingType(Type oldType, Type newType) + { + type = newType; + fireEvent(new RepositoryTypeChangedEvent(this, oldType, newType)); + + if (sessionManager != null) + { + sessionManager.sendRepositoryTypeNotification(oldType, newType); + } + } + + public State getState() + { + return state; + } + + public void setState(State state) + { + checkArg(state, "state"); //$NON-NLS-1$ + if (this.state != state) + { + changingState(this.state, state); + } + } + + protected void changingState(State oldState, State newState) + { + state = newState; + fireEvent(new RepositoryStateChangedEvent(this, oldState, newState)); + + if (sessionManager != null) + { + sessionManager.sendRepositoryStateNotification(oldState, newState, getRootResourceID()); + } + } + + public synchronized Map<String, String> getProperties() + { + if (properties == null) + { + properties = new HashMap<String, String>(); + } + + return properties; + } + + public synchronized void setProperties(Map<String, String> properties) + { + this.properties = properties; + } + + public boolean isSupportingAudits() + { + return supportingAudits; + } + + public boolean isSupportingBranches() + { + return supportingBranches; + } + + public boolean isSupportingEcore() + { + return supportingEcore; + } + + public boolean isEnsuringReferentialIntegrity() + { + return ensuringReferentialIntegrity; + } + + public IDGenerationLocation getIDGenerationLocation() + { + return idGenerationLocation; + } + + public String getStoreType() + { + return store.getType(); + } + + public Set<CDOID.ObjectType> getObjectIDTypes() + { + return store.getObjectIDTypes(); + } + + public CDOID getRootResourceID() + { + return rootResourceID; + } + + public void setRootResourceID(CDOID rootResourceID) + { + this.rootResourceID = rootResourceID; + } + + public Object processPackage(Object value) + { + CDOFactoryImpl.prepareDynamicEPackage(value); + return value; + } + + public EPackage[] loadPackages(CDOPackageUnit packageUnit) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.loadPackageUnit((InternalCDOPackageUnit)packageUnit); + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + if (!isSupportingBranches()) + { + throw new IllegalStateException("Branching is not supported by " + this); + } + + long baseTimeStamp = branchInfo.getBaseTimeStamp(); + if (baseTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + baseTimeStamp = getTimeStamp(); + branchInfo = new BranchInfo(branchInfo.getName(), branchInfo.getBaseBranchID(), baseTimeStamp); + } + + synchronized (createBranchLock) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.createBranch(branchID, branchInfo); + } + } + + public BranchInfo loadBranch(int branchID) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.loadBranch(branchID); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.loadSubBranches(branchID); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.loadBranches(startID, endID, branchHandler); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + accessor.loadCommitInfos(branch, startTime, endTime, handler); + } + + public CDOCommitData loadCommitData(long timeStamp) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.loadCommitData(timeStamp); + } + + public List<InternalCDORevision> loadRevisions(List<RevisionInfo> infos, CDOBranchPoint branchPoint, + int referenceChunk, int prefetchDepth) + { + for (RevisionInfo info : infos) + { + CDOID id = info.getID(); + RevisionInfo.Type type = info.getType(); + switch (type) + { + case AVAILABLE_NORMAL: // direct == false + { + RevisionInfo.Available.Normal availableInfo = (RevisionInfo.Available.Normal)info; + checkArg(availableInfo.isDirect() == false, "Load is not needed"); + break; + } + + case AVAILABLE_POINTER: // direct == false || target == null + { + RevisionInfo.Available.Pointer pointerInfo = (RevisionInfo.Available.Pointer)info; + boolean needsTarget = !pointerInfo.hasTarget(); + checkArg(pointerInfo.isDirect() == false || needsTarget, "Load is not needed"); + + if (needsTarget) + { + CDOBranchVersion targetBranchVersion = pointerInfo.getTargetBranchVersion(); + InternalCDORevision target = loadRevisionByVersion(id, targetBranchVersion, referenceChunk); + PointerCDORevision pointer = new PointerCDORevision(target.getEClass(), id, pointerInfo + .getAvailableBranchVersion().getBranch(), CDORevision.UNSPECIFIED_DATE, target); + + info.setResult(target); + info.setSynthetic(pointer); + continue; + } + + break; + } + + case AVAILABLE_DETACHED: // direct == false + { + RevisionInfo.Available.Detached detachedInfo = (RevisionInfo.Available.Detached)info; + checkArg(detachedInfo.isDirect() == false, "Load is not needed"); + break; + } + + case MISSING: + { + break; + } + + default: + throw new IllegalStateException("Invalid revision info type: " + type); + } + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + InternalCDORevision revision = accessor.readRevision(id, branchPoint, referenceChunk, revisionManager); + if (revision == null) + { + if (isSupportingAudits()) + { + // Case "Pointer"? + InternalCDORevision target = loadRevisionTarget(id, branchPoint, referenceChunk, accessor); + if (target != null) + { + CDOBranch branch = branchPoint.getBranch(); + long revised = loadRevisionRevised(id, branch); + PointerCDORevision pointer = new PointerCDORevision(target.getEClass(), id, branch, revised, target); + info.setSynthetic(pointer); + } + + info.setResult(target); + } + else + { + DetachedCDORevision detachedRevision = new DetachedCDORevision(EcorePackage.Literals.ECLASS, id, + branchPoint.getBranch(), 0, CDORevision.UNSPECIFIED_DATE); + info.setSynthetic(detachedRevision); + } + } + else if (revision instanceof DetachedCDORevision) + { + DetachedCDORevision detached = (DetachedCDORevision)revision; + info.setSynthetic(detached); + } + else + { + revision.freeze(); + info.setResult(revision); + } + } + + return null; + } + + private InternalCDORevision loadRevisionTarget(CDOID id, CDOBranchPoint branchPoint, int referenceChunk, + IStoreAccessor accessor) + { + CDOBranch branch = branchPoint.getBranch(); + while (!branch.isMainBranch()) + { + branchPoint = branch.getBase(); + branch = branchPoint.getBranch(); + + InternalCDORevision revision = accessor.readRevision(id, branchPoint, referenceChunk, revisionManager); + if (revision != null) + { + revision.freeze(); + return revision; + } + } + + return null; + } + + private long loadRevisionRevised(CDOID id, CDOBranch branch) + { + InternalCDORevision revision = loadRevisionByVersion(id, branch.getVersion(CDORevision.FIRST_VERSION), + CDORevision.UNCHUNKED); + if (revision != null) + { + return revision.getTimeStamp() - 1; + } + + return CDORevision.UNSPECIFIED_DATE; + } + + public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + return accessor.readRevisionByVersion(id, branchVersion, referenceChunk, revisionManager); + } + + protected void ensureChunks(InternalCDORevision revision, int referenceChunk, IStoreAccessor accessor) + { + EClass eClass = revision.getEClass(); + EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass); + for (int i = 0; i < features.length; i++) + { + EStructuralFeature feature = features[i]; + if (feature.isMany()) + { + MoveableList<Object> list = revision.getList(feature); + int chunkEnd = Math.min(referenceChunk, list.size()); + accessor = ensureChunk(revision, feature, accessor, list, 0, chunkEnd); + } + } + } + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, + int chunkEnd) + { + MoveableList<Object> list = revision.getList(feature); + chunkEnd = Math.min(chunkEnd, list.size()); + return ensureChunk(revision, feature, StoreThreadLocal.getAccessor(), list, chunkStart, chunkEnd); + } + + protected IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, + IStoreAccessor accessor, MoveableList<Object> list, int chunkStart, int chunkEnd) + { + IStoreChunkReader chunkReader = null; + int fromIndex = -1; + for (int j = chunkStart; j < chunkEnd; j++) + { + if (list.get(j) == InternalCDOList.UNINITIALIZED) + { + if (fromIndex == -1) + { + fromIndex = j; + } + } + else + { + if (fromIndex != -1) + { + if (chunkReader == null) + { + if (accessor == null) + { + accessor = StoreThreadLocal.getAccessor(); + } + + chunkReader = accessor.createChunkReader(revision, feature); + } + + int toIndex = j; + if (fromIndex == toIndex - 1) + { + chunkReader.addSimpleChunk(fromIndex); + } + else + { + chunkReader.addRangedChunk(fromIndex, toIndex); + } + + fromIndex = -1; + } + } + } + + // Add last chunk + if (fromIndex != -1) + { + if (chunkReader == null) + { + if (accessor == null) + { + accessor = StoreThreadLocal.getAccessor(); + } + + chunkReader = accessor.createChunkReader(revision, feature); + } + + int toIndex = chunkEnd; + if (fromIndex == toIndex - 1) + { + chunkReader.addSimpleChunk(fromIndex); + } + else + { + chunkReader.addRangedChunk(fromIndex, toIndex); + } + } + + if (chunkReader != null) + { + InternalCDOList cdoList = list instanceof InternalCDOList ? (InternalCDOList)list : null; + + List<Chunk> chunks = chunkReader.executeRead(); + for (Chunk chunk : chunks) + { + int startIndex = chunk.getStartIndex(); + for (int indexInChunk = 0; indexInChunk < chunk.size(); indexInChunk++) + { + Object id = chunk.get(indexInChunk); + if (cdoList != null) + { + cdoList.setWithoutFrozenCheck(startIndex + indexInChunk, id); + } + else + { + list.set(startIndex + indexInChunk, id); + } + } + } + } + + return accessor; + } + + public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext) + { + if (considerCommitContext) + { + IStoreAccessor.CommitContext commitContext = StoreThreadLocal.getCommitContext(); + if (commitContext != null) + { + InternalCDOPackageRegistry contextualPackageRegistry = commitContext.getPackageRegistry(); + if (contextualPackageRegistry != null) + { + return contextualPackageRegistry; + } + } + } + + return packageRegistry; + } + + public Semaphore getPackageRegistryCommitLock() + { + return packageRegistryCommitLock; + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + return getPackageRegistry(true); + } + + public void setPackageRegistry(InternalCDOPackageRegistry packageRegistry) + { + checkInactive(); + this.packageRegistry = packageRegistry; + } + + public InternalSessionManager getSessionManager() + { + return sessionManager; + } + + /** + * @since 2.0 + */ + public void setSessionManager(InternalSessionManager sessionManager) + { + checkInactive(); + this.sessionManager = sessionManager; + } + + public InternalCDOBranchManager getBranchManager() + { + return branchManager; + } + + public void setBranchManager(InternalCDOBranchManager branchManager) + { + checkInactive(); + this.branchManager = branchManager; + } + + public InternalCDOCommitInfoManager getCommitInfoManager() + { + return commitInfoManager; + } + + public void setCommitInfoManager(InternalCDOCommitInfoManager commitInfoManager) + { + checkInactive(); + this.commitInfoManager = commitInfoManager; + } + + public InternalCDORevisionManager getRevisionManager() + { + return revisionManager; + } + + /** + * @since 2.0 + */ + public void setRevisionManager(InternalCDORevisionManager revisionManager) + { + checkInactive(); + this.revisionManager = revisionManager; + } + + /** + * @since 2.0 + */ + public InternalQueryManager getQueryManager() + { + return queryManager; + } + + /** + * @since 2.0 + */ + public void setQueryManager(InternalQueryManager queryManager) + { + checkInactive(); + this.queryManager = queryManager; + } + + /** + * @since 2.0 + */ + public InternalCommitManager getCommitManager() + { + return commitManager; + } + + /** + * @since 2.0 + */ + public void setCommitManager(InternalCommitManager commitManager) + { + checkInactive(); + this.commitManager = commitManager; + } + + /** + * @since 2.0 + */ + public InternalLockManager getLockManager() + { + return lockManager; + } + + /** + * @since 2.0 + */ + public void setLockManager(InternalLockManager lockManager) + { + checkInactive(); + this.lockManager = lockManager; + } + + public InternalCommitContext createCommitContext(InternalTransaction transaction) + { + return new TransactionCommitContext(transaction); + } + + public long getLastCommitTimeStamp() + { + return timeStampAuthority.getLastFinishedTimeStamp(); + } + + public void setLastCommitTimeStamp(long lastCommitTimeStamp) + { + timeStampAuthority.setLastFinishedTimeStamp(lastCommitTimeStamp); + } + + public long waitForCommit(long timeout) + { + return timeStampAuthority.waitForCommit(timeout); + } + + public long[] createCommitTimeStamp(OMMonitor monitor) + { + return timeStampAuthority.startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor); + } + + public long[] forceCommitTimeStamp(long override, OMMonitor monitor) + { + return timeStampAuthority.startCommit(override, monitor); + } + + public void endCommit(long timestamp) + { + timeStampAuthority.endCommit(timestamp); + } + + public void failCommit(long timestamp) + { + timeStampAuthority.failCommit(timestamp); + } + + /** + * @since 4.0 + */ + public void addCommitInfoHandler(CDOCommitInfoHandler handler) + { + synchronized (commitInfoHandlers) + { + if (!commitInfoHandlers.contains(handler)) + { + commitInfoHandlers.add(handler); + } + } + } + + /** + * @since 4.0 + */ + public void removeCommitInfoHandler(CDOCommitInfoHandler handler) + { + synchronized (commitInfoHandlers) + { + commitInfoHandlers.remove(handler); + } + } + + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo) + { + sessionManager.sendCommitNotification(sender, commitInfo); + + CDOCommitInfoHandler[] handlers; + synchronized (commitInfoHandlers) + { + handlers = commitInfoHandlers.toArray(new CDOCommitInfoHandler[commitInfoHandlers.size()]); + } + + for (CDOCommitInfoHandler handler : handlers) + { + try + { + handler.handleCommitInfo(commitInfo); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + + /** + * @since 2.0 + */ + public IQueryHandlerProvider getQueryHandlerProvider() + { + return queryHandlerProvider; + } + + /** + * @since 2.0 + */ + public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider) + { + this.queryHandlerProvider = queryHandlerProvider; + } + + /** + * @since 2.0 + */ + public synchronized IQueryHandler getQueryHandler(CDOQueryInfo info) + { + String language = info.getQueryLanguage(); + if (CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES.equals(language)) + { + return new ResourcesQueryHandler(); + } + + if (CDOProtocolConstants.QUERY_LANGUAGE_XREFS.equals(language)) + { + return new XRefsQueryHandler(); + } + + IStoreAccessor storeAccessor = StoreThreadLocal.getAccessor(); + if (storeAccessor != null) + { + IQueryHandler handler = storeAccessor.getQueryHandler(info); + if (handler != null) + { + return handler; + } + } + + if (queryHandlerProvider == null) + { + queryHandlerProvider = new ContainerQueryHandlerProvider(IPluginContainer.INSTANCE); + } + + IQueryHandler handler = queryHandlerProvider.getQueryHandler(info); + if (handler != null) + { + return handler; + } + + return null; + } + + public Object[] getElements() + { + final Object[] elements = { packageRegistry, branchManager, revisionManager, sessionManager, queryManager, + commitManager, commitInfoManager, lockManager, store }; + return elements; + } + + @Override + public boolean isEmpty() + { + return false; + } + + /** + * @since 2.0 + */ + public long getCreationTime() + { + return store.getCreationTime(); + } + + /** + * @since 2.0 + */ + public void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + long creationTimeStamp = getCreationTime(); + if (timeStamp < creationTimeStamp) + { + throw new IllegalArgumentException( + MessageFormat + .format( + "timeStamp ({0}) < repository creation time ({1})", CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(creationTimeStamp))); //$NON-NLS-1$ + } + + long currentTimeStamp = getTimeStamp(); + if (timeStamp > currentTimeStamp) + { + throw new IllegalArgumentException( + MessageFormat + .format( + "timeStamp ({0}) > current time ({1})", CDOCommonUtil.formatTimeStamp(timeStamp), CDOCommonUtil.formatTimeStamp(currentTimeStamp))); //$NON-NLS-1$ + } + } + + public long getTimeStamp() + { + return System.currentTimeMillis(); + } + + /** + * @since 2.0 + */ + public void addHandler(Handler handler) + { + if (handler instanceof ReadAccessHandler) + { + synchronized (readAccessHandlers) + { + if (!readAccessHandlers.contains(handler)) + { + readAccessHandlers.add((ReadAccessHandler)handler); + } + } + } + + if (handler instanceof WriteAccessHandler) + { + synchronized (writeAccessHandlers) + { + if (!writeAccessHandlers.contains(handler)) + { + writeAccessHandlers.add((WriteAccessHandler)handler); + } + } + } + } + + /** + * @since 2.0 + */ + public void removeHandler(Handler handler) + { + if (handler instanceof ReadAccessHandler) + { + synchronized (readAccessHandlers) + { + readAccessHandlers.remove(handler); + } + } + + if (handler instanceof WriteAccessHandler) + { + synchronized (writeAccessHandlers) + { + writeAccessHandlers.remove(handler); + } + } + } + + /** + * @since 2.0 + */ + public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions, + List<CDORevision> additionalRevisions) + { + ReadAccessHandler[] handlers; + synchronized (readAccessHandlers) + { + int size = readAccessHandlers.size(); + if (size == 0) + { + return; + } + + handlers = readAccessHandlers.toArray(new ReadAccessHandler[size]); + } + + for (ReadAccessHandler handler : handlers) + { + // Do *not* protect against unchecked exceptions from handlers! + handler.handleRevisionsBeforeSending(session, revisions, additionalRevisions); + } + } + + public void notifyWriteAccessHandlers(ITransaction transaction, IStoreAccessor.CommitContext commitContext, + boolean beforeCommit, OMMonitor monitor) + { + WriteAccessHandler[] handlers; + synchronized (writeAccessHandlers) + { + int size = writeAccessHandlers.size(); + if (size == 0) + { + return; + } + + handlers = writeAccessHandlers.toArray(new WriteAccessHandler[size]); + } + + try + { + monitor.begin(handlers.length); + for (WriteAccessHandler handler : handlers) + { + try + { + if (beforeCommit) + { + handler.handleTransactionBeforeCommitting(transaction, commitContext, monitor.fork()); + } + else + { + handler.handleTransactionAfterCommitted(transaction, commitContext, monitor.fork()); + } + } + catch (RuntimeException ex) + { + if (!beforeCommit) + { + OM.LOG.error(ex); + } + else + { + // Do *not* protect against unchecked exceptions from handlers on before case! + throw ex; + } + } + } + } + finally + { + monitor.done(); + } + } + + public void setInitialPackages(EPackage... initialPackages) + { + checkInactive(); + this.initialPackages = initialPackages; + } + + public CDOReplicationInfo replicateRaw(CDODataOutput out, int lastReplicatedBranchID, long lastReplicatedCommitTime) + throws IOException + { + final int fromBranchID = lastReplicatedBranchID + 1; + final int toBranchID = getStore().getLastBranchID(); + + final long fromCommitTime = lastReplicatedCommitTime + 1L; + final long toCommitTime = getStore().getLastCommitTime(); + + out.writeInt(toBranchID); + out.writeLong(toCommitTime); + + IStoreAccessor.Raw accessor = (IStoreAccessor.Raw)StoreThreadLocal.getAccessor(); + accessor.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + + return new CDOReplicationInfo() + { + public int getLastReplicatedBranchID() + { + return toBranchID; + } + + public long getLastReplicatedCommitTime() + { + return toCommitTime; + } + + public String[] getLockAreaIDs() + { + return null; // TODO (CD) Raw replication of lockAreas + } + }; + } + + public void replicate(CDOReplicationContext context) + { + int startID = context.getLastReplicatedBranchID() + 1; + branchManager.getBranches(startID, 0, context); + + long startTime = context.getLastReplicatedCommitTime(); + commitInfoManager.getCommitInfos(null, startTime + 1L, CDOBranchPoint.UNSPECIFIED_DATE, context); + + lockManager.getLockAreas(null, context); + } + + public CDOChangeSetData getChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint) + { + CDOChangeSetSegment[] segments = CDOChangeSetSegment.createFrom(startPoint, endPoint); + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + Set<CDOID> ids = accessor.readChangeSet(new Monitor(), segments); + + return CDORevisionUtil.createChangeSetData(ids, startPoint, endPoint, revisionManager); + } + + public Set<CDOID> getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor) + { + CDOBranchPoint target = targetInfo.getBranchPoint(); + CDOBranchPoint source = sourceInfo.getBranchPoint(); + + monitor.begin(5); + + try + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + Set<CDOID> ids = new HashSet<CDOID>(); + + if (targetBaseInfo == null && sourceBaseInfo == null) + { + if (CDOBranchUtil.isContainedBy(source, target)) + { + ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(source, target))); + } + else if (CDOBranchUtil.isContainedBy(target, source)) + { + ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(target, source))); + } + else + { + CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source); + ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, target))); + ids.addAll(accessor.readChangeSet(monitor.fork(), CDOChangeSetSegment.createFrom(ancestor, source))); + } + } + else + { + CDORevisionAvailabilityInfo sourceBaseInfoToUse = sourceBaseInfo == null ? targetBaseInfo : sourceBaseInfo; + + ids.addAll(accessor.readChangeSet(monitor.fork(), + CDOChangeSetSegment.createFrom(targetBaseInfo.getBranchPoint(), target))); + + ids.addAll(accessor.readChangeSet(monitor.fork(), + CDOChangeSetSegment.createFrom(sourceBaseInfoToUse.getBranchPoint(), source))); + } + + loadMergeData(ids, targetInfo, monitor.fork()); + loadMergeData(ids, sourceInfo, monitor.fork()); + + if (targetBaseInfo != null) + { + loadMergeData(ids, targetBaseInfo, monitor.fork()); + } + + if (sourceBaseInfo != null) + { + loadMergeData(ids, sourceBaseInfo, monitor.fork()); + } + + return ids; + } + finally + { + monitor.done(); + } + } + + private void loadMergeData(Set<CDOID> ids, CDORevisionAvailabilityInfo info, OMMonitor monitor) + { + int size = ids.size(); + monitor.begin(size); + + try + { + CDOBranchPoint branchPoint = info.getBranchPoint(); + for (CDOID id : ids) + { + if (info.containsRevision(id)) + { + info.removeRevision(id); + } + else + { + InternalCDORevision revision = getRevisionFromBranch(id, branchPoint); + if (revision != null) + { + info.addRevision(revision); + } + else + { + info.removeRevision(id); + } + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + private InternalCDORevision getRevisionFromBranch(CDOID id, CDOBranchPoint branchPoint) + { + InternalCDORevision revision = revisionManager.getRevision(id, branchPoint, CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + // if (revision == null || !ObjectUtil.equals(revision.getBranch(), branchPoint.getBranch())) + // { + // return null; + // } + + return revision; + } + + public void queryLobs(List<byte[]> ids) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + accessor.queryLobs(ids); + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + accessor.handleLobs(fromTime, toTime, handler); + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + accessor.loadLob(id, out); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime, + final CDORevisionHandler handler) + { + CDORevisionHandler wrapper = handler; + if (!exactBranch && !branch.isMainBranch()) + { + if (exactTime && timeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + throw new IllegalArgumentException("Time stamp must be specified if exactBranch==false and exactTime==true"); + } + + wrapper = new CDORevisionHandler() + { + private Set<CDOID> handled = new HashSet<CDOID>(); + + public boolean handleRevision(CDORevision revision) + { + CDOID id = revision.getID(); + if (handled.add(id)) + { + return handler.handleRevision(revision); + } + + return true; + } + }; + } + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + while (branch != null) + { + accessor.handleRevisions(eClass, branch, timeStamp, exactTime, wrapper); + if (exactBranch) + { + break; + } + + CDOBranchPoint base = branch.getBase(); + branch = base.getBranch(); + timeStamp = base.getTimeStamp(); + } + } + + public static List<Object> revisionKeysToObjects(List<CDORevisionKey> revisionKeys, CDOBranch viewedBranch, + boolean isSupportingBranches) + { + List<Object> lockables = new ArrayList<Object>(); + for (CDORevisionKey revKey : revisionKeys) + { + CDOID id = revKey.getID(); + if (isSupportingBranches) + { + lockables.add(CDOIDUtil.createIDAndBranch(id, viewedBranch)); + } + else + { + lockables.add(id); + } + } + return lockables; + } + + public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revKeys, long timeout) + { + List<Object> lockables = revisionKeysToObjects(revKeys, view.getBranch(), isSupportingBranches()); + return lock(view, lockType, lockables, revKeys, timeout); + } + + protected LockObjectsResult lock(InternalView view, LockType type, List<Object> lockables, + List<CDORevisionKey> loadedRevs, long timeout) + { + List<LockState<Object, IView>> newLockStates = null; + try + { + newLockStates = lockManager.lock2(true, type, view, lockables, timeout); + } + catch (TimeoutRuntimeException ex) + { + return new LockObjectsResult(false, true, false, 0, new CDORevisionKey[0], new CDOLockState[0], getTimeStamp()); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + + long[] requiredTimestamp = { 0L }; + CDORevisionKey[] staleRevisionsArray = checkStaleRevisions(view, loadedRevs, lockables, type, requiredTimestamp); + + // If some of the clients' revisions are stale and it has passiveUpdates disabled, + // then the locks are useless so we release them and report the stale revisions + // + InternalSession session = view.getSession(); + boolean staleNoUpdate = staleRevisionsArray.length > 0 && !session.isPassiveUpdateEnabled(); + if (staleNoUpdate) + { + lockManager.unlock2(true, type, view, lockables); + return new LockObjectsResult(false, false, false, requiredTimestamp[0], staleRevisionsArray, new CDOLockState[0], + getTimeStamp()); + } + + CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates); + sendLockNotifications(view, Operation.LOCK, type, cdoLockStates); + + boolean waitForUpdate = staleRevisionsArray.length > 0; + return new LockObjectsResult(true, false, waitForUpdate, requiredTimestamp[0], staleRevisionsArray, cdoLockStates, + getTimeStamp()); + } + + private CDORevisionKey[] checkStaleRevisions(InternalView view, List<CDORevisionKey> revisionKeys, + List<Object> objectsToLock, LockType lockType, long[] requiredTimestamp) + { + List<CDORevisionKey> staleRevisions = new LinkedList<CDORevisionKey>(); + if (revisionKeys != null) + { + InternalCDORevisionManager revManager = getRevisionManager(); + CDOBranch viewedBranch = view.getBranch(); + for (CDORevisionKey revKey : revisionKeys) + { + CDOID id = revKey.getID(); + InternalCDORevision rev = revManager.getRevision(id, viewedBranch.getHead(), CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + + if (rev == null) + { + lockManager.unlock2(true, lockType, view, objectsToLock); + throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id, + viewedBranch)); + } + + if (!revKey.equals(rev)) + { + staleRevisions.add(revKey); + requiredTimestamp[0] = Math.max(requiredTimestamp[0], rev.getTimeStamp()); + } + } + } + + // Convert the list to an array, to satisfy the API later + // + CDORevisionKey[] staleRevisionsArray = new CDORevisionKey[staleRevisions.size()]; + staleRevisions.toArray(staleRevisionsArray); + + return staleRevisionsArray; + } + + private void sendLockNotifications(IView view, Operation operation, LockType lockType, CDOLockState[] cdoLockStates) + { + long timestamp = getTimeStamp(); + CDOLockChangeInfo lockChangeInfo = CDOLockUtil.createLockChangeInfo(timestamp, view, view.getBranch(), operation, + lockType, cdoLockStates); + getSessionManager().sendLockNotification((InternalSession)view.getSession(), lockChangeInfo); + } + + // TODO (CD) This doesn't really belong here.. but getting it into CDOLockUtil isn't possible + public static CDOLockState[] toCDOLockStates(List<LockState<Object, IView>> lockStates) + { + CDOLockState[] cdoLockStates = new CDOLockState[lockStates.size()]; + int i = 0; + + for (LockState<Object, ? extends CDOCommonView> lockState : lockStates) + { + CDOLockState cdoLockState = CDOLockUtil.createLockState(lockState); + cdoLockStates[i++] = cdoLockState; + } + + return cdoLockStates; + } + + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + List<Object> unlockables = null; + if (objectIDs != null) + { + unlockables = new ArrayList<Object>(objectIDs.size()); + CDOBranch branch = view.getBranch(); + for (CDOID id : objectIDs) + { + Object key = supportingBranches ? CDOIDUtil.createIDAndBranch(id, branch) : id; + unlockables.add(key); + } + } + + return doUnlock(view, lockType, unlockables); + } + + protected UnlockObjectsResult doUnlock(InternalView view, LockType lockType, List<Object> unlockables) + { + List<LockState<Object, IView>> newLockStates = null; + if (lockType == null) // Signals an unlock-all operation + { + newLockStates = lockManager.unlock2(true, view); + } + else + { + newLockStates = lockManager.unlock2(true, lockType, view, unlockables); + } + + long timestamp = getTimeStamp(); + CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates); + sendLockNotifications(view, Operation.UNLOCK, lockType, cdoLockStates); + + return new UnlockObjectsResult(cdoLockStates, timestamp); + } + + @Override + public String toString() + { + return MessageFormat.format("Repository[{0}]", name); //$NON-NLS-1$ + } + + public boolean isSkipInitialization() + { + return skipInitialization; + } + + public void setSkipInitialization(boolean skipInitialization) + { + this.skipInitialization = skipInitialization; + } + + protected void initProperties() + { + String valueAudits = properties.get(Props.SUPPORTING_AUDITS); + if (valueAudits != null) + { + supportingAudits = Boolean.valueOf(valueAudits); + } + else + { + supportingAudits = store.getRevisionTemporality() == IStore.RevisionTemporality.AUDITING; + } + + String valueBranches = properties.get(Props.SUPPORTING_BRANCHES); + if (valueBranches != null) + { + supportingBranches = Boolean.valueOf(valueBranches); + } + else + { + supportingBranches = store.getRevisionParallelism() == IStore.RevisionParallelism.BRANCHING; + } + + String valueEcore = properties.get(Props.SUPPORTING_ECORE); + if (valueEcore != null) + { + supportingEcore = Boolean.valueOf(valueEcore); + } + + String valueIntegrity = properties.get(Props.ENSURE_REFERENTIAL_INTEGRITY); + if (valueIntegrity != null) + { + ensuringReferentialIntegrity = Boolean.valueOf(valueIntegrity); + } + + String valueIDLocation = properties.get(Props.ID_GENERATION_LOCATION); + if (valueIDLocation != null) + { + idGenerationLocation = IDGenerationLocation.valueOf(valueIDLocation); + } + + if (idGenerationLocation == null) + { + idGenerationLocation = IDGenerationLocation.STORE; + } + } + + public void initSystemPackages() + { + IStoreAccessor writer = store.getWriter(null); + StoreThreadLocal.setAccessor(writer); + + try + { + List<InternalCDOPackageUnit> units = new ArrayList<InternalCDOPackageUnit>(); + units.add(initSystemPackage(EcorePackage.eINSTANCE)); + units.add(initSystemPackage(EresourcePackage.eINSTANCE)); + units.add(initSystemPackage(EtypesPackage.eINSTANCE)); + + if (initialPackages != null) + { + for (EPackage initialPackage : initialPackages) + { + if (!packageRegistry.containsKey(initialPackage.getNsURI())) + { + units.add(initSystemPackage(initialPackage)); + } + } + } + + InternalCDOPackageUnit[] systemUnits = units.toArray(new InternalCDOPackageUnit[units.size()]); + writer.writePackageUnits(systemUnits, new Monitor()); + writer.commit(new Monitor()); + } + finally + { + StoreThreadLocal.release(); + } + } + + protected InternalCDOPackageUnit initSystemPackage(EPackage ePackage) + { + EMFUtil.registerPackage(ePackage, packageRegistry); + InternalCDOPackageInfo packageInfo = packageRegistry.getPackageInfo(ePackage); + + InternalCDOPackageUnit packageUnit = packageInfo.getPackageUnit(); + packageUnit.setTimeStamp(store.getCreationTime()); + packageUnit.setState(CDOPackageUnit.State.LOADED); + return packageUnit; + } + + public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp) + { + branchManager.initMainBranch(false, timeStamp); + } + + protected void initRootResource() + { + CDOBranchPoint head = branchManager.getMainBranch().getHead(); + + CDORevisionFactory factory = getRevisionManager().getFactory(); + InternalCDORevision rootResource = (InternalCDORevision)factory + .createRevision(EresourcePackage.Literals.CDO_RESOURCE); + + rootResource.setBranchPoint(head); + rootResource.setContainerID(CDOID.NULL); + rootResource.setContainingFeatureID(0); + + CDOID id = createRootResourceID(); + rootResource.setID(id); + rootResource.setResourceID(id); + + InternalSession session = getSessionManager().openSession(null); + InternalTransaction transaction = session.openTransaction(1, head); + InternalCommitContext commitContext = new TransactionCommitContext(transaction) + { + @Override + protected long[] createTimeStamp(OMMonitor monitor) + { + InternalRepository repository = getTransaction().getSession().getManager().getRepository(); + return repository.forceCommitTimeStamp(store.getCreationTime(), monitor); + } + + @Override + public String getUserID() + { + return SYSTEM_USER_ID; + } + + @Override + public String getCommitComment() + { + return "<initialize>"; //$NON-NLS-1$ + } + }; + + commitContext.setNewObjects(new InternalCDORevision[] { rootResource }); + commitContext.preWrite(); + + commitContext.write(new Monitor()); + commitContext.commit(new Monitor()); + + String rollbackMessage = commitContext.getRollbackMessage(); + if (rollbackMessage != null) + { + throw new TransactionException(rollbackMessage); + } + + rootResourceID = id instanceof CDOIDTemp ? commitContext.getIDMappings().get(id) : id; + + commitContext.postCommit(true); + session.close(); + } + + protected CDOID createRootResourceID() + { + if (getIDGenerationLocation() == IDGenerationLocation.STORE) + { + return CDOIDUtil.createTempObject(1); + } + + return CDOIDGenerator.UUID.generateCDOID(null); + } + + protected void readRootResource() + { + IStoreAccessor reader = store.getReader(null); + StoreThreadLocal.setAccessor(reader); + + try + { + CDOBranchPoint head = branchManager.getMainBranch().getHead(); + rootResourceID = reader.readResourceID(CDOID.NULL, null, head); + } + finally + { + StoreThreadLocal.release(); + } + } + + protected void readPackageUnits() + { + IStoreAccessor reader = store.getReader(null); + StoreThreadLocal.setAccessor(reader); + + try + { + Collection<InternalCDOPackageUnit> packageUnits = reader.readPackageUnits(); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + packageRegistry.putPackageUnit(packageUnit); + } + } + finally + { + StoreThreadLocal.release(); + } + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + checkState(store, "store"); //$NON-NLS-1$ + checkState(!StringUtil.isEmpty(name), "name is empty"); //$NON-NLS-1$ + checkState(packageRegistry, "packageRegistry"); //$NON-NLS-1$ + checkState(sessionManager, "sessionManager"); //$NON-NLS-1$ + checkState(branchManager, "branchManager"); //$NON-NLS-1$ + checkState(revisionManager, "revisionManager"); //$NON-NLS-1$ + checkState(queryManager, "queryManager"); //$NON-NLS-1$ + checkState(commitInfoManager, "commitInfoManager"); //$NON-NLS-1$ + checkState(commitManager, "commitManager"); //$NON-NLS-1$ + checkState(lockManager, "lockingManager"); //$NON-NLS-1$ + + packageRegistry.setReplacingDescriptors(true); + packageRegistry.setPackageProcessor(this); + packageRegistry.setPackageLoader(this); + + branchManager.setBranchLoader(this); + branchManager.setTimeProvider(this); + + revisionManager.setRevisionLoader(this); + sessionManager.setRepository(this); + queryManager.setRepository(this); + commitInfoManager.setCommitInfoLoader(this); + commitManager.setRepository(this); + lockManager.setRepository(this); + store.setRepository(this); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + initProperties(); + if (idGenerationLocation == IDGenerationLocation.CLIENT && !(store instanceof CanHandleClientAssignedIDs)) + { + throw new IllegalStateException("Store can not handle client assigned IDs: " + store); + } + + store.setRevisionTemporality(supportingAudits ? IStore.RevisionTemporality.AUDITING + : IStore.RevisionTemporality.NONE); + store.setRevisionParallelism(supportingBranches ? IStore.RevisionParallelism.BRANCHING + : IStore.RevisionParallelism.NONE); + revisionManager.setSupportingAudits(supportingAudits); + revisionManager.setSupportingBranches(supportingBranches); + + LifecycleUtil.activate(store); + LifecycleUtil.activate(packageRegistry); + LifecycleUtil.activate(sessionManager); + LifecycleUtil.activate(revisionManager); + LifecycleUtil.activate(branchManager); + LifecycleUtil.activate(queryManager); + LifecycleUtil.activate(commitInfoManager); + LifecycleUtil.activate(commitManager); + LifecycleUtil.activate(queryHandlerProvider); + + if (!skipInitialization) + { + long lastCommitTimeStamp = Math.max(store.getCreationTime(), store.getLastCommitTime()); + timeStampAuthority.setLastFinishedTimeStamp(lastCommitTimeStamp); + initMainBranch(branchManager, lastCommitTimeStamp); + + if (store.isFirstStart()) + { + initSystemPackages(); + initRootResource(); + } + else + { + readPackageUnits(); + readRootResource(); + } + + // This check does not work for CDOWorkspace: + // if (CDOIDUtil.isNull(rootResourceID)) + // { + // throw new IllegalStateException("Root resource ID is null"); + // } + } + + LifecycleUtil.activate(lockManager); // Needs an initialized main branch / branch manager + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(lockManager); + LifecycleUtil.deactivate(queryHandlerProvider); + LifecycleUtil.deactivate(commitManager); + LifecycleUtil.deactivate(commitInfoManager); + LifecycleUtil.deactivate(queryManager); + LifecycleUtil.deactivate(revisionManager); + LifecycleUtil.deactivate(sessionManager); + LifecycleUtil.deactivate(store); + LifecycleUtil.deactivate(branchManager); + LifecycleUtil.deactivate(packageRegistry); + super.doDeactivate(); + } + + /** + * @author Eike Stepper + * @since 2.0 + */ + public static class Default extends Repository + { + public Default() + { + } + + @Override + protected void doBeforeActivate() throws Exception + { + if (getPackageRegistry(false) == null) + { + setPackageRegistry(createPackageRegistry()); + } + + if (getSessionManager() == null) + { + setSessionManager(createSessionManager()); + } + + if (getBranchManager() == null) + { + setBranchManager(createBranchManager()); + } + + if (getRevisionManager() == null) + { + setRevisionManager(createRevisionManager()); + } + + if (getQueryManager() == null) + { + setQueryManager(createQueryManager()); + } + + if (getCommitInfoManager() == null) + { + setCommitInfoManager(createCommitInfoManager()); + } + + if (getCommitManager() == null) + { + setCommitManager(createCommitManager()); + } + + if (getLockManager() == null) + { + setLockManager(createLockManager()); + } + + super.doBeforeActivate(); + } + + protected InternalCDOPackageRegistry createPackageRegistry() + { + return new CDOPackageRegistryImpl(); + } + + protected InternalSessionManager createSessionManager() + { + return new SessionManager(); + } + + protected InternalCDOBranchManager createBranchManager() + { + return CDOBranchUtil.createBranchManager(); + } + + protected InternalCDORevisionManager createRevisionManager() + { + return (InternalCDORevisionManager)CDORevisionUtil.createRevisionManager(); + } + + protected InternalQueryManager createQueryManager() + { + return new QueryManager(); + } + + protected InternalCDOCommitInfoManager createCommitInfoManager() + { + return CDOCommitInfoUtil.createCommitInfoManager(); + } + + protected InternalCommitManager createCommitManager() + { + return new CommitManager(); + } + + protected InternalLockManager createLockManager() + { + return new LockManager(); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java new file mode 100644 index 0000000000..81c653db65 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryContext; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.server.QueryHandlerFactory; + +import org.eclipse.net4j.util.factory.ProductCreationException; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class ResourcesQueryHandler implements IQueryHandler +{ + public ResourcesQueryHandler() + { + } + + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + QueryContext resourcesContext = new QueryContext(info, context); + accessor.queryResources(resourcesContext); + + CDOBranchPoint branchPoint = context; + CDOBranch branch = branchPoint.getBranch(); + while (!branch.isMainBranch() && resourcesContext.getResourceIDs().size() < info.getMaxResults()) + { + branchPoint = branch.getBase(); + branch = branchPoint.getBranch(); + + resourcesContext.setBranchPoint(branchPoint); + accessor.queryResources(resourcesContext); + } + } + + /** + * @author Eike Stepper + * @since 3.0 + */ + private static final class QueryContext implements IStoreAccessor.QueryResourcesContext + { + private CDOQueryInfo info; + + private IQueryContext context; + + private CDOBranchPoint branchPoint; + + private Set<CDOID> resourceIDs = new HashSet<CDOID>(); + + public QueryContext(CDOQueryInfo info, IQueryContext context) + { + this.info = info; + this.context = context; + branchPoint = context; + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + this.branchPoint = branchPoint; + } + + public Set<CDOID> getResourceIDs() + { + return resourceIDs; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public CDOID getFolderID() + { + return (CDOID)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES_FOLDER_ID); + } + + public String getName() + { + return info.getQueryString(); + } + + public boolean exactMatch() + { + return (Boolean)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES_EXACT_MATCH); + } + + public int getMaxResults() + { + return info.getMaxResults(); + } + + public boolean addResource(CDOID resourceID) + { + if (resourceIDs.add(resourceID)) + { + return context.addResult(resourceID); + } + + return true; + } + } + + /** + * @author Eike Stepper + * @since 2.0 + */ + public static class Factory extends QueryHandlerFactory + { + public Factory() + { + super(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES); + } + + @Override + public ResourcesQueryHandler create(String description) throws ProductCreationException + { + return new ResourcesQueryHandler(); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java new file mode 100644 index 0000000000..f551a61840 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java @@ -0,0 +1,901 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; +import org.eclipse.emf.cdo.common.id.CDOIDGenerator; +import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.session.CDORepositoryInfo; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.view.CDOAdapterPolicy; +import org.eclipse.emf.cdo.view.CDOFeatureAnalyzer; +import org.eclipse.emf.cdo.view.CDOFetchRuleManager; +import org.eclipse.emf.cdo.view.CDOInvalidationPolicy; +import org.eclipse.emf.cdo.view.CDORevisionPrefetchingPolicy; +import org.eclipse.emf.cdo.view.CDOStaleReferencePolicy; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.emf.internal.cdo.session.SessionUtil; +import org.eclipse.emf.internal.cdo.view.AbstractCDOView; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.lifecycle.LifecycleException; +import org.eclipse.net4j.util.lifecycle.LifecycleState; +import org.eclipse.net4j.util.ref.ReferenceType; +import org.eclipse.net4j.util.ref.ReferenceValueMap; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult; +import org.eclipse.emf.spi.cdo.InternalCDOObject; +import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager; +import org.eclipse.emf.spi.cdo.InternalCDOSession; +import org.eclipse.emf.spi.cdo.InternalCDOTransaction; +import org.eclipse.emf.spi.cdo.InternalCDOView; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cdo.view.CDOView.Options +{ + private static final CDOAdapterPolicy[] ADAPTER_POLICIES = new CDOAdapterPolicy[0]; + + private static final CDORevisionPrefetchingPolicy REVISION_PREFETCHING = CDOUtil + .createRevisionPrefetchingPolicy(NO_REVISION_PREFETCHING); + + private InternalCDOSession session; + + private CDORevisionProvider revisionProvider; + + public ServerCDOView(InternalSession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled, + CDORevisionProvider revisionProvider) + { + super(branchPoint, legacyModeEnabled); + this.session = new ServerCDOSession(session); + this.revisionProvider = revisionProvider; + + setViewSet(SessionUtil.prepareResourceSet(new ResourceSetImpl())); + setObjects(new ReferenceValueMap.Weak<CDOID, InternalCDOObject>()); + activate(); + } + + public int getViewID() + { + return 1; + } + + public InternalCDOSession getSession() + { + return session; + } + + public long getLastUpdateTime() + { + return getTimeStamp(); + } + + public void setLastUpdateTime(long lastUpdateTime) + { + throw new UnsupportedOperationException(); + } + + public Options options() + { + return this; + } + + public synchronized InternalCDORevision getRevision(CDOID id, boolean loadOnDemand) + { + return (InternalCDORevision)revisionProvider.getRevision(id); + } + + @Override + protected synchronized void excludeNewObject(CDOID id) + { + // Do nothing + } + + public boolean isInvalidationRunnerActive() + { + return false; + } + + public boolean setBranchPoint(CDOBranchPoint branchPoint) + { + throw new UnsupportedOperationException(); + } + + public void lockObjects(Collection<? extends CDOObject> objects, LockType lockType, long timeout) + throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(Collection<? extends CDOObject> objects, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects() + { + throw new UnsupportedOperationException(); + } + + public boolean waitForUpdate(long updateTime, long timeoutMillis) + { + throw new UnsupportedOperationException(); + } + + public void setViewID(int viewId) + { + throw new UnsupportedOperationException(); + } + + public void setSession(InternalCDOSession session) + { + throw new UnsupportedOperationException(); + } + + public String getDurableLockingID() + { + return null; + } + + public String enableDurableLocking(boolean enable) + { + throw new UnsupportedOperationException(); + } + + public CDOFeatureAnalyzer getFeatureAnalyzer() + { + return CDOFeatureAnalyzer.NOOP; + } + + public void setFeatureAnalyzer(CDOFeatureAnalyzer featureAnalyzer) + { + throw new UnsupportedOperationException(); + } + + public InternalCDOTransaction toTransaction() + { + throw new UnsupportedOperationException(); + } + + public void invalidate(CDOBranch branch, long lastUpdateTime, List<CDORevisionKey> allChangedObjects, + List<CDOIDAndVersion> allDetachedObjects, Map<CDOID, InternalCDORevision> oldRevisions, boolean async) + { + throw new UnsupportedOperationException(); + } + + public void handleLockNotification(InternalCDOView sender, CDOLockChangeInfo lockChangeInfo) + { + // Do nothing + } + + public void prefetchRevisions(CDOID id, int depth) + { + throw new UnsupportedOperationException(); + } + + public boolean isObjectLocked(CDOObject object, LockType lockType, boolean byOthers) + { + return false; + } + + public void handleAddAdapter(InternalCDOObject eObject, Adapter adapter) + { + // Do nothing + } + + public void handleRemoveAdapter(InternalCDOObject eObject, Adapter adapter) + { + // Do nothing + } + + public void subscribe(EObject eObject, Adapter adapter) + { + throw new UnsupportedOperationException(); + } + + public void unsubscribe(EObject eObject, Adapter adapter) + { + throw new UnsupportedOperationException(); + } + + public boolean hasSubscription(CDOID id) + { + return false; + } + + public CDOView getContainer() + { + return this; + } + + public ReferenceType getCacheReferenceType() + { + return ReferenceType.WEAK; + } + + public boolean setCacheReferenceType(ReferenceType referenceType) + { + throw new UnsupportedOperationException(); + } + + public CDOInvalidationPolicy getInvalidationPolicy() + { + return CDOInvalidationPolicy.DEFAULT; + } + + public void setInvalidationPolicy(CDOInvalidationPolicy policy) + { + throw new UnsupportedOperationException(); + } + + public boolean isInvalidationNotificationEnabled() + { + return false; + } + + public void setInvalidationNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public boolean isLockNotificationEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + + public CDOAdapterPolicy[] getChangeSubscriptionPolicies() + { + return ADAPTER_POLICIES; + } + + public void addChangeSubscriptionPolicy(CDOAdapterPolicy policy) + { + throw new UnsupportedOperationException(); + } + + public void removeChangeSubscriptionPolicy(CDOAdapterPolicy policy) + { + throw new UnsupportedOperationException(); + } + + public CDOAdapterPolicy getStrongReferencePolicy() + { + return CDOAdapterPolicy.ALL; + } + + public void setStrongReferencePolicy(CDOAdapterPolicy policy) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public CDOStaleReferencePolicy getStaleReferenceBehaviour() + { + return getStaleReferencePolicy(); + } + + @Deprecated + public void setStaleReferenceBehaviour(CDOStaleReferencePolicy policy) + { + setStaleReferencePolicy(policy); + } + + public CDOStaleReferencePolicy getStaleReferencePolicy() + { + return CDOStaleReferencePolicy.EXCEPTION; + } + + public void setStaleReferencePolicy(CDOStaleReferencePolicy policy) + { + throw new UnsupportedOperationException(); + } + + public CDORevisionPrefetchingPolicy getRevisionPrefetchingPolicy() + { + return REVISION_PREFETCHING; + } + + public void setRevisionPrefetchingPolicy(CDORevisionPrefetchingPolicy prefetchingPolicy) + { + throw new UnsupportedOperationException(); + } + + public CDOLockState[] getLockStates(Collection<CDOID> ids) + { + throw new UnsupportedOperationException(); + } + + /** + * @author Eike Stepper + */ + private final class ServerCDOSession implements InternalCDOSession, CDORepositoryInfo + { + private InternalSession internalSession; + + private InternalRepository repository; + + public ServerCDOSession(InternalSession internalSession) + { + this.internalSession = internalSession; + repository = internalSession.getManager().getRepository(); + } + + public CDOView[] getElements() + { + return new ServerCDOView[] { ServerCDOView.this }; + } + + public InternalCDOTransaction getTransaction(int viewID) + { + return null; + } + + public InternalCDOTransaction[] getTransactions() + { + return new InternalCDOTransaction[0]; + } + + public CDOView[] getViews() + { + return getElements(); + } + + public CDOView getView(int viewID) + { + return viewID == getViewID() ? ServerCDOView.this : null; + } + + public CDOSessionProtocol getSessionProtocol() + { + throw new UnsupportedOperationException(); + } + + public CDOLobStore getLobStore() + { + throw new UnsupportedOperationException(); + } + + public InternalCDORevisionManager getRevisionManager() + { + return repository.getRevisionManager(); + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + if (revisionProvider instanceof IStoreAccessor.CommitContext) + { + IStoreAccessor.CommitContext context = (IStoreAccessor.CommitContext)revisionProvider; + return context.getPackageRegistry(); + } + + return repository.getPackageRegistry(false); + } + + public InternalCDOCommitInfoManager getCommitInfoManager() + { + return repository.getCommitInfoManager(); + } + + public InternalCDOBranchManager getBranchManager() + { + return repository.getBranchManager(); + } + + public void setMainBranchLocal(boolean mainBranchLocal) + { + // Do nothing + } + + public boolean hasListeners() + { + return false; + } + + public IListener[] getListeners() + { + return null; + } + + public void addListener(IListener listener) + { + // Do nothing + } + + public void removeListener(IListener listener) + { + // Do nothing + } + + public void activate() throws LifecycleException + { + throw new UnsupportedOperationException(); + } + + public Exception deactivate() + { + return ServerCDOView.this.deactivate(); + } + + public LifecycleState getLifecycleState() + { + return LifecycleState.ACTIVE; + } + + public boolean isActive() + { + return ServerCDOView.this.isActive(); + } + + public boolean isClosed() + { + return !isActive(); + } + + public void close() + { + deactivate(); + } + + public CDORepositoryInfo getRepositoryInfo() + { + return this; + } + + public String getName() + { + return repository.getName(); + } + + public String getUUID() + { + return repository.getUUID(); + } + + public Type getType() + { + return repository.getType(); + } + + public State getState() + { + return repository.getState(); + } + + public long getCreationTime() + { + return repository.getCreationTime(); + } + + public long getTimeStamp() + { + return repository.getTimeStamp(); + } + + public long getTimeStamp(boolean forceRefresh) + { + return getTimeStamp(); + } + + public String getStoreType() + { + return repository.getStoreType(); + } + + public Set<ObjectType> getObjectIDTypes() + { + return repository.getObjectIDTypes(); + } + + public CDOID getRootResourceID() + { + return repository.getRootResourceID(); + } + + public boolean isSupportingAudits() + { + return repository.isSupportingAudits(); + } + + public boolean isSupportingBranches() + { + return repository.isSupportingBranches(); + } + + public boolean isSupportingEcore() + { + return repository.isSupportingEcore(); + } + + public boolean isEnsuringReferentialIntegrity() + { + return repository.isEnsuringReferentialIntegrity(); + } + + public IDGenerationLocation getIDGenerationLocation() + { + return repository.getIDGenerationLocation(); + } + + public void handleRepositoryTypeChanged(Type oldType, Type newType) + { + } + + public void handleRepositoryStateChanged(State oldState, State newState) + { + } + + public EPackage[] loadPackages(CDOPackageUnit packageUnit) + { + return null; + } + + public void releaseAtomicRequestLock(Object key) + { + // Do nothing + } + + public void acquireAtomicRequestLock(Object key) + { + // Do nothing + } + + public Object processPackage(Object value) + { + return value; + } + + public boolean isEmpty() + { + return false; + } + + public boolean waitForUpdate(long updateTime, long timeoutMillis) + { + throw new UnsupportedOperationException(); + } + + public void waitForUpdate(long updateTime) + { + throw new UnsupportedOperationException(); + } + + public long getLastUpdateTime() + { + return getBranchPoint().getTimeStamp(); + } + + public String getUserID() + { + return null; + } + + public int getSessionID() + { + return internalSession.getSessionID(); + } + + public long refresh() + { + throw new UnsupportedOperationException(); + } + + public Options options() + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(String durableLockingID) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(String durableLockingID, ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView() + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(long timeStamp) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(CDOBranch branch) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(CDOBranch branch, long timeStamp) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(CDOBranch branch, long timeStamp, ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(CDOBranchPoint target, ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(CDOBranchPoint target) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(CDOBranchPoint target, ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOView openView(CDOBranchPoint target) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(String durableLockingID) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(String durableLockingID, ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction() + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(CDOBranch branch) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOTransaction openTransaction(CDOBranch branch, ResourceSet resourceSet) + { + throw new UnsupportedOperationException(); + } + + public CDOFetchRuleManager getFetchRuleManager() + { + return null; + } + + public ExceptionHandler getExceptionHandler() + { + return null; + } + + public CDOIDGenerator getIDGenerator() + { + return null; + } + + public void viewDetached(InternalCDOView view) + { + // Do nothing + } + + public void setUserID(String userID) + { + throw new UnsupportedOperationException(); + } + + public void setSessionProtocol(CDOSessionProtocol sessionProtocol) + { + throw new UnsupportedOperationException(); + } + + public void setSessionID(int sessionID) + { + throw new UnsupportedOperationException(); + } + + public void setRepositoryInfo(CDORepositoryInfo repositoryInfo) + { + throw new UnsupportedOperationException(); + } + + public void setRemoteSessionManager(InternalCDORemoteSessionManager remoteSessionManager) + { + throw new UnsupportedOperationException(); + } + + public void setLastUpdateTime(long lastUpdateTime) + { + throw new UnsupportedOperationException(); + } + + public void setFetchRuleManager(CDOFetchRuleManager fetchRuleManager) + { + throw new UnsupportedOperationException(); + } + + public void setExceptionHandler(ExceptionHandler exceptionHandler) + { + throw new UnsupportedOperationException(); + } + + public void setIDGenerator(CDOIDGenerator idGenerator) + { + throw new UnsupportedOperationException(); + } + + public Object resolveElementProxy(CDORevision revision, EStructuralFeature feature, int accessIndex, int serverIndex) + { + throw new UnsupportedOperationException(); + } + + public void resolveAllElementProxies(CDORevision revision) + { + throw new UnsupportedOperationException(); + } + + public void processRefreshSessionResult(RefreshSessionResult result, CDOBranch branch, + List<InternalCDOView> branchViews, Map<CDOBranch, Map<CDOID, InternalCDORevision>> viewedRevisions) + { + throw new UnsupportedOperationException(); + } + + public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender) + { + throw new UnsupportedOperationException(); + } + + public void handleCommitNotification(CDOCommitInfo commitInfo) + { + throw new UnsupportedOperationException(); + } + + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo, InternalCDOView sender) + { + throw new UnsupportedOperationException(); + } + + public void handleBranchNotification(InternalCDOBranch branch) + { + throw new UnsupportedOperationException(); + } + + public InternalCDORemoteSessionManager getRemoteSessionManager() + { + throw new UnsupportedOperationException(); + } + + public CDOAuthenticator getAuthenticator() + { + throw new UnsupportedOperationException(); + } + + public void setAuthenticator(CDOAuthenticator authenticator) + { + throw new UnsupportedOperationException(); + } + + public void setRevisionManager(InternalCDORevisionManager revisionManager) + { + throw new UnsupportedOperationException(); + } + + public void setBranchManager(InternalCDOBranchManager branchManager) + { + throw new UnsupportedOperationException(); + } + + public void setCommitInfoManager(InternalCDOCommitInfoManager commitInfoManager) + { + throw new UnsupportedOperationException(); + } + + public void setPackageRegistry(InternalCDOPackageRegistry packageRegistry) + { + throw new UnsupportedOperationException(); + } + + public boolean isSticky() + { + return false; + } + + public CDOBranchPoint getCommittedSinceLastRefresh(CDOID id) + { + throw new UnsupportedOperationException(); + } + + public void setCommittedSinceLastRefresh(CDOID id, CDOBranchPoint branchPoint) + { + throw new UnsupportedOperationException(); + } + + public void clearCommittedSinceLastRefresh() + { + throw new UnsupportedOperationException(); + } + + public CDOChangeSetData compareRevisions(CDOBranchPoint source, CDOBranchPoint target) + { + throw new UnsupportedOperationException(); + } + + public CDORevisionAvailabilityInfo createRevisionAvailabilityInfo(CDOBranchPoint branchPoint) + { + throw new UnsupportedOperationException(); + } + + public void cacheRevisions(CDORevisionAvailabilityInfo info) + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java new file mode 100644 index 0000000000..bb0205d2ba --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java @@ -0,0 +1,574 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 201266 + * Simon McDuff - bug 230832 + * Simon McDuff - bug 233490 + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.CDOCommonSession; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.internal.common.commit.DelegatingCommitInfo; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.ISessionProtocol; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalSessionManager; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.collection.IndexedList; +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.event.EventUtil; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.lifecycle.ILifecycle; +import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.log.OMLogger; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + */ +public class Session extends Container<IView> implements InternalSession +{ + private InternalSessionManager manager; + + private ISessionProtocol protocol; + + private int sessionID; + + private String userID; + + private boolean passiveUpdateEnabled = true; + + private PassiveUpdateMode passiveUpdateMode = PassiveUpdateMode.INVALIDATIONS; + + private LockNotificationMode lockNotificationMode = LockNotificationMode.IF_REQUIRED_BY_VIEWS; + + private long lastUpdateTime; + + @ExcludeFromDump + private Object lastUpdateTimeLock = new Object(); + + private ConcurrentMap<Integer, InternalView> views = new ConcurrentHashMap<Integer, InternalView>(); + + private AtomicInteger lastTempViewID = new AtomicInteger(); + + @ExcludeFromDump + private IListener protocolListener = new LifecycleEventAdapter() + { + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + deactivate(); + } + }; + + private boolean subscribed; + + /** + * @since 2.0 + */ + public Session(InternalSessionManager manager, ISessionProtocol protocol, int sessionID, String userID) + { + this.manager = manager; + this.protocol = protocol; + this.sessionID = sessionID; + this.userID = userID; + + EventUtil.addListener(protocol, protocolListener); + activate(); + } + + /** + * @since 2.0 + */ + public Options options() + { + return this; + } + + /** + * @since 2.0 + */ + public CDOCommonSession getContainer() + { + return this; + } + + public InternalSessionManager getManager() + { + return manager; + } + + public ISessionProtocol getProtocol() + { + return protocol; + } + + public int getSessionID() + { + return sessionID; + } + + /** + * @since 2.0 + */ + public String getUserID() + { + return userID; + } + + /** + * @since 2.0 + */ + public boolean isSubscribed() + { + return subscribed; + } + + /** + * @since 2.0 + */ + public void setSubscribed(boolean subscribed) + { + checkActive(); + if (this.subscribed != subscribed) + { + this.subscribed = subscribed; + byte opcode = subscribed ? CDOProtocolConstants.REMOTE_SESSION_SUBSCRIBED + : CDOProtocolConstants.REMOTE_SESSION_UNSUBSCRIBED; + manager.sendRemoteSessionNotification(this, opcode); + } + } + + /** + * @since 2.0 + */ + public boolean isPassiveUpdateEnabled() + { + return passiveUpdateEnabled; + } + + /** + * @since 2.0 + */ + public void setPassiveUpdateEnabled(boolean passiveUpdateEnabled) + { + checkActive(); + this.passiveUpdateEnabled = passiveUpdateEnabled; + } + + public PassiveUpdateMode getPassiveUpdateMode() + { + return passiveUpdateMode; + } + + public void setPassiveUpdateMode(PassiveUpdateMode passiveUpdateMode) + { + checkActive(); + checkArg(passiveUpdateMode, "passiveUpdateMode"); + this.passiveUpdateMode = passiveUpdateMode; + } + + public LockNotificationMode getLockNotificationMode() + { + return lockNotificationMode; + } + + public void setLockNotificationMode(LockNotificationMode lockNotificationMode) + { + checkActive(); + checkArg(lockNotificationMode, "lockNotificationMode"); + this.lockNotificationMode = lockNotificationMode; + } + + public long getLastUpdateTime() + { + synchronized (lastUpdateTimeLock) + { + return lastUpdateTime; + } + } + + public InternalView[] getElements() + { + checkActive(); + return getViews(); + } + + @Override + public boolean isEmpty() + { + checkActive(); + return views.isEmpty(); + } + + public InternalView[] getViews() + { + checkActive(); + return getViewsArray(); + } + + private InternalView[] getViewsArray() + { + return views.values().toArray(new InternalView[views.size()]); + } + + public InternalView getView(int viewID) + { + checkActive(); + return views.get(viewID); + } + + /** + * @since 2.0 + */ + public InternalView openView(int viewID, CDOBranchPoint branchPoint) + { + checkActive(); + if (viewID == TEMP_VIEW_ID) + { + viewID = -lastTempViewID.incrementAndGet(); + } + + InternalView view = new View(this, viewID, branchPoint); + view.activate(); + addView(view); + return view; + } + + /** + * @since 2.0 + */ + public InternalTransaction openTransaction(int viewID, CDOBranchPoint branchPoint) + { + checkActive(); + if (viewID == TEMP_VIEW_ID) + { + viewID = -lastTempViewID.incrementAndGet(); + } + + InternalTransaction transaction = new Transaction(this, viewID, branchPoint); + transaction.activate(); + addView(transaction); + return transaction; + } + + private void addView(InternalView view) + { + checkActive(); + int viewID = view.getViewID(); + views.put(viewID, view); + fireElementAddedEvent(view); + } + + /** + * @since 2.0 + */ + public void viewClosed(InternalView view) + { + int viewID = view.getViewID(); + if (views.remove(viewID) == view) + { + view.doClose(); + fireElementRemovedEvent(view); + } + } + + /** + * TODO I can't see how recursion is controlled/limited + * + * @since 2.0 + */ + public void collectContainedRevisions(InternalCDORevision revision, CDOBranchPoint branchPoint, int referenceChunk, + Set<CDOID> revisions, List<CDORevision> additionalRevisions) + { + InternalCDORevisionManager revisionManager = getManager().getRepository().getRevisionManager(); + EClass eClass = revision.getEClass(); + EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass); + for (int i = 0; i < features.length; i++) + { + EStructuralFeature feature = features[i]; + // TODO Clarify feature maps + if (feature instanceof EReference && !feature.isMany() && ((EReference)feature).isContainment()) + { + Object value = revision.getValue(feature); + if (value instanceof CDOID) + { + CDOID id = (CDOID)value; + if (!CDOIDUtil.isNull(id) && !revisions.contains(id)) + { + InternalCDORevision containedRevision = revisionManager.getRevision(id, branchPoint, referenceChunk, + CDORevision.DEPTH_NONE, true); + revisions.add(id); + additionalRevisions.add(containedRevision); + + // Recurse + collectContainedRevisions(containedRevision, branchPoint, referenceChunk, revisions, additionalRevisions); + } + } + } + } + } + + public CDOID provideCDOID(Object idObject) + { + return (CDOID)idObject; + } + + public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType) + throws Exception + { + if (protocol != null) + { + protocol.sendRepositoryTypeNotification(oldType, newType); + } + } + + @Deprecated + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState) + throws Exception + { + sendRepositoryStateNotification(oldState, newState, null); + } + + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState, + CDOID rootResourceID) throws Exception + { + if (protocol != null) + { + protocol.sendRepositoryStateNotification(oldState, newState, rootResourceID); + } + } + + public void sendBranchNotification(InternalCDOBranch branch) throws Exception + { + if (protocol != null) + { + protocol.sendBranchNotification(branch); + } + } + + public void sendCommitNotification(final CDOCommitInfo commitInfo) throws Exception + { + if (protocol == null) + { + return; + } + + if (!isPassiveUpdateEnabled()) + { + return; + } + + final InternalView[] views = getViews(); + protocol.sendCommitNotification(new DelegatingCommitInfo() + { + private final PassiveUpdateMode passiveUpdateMode = getPassiveUpdateMode(); + + private final boolean additions = passiveUpdateMode == PassiveUpdateMode.ADDITIONS; + + private final boolean changes = passiveUpdateMode == PassiveUpdateMode.CHANGES; + + @Override + protected CDOCommitInfo getDelegate() + { + return commitInfo; + } + + @Override + public List<CDOIDAndVersion> getNewObjects() + { + final List<CDOIDAndVersion> newObjects = super.getNewObjects(); + return new IndexedList<CDOIDAndVersion>() + { + @Override + public CDOIDAndVersion get(int index) + { + // The following will always be a CDORevision! + CDOIDAndVersion newObject = newObjects.get(index); + if (additions) + { + // Return full revisions if not in INVALIDATION mode + return newObject; + } + + // Prevent sending whole revisions by copying the id and version + return CDOIDUtil.createIDAndVersion(newObject); + } + + @Override + public int size() + { + return newObjects.size(); + } + }; + } + + @Override + public List<CDORevisionKey> getChangedObjects() + { + final List<CDORevisionKey> changedObjects = super.getChangedObjects(); + return new IndexedList<CDORevisionKey>() + { + @Override + public CDORevisionKey get(int index) + { + // The following will always be a CDORevisionDelta! + CDORevisionKey changedObject = changedObjects.get(index); + if (changes || additions || hasSubscription(changedObject.getID(), views)) + { + return changedObject; + } + + // Prevent sending whole revisions by copying the id and version + return CDORevisionUtil.copyRevisionKey(changedObject); + } + + @Override + public int size() + { + return changedObjects.size(); + } + }; + } + }); + + synchronized (lastUpdateTimeLock) + { + lastUpdateTime = commitInfo.getTimeStamp(); + } + } + + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception + { + if (protocol != null) + { + if (options().getLockNotificationMode() == LockNotificationMode.ALWAYS) + { + protocol.sendLockNotification(lockChangeInfo); + return; + } + + if (options().getLockNotificationMode() == LockNotificationMode.IF_REQUIRED_BY_VIEWS) + { + // If this session has one (or more) views configured for this branch, + // only then do we send the lockChangeInfo. + for (InternalView view : getViews()) + { + if (view.options().isLockNotificationEnabled() && view.getBranch().equals(lockChangeInfo.getBranch())) + { + protocol.sendLockNotification(lockChangeInfo); + break; + } + } + } + } + } + + private boolean hasSubscription(CDOID id, InternalView[] views) + { + for (InternalView view : views) + { + if (view.hasSubscription(id)) + { + return true; + } + } + + return false; + } + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception + { + if (protocol != null) + { + protocol.sendRemoteSessionNotification(sender, opcode); + } + } + + public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception + { + if (protocol != null) + { + protocol.sendRemoteMessageNotification(sender, message); + } + } + + @Override + public String toString() + { + return MessageFormat.format("Session[{0}]", sessionID); //$NON-NLS-1$ + } + + /** + * @since 2.0 + */ + public void close() + { + LifecycleUtil.deactivate(this, OMLogger.Level.DEBUG); + } + + /** + * @since 2.0 + */ + public boolean isClosed() + { + return !isActive(); + } + + @Override + protected void doDeactivate() throws Exception + { + EventUtil.removeListener(protocol, protocolListener); + protocolListener = null; + + LifecycleUtil.deactivate(protocol, OMLogger.Level.DEBUG); + protocol = null; + + for (IView view : getViewsArray()) + { + view.close(); + } + + views = null; + manager.sessionClosed(this); + manager = null; + super.doDeactivate(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java new file mode 100644 index 0000000000..22f71b80d7 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java @@ -0,0 +1,482 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 201266 + * Simon McDuff - bug 202725 + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.server.ISessionProtocol; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalSessionManager; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.container.Container; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.net4j.util.security.IRandomizer; +import org.eclipse.net4j.util.security.IUserManager; +import org.eclipse.net4j.util.security.NegotiationException; +import org.eclipse.net4j.util.security.Randomizer; +import org.eclipse.net4j.util.security.SecurityUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + */ +public class SessionManager extends Container<ISession> implements InternalSessionManager +{ + public static final int DEFAULT_TOKEN_LENGTH = 1024; + + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SESSION, SessionManager.class); + + private InternalRepository repository; + + @ExcludeFromDump + private String encryptionAlgorithmName = SecurityUtil.PBE_WITH_MD5_AND_DES; + + @ExcludeFromDump + private byte[] encryptionSaltBytes = SecurityUtil.DEFAULT_SALT; + + @ExcludeFromDump + private int encryptionIterationCount = SecurityUtil.DEFAULT_ITERATION_COUNT; + + private int tokenLength = DEFAULT_TOKEN_LENGTH; + + private IRandomizer randomizer; + + private IUserManager userManager; + + private final Map<Integer, InternalSession> sessions = new HashMap<Integer, InternalSession>(); + + private final AtomicInteger lastSessionID = new AtomicInteger(); + + /** + * @since 2.0 + */ + public SessionManager() + { + } + + /** + * @since 2.0 + */ + public InternalRepository getRepository() + { + return repository; + } + + /** + * @since 2.0 + */ + public void setRepository(InternalRepository repository) + { + checkInactive(); + this.repository = repository; + } + + public String getEncryptionAlgorithmName() + { + return encryptionAlgorithmName; + } + + public void setEncryptionAlgorithmName(String encryptionAlgorithmName) + { + checkInactive(); + this.encryptionAlgorithmName = encryptionAlgorithmName; + } + + public byte[] getEncryptionSaltBytes() + { + return encryptionSaltBytes; + } + + public void setEncryptionSaltBytes(byte[] encryptionSaltBytes) + { + checkInactive(); + this.encryptionSaltBytes = encryptionSaltBytes; + } + + public int getEncryptionIterationCount() + { + return encryptionIterationCount; + } + + public void setEncryptionIterationCount(int encryptionIterationCount) + { + checkInactive(); + this.encryptionIterationCount = encryptionIterationCount; + } + + public int getTokenLength() + { + return tokenLength; + } + + public void setTokenLength(int tokenLength) + { + checkInactive(); + this.tokenLength = tokenLength; + } + + public IRandomizer getRandomizer() + { + return randomizer; + } + + public void setRandomizer(IRandomizer randomizer) + { + checkInactive(); + this.randomizer = randomizer; + } + + public IUserManager getUserManager() + { + return userManager; + } + + public void setUserManager(IUserManager userManager) + { + this.userManager = userManager; + } + + public InternalSession[] getSessions() + { + synchronized (sessions) + { + return sessions.values().toArray(new InternalSession[sessions.size()]); + } + } + + /** + * @since 2.0 + */ + public InternalSession getSession(int sessionID) + { + checkActive(); + synchronized (sessions) + { + return sessions.get(sessionID); + } + } + + public InternalSession[] getElements() + { + return getSessions(); + } + + @Override + public boolean isEmpty() + { + synchronized (sessions) + { + return sessions.isEmpty(); + } + } + + /** + * @since 2.0 + */ + public InternalSession openSession(ISessionProtocol sessionProtocol) + { + int id = lastSessionID.incrementAndGet(); + if (TRACER.isEnabled()) + { + TRACER.trace("Opening session " + id); //$NON-NLS-1$ + } + + String userID = authenticateUser(sessionProtocol); + InternalSession session = createSession(id, userID, sessionProtocol); + LifecycleUtil.activate(session); + + synchronized (sessions) + { + sessions.put(id, session); + } + + fireElementAddedEvent(session); + sendRemoteSessionNotification(session, CDOProtocolConstants.REMOTE_SESSION_OPENED); + return session; + } + + protected InternalSession createSession(int id, String userID, ISessionProtocol protocol) + { + return new Session(this, protocol, id, userID); + } + + public void sessionClosed(InternalSession session) + { + int sessionID = session.getSessionID(); + InternalSession removeSession = null; + synchronized (sessions) + { + removeSession = sessions.remove(sessionID); + } + + if (removeSession != null) + { + fireElementRemovedEvent(session); + sendRemoteSessionNotification(session, CDOProtocolConstants.REMOTE_SESSION_CLOSED); + } + } + + public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType) + { + for (InternalSession session : getSessions()) + { + try + { + session.sendRepositoryTypeNotification(oldType, newType); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + + @Deprecated + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState) + { + sendRepositoryStateNotification(oldState, newState, null); + } + + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState, + CDOID rootResourceID) + { + for (InternalSession session : getSessions()) + { + try + { + session.sendRepositoryStateNotification(oldState, newState, rootResourceID); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch) + { + for (InternalSession session : getSessions()) + { + if (session != sender) + { + try + { + session.sendBranchNotification(branch); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + } + + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo) + { + for (InternalSession session : getSessions()) + { + if (session != sender) + { + try + { + session.sendCommitNotification(commitInfo); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + } + + public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo) + { + for (InternalSession session : getSessions()) + { + if (session == sender || session.options().getLockNotificationMode() == LockNotificationMode.OFF) + { + continue; + } + + try + { + session.sendLockNotification(lockChangeInfo); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + + /** + * @since 2.0 + */ + public void sendRemoteSessionNotification(InternalSession sender, byte opcode) + { + try + { + for (InternalSession session : getSessions()) + { + if (session != sender && session.isSubscribed()) + { + try + { + session.sendRemoteSessionNotification(sender, opcode); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + } + catch (Exception ex) + { + OM.LOG.warn("A problem occured while notifying other sessions", ex); + } + } + + public List<Integer> sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, + int[] recipients) + { + List<Integer> result = new ArrayList<Integer>(); + for (int i = 0; i < recipients.length; i++) + { + InternalSession recipient = getSession(recipients[i]); + + try + { + if (recipient != null && recipient.isSubscribed()) + { + recipient.sendRemoteMessageNotification(sender, message); + result.add(recipient.getSessionID()); + } + } + catch (Exception ex) + { + handleNotificationProblem(recipient, ex); + } + } + + return result; + } + + protected void handleNotificationProblem(InternalSession session, Throwable t) + { + OM.LOG.warn("A problem occured while notifying session " + session, t); + } + + protected String authenticateUser(ISessionProtocol protocol) throws SecurityException + { + if (protocol == null) + { + return null; + } + + if (userManager == null) + { + return null; + } + + try + { + byte[] randomToken = createRandomToken(); + CDOAuthenticationResult result = protocol.sendAuthenticationChallenge(randomToken); + String userID = result.getUserID(); + + byte[] cryptedToken = encryptToken(userID, randomToken); + boolean success = Arrays.equals(result.getCryptedToken(), cryptedToken); + if (success) + { + return userID; + } + + throw new SecurityException("User not authenticated"); //$NON-NLS-1$ + } + catch (SecurityException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new SecurityException(ex); + } + } + + protected byte[] createRandomToken() + { + byte[] token = new byte[tokenLength]; + randomizer.nextBytes(token); + return token; + } + + protected byte[] encryptToken(String userID, byte[] token) throws NegotiationException + { + try + { + return userManager.encrypt(userID, token, getEncryptionAlgorithmName(), getEncryptionSaltBytes(), + getEncryptionIterationCount()); + } + catch (Exception ex) + { + OM.LOG.error("Token encryption failed", ex); //$NON-NLS-1$ + return null; + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + if (userManager != null) + { + if (randomizer == null) + { + randomizer = new Randomizer(); + } + + LifecycleUtil.activate(randomizer); + } + } + + @Override + protected void doDeactivate() throws Exception + { + InternalSession[] activeSessions = getSessions(); + for (int i = 0; i < activeSessions.length; i++) + { + LifecycleUtil.deactivate(activeSessions[i]); + } + + super.doDeactivate(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java new file mode 100644 index 0000000000..eebbbfbc73 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2004 - 2009 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.LinkedList; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Bugzilla 297940, 290032 + * + * @author Caspar De Groot + */ +class TimeStampAuthority +{ + private InternalRepository repository; + + /** + * Holds the timestamp that was issued in response to the last call to {@link #createTimestamp()} + */ + @ExcludeFromDump + private transient long lastIssuedTimeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + + /** + * Holds the timestamp that was last reported finished by a call to {@link #endCommit(long)} + */ + private long lastFinishedTimeStamp; + + private LastCommitTimeStampLock lastCommitTimeStampLock = new LastCommitTimeStampLock(); + + private boolean strictOrdering; // TODO (CD) Should be a repo property + + /** + * A lock to block on if strict commit ordering is enabled + */ + private StrictOrderingLock strictOrderingLock = new StrictOrderingLock(); + + /** + * An ordered list of timestamps that have been issued but have not (yet) been reported finished. (It is ordered + * because the timestamps are added sequentially.) + */ + private List<Long> runningTransactions = new LinkedList<Long>(); + + /** + * A set of timestamps that have been reported finished but have not yet been + */ + private SortedSet<Long> finishedTransactions = new TreeSet<Long>(); + + TimeStampAuthority(InternalRepository repository) + { + this.repository = repository; + } + + synchronized long[] startCommit(OMMonitor monitor) + { + return startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor); + } + + synchronized long[] startCommit(long timeStampOverride, OMMonitor monitor) + { + monitor.begin(); + + if (strictOrdering) + { + strictOrderingLock.lock(); + } + + try + { + long now = repository.getTimeStamp(); + if (lastIssuedTimeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + while (lastIssuedTimeStamp == now) + { + ConcurrencyUtil.sleep(1); + now = repository.getTimeStamp(); + monitor.checkCanceled(); + } + } + + if (timeStampOverride != CDOBranchPoint.UNSPECIFIED_DATE) + { + now = timeStampOverride; + } + + lastIssuedTimeStamp = now; + + runningTransactions.add(lastIssuedTimeStamp); + return new long[] { lastIssuedTimeStamp, getLastFinishedTimeStamp() }; + } + finally + { + monitor.done(); + } + } + + synchronized void endCommit(long timeStamp) + { + if (!runningTransactions.remove(timeStamp)) + { + throw new IllegalArgumentException("Cannot end transaction with unknown timestamp " + timeStamp); + } + + finishedTransactions.add(timeStamp); + + // We can remove a timestamp from finishedTransactions if it is smaller (i.e. older) than any + // of the runningTransactions. Since both sets are sorted, we only need to compare the heads. + long oldestRunning = runningTransactions.isEmpty() ? Long.MAX_VALUE : runningTransactions.get(0); + long oldestFinished; + synchronized (lastCommitTimeStampLock) + { + long oldValue = lastFinishedTimeStamp; + while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning) + { + finishedTransactions.remove(oldestFinished); + lastFinishedTimeStamp = oldestFinished; + } + + // If we actually changed the lastFinishedTimeStamp, we need to notify waiting threads + if (lastFinishedTimeStamp != oldValue) + { + lastCommitTimeStampLock.notifyAll(); + } + } + + if (strictOrdering) + { + strictOrderingLock.unlock(); + } + } + + synchronized void failCommit(long timeStamp) + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) // Exclude problems before TransactionCommitContext.setTimeStamp() + { + if (!runningTransactions.remove(timeStamp)) + { + throw new IllegalArgumentException("Cannot fail transaction with unknown timestamp " + timeStamp); + } + } + + if (strictOrdering) + { + strictOrderingLock.unlock(); + } + } + + synchronized long getLastFinishedTimeStamp() + { + if (lastFinishedTimeStamp != 0) + { + return lastFinishedTimeStamp; + } + + // If we get here, no commit has finished since the server was started + if (lastIssuedTimeStamp == 0) // No commit has started either + { + // We can safely return the current system time minus one milli. + return repository.getTimeStamp() - 1; + } + + // If we get here, one or more commits are running + // We can safely return the start time of the longest-running, minus one milli. + return runningTransactions.get(0) - 1; + } + + long waitForCommit(long timeout) + { + synchronized (lastCommitTimeStampLock) + { + try + { + lastCommitTimeStampLock.wait(timeout); + } + catch (Exception ignore) + { + } + + return lastFinishedTimeStamp; + } + } + + void setLastFinishedTimeStamp(long lastCommitTimeStamp) + { + synchronized (lastCommitTimeStampLock) + { + if (lastFinishedTimeStamp < lastCommitTimeStamp) + { + lastFinishedTimeStamp = lastCommitTimeStamp; + lastCommitTimeStampLock.notifyAll(); + } + } + } + + /** + * A separate class for better monitor debugging. + * + * @author Eike Stepper + */ + private static final class LastCommitTimeStampLock + { + } + + /** + * A separate class for better monitor debugging. + * + * @author Eike Stepper + */ + private static final class StrictOrderingLock extends ReentrantLock + { + private static final long serialVersionUID = 1L; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java new file mode 100644 index 0000000000..31f7452c78 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 233490 + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class Transaction extends View implements InternalTransaction +{ + public Transaction(InternalSession session, int viewID, CDOBranchPoint branchPoint) + { + super(session, viewID, branchPoint); + } + + @Override + public boolean isReadOnly() + { + return false; + } + + @Override + protected String getClassName() + { + return "Transaction"; //$NON-NLS-1$ + } + + /** + * @since 2.0 + */ + public InternalCommitContext createCommitContext() + { + checkOpen(); + return getRepository().createCommitContext(this); + } + + /** + * For tests only. + * + * @since 2.0 + */ + public InternalCommitContext testCreateCommitContext(final long timeStamp) + { + checkOpen(); + return new TransactionCommitContext(this) + { + @Override + protected long[] createTimeStamp(OMMonitor monitor) + { + return new long[] { timeStamp, CDOBranchPoint.UNSPECIFIED_DATE }; + } + }; + } + + @Override + protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + if (timeStamp != UNSPECIFIED_DATE) + { + throw new IllegalArgumentException("Changing the target time is not supported by transactions"); + } + } + + private void checkOpen() + { + if (isClosed()) + { + throw new IllegalStateException("View closed"); //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java new file mode 100644 index 0000000000..e978f5c7e0 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java @@ -0,0 +1,1404 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + * Martin Fluegge - maintenance, bug 318518 + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDObject; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl; +import org.eclipse.emf.cdo.internal.common.commit.FailureCommitInfo; +import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.ContainmentCycleDetectedException; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.CDOFeatureDeltaVisitorImpl; +import org.eclipse.emf.cdo.spi.common.revision.CDOIDMapper; +import org.eclipse.emf.cdo.spi.common.revision.CDOReferenceAdjuster; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.common.revision.StubCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.IndexedList; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class TransactionCommitContext implements InternalCommitContext +{ + private static final InternalCDORevision DETACHED = new StubCDORevision(null); + + private final InternalTransaction transaction; + + private InternalRepository repository; + + private InternalCDORevisionManager revisionManager; + + private InternalLockManager lockManager; + + private InternalCDOPackageRegistry repositoryPackageRegistry; + + private boolean packageRegistryLocked; + + private TransactionPackageRegistry packageRegistry; + + private IStoreAccessor accessor; + + private long timeStamp = CDORevision.UNSPECIFIED_DATE; + + private long previousTimeStamp = CDORevision.UNSPECIFIED_DATE; + + private String commitComment; + + private InternalCDOPackageUnit[] newPackageUnits = new InternalCDOPackageUnit[0]; + + private InternalCDORevision[] newObjects = new InternalCDORevision[0]; + + private InternalCDORevisionDelta[] dirtyObjectDeltas = new InternalCDORevisionDelta[0]; + + private CDOID[] detachedObjects = new CDOID[0]; + + private Map<CDOID, EClass> detachedObjectTypes; + + private InternalCDORevision[] dirtyObjects = new InternalCDORevision[0]; + + private InternalCDORevision[] cachedDetachedRevisions = new InternalCDORevision[0]; + + private Map<CDOID, InternalCDORevision> cachedRevisions; + + private Set<Object> lockedObjects = new HashSet<Object>(); + + private List<CDOID> lockedTargets; + + private ConcurrentMap<CDOID, CDOID> idMappings = new ConcurrentHashMap<CDOID, CDOID>(); + + private CDOReferenceAdjuster idMapper = new CDOIDMapper(idMappings); + + private String rollbackMessage; + + private List<CDOIDReference> xRefs; + + private List<LockState<Object, IView>> postCommitLockStates; + + private boolean ensuringReferentialIntegrity; + + private boolean autoReleaseLocksEnabled; + + private ExtendedDataInputStream lobs; + + public TransactionCommitContext(InternalTransaction transaction) + { + this.transaction = transaction; + + repository = transaction.getRepository(); + revisionManager = repository.getRevisionManager(); + lockManager = repository.getLockManager(); + ensuringReferentialIntegrity = repository.isEnsuringReferentialIntegrity(); + + repositoryPackageRegistry = repository.getPackageRegistry(false); + packageRegistry = new TransactionPackageRegistry(repositoryPackageRegistry); + packageRegistry.activate(); + } + + public InternalTransaction getTransaction() + { + return transaction; + } + + public CDOBranchPoint getBranchPoint() + { + return transaction.getBranch().getPoint(timeStamp); + } + + public String getUserID() + { + return transaction.getSession().getUserID(); + } + + public String getCommitComment() + { + return commitComment; + } + + public boolean isAutoReleaseLocksEnabled() + { + return autoReleaseLocksEnabled; + } + + public String getRollbackMessage() + { + return rollbackMessage; + } + + public List<CDOIDReference> getXRefs() + { + return xRefs; + } + + public InternalCDOPackageRegistry getPackageRegistry() + { + return packageRegistry; + } + + public InternalCDOPackageUnit[] getNewPackageUnits() + { + return newPackageUnits; + } + + public InternalCDORevision[] getNewObjects() + { + return newObjects; + } + + public InternalCDORevision[] getDirtyObjects() + { + return dirtyObjects; + } + + public CDOID[] getDetachedObjects() + { + return detachedObjects; + } + + public Map<CDOID, EClass> getDetachedObjectTypes() + { + return detachedObjectTypes; + } + + public InternalCDORevision[] getDetachedRevisions() + { + // TODO This array can contain null values as they only come from the cache + for (InternalCDORevision cachedDetachedRevision : cachedDetachedRevisions) + { + if (cachedDetachedRevision == null) + { + throw new AssertionError("Detached revisions are incomplete"); + } + } + + return cachedDetachedRevisions; + } + + public InternalCDORevisionDelta[] getDirtyObjectDeltas() + { + return dirtyObjectDeltas; + } + + public CDORevision getRevision(CDOID id) + { + if (cachedRevisions == null) + { + cachedRevisions = cacheRevisions(); + } + + // Try "after state" + InternalCDORevision revision = cachedRevisions.get(id); + if (revision == DETACHED) + { + return null; + } + + if (revision != null) + { + return revision; + } + + // Fall back to "before state" + return transaction.getRevision(id); + } + + private Map<CDOID, InternalCDORevision> cacheRevisions() + { + Map<CDOID, InternalCDORevision> cache = new HashMap<CDOID, InternalCDORevision>(); + if (newObjects != null) + { + for (int i = 0; i < newObjects.length; i++) + { + InternalCDORevision revision = newObjects[i]; + cache.put(revision.getID(), revision); + } + } + + if (dirtyObjects != null) + { + for (int i = 0; i < dirtyObjects.length; i++) + { + InternalCDORevision revision = dirtyObjects[i]; + cache.put(revision.getID(), revision); + } + } + + if (detachedObjects != null) + { + for (int i = 0; i < detachedObjects.length; i++) + { + cache.put(detachedObjects[i], DETACHED); + } + } + + return cache; + } + + public Map<CDOID, CDOID> getIDMappings() + { + return Collections.unmodifiableMap(idMappings); + } + + public void addIDMapping(CDOID oldID, CDOID newID) + { + if (CDOIDUtil.isNull(newID) || newID.isTemporary()) + { + throw new IllegalStateException("newID=" + newID); //$NON-NLS-1$ + } + + CDOID previousMapping = idMappings.putIfAbsent(oldID, newID); + if (previousMapping != null) + { + throw new IllegalStateException("previousMapping != null"); //$NON-NLS-1$ + } + } + + public void applyIDMappings(OMMonitor monitor) + { + if (idMappings.isEmpty()) + { + return; + } + + try + { + monitor.begin(newObjects.length + dirtyObjects.length + dirtyObjectDeltas.length); + applyIDMappings(newObjects, monitor.fork(newObjects.length)); + applyIDMappings(dirtyObjects, monitor.fork(dirtyObjects.length)); + for (CDORevisionDelta dirtyObjectDelta : dirtyObjectDeltas) + { + ((InternalCDORevisionDelta)dirtyObjectDelta).adjustReferences(idMapper); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + public void preWrite() + { + // Allocate a store writer + accessor = repository.getStore().getWriter(transaction); + + // Make the store writer available in a ThreadLocal variable + StoreThreadLocal.setAccessor(accessor); + StoreThreadLocal.setCommitContext(this); + } + + public void setNewPackageUnits(InternalCDOPackageUnit[] newPackageUnits) + { + this.newPackageUnits = newPackageUnits; + } + + public void setNewObjects(InternalCDORevision[] newObjects) + { + this.newObjects = newObjects; + } + + public void setDirtyObjectDeltas(InternalCDORevisionDelta[] dirtyObjectDeltas) + { + this.dirtyObjectDeltas = dirtyObjectDeltas; + } + + public void setDetachedObjects(CDOID[] detachedObjects) + { + this.detachedObjects = detachedObjects; + } + + public void setDetachedObjectTypes(Map<CDOID, EClass> detachedObjectTypes) + { + this.detachedObjectTypes = detachedObjectTypes; + } + + public void setAutoReleaseLocksEnabled(boolean on) + { + autoReleaseLocksEnabled = on; + } + + public void setCommitComment(String commitComment) + { + this.commitComment = commitComment; + } + + public ExtendedDataInputStream getLobs() + { + return lobs; + } + + public void setLobs(ExtendedDataInputStream in) + { + lobs = in; + } + + /** + * @since 2.0 + */ + public void write(OMMonitor monitor) + { + try + { + monitor.begin(107); + dirtyObjects = new InternalCDORevision[dirtyObjectDeltas.length]; + + if (newPackageUnits.length != 0) + { + repository.getPackageRegistryCommitLock().acquire(); + packageRegistryLocked = true; + + List<InternalCDOPackageUnit> noDuplicates = new ArrayList<InternalCDOPackageUnit>(); + for (InternalCDOPackageUnit newPackageUnit : newPackageUnits) + { + String id = newPackageUnit.getID(); + if (!repositoryPackageRegistry.containsKey(id)) + { + noDuplicates.add(newPackageUnit); + } + } + + int newSize = noDuplicates.size(); + if (newPackageUnits.length != newSize) + { + newPackageUnits = noDuplicates.toArray(new InternalCDOPackageUnit[newSize]); + } + } + + lockObjects(); // Can take long and must come before setTimeStamp() + monitor.worked(); + + setTimeStamp(monitor.fork()); + + adjustForCommit(); + monitor.worked(); + + computeDirtyObjects(monitor.fork()); + + checkXRefs(); + monitor.worked(); + + if (rollbackMessage == null) + { + detachObjects(monitor.fork()); + repository.notifyWriteAccessHandlers(transaction, this, true, monitor.fork()); + accessor.write(this, monitor.fork(100)); + } + } + catch (Throwable t) + { + handleException(t); + } + finally + { + finishMonitor(monitor); + } + } + + public void commit(OMMonitor monitor) + { + try + { + monitor.begin(101); + accessor.commit(monitor.fork(100)); + updateInfraStructure(monitor.fork()); + + // Bugzilla 297940 + repository.endCommit(timeStamp); + } + catch (Throwable ex) + { + handleException(ex); + } + finally + { + finishMonitor(monitor); + } + } + + public List<LockState<Object, IView>> getPostCommmitLockStates() + { + return postCommitLockStates; + } + + private void handleException(Throwable ex) + { + try + { + OM.LOG.error(ex); + String storeClass = repository.getStore().getClass().getSimpleName(); + rollback("Rollback in " + storeClass + ": " + StringUtil.formatException(ex)); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (Exception ex1) + { + if (rollbackMessage == null) + { + rollbackMessage = ex1.getMessage(); + } + + try + { + OM.LOG.error(ex1); + } + catch (Exception ignore) + { + } + } + } + + private void finishMonitor(OMMonitor monitor) + { + try + { + monitor.done(); + } + catch (Exception ex) + { + try + { + OM.LOG.warn(ex); + } + catch (Exception ignore) + { + } + } + } + + private void setTimeStamp(OMMonitor mmonitor) + { + long[] times = createTimeStamp(mmonitor); // Could throw an exception + timeStamp = times[0]; + previousTimeStamp = times[1]; + CheckUtil.checkState(timeStamp != CDOBranchPoint.UNSPECIFIED_DATE, "Commit timestamp must not be 0"); + } + + protected long[] createTimeStamp(OMMonitor monitor) + { + return repository.createCommitTimeStamp(monitor); + } + + protected long getTimeStamp() + { + return timeStamp; + } + + protected void setTimeStamp(long timeStamp) + { + repository.forceCommitTimeStamp(timeStamp, new Monitor()); + this.timeStamp = timeStamp; + } + + public long getPreviousTimeStamp() + { + return previousTimeStamp; + } + + public void postCommit(boolean success) + { + if (packageRegistryLocked) + { + repository.getPackageRegistryCommitLock().release(); + } + + try + { + InternalSession sender = transaction.getSession(); + CDOCommitInfo commitInfo = success ? createCommitInfo() : createFailureCommitInfo(); + + repository.sendCommitNotification(sender, commitInfo); + } + catch (Exception ex) + { + OM.LOG.warn("A problem occured while notifying other sessions", ex); + } + finally + { + StoreThreadLocal.release(); + accessor = null; + lockedTargets = null; + + if (packageRegistry != null) + { + packageRegistry.deactivate(); + packageRegistry = null; + } + } + } + + public CDOCommitInfo createCommitInfo() + { + CDOBranch branch = transaction.getBranch(); + String userID = transaction.getSession().getUserID(); + CDOCommitData commitData = createCommitData(); + + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, commitData); + } + + public CDOCommitInfo createFailureCommitInfo() + { + return new FailureCommitInfo(timeStamp, previousTimeStamp); + } + + private CDOCommitData createCommitData() + { + List<CDOPackageUnit> newPackageUnitsCollection = new IndexedList.ArrayBacked<CDOPackageUnit>() + { + @Override + protected CDOPackageUnit[] getArray() + { + return newPackageUnits; + } + }; + + List<CDOIDAndVersion> newObjectsCollection = new IndexedList.ArrayBacked<CDOIDAndVersion>() + { + @Override + protected CDOIDAndVersion[] getArray() + { + return newObjects; + } + }; + + List<CDORevisionKey> changedObjectsCollection = new IndexedList.ArrayBacked<CDORevisionKey>() + { + @Override + protected CDORevisionKey[] getArray() + { + return dirtyObjectDeltas; + } + }; + + List<CDOIDAndVersion> detachedObjectsCollection = new IndexedList<CDOIDAndVersion>() + { + @Override + public CDOIDAndVersion get(int i) + { + if (cachedDetachedRevisions[i] != null) + { + return cachedDetachedRevisions[i]; + } + + return CDOIDUtil.createIDAndVersion(detachedObjects[i], CDORevision.UNSPECIFIED_VERSION); + } + + @Override + public int size() + { + return detachedObjects.length; + } + }; + + return new CDOCommitDataImpl(newPackageUnitsCollection, newObjectsCollection, changedObjectsCollection, + detachedObjectsCollection); + } + + protected void adjustForCommit() + { + for (InternalCDOPackageUnit newPackageUnit : newPackageUnits) + { + newPackageUnit.setTimeStamp(timeStamp); + } + + CDOBranch branch = transaction.getBranch(); + for (InternalCDORevision newObject : newObjects) + { + newObject.adjustForCommit(branch, timeStamp); + } + } + + protected void lockObjects() throws InterruptedException + { + lockedObjects.clear(); + lockedTargets = null; + + try + { + final boolean supportingBranches = repository.isSupportingBranches(); + + CDOFeatureDeltaVisitor deltaTargetLocker = null; + if (ensuringReferentialIntegrity) + { + final Set<CDOID> newIDs = new HashSet<CDOID>(); + for (int i = 0; i < newObjects.length; i++) + { + InternalCDORevision newRevision = newObjects[i]; + CDOID newID = newRevision.getID(); + if (newID instanceof CDOIDObject) + { + // After merges newObjects may contain non-TEMP ids + newIDs.add(newID); + } + } + + deltaTargetLocker = new CDOFeatureDeltaVisitorImpl() + { + @Override + public void visit(CDOAddFeatureDelta delta) + { + lockTarget(delta.getValue(), newIDs, supportingBranches); + } + + @Override + public void visit(CDOSetFeatureDelta delta) + { + lockTarget(delta.getValue(), newIDs, supportingBranches); + } + }; + + CDOReferenceAdjuster revisionTargetLocker = new CDOReferenceAdjuster() + { + public Object adjustReference(Object value, EStructuralFeature feature, int index) + { + lockTarget(value, newIDs, supportingBranches); + return value; + } + }; + + for (int i = 0; i < newObjects.length; i++) + { + InternalCDORevision newRevision = newObjects[i]; + newRevision.adjustReferences(revisionTargetLocker); + } + } + + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + InternalCDORevisionDelta delta = dirtyObjectDeltas[i]; + CDOID id = delta.getID(); + Object key = lockManager.getLockKey(id, transaction.getBranch()); + lockedObjects.add(new DeltaLockWrapper(key, delta)); + + if (hasContainmentChanges(delta)) + { + if (isContainerLocked(delta)) + { + throw new ContainmentCycleDetectedException("Parent (" + key + + ") is already locked for containment changes"); + } + } + } + + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + InternalCDORevisionDelta delta = dirtyObjectDeltas[i]; + if (deltaTargetLocker != null) + { + delta.accept(deltaTargetLocker); + } + } + + for (int i = 0; i < detachedObjects.length; i++) + { + CDOID id = detachedObjects[i]; + Object key = lockManager.getLockKey(id, transaction.getBranch()); + lockedObjects.add(key); + } + + if (!lockedObjects.isEmpty()) + { + // First lock all objects (incl. possible ref targets). + // This is a transient operation, it does not check for existance! + lockManager.lock(LockType.WRITE, transaction, lockedObjects, 1000); + + // If all locks could be acquired, check if locked targets do still exist + if (lockedTargets != null) + { + for (CDOID id : lockedTargets) + { + InternalCDORevision revision = // + revisionManager.getRevision(id, transaction, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + + if (revision == null || revision instanceof DetachedCDORevision) + { + throw new IllegalStateException("Object " + id + + " can not be referenced anymore because it has been detached"); + } + } + } + } + } + catch (RuntimeException ex) + { + lockedObjects.clear(); + lockedTargets = null; + throw ex; + } + } + + /** + * Iterates up the eContainers of an object and returns <code>true</code> on the first parent locked by another view. + * + * @return <code>true</code> if any parent is locked, <code>false</code> otherwise. + */ + private boolean isContainerLocked(InternalCDORevisionDelta delta) + { + CDOID id = delta.getID(); + InternalCDORevision revision = revisionManager.getRevisionByVersion(id, delta, CDORevision.UNCHUNKED, true); + if (revision == null) + { + // Can happen with non-auditing cache + throw new ConcurrentModificationException("Attempt by " + transaction + " to modify historical revision: " + + CDORevisionUtil.copyRevisionKey(delta)); + } + + return isContainerLocked(revision); + } + + private boolean isContainerLocked(InternalCDORevision revision) + { + CDOID id = (CDOID)revision.getContainerID(); + if (CDOIDUtil.isNull(id)) + { + return false; + } + + Object key = lockManager.getLockKey(id, transaction.getBranch()); + DeltaLockWrapper lockWrapper = new DeltaLockWrapper(key, null); + + if (lockManager.hasLockByOthers(LockType.WRITE, transaction, lockWrapper)) + { + Object object = lockManager.getLockEntryObject(lockWrapper); + if (object instanceof DeltaLockWrapper) + { + InternalCDORevisionDelta delta = ((DeltaLockWrapper)object).getDelta(); + if (delta != null && hasContainmentChanges(delta)) + { + return true; + } + } + } + + InternalCDORevision parent = revisionManager.getRevision(id, transaction, CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + + if (parent != null) + { + return isContainerLocked(parent); + } + + return false; + } + + private boolean hasContainmentChanges(InternalCDORevisionDelta delta) + { + for (CDOFeatureDelta featureDelta : delta.getFeatureDeltas()) + { + EStructuralFeature feature = featureDelta.getFeature(); + if (feature instanceof EReference) + { + if (((EReference)feature).isContainment()) + { + return true; + } + } + } + + return false; + } + + private void lockTarget(Object value, Set<CDOID> newIDs, boolean supportingBranches) + { + if (value instanceof CDOIDObject) + { + CDOIDObject id = (CDOIDObject)value; + if (id.isNull()) + { + return; + } + + if (newIDs.contains(id)) + { + // After merges newObjects may contain non-TEMP ids + return; + } + + if (detachedObjectTypes != null && detachedObjectTypes.containsKey(id)) + { + throw new IllegalStateException("This commit deletes object " + id + " and adds a reference at the same time"); + } + + // Let this object be locked + Object key = lockManager.getLockKey(id, transaction.getBranch()); + lockedObjects.add(key); + + // Let this object be checked for existance after it has been locked + if (lockedTargets == null) + { + lockedTargets = new ArrayList<CDOID>(); + } + + lockedTargets.add(id); + } + } + + protected void checkXRefs() + { + if (ensuringReferentialIntegrity && detachedObjectTypes != null) + { + XRefContext context = new XRefContext(); + xRefs = context.getXRefs(accessor); + if (!xRefs.isEmpty()) + { + rollbackMessage = "Referential integrity violated"; + } + } + } + + private synchronized void unlockObjects() + { + if (!lockedObjects.isEmpty()) + { + lockManager.unlock(LockType.WRITE, transaction, lockedObjects); + lockedObjects.clear(); + } + } + + private void computeDirtyObjects(OMMonitor monitor) + { + try + { + monitor.begin(dirtyObjectDeltas.length); + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + dirtyObjects[i] = computeDirtyObject(dirtyObjectDeltas[i]); + if (dirtyObjects[i] == null) + { + throw new IllegalStateException("Can not retrieve origin revision for " + dirtyObjectDeltas[i]); //$NON-NLS-1$ + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + private InternalCDORevision computeDirtyObject(InternalCDORevisionDelta delta) + { + CDOID id = delta.getID(); + + InternalCDORevision oldRevision = revisionManager.getRevisionByVersion(id, delta, CDORevision.UNCHUNKED, true); + if (oldRevision == null) + { + throw new IllegalStateException("Origin revision not found for " + delta); + } + + CDOBranch branch = transaction.getBranch(); + if (ObjectUtil.equals(oldRevision.getBranch(), branch) && oldRevision.isHistorical()) + { + throw new ConcurrentModificationException("Attempt by " + transaction + " to modify historical revision: " + + oldRevision); + } + + // Make sure all chunks are loaded + for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(oldRevision.getEClass())) + { + if (feature.isMany()) + { + repository.ensureChunk(oldRevision, feature, 0, oldRevision.getList(feature).size()); + } + } + + InternalCDORevision newRevision = oldRevision.copy(); + newRevision.adjustForCommit(branch, timeStamp); + + delta.apply(newRevision); + return newRevision; + } + + private void applyIDMappings(InternalCDORevision[] revisions, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + for (InternalCDORevision revision : revisions) + { + if (revision != null) + { + CDOID newID = idMappings.get(revision.getID()); + if (newID != null) + { + revision.setID(newID); + } + + revision.adjustReferences(idMapper); + monitor.worked(); + } + } + } + finally + { + monitor.done(); + } + } + + public synchronized void rollback(String message) + { + // Check if we already rolled back + if (rollbackMessage == null) + { + rollbackMessage = message; + unlockObjects(); + if (accessor != null) + { + try + { + accessor.rollback(); + } + catch (RuntimeException ex) + { + OM.LOG.warn("Problem while rolling back the transaction", ex); //$NON-NLS-1$ + } + finally + { + repository.failCommit(timeStamp); + } + } + } + } + + protected IStoreAccessor getAccessor() + { + return accessor; + } + + private void updateInfraStructure(OMMonitor monitor) + { + try + { + monitor.begin(7); + addNewPackageUnits(monitor.fork()); + addRevisions(newObjects, monitor.fork()); + addRevisions(dirtyObjects, monitor.fork()); + reviseDetachedObjects(monitor.fork()); + + unlockObjects(); + monitor.worked(); + + if (isAutoReleaseLocksEnabled()) + { + postCommitLockStates = repository.getLockManager().unlock2(true, transaction); + if (!postCommitLockStates.isEmpty()) + { + sendLockNotifications(postCommitLockStates); + } + } + + monitor.worked(); + repository.notifyWriteAccessHandlers(transaction, this, false, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + private void sendLockNotifications(List<LockState<Object, IView>> newLockStates) + { + CDOLockState[] newStates = Repository.toCDOLockStates(newLockStates); + + long timeStamp = getTimeStamp(); + InternalTransaction tx = getTransaction(); + CDOBranch branch = tx.getBranch(); + Operation unlock = Operation.UNLOCK; + + CDOLockChangeInfo info = CDOLockUtil.createLockChangeInfo(timeStamp, tx, branch, unlock, null, newStates); + repository.getSessionManager().sendLockNotification(tx.getSession(), info); + } + + private void addNewPackageUnits(OMMonitor monitor) + { + InternalCDOPackageRegistry repositoryPackageRegistry = repository.getPackageRegistry(false); + synchronized (repositoryPackageRegistry) + { + try + { + monitor.begin(newPackageUnits.length); + for (int i = 0; i < newPackageUnits.length; i++) + { + newPackageUnits[i].setState(CDOPackageUnit.State.LOADED); + repositoryPackageRegistry.putPackageUnit(newPackageUnits[i]); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + } + + private void addRevisions(CDORevision[] revisions, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + for (CDORevision revision : revisions) + { + if (revision != null) + { + revisionManager.addRevision(revision); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + private void reviseDetachedObjects(OMMonitor monitor) + { + try + { + monitor.begin(cachedDetachedRevisions.length); + long revised = getBranchPoint().getTimeStamp() - 1; + for (InternalCDORevision revision : cachedDetachedRevisions) + { + if (revision != null) + { + revision.setRevised(revised); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + private void detachObjects(OMMonitor monitor) + { + int size = detachedObjects.length; + cachedDetachedRevisions = new InternalCDORevision[size]; + + CDOID[] detachedObjects = getDetachedObjects(); + + try + { + monitor.begin(size); + for (int i = 0; i < size; i++) + { + CDOID id = detachedObjects[i]; + + // Remember the cached revision that must be revised after successful commit through updateInfraStructure + cachedDetachedRevisions[i] = (InternalCDORevision)revisionManager.getCache().getRevision(id, transaction); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + @Override + public String toString() + { + return MessageFormat.format("TransactionCommitContext[{0}, {1}, {2}]", transaction.getSession(), transaction, //$NON-NLS-1$ + CDOCommonUtil.formatTimeStamp(timeStamp)); + } + + /** + * @author Eike Stepper + */ + public static final class TransactionPackageRegistry extends CDOPackageRegistryImpl + { + private static final long serialVersionUID = 1L; + + public TransactionPackageRegistry(InternalCDOPackageRegistry repositoryPackageRegistry) + { + delegateRegistry = repositoryPackageRegistry; + setPackageLoader(repositoryPackageRegistry.getPackageLoader()); + } + + @Override + public synchronized void putPackageUnit(InternalCDOPackageUnit packageUnit) + { + LifecycleUtil.checkActive(this); + packageUnit.setPackageRegistry(this); + for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos()) + { + EPackage ePackage = packageInfo.getEPackage(); + basicPut(ePackage.getNsURI(), ePackage); + } + + resetInternalCaches(); + } + + @Override + protected void disposePackageUnits() + { + // Do nothing + } + + @Override + public Collection<Object> values() + { + throw new UnsupportedOperationException(); + } + } + + /** + * @author Martin Fluegge + */ + private static final class DeltaLockWrapper implements CDOIDAndBranch + { + private Object key; + + private InternalCDORevisionDelta delta; + + public DeltaLockWrapper(Object key, InternalCDORevisionDelta delta) + { + this.key = key; + this.delta = delta; + } + + public Object getKey() + { + return key; + } + + public InternalCDORevisionDelta getDelta() + { + return delta; + } + + public CDOID getID() + { + return key instanceof CDOIDAndBranch ? ((CDOIDAndBranch)key).getID() : (CDOID)key; + } + + public CDOBranch getBranch() + { + return key instanceof CDOIDAndBranch ? ((CDOIDAndBranch)key).getBranch() : null; + } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof DeltaLockWrapper) + { + DeltaLockWrapper wrapper = (DeltaLockWrapper)obj; + return key.equals(wrapper.getKey()); + } + + return key.equals(obj); + } + + @Override + public int hashCode() + { + return key.hashCode(); + } + + @Override + public String toString() + { + return key.toString(); + } + } + + /** + * @author Eike Stepper + */ + private final class XRefContext implements QueryXRefsContext + { + private Map<EClass, List<EReference>> sourceCandidates = new HashMap<EClass, List<EReference>>(); + + private Set<CDOID> detachedIDs = new HashSet<CDOID>(); + + private Set<CDOID> dirtyIDs = new HashSet<CDOID>(); + + private List<CDOIDReference> result = new ArrayList<CDOIDReference>(); + + public XRefContext() + { + XRefsQueryHandler.collectSourceCandidates(transaction, detachedObjectTypes.values(), sourceCandidates); + + for (CDOID id : detachedObjects) + { + detachedIDs.add(id); + } + + for (InternalCDORevision revision : dirtyObjects) + { + dirtyIDs.add(revision.getID()); + } + } + + public List<CDOIDReference> getXRefs(IStoreAccessor accessor) + { + accessor.queryXRefs(this); + checkDirtyObjects(); + return result; + } + + private void checkDirtyObjects() + { + final CDOID[] dirtyID = { null }; + CDOReferenceAdjuster dirtyObjectChecker = new CDOReferenceAdjuster() + { + public Object adjustReference(Object targetID, EStructuralFeature feature, int index) + { + if (feature != CDOContainerFeatureDelta.CONTAINER_FEATURE) + { + if (detachedIDs.contains(targetID)) + { + result.add(new CDOIDReference((CDOID)targetID, dirtyID[0], feature, index)); + } + + } + + return targetID; + } + }; + + for (InternalCDORevision dirtyObject : dirtyObjects) + { + dirtyID[0] = dirtyObject.getID(); + dirtyObject.adjustReferences(dirtyObjectChecker); + } + } + + public long getTimeStamp() + { + return CDOBranchPoint.UNSPECIFIED_DATE; + } + + public CDOBranch getBranch() + { + return transaction.getBranch(); + } + + public Map<CDOID, EClass> getTargetObjects() + { + return detachedObjectTypes; + } + + public EReference[] getSourceReferences() + { + return new EReference[0]; + } + + public Map<EClass, List<EReference>> getSourceCandidates() + { + return sourceCandidates; + } + + public int getMaxResults() + { + return CDOQueryInfo.UNLIMITED_RESULTS; + } + + public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex) + { + if (CDOIDUtil.isNull(targetID)) + { + // Compensate potential issues with the XRef implementation in the store accessor. + return true; + } + + if (detachedIDs.contains(sourceID)) + { + // Ignore XRefs from objects that are about to be detached themselves by this commit. + return true; + } + + if (dirtyIDs.contains(sourceID)) + { + // Ignore XRefs from objects that are about to be modified by this commit. They're handled later in getXRefs(). + return true; + } + + result.add(new CDOIDReference(targetID, sourceID, sourceReference, sourceIndex)); + return true; + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java new file mode 100644 index 0000000000..8e72f32596 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java @@ -0,0 +1,287 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 233490 + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.CDOCommonView; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.options.IOptionsContainer; + +import java.text.MessageFormat; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class View extends Lifecycle implements InternalView, CDOCommonView.Options +{ + private InternalSession session; + + private final int viewID; + + private final int sessionID; // Needed here so we can compute the hashCode even after session becomes null due to + // deactivation! + + private CDOBranchPoint branchPoint; + + private String durableLockingID; + + private InternalRepository repository; + + private Set<CDOID> changeSubscriptionIDs = new HashSet<CDOID>(); + + private boolean lockNotificationsEnabled; + + /** + * @since 2.0 + */ + public View(InternalSession session, int viewID, CDOBranchPoint branchPoint) + { + this.session = session; + this.viewID = viewID; + sessionID = session.getSessionID(); + + repository = session.getManager().getRepository(); + setBranchPoint(branchPoint); + } + + public InternalSession getSession() + { + return session; + } + + public int getViewID() + { + return viewID; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public boolean isReadOnly() + { + return true; + } + + public String getDurableLockingID() + { + return durableLockingID; + } + + /** + * @since 2.0 + */ + public InternalRepository getRepository() + { + checkOpen(); + return repository; + } + + public InternalCDORevision getRevision(CDOID id) + { + CDORevisionManager revisionManager = repository.getRevisionManager(); + return (InternalCDORevision)revisionManager.getRevision(id, this, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, + true); + } + + public void changeTarget(CDOBranchPoint branchPoint, List<CDOID> invalidObjects, + List<CDORevisionDelta> allChangedObjects, List<CDOID> allDetachedObjects) + { + List<CDORevision> oldRevisions = getRevisions(invalidObjects); + setBranchPoint(branchPoint); + List<CDORevision> newRevisions = getRevisions(invalidObjects); + + Iterator<CDORevision> it = newRevisions.iterator(); + for (CDORevision oldRevision : oldRevisions) + { + CDORevision newRevision = it.next(); + if (newRevision == null) + { + allDetachedObjects.add(oldRevision.getID()); + } + else if (newRevision != oldRevision) + { + CDORevisionDelta delta = newRevision.compare(oldRevision); + allChangedObjects.add(delta); + } + } + } + + private List<CDORevision> getRevisions(List<CDOID> ids) + { + return repository.getRevisionManager().getRevisions(ids, branchPoint, CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + checkOpen(); + long timeStamp = branchPoint.getTimeStamp(); + branchPoint = branchPoint.getBranch().getPoint(timeStamp); + validateTimeStamp(timeStamp); + this.branchPoint = branchPoint; + } + + protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException + { + if (timeStamp != UNSPECIFIED_DATE) + { + repository.validateTimeStamp(timeStamp); + } + } + + public void setDurableLockingID(String durableLockingID) + { + this.durableLockingID = durableLockingID; + } + + /** + * @since 2.0 + */ + public synchronized void subscribe(CDOID id) + { + checkOpen(); + changeSubscriptionIDs.add(id); + } + + /** + * @since 2.0 + */ + public synchronized void unsubscribe(CDOID id) + { + checkOpen(); + changeSubscriptionIDs.remove(id); + } + + /** + * @since 2.0 + */ + public synchronized boolean hasSubscription(CDOID id) + { + checkOpen(); + return changeSubscriptionIDs.contains(id); + } + + /** + * @since 2.0 + */ + public synchronized void clearChangeSubscription() + { + checkOpen(); + changeSubscriptionIDs.clear(); + } + + @Override + public int hashCode() + { + return ObjectUtil.hashCode(sessionID, viewID); + } + + @Override + public String toString() + { + int sessionID = session == null ? 0 : session.getSessionID(); + return MessageFormat.format("{0}[{1}:{2}]", getClassName(), sessionID, viewID); //$NON-NLS-1$ + } + + protected String getClassName() + { + return "View"; //$NON-NLS-1$ + } + + /** + * @since 2.0 + */ + public void close() + { + deactivate(); + } + + @Override + protected void doDeactivate() throws Exception + { + if (!isClosed()) + { + session.viewClosed(this); + } + + super.doDeactivate(); + } + + /** + * @since 2.0 + */ + public void doClose() + { + clearChangeSubscription(); + changeSubscriptionIDs = null; + session = null; + repository = null; + } + + /** + * @since 2.0 + */ + public boolean isClosed() + { + return repository == null; + } + + private void checkOpen() + { + if (isClosed()) + { + throw new IllegalStateException("View closed"); //$NON-NLS-1$ + } + } + + public IOptionsContainer getContainer() + { + return this; + } + + public Options options() + { + return this; + } + + public boolean isLockNotificationEnabled() + { + return lockNotificationsEnabled; + } + + public void setLockNotificationEnabled(boolean enable) + { + lockNotificationsEnabled = enable; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java new file mode 100644 index 0000000000..a3c23c0f8b --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.ConcurrentValue; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class XATransactionCommitContext extends TransactionCommitContext +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, XATransactionCommitContext.class); + + private ConcurrentValue<CommitState> state = new ConcurrentValue<CommitState>(CommitState.STARTING); + + public XATransactionCommitContext(InternalTransaction transaction) + { + super(transaction); + } + + public ConcurrentValue<CommitState> getState() + { + return state; + } + + @Override + public void preWrite() + { + super.preWrite(); + StoreThreadLocal.setAccessor(null); + } + + @Override + public void commit(OMMonitor monitor) + { + StoreThreadLocal.setAccessor(getAccessor()); + try + { + super.commit(monitor); + } + finally + { + StoreThreadLocal.setAccessor(null); + } + } + + @Override + public void write(OMMonitor monitor) + { + StoreThreadLocal.setAccessor(getAccessor()); + try + { + super.write(monitor); + } + finally + { + StoreThreadLocal.setAccessor(null); + } + } + + @Override + public void postCommit(boolean success) + { + StoreThreadLocal.setAccessor(getAccessor()); + InternalRepository repository = getTransaction().getRepository(); + repository.getCommitManager().remove(this); + super.postCommit(success); + } + + @Override + public synchronized void rollback(String message) + { + super.rollback(message); + + // Change the state to unblock call. + state.set(CommitState.ROLLED_BACK); + } + + /** + * Wait until another thread fills ID mapping for external objects. + */ + @Override + public void applyIDMappings(OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Notify phase2 to fill ID mapping."); //$NON-NLS-1$ + } + + state.set(CommitState.APPLY_ID_MAPPING); + if (TRACER.isEnabled()) + { + TRACER.format("Waiting for phase2 to be completed before continueing."); //$NON-NLS-1$ + } + + try + { + state.acquire(PHASEAPPLYMAPPING_DONE); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Received signal to continue."); //$NON-NLS-1$ + } + + super.applyIDMappings(monitor); + } + + /** + * Object to test if the process is at ApplyIDMapping + */ + final public static Object PHASEAPPLYMAPPING = new Object() + { + @Override + public int hashCode() + { + return CommitState.APPLY_ID_MAPPING.hashCode(); + } + + @Override + public boolean equals(Object object) + { + if (object == CommitState.ROLLED_BACK) + { + throw new RuntimeException("RolledBack"); //$NON-NLS-1$ + } + + return CommitState.APPLY_ID_MAPPING == object; + } + }; + + /** + * Object to test if the process did applyIDMapping + */ + final public static Object PHASEAPPLYMAPPING_DONE = new Object() + { + @Override + public int hashCode() + { + return CommitState.APPLY_ID_MAPPING_DONE.hashCode(); + } + + @Override + public boolean equals(Object object) + { + if (object == CommitState.ROLLED_BACK) + { + throw new RuntimeException("RolledBack"); //$NON-NLS-1$ + } + + return CommitState.APPLY_ID_MAPPING_DONE == object; + } + }; + + /** + * @author Simon McDuff + * @since 2.0 + */ + public enum CommitState + { + STARTING, APPLY_ID_MAPPING, APPLY_ID_MAPPING_DONE, ROLLED_BACK + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java new file mode 100644 index 0000000000..248d30cafc --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java @@ -0,0 +1,357 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - Bug 331619 - Support cross-referencing (XRef) for abstract classes and class hierarchies + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.model.CDOPackageInfo; +import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit.State; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryContext; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.QueryHandlerFactory; + +import org.eclipse.net4j.util.factory.ProductCreationException; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * @author Eike Stepper + */ +public class XRefsQueryHandler implements IQueryHandler +{ + public XRefsQueryHandler() + { + } + + public void executeQuery(CDOQueryInfo info, IQueryContext context) + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + QueryContext xrefsContext = new QueryContext(info, context); + accessor.queryXRefs(xrefsContext); + + CDOBranchPoint branchPoint = context; + CDOBranch branch = branchPoint.getBranch(); + while (!branch.isMainBranch() && context.getResultCount() < info.getMaxResults()) + { + branchPoint = branch.getBase(); + branch = branchPoint.getBranch(); + + xrefsContext.setBranchPoint(branchPoint); + accessor.queryXRefs(xrefsContext); + } + } + + public static void collectSourceCandidates(IView view, Collection<EClass> concreteTypes, + Map<EClass, List<EReference>> sourceCandidates) + { + InternalRepository repository = (InternalRepository)view.getRepository(); + CDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + + for (CDOPackageInfo packageInfo : packageRegistry.getPackageInfos()) + { + // System.out.println(); + // System.out.println(); + // System.out.println(packageInfo); + collectSourceCandidates(packageInfo, concreteTypes, sourceCandidates); + // for (Entry<EClass, List<EReference>> entry : sourceCandidates.entrySet()) + // { + // System.out.println(" ---> " + entry.getKey().getName()); + // for (EReference eReference : entry.getValue()) + // { + // System.out.println(" ---> " + eReference.getName()); + // } + // } + // + // System.out.println(); + // System.out.println(); + } + } + + public static void collectSourceCandidates(CDOPackageInfo packageInfo, Collection<EClass> concreteTypes, + Map<EClass, List<EReference>> sourceCandidates) + { + State state = packageInfo.getPackageUnit().getState(); + if (state == CDOPackageUnit.State.LOADED || state == CDOPackageUnit.State.PROXY) + { + EPackage ePackage = packageInfo.getEPackage(); + for (EClassifier eClassifier : ePackage.getEClassifiers()) + { + if (eClassifier instanceof EClass) + { + collectSourceCandidates((EClass)eClassifier, concreteTypes, sourceCandidates); + } + } + } + } + + public static void collectSourceCandidates(EClass eClass, Collection<EClass> concreteTypes, + Map<EClass, List<EReference>> sourceCandidates) + { + if (!eClass.isAbstract() && !eClass.isInterface()) + { + for (EStructuralFeature eStructuralFeature : eClass.getEAllStructuralFeatures()) + { + if (eStructuralFeature instanceof EReference && EMFUtil.isPersistent(eStructuralFeature)) + { + collectSourceCandidates(eClass, (EReference)eStructuralFeature, concreteTypes, sourceCandidates); + } + } + } + } + + public static void collectSourceCandidates(EReference eReference, Collection<EClass> concreteTypes, + Map<EClass, List<EReference>> sourceCandidates, CDOPackageRegistry packageRegistry) + { + EClass rootClass = eReference.getEContainingClass(); + collectSourceCandidates(rootClass, eReference, concreteTypes, sourceCandidates); + + Collection<EClass> descendentClasses = packageRegistry.getSubTypes().get(rootClass); + if (descendentClasses != null) + { + for (EClass candidateClass : descendentClasses) + { + collectSourceCandidates(candidateClass, eReference, concreteTypes, sourceCandidates); + } + } + } + + public static void collectSourceCandidates(EClass eClass, EReference eReference, Collection<EClass> concreteTypes, + Map<EClass, List<EReference>> sourceCandidates) + { + if (!eClass.isAbstract() && !eClass.isInterface()) + { + if (!eReference.isContainer() && !eReference.isContainment()) + { + if (canReference(eReference.getEReferenceType(), concreteTypes)) + { + List<EReference> list = sourceCandidates.get(eClass); + if (list == null) + { + list = new ArrayList<EReference>(); + sourceCandidates.put(eClass, list); + } + + list.add(eReference); + } + } + } + } + + private static boolean canReference(EClass declaredType, Collection<EClass> concreteTypes) + { + for (EClass concreteType : concreteTypes) + { + if (declaredType.isSuperTypeOf(concreteType)) + { + return true; + } + } + + return false; + } + + /** + * @author Eike Stepper + * @since 3.0 + */ + private static final class QueryContext implements IStoreAccessor.QueryXRefsContext + { + private CDOQueryInfo info; + + private IQueryContext context; + + private CDOBranchPoint branchPoint; + + private Map<CDOID, EClass> targetObjects; + + private Map<EClass, List<EReference>> sourceCandidates; + + private EReference[] sourceReferences; + + public QueryContext(CDOQueryInfo info, IQueryContext context) + { + this.info = info; + this.context = context; + branchPoint = context; + } + + public void setBranchPoint(CDOBranchPoint branchPoint) + { + this.branchPoint = branchPoint; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public Map<CDOID, EClass> getTargetObjects() + { + if (targetObjects == null) + { + IRepository repository = context.getView().getRepository(); + IStore store = repository.getStore(); + CDOPackageRegistry packageRegistry = repository.getPackageRegistry(); + + targetObjects = new HashMap<CDOID, EClass>(); + StringTokenizer tokenizer = new StringTokenizer(info.getQueryString(), "|"); + while (tokenizer.hasMoreTokens()) + { + String val = tokenizer.nextToken(); + CDOID id = store.createObjectID(val); + + CDOClassifierRef classifierRef; + if (id instanceof CDOClassifierRef.Provider) + { + classifierRef = ((CDOClassifierRef.Provider)id).getClassifierRef(); + } + else + { + val = tokenizer.nextToken(); + classifierRef = new CDOClassifierRef(val); + } + + EClass eClass = (EClass)classifierRef.resolve(packageRegistry); + targetObjects.put(id, eClass); + } + } + + return targetObjects; + } + + public EReference[] getSourceReferences() + { + if (sourceReferences == null) + { + sourceReferences = parseSourceReferences(); + } + + return sourceReferences; + } + + private EReference[] parseSourceReferences() + { + List<EReference> result = new ArrayList<EReference>(); + CDOPackageRegistry packageRegistry = context.getView().getRepository().getPackageRegistry(); + + String params = (String)info.getParameters().get(CDOProtocolConstants.QUERY_LANGUAGE_XREFS_SOURCE_REFERENCES); + if (params == null) + { + return new EReference[0]; + } + + StringTokenizer tokenizer = new StringTokenizer(params, "|"); + while (tokenizer.hasMoreTokens()) + { + String className = tokenizer.nextToken(); + CDOClassifierRef classifierRef = new CDOClassifierRef(className); + EClass eClass = (EClass)classifierRef.resolve(packageRegistry); + + String featureName = tokenizer.nextToken(); + EReference sourceReference = (EReference)eClass.getEStructuralFeature(featureName); + result.add(sourceReference); + } + + return result.toArray(new EReference[result.size()]); + } + + public Map<EClass, List<EReference>> getSourceCandidates() + { + if (sourceCandidates == null) + { + sourceCandidates = new HashMap<EClass, List<EReference>>(); + Collection<EClass> concreteTypes = getTargetObjects().values(); + EReference[] sourceReferences = getSourceReferences(); + + if (sourceReferences.length != 0) + { + InternalRepository repository = (InternalRepository)context.getView().getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (EReference eReference : sourceReferences) + { + collectSourceCandidates(eReference, concreteTypes, sourceCandidates, packageRegistry); + } + } + else + { + collectSourceCandidates(context.getView(), concreteTypes, sourceCandidates); + } + } + + return sourceCandidates; + } + + public int getMaxResults() + { + return info.getMaxResults(); + } + + public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex) + { + if (CDOIDUtil.isNull(targetID)) + { + return true; + } + + return context.addResult(new CDOIDReference(targetID, sourceID, sourceReference, sourceIndex)); + } + } + + /** + * @author Eike Stepper + */ + public static class Factory extends QueryHandlerFactory + { + public Factory() + { + super(CDOProtocolConstants.QUERY_LANGUAGE_RESOURCES); + } + + @Override + public XRefsQueryHandler create(String description) throws ProductCreationException + { + return new XRefsQueryHandler(); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java new file mode 100644 index 0000000000..6f7df7da50 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java @@ -0,0 +1,405 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.bundle; + +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.server.CDOServerExporter; +import org.eclipse.emf.cdo.server.CDOServerImporter; +import org.eclipse.emf.cdo.server.CDOServerUtil; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalSessionManager; +import org.eclipse.emf.cdo.spi.server.InternalView; +import org.eclipse.emf.cdo.spi.server.RepositoryConfigurator; +import org.eclipse.emf.cdo.spi.server.RepositoryFactory; + +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.osgi.framework.console.CommandInterpreter; +import org.eclipse.osgi.framework.console.CommandProvider; + +import org.osgi.framework.BundleContext; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author Eike Stepper + */ +public class CDOCommandProvider implements CommandProvider +{ + private static final String INDENT = " "; + + public CDOCommandProvider(BundleContext bundleContext) + { + bundleContext.registerService(CommandProvider.class.getName(), this, null); + } + + public String getHelp() + { + StringBuffer buffer = new StringBuffer(); + buffer.append("---CDO commands---\n"); + buffer.append("\tcdo list - list all active repositories\n"); + buffer.append("\tcdo start - start repositories from a config file\n"); + buffer.append("\tcdo stop - stop a repository\n"); + buffer.append("\tcdo export - export the contents of a repository to an XML file\n"); + buffer.append("\tcdo import - import the contents of a repository from an XML file\n"); + buffer.append("\tcdo sessions - dump the sessions of a repository\n"); + buffer.append("\tcdo packages - dump the packages of a repository\n"); + buffer.append("\tcdo branches - dump the branches of a repository\n"); + buffer.append("\tcdo locks - dump the durable locking areas of a repository\n"); + buffer.append("\tcdo deletelocks - delete a durable locking area of a repository\n"); + return buffer.toString(); + } + + public Object _cdo(CommandInterpreter interpreter) + { + try + { + String cmd = interpreter.nextArgument(); + if ("list".equals(cmd)) + { + list(interpreter); + return null; + } + + if ("start".equals(cmd)) + { + start(interpreter); + return null; + } + + if ("stop".equals(cmd)) + { + stop(interpreter); + return null; + } + + if ("export".equals(cmd)) + { + exportXML(interpreter); + return null; + } + + if ("import".equals(cmd)) + { + importXML(interpreter); + return null; + } + + if ("sessions".equals(cmd)) + { + sessions(interpreter); + return null; + } + + if ("packages".equals(cmd)) + { + packages(interpreter); + return null; + } + + if ("branches".equals(cmd)) + { + branches(interpreter); + return null; + } + + if ("locks".equals(cmd)) + { + locks(interpreter); + return null; + } + + if ("deletelocks".equals(cmd)) + { + deleteLocks(interpreter); + return null; + } + + interpreter.println(getHelp()); + } + catch (CommandException ex) + { + interpreter.println(ex.getMessage()); + } + catch (Exception ex) + { + interpreter.printStackTrace(ex); + } + + return null; + } + + protected void list(CommandInterpreter interpreter) throws Exception + { + IManagedContainer container = CDOServerApplication.getContainer(); + for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP)) + { + if (element instanceof InternalRepository) + { + InternalRepository repository = (InternalRepository)element; + interpreter.println(repository.getName()); + } + } + } + + protected void start(CommandInterpreter interpreter) throws Exception + { + String configFile = nextArgument(interpreter, "Syntax: cdo start <config-file>"); + + IManagedContainer container = CDOServerApplication.getContainer(); + RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container); + IRepository[] repositories = repositoryConfigurator.configure(new File(configFile)); + + interpreter.println("Repositories started:"); + if (repositories != null) + { + for (IRepository repository : repositories) + { + interpreter.println(repository.getName()); + } + } + } + + protected void stop(CommandInterpreter interpreter) throws Exception + { + InternalRepository repository = getRepository(interpreter, "Syntax: cdo stop <repository-name>"); + LifecycleUtil.deactivate(repository); + interpreter.println("Repository stopped"); + } + + protected void exportXML(CommandInterpreter interpreter) throws Exception + { + String syntax = "Syntax: cdo export <repository-name> <export-file>"; + InternalRepository repository = getRepository(interpreter, syntax); + String exportFile = nextArgument(interpreter, syntax); + OutputStream out = null; + + try + { + out = new FileOutputStream(exportFile); + + CDOServerExporter.XML exporter = new CDOServerExporter.XML(repository); + exporter.exportRepository(out); + interpreter.println("Repository exported"); + } + finally + { + IOUtil.close(out); + } + } + + protected void importXML(CommandInterpreter interpreter) throws Exception + { + String syntax = "Syntax: cdo import <repository-name> <import-file>"; + InternalRepository repository = getRepository(interpreter, syntax); + String importFile = nextArgument(interpreter, syntax); + InputStream in = null; + + try + { + in = new FileInputStream(importFile); + LifecycleUtil.deactivate(repository); + + CDOServerImporter.XML importer = new CDOServerImporter.XML(repository); + importer.importRepository(in); + + IManagedContainer container = CDOServerApplication.getContainer(); + CDOServerUtil.addRepository(container, repository); + + interpreter.println("Repository imported"); + } + finally + { + IOUtil.close(in); + } + } + + protected void sessions(CommandInterpreter interpreter) + { + InternalRepository repository = getRepository(interpreter, "Syntax: cdo sessions <repository-name>"); + InternalSessionManager sessionManager = repository.getSessionManager(); + for (InternalSession session : sessionManager.getSessions()) + { + interpreter.println(session); + for (InternalView view : session.getViews()) + { + interpreter.println(INDENT + view); + } + } + } + + protected void packages(CommandInterpreter interpreter) + { + InternalRepository repository = getRepository(interpreter, "Syntax: cdo packages <repository-name>"); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (InternalCDOPackageUnit packageUnit : packageRegistry.getPackageUnits()) + { + interpreter.println(packageUnit); + for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos()) + { + interpreter.println(INDENT + packageInfo); + } + } + } + + protected void branches(CommandInterpreter interpreter) + { + InternalRepository repository = getRepository(interpreter, "Syntax: cdo branches <repository-name>"); + branches(interpreter, repository.getBranchManager().getMainBranch(), ""); + } + + protected void locks(final CommandInterpreter interpreter) + { + final InternalRepository repository = getRepository(interpreter, + "Syntax: cdo locks <repository-name> [<username-prefix>]"); + final String userIDPrefix = nextArgument(interpreter, null); + + new WithAccessor() + { + @Override + protected void doExecute(IStoreAccessor accessor) + { + repository.getLockManager().getLockAreas(userIDPrefix, new IDurableLockingManager.LockArea.Handler() + { + public boolean handleLockArea(LockArea area) + { + interpreter.println(area.getDurableLockingID()); + interpreter.println(INDENT + "userID = " + area.getUserID()); + interpreter.println(INDENT + "branch = " + area.getBranch()); + interpreter.println(INDENT + "timeStamp = " + CDOCommonUtil.formatTimeStamp(area.getTimeStamp())); + interpreter.println(INDENT + "readOnly = " + area.isReadOnly()); + interpreter.println(INDENT + "locks = " + area.getLocks()); + return true; + } + }); + } + }.execute(repository); + } + + protected void deleteLocks(CommandInterpreter interpreter) + { + String syntax = "Syntax: cdo deletelocks <repository-name> <area-id>"; + final InternalRepository repository = getRepository(interpreter, syntax); + final String durableLockingID = nextArgument(interpreter, syntax); + + new WithAccessor() + { + @Override + protected void doExecute(IStoreAccessor accessor) + { + repository.getLockManager().deleteLockArea(durableLockingID); + } + }.execute(repository); + } + + private void branches(CommandInterpreter interpreter, InternalCDOBranch branch, String prefix) + { + interpreter.println(prefix + branch); + prefix += INDENT; + for (InternalCDOBranch child : branch.getBranches()) + { + branches(interpreter, child, prefix); + } + } + + private String nextArgument(CommandInterpreter interpreter, String syntax) + { + String argument = interpreter.nextArgument(); + if (argument == null && syntax != null) + { + throw new CommandException(syntax); + } + + return argument; + } + + private InternalRepository getRepository(CommandInterpreter interpreter, String syntax) + { + String repositoryName = nextArgument(interpreter, syntax); + InternalRepository repository = getRepository(repositoryName); + if (repository == null) + { + throw new CommandException("Repository not found: " + repositoryName); + } + + return repository; + } + + private InternalRepository getRepository(String name) + { + IManagedContainer container = CDOServerApplication.getContainer(); + for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP)) + { + if (element instanceof InternalRepository) + { + InternalRepository repository = (InternalRepository)element; + if (repository.getName().equals(name)) + { + return repository; + } + } + } + + return null; + } + + /** + * @author Eike Stepper + */ + protected static abstract class WithAccessor + { + public void execute(InternalRepository repository) + { + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + try + { + doExecute(accessor); + } + finally + { + StoreThreadLocal.release(); + } + } + + protected abstract void doExecute(IStoreAccessor accessor); + } + + /** + * @author Eike Stepper + */ + private static final class CommandException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + public CommandException(String message) + { + super(message); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java new file mode 100644 index 0000000000..deebb3659c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.bundle; + +import org.eclipse.emf.cdo.internal.server.messages.Messages; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.spi.server.IAppExtension; +import org.eclipse.emf.cdo.spi.server.RepositoryConfigurator; + +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.om.OSGiApplication; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class CDOServerApplication extends OSGiApplication +{ + public static final String ID = OM.BUNDLE_ID + ".app"; //$NON-NLS-1$ + + public static final String PROP_BROWSER_PORT = "org.eclipse.emf.cdo.server.browser.port"; //$NON-NLS-1$ + + private IRepository[] repositories; + + private List<IAppExtension> extensions = new ArrayList<IAppExtension>(); + + public CDOServerApplication() + { + super(ID); + } + + @Override + protected void doStart() throws Exception + { + super.doStart(); + IManagedContainer container = getContainer(); + + OM.LOG.info(Messages.getString("CDOServerApplication.1")); //$NON-NLS-1$ + File configFile = OMPlatform.INSTANCE.getConfigFile("cdo-server.xml"); //$NON-NLS-1$ + if (configFile != null && configFile.exists()) + { + RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container); + repositories = repositoryConfigurator.configure(configFile); + if (repositories == null || repositories.length == 0) + { + OM.LOG.warn(Messages.getString("CDOServerApplication.3") + " " + configFile.getAbsolutePath()); //$NON-NLS-1$ + } + + String port = OMPlatform.INSTANCE.getProperty(PROP_BROWSER_PORT); + if (port != null) + { + container.getElement("org.eclipse.emf.cdo.server.browsers", "default", port); //$NON-NLS-1$ //$NON-NLS-2$ + } + + startExtensions(configFile); + } + else + { + OM.LOG.warn(Messages.getString("CDOServerApplication.5") + " " + configFile.getAbsolutePath()); //$NON-NLS-1$ + } + + OM.LOG.info(Messages.getString("CDOServerApplication.6")); //$NON-NLS-1$ + } + + @Override + protected void doStop() throws Exception + { + OM.LOG.info(Messages.getString("CDOServerApplication.7")); //$NON-NLS-1$ + for (IAppExtension extension : extensions) + { + try + { + extension.stop(); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + + if (repositories != null) + { + for (IRepository repository : repositories) + { + LifecycleUtil.deactivate(repository); + } + } + + OM.LOG.info(Messages.getString("CDOServerApplication.8")); //$NON-NLS-1$ + super.doStop(); + } + + private void startExtensions(File configFile) + { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, IAppExtension.EXT_POINT); + for (final IConfigurationElement element : elements) + { + if ("appExtension".equals(element.getName())) //$NON-NLS-1$ + { + try + { + IAppExtension extension = (IAppExtension)element.createExecutableExtension("class"); //$NON-NLS-1$ + extension.start(configFile); + extensions.add(extension); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + } + } + + public static IManagedContainer getContainer() + { + return IPluginContainer.INSTANCE; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java new file mode 100644 index 0000000000..245e61b703 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.bundle; + +import org.eclipse.net4j.util.om.OMBundle; +import org.eclipse.net4j.util.om.OMPlatform; +import org.eclipse.net4j.util.om.OSGiActivator; +import org.eclipse.net4j.util.om.log.OMLogger; +import org.eclipse.net4j.util.om.trace.OMTracer; + +/** + * The <em>Operations & Maintenance</em> class of this bundle. + * + * @author Eike Stepper + */ +public abstract class OM +{ + public static final String BUNDLE_ID = "org.eclipse.emf.cdo.server"; //$NON-NLS-1$ + + public static final OMBundle BUNDLE = OMPlatform.INSTANCE.bundle(BUNDLE_ID, OM.class); + + public static final OMTracer DEBUG = BUNDLE.tracer("debug"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_PROTOCOL = DEBUG.tracer("protocol"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_REPOSITORY = DEBUG.tracer("repository"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_SESSION = DEBUG.tracer("session"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_TRANSACTION = DEBUG.tracer("transaction"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_REVISION = DEBUG.tracer("revision"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_RESOURCE = DEBUG.tracer("resource"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_STORE = DEBUG.tracer("store"); //$NON-NLS-1$ + + public static final OMTracer DEBUG_TYPES = DEBUG.tracer("types"); //$NON-NLS-1$ + + public static final OMLogger LOG = BUNDLE.logger(); + + /** + * @author Eike Stepper + */ + public static final class Activator extends OSGiActivator + { + public Activator() + { + super(BUNDLE); + } + + @Override + protected void doStart() throws Exception + { + new CDOCommandProvider(bundleContext); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java new file mode 100644 index 0000000000..0552041bc8 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Andre Dietisheim - bug 256649 + */ +package org.eclipse.emf.cdo.internal.server.embedded; + +import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.revision.CDORevisionCache; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration.RepositoryInfo; +import org.eclipse.emf.cdo.server.embedded.CDOSession; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.emf.internal.cdo.session.CDOSessionImpl; + +/** + * @author Eike Stepper + */ +public class EmbeddedClientSession extends CDOSessionImpl implements CDOSession +{ + private InternalRepository repository; + + public EmbeddedClientSession() + { + } + + public InternalRepository getRepository() + { + return repository; + } + + @Override + public InternalCDOPackageRegistry getPackageRegistry() + { + return getRepository().getPackageRegistry(); + } + + @Override + public InternalCDOBranchManager getBranchManager() + { + return getRepository().getBranchManager(); + } + + @Override + public InternalCDOCommitInfoManager getCommitInfoManager() + { + return getRepository().getCommitInfoManager(); + } + + @Override + public CDOLobStore getLobStore() + { + throw new UnsupportedOperationException(); + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + EmbeddedClientSessionProtocol protocol = new EmbeddedClientSessionProtocol(this); + setSessionProtocol(protocol); + protocol.activate(); + protocol.openSession(options().isPassiveUpdateEnabled()); + + setLastUpdateTime(repository.getLastCommitTimeStamp()); + setRepositoryInfo(new RepositoryInfo(this)); + + InternalCDORevisionManager revisionManager = (InternalCDORevisionManager)CDORevisionUtil.createRevisionManager(); + setRevisionManager(revisionManager); + revisionManager.setSupportingAudits(getRepositoryInfo().isSupportingAudits()); + revisionManager.setSupportingBranches(getRepositoryInfo().isSupportingBranches()); + revisionManager.setCache(CDORevisionCache.NOOP); + revisionManager.setRevisionLoader(getSessionProtocol()); + revisionManager.setRevisionLocker(this); + revisionManager.activate(); + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + + getRevisionManager().deactivate(); + setRevisionManager(null); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java new file mode 100644 index 0000000000..38706105b2 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.embedded; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.emf.internal.cdo.session.CDOSessionConfigurationImpl; + +import org.eclipse.net4j.util.CheckUtil; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class EmbeddedClientSessionConfiguration extends CDOSessionConfigurationImpl implements CDOSessionConfiguration +{ + private InternalRepository repository; + + public EmbeddedClientSessionConfiguration() + { + throw new UnsupportedOperationException("Embedded sessions are not yet supported"); + } + + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(IRepository repository) + { + checkNotOpen(); + this.repository = (InternalRepository)repository; + } + + @Override + public org.eclipse.emf.cdo.server.embedded.CDOSession openSession() + { + return (org.eclipse.emf.cdo.server.embedded.CDOSession)super.openSession(); + } + + public InternalCDOSession createSession() + { + if (isActivateOnOpen()) + { + CheckUtil.checkState(repository, "Specify a repository"); //$NON-NLS-1$ + } + + return new EmbeddedClientSession(); + } + + /** + * @author Eike Stepper + */ + protected static class RepositoryInfo implements org.eclipse.emf.cdo.session.CDORepositoryInfo + { + private EmbeddedClientSession session; + + public RepositoryInfo(EmbeddedClientSession session) + { + this.session = session; + } + + public String getName() + { + return session.getRepository().getName(); + } + + public String getUUID() + { + return session.getRepository().getUUID(); + } + + public Type getType() + { + return session.getRepository().getType(); + } + + public State getState() + { + return session.getRepository().getState(); + } + + public long getCreationTime() + { + return session.getRepository().getCreationTime(); + } + + public long getTimeStamp() + { + return getTimeStamp(false); + } + + public long getTimeStamp(boolean forceRefresh) + { + return System.currentTimeMillis(); + } + + public CDOID getRootResourceID() + { + return session.getRepository().getRootResourceID(); + } + + public boolean isSupportingAudits() + { + return session.getRepository().isSupportingAudits(); + } + + public boolean isSupportingBranches() + { + return session.getRepository().isSupportingBranches(); + } + + public boolean isSupportingEcore() + { + return session.getRepository().isSupportingEcore(); + } + + public boolean isEnsuringReferentialIntegrity() + { + return session.getRepository().isEnsuringReferentialIntegrity(); + } + + public IDGenerationLocation getIDGenerationLocation() + { + return session.getRepository().getIDGenerationLocation(); + } + + public String getStoreType() + { + return session.getRepository().getStoreType(); + } + + public Set<ObjectType> getObjectIDTypes() + { + return session.getRepository().getObjectIDTypes(); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java new file mode 100644 index 0000000000..531f5a9494 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java @@ -0,0 +1,584 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.embedded; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchPointRange; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.lob.CDOLob; +import org.eclipse.emf.cdo.common.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.util.CDOQueryQueue; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.session.remote.CDORemoteSession; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult; +import org.eclipse.emf.cdo.spi.common.CDORawReplicationContext; +import org.eclipse.emf.cdo.spi.common.CDOReplicationContext; +import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalQueryManager; +import org.eclipse.emf.cdo.spi.server.InternalQueryResult; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalView; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.spi.cdo.AbstractQueryIterator; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.InternalCDOObject; +import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager; +import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitContext; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + */ +public class EmbeddedClientSessionProtocol extends Lifecycle implements CDOSessionProtocol +{ + private EmbeddedClientSession session; + + // A separate session protocol instance is required because the getSession() methods are ambiguous! + private EmbeddedServerSessionProtocol serverSessionProtocol; + + private InternalRepository repository; + + public EmbeddedClientSessionProtocol(EmbeddedClientSession session) + { + this.session = session; + } + + public EmbeddedClientSession getSession() + { + return session; + } + + public EmbeddedServerSessionProtocol getServerSessionProtocol() + { + return serverSessionProtocol; + } + + public InternalSession openSession(boolean passiveUpdateEnabled) + { + repository = session.getRepository(); + activate(); + return serverSessionProtocol.openSession(repository, passiveUpdateEnabled); + } + + public EPackage[] loadPackages(CDOPackageUnit packageUnit) + { + throw new UnsupportedOperationException(); + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + throw new UnsupportedOperationException(); + } + + public BranchInfo loadBranch(int branchID) + { + throw new UnsupportedOperationException(); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + throw new UnsupportedOperationException(); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + throw new UnsupportedOperationException(); + } + + public RepositoryTimeResult getRepositoryTime() + { + RepositoryTimeResult result = new RepositoryTimeResult(); + long timeStamp = System.currentTimeMillis(); + result.setRequested(timeStamp); + result.setIndicated(timeStamp); + result.setResponded(timeStamp); + result.setConfirmed(timeStamp); + return result; + } + + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids) + { + throw new UnsupportedOperationException(); + } + + public void enableLockNotifications(int viewID, boolean enable) + { + throw new UnsupportedOperationException(); + } + + public void disablePassiveUpdate() + { + // serverSessionProtocol.getSession().setPassiveUpdateEnabled(passiveUpdateEnabled); + // TODO: implement EmbeddedClientSessionProtocol.setPassiveUpdate(idAndVersions, initialChunkSize, + // passiveUpdateEnabled) + throw new UnsupportedOperationException(); + } + + public void setPassiveUpdateMode(PassiveUpdateMode mode) + { + // TODO: implement EmbeddedClientSessionProtocol.setPassiveUpdateMode(mode) + throw new UnsupportedOperationException(); + } + + public void setLockNotificationMode(LockNotificationMode mode) + { + throw new UnsupportedOperationException(); + } + + public Object loadChunk(InternalCDORevision revision, EStructuralFeature feature, int accessIndex, int fetchIndex, + int fromIndex, int toIndex) + { + throw new UnsupportedOperationException(); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + throw new UnsupportedOperationException(); + } + + public CDOCommitData loadCommitData(long timeStamp) + { + throw new UnsupportedOperationException(); + } + + public InternalCDORevision loadRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk) + { + try + { + InternalSession session = serverSessionProtocol.getSession(); + StoreThreadLocal.setSession(session); + return repository.getRevisionManager().getRevisionByVersion(id, branchVersion, referenceChunk, true); + } + finally + { + StoreThreadLocal.release(); + } + } + + public List<InternalCDORevision> loadRevisions(List<RevisionInfo> infos, CDOBranchPoint branchPoint, + int referenceChunk, int prefetchDepth) + { + try + { + InternalSession session = serverSessionProtocol.getSession(); + StoreThreadLocal.setSession(session); + + List<CDOID> ids = new ArrayList<CDOID>(infos.size()); + for (RevisionInfo info : infos) + { + ids.add(info.getID()); + } + + // @SuppressWarnings("unchecked") + // List<InternalCDORevision> revisions = (List<InternalCDORevision>)(List<?>)repository.getRevisionManager() + // .getRevisions(ids, branchPoint, referenceChunk, prefetchDepth, true); + + // TODO: implement EmbeddedClientSessionProtocol.loadRevisions(infos, branchPoint, referenceChunk, prefetchDepth) + throw new UnsupportedOperationException(); + } + finally + { + StoreThreadLocal.release(); + } + } + + public RefreshSessionResult refresh(long lastUpdateTime, + Map<CDOBranch, Map<CDOID, InternalCDORevision>> viewedRevisions, int initialChunkSize, + boolean enablePassiveUpdates) + { + throw new UnsupportedOperationException(); + } + + public void openView(int viewID, boolean readOnly, CDOBranchPoint branchPoint) + { + InternalSession session = serverSessionProtocol.getSession(); + if (readOnly) + { + session.openView(viewID, branchPoint); + } + else + { + session.openTransaction(viewID, branchPoint); + } + } + + public CDOBranchPoint openView(int viewID, boolean readOnly, String durableLockingID) + { + throw new UnsupportedOperationException(); + } + + public void switchTarget(int viewID, CDOBranchPoint branchPoint, List<InternalCDOObject> invalidObjects, + List<CDORevisionKey> allChangedObjects, List<CDOIDAndVersion> allDetachedObjects, OMMonitor monitor) + { + // TODO: implement EmbeddedClientSessionProtocol.changeView(viewID, branchPoint, invalidObjects, allChangedObjects, + // allDetachedObjects, monitor) + throw new UnsupportedOperationException(); + + // try + // { + // monitor.begin(); + // Async async = monitor.forkAsync(); + // + // try + // { + // InternalView view = serverSessionProtocol.getSession().getView(viewID); + // if (view != null) + // { + // List<CDOID> ids = new ArrayList<CDOID>(invalidObjects.size()); + // for (InternalCDOObject object : invalidObjects) + // { + // ids.add(object.cdoID()); + // } + // + // view.changeTarget(branchPoint, ids, allChangedObjects, allDetachedObjects); + // } + // } + // finally + // { + // async.stop(); + // } + // } + // finally + // { + // monitor.done(); + // } + } + + public void closeView(int viewID) + { + InternalView view = serverSessionProtocol.getSession().getView(viewID); + if (view != null) + { + view.close(); + } + } + + public void changeSubscription(int viewID, List<CDOID> ids, boolean subscribeMode, boolean clear) + { + throw new UnsupportedOperationException(); + } + + public void query(CDOView view, AbstractQueryIterator<?> query) + { + InternalView serverView = serverSessionProtocol.getSession().getView(view.getViewID()); + InternalQueryManager queryManager = repository.getQueryManager(); + InternalQueryResult result = queryManager.execute(serverView, query.getQueryInfo()); + + query.setQueryID(result.getQueryID()); + CDOQueryQueue<Object> resultQueue = query.getQueue(); + + try + { + while (result.hasNext()) + { + Object object = result.next(); + resultQueue.add(object); + } + } + catch (RuntimeException ex) + { + resultQueue.setException(ex); + } + catch (Throwable throwable) + { + resultQueue.setException(new RuntimeException(throwable.getMessage(), throwable)); + } + finally + { + resultQueue.close(); + } + } + + public boolean cancelQuery(int queryID) + { + repository.getQueryManager().cancel(queryID); + return true; + } + + public boolean isObjectLocked(CDOView view, CDOObject object, LockType lockType, boolean byOthers) + { + throw new UnsupportedOperationException(); + } + + @Deprecated + public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch, + LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.1 + */ + public LockObjectsResult lockObjects2(List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch, + LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(CDOView view, Collection<CDOID> objectIDs, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult unlockObjects2(CDOView view, Collection<CDOID> objectIDs, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public LockObjectsResult delegateLockObjects(String lockAreaID, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection<CDOID> objectIDs, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public String changeLockArea(CDOView view, boolean create) + { + throw new UnsupportedOperationException(); + } + + public List<byte[]> queryLobs(Set<byte[]> ids) + { + // TODO: implement EmbeddedClientSessionProtocol.queryLobs(ids) + throw new UnsupportedOperationException(); + } + + public void loadLob(CDOLobInfo info, Object outputStreamOrWriter) + { + // TODO: implement EmbeddedClientSessionProtocol.loadLob(info, out) + throw new UnsupportedOperationException(); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + // TODO: implement EmbeddedClientSessionProtocol.handleRevisions(eClass, branch, exactBranch, timeStamp, exactTime, + // handler) + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitTransaction(int transactionID, String comment, boolean releaseLocks, + CDOIDProvider idProvider, CDOCommitData commitData, Collection<CDOLob<?>> lobs, OMMonitor monitor) + { + monitor.begin(2); + boolean success = false; + InternalCommitContext serverCommitContext = null; + CommitTransactionResult result = null; + + try + { + InternalTransaction serverTransaction = (InternalTransaction)serverSessionProtocol.getSession().getView( + transactionID); + serverCommitContext = serverTransaction.createCommitContext(); + serverCommitContext.preWrite(); + serverCommitContext.setAutoReleaseLocksEnabled(releaseLocks); + + List<CDOPackageUnit> npu = commitData.getNewPackageUnits(); + serverCommitContext.setNewPackageUnits(npu.toArray(new InternalCDOPackageUnit[npu.size()])); + + List<CDOIDAndVersion> no = commitData.getNewObjects(); + InternalCDORevision[] array = new InternalCDORevision[no.size()]; + int index = 0; + for (CDOIDAndVersion object : no) + { + InternalCDORevision revision = (InternalCDORevision)object; + // revision.convertEObjects(clientTransaction); + array[index++] = revision; + } + + serverCommitContext.setNewObjects(array); + + List<CDORevisionKey> rd = commitData.getChangedObjects(); + serverCommitContext.setDirtyObjectDeltas(rd.toArray(new InternalCDORevisionDelta[rd.size()])); + + List<CDOIDAndVersion> detachedObjects = commitData.getDetachedObjects(); + serverCommitContext.setDetachedObjects(detachedObjects.toArray(new CDOID[detachedObjects.size()])); + + serverCommitContext.write(monitor.fork()); + success = serverCommitContext.getRollbackMessage() == null; + if (success) + { + serverCommitContext.commit(monitor.fork()); + } + else + { + monitor.worked(); + } + + // result = new CommitTransactionResult(commitData, serverCommitContext.getBranchPoint().getTimeStamp()); + // for (Entry<CDOID, CDOID> entry : serverCommitContext.getIDMappings().entrySet()) + // { + // result.addIDMapping(entry.getKey(), entry.getValue()); + // } + } + finally + { + if (serverCommitContext != null) + { + serverCommitContext.postCommit(success); + } + + monitor.done(); + } + + return result; + } + + public CommitTransactionResult commitDelegation(CDOBranch branch, String userID, String comment, + CDOCommitData commitData, Map<CDOID, EClass> detachedObjectTypes, Collection<CDOLob<?>> lobs, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitXATransactionCancel(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitXATransactionPhase1(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitXATransactionPhase2(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CommitTransactionResult commitXATransactionPhase3(InternalCDOXACommitContext xaContext, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public List<CDORemoteSession> getRemoteSessions(InternalCDORemoteSessionManager manager, boolean subscribe) + { + throw new UnsupportedOperationException(); + } + + public Set<Integer> sendRemoteMessage(CDORemoteSessionMessage message, List<CDORemoteSession> recipients) + { + throw new UnsupportedOperationException(); + } + + public boolean unsubscribeRemoteSessions() + { + throw new UnsupportedOperationException(); + } + + public void replicateRepository(CDOReplicationContext context, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public void replicateRepositoryRaw(CDORawReplicationContext context, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public CDOChangeSetData[] loadChangeSets(CDOBranchPointRange... ranges) + { + throw new UnsupportedOperationException(); + } + + public Set<CDOID> loadMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo) + { + throw new UnsupportedOperationException(); + } + + public CDOAuthenticationResult handleAuthenticationChallenge(byte[] randomToken) throws Exception + { + CDOAuthenticator authenticator = getSession().getAuthenticator(); + if (authenticator == null) + { + throw new IllegalStateException("No authenticator configured"); //$NON-NLS-1$ + } + + CDOAuthenticationResult result = authenticator.authenticate(randomToken); + if (result == null) + { + throw new SecurityException("Not authenticated"); //$NON-NLS-1$ + } + + String userID = result.getUserID(); + if (userID == null) + { + throw new SecurityException("No user ID"); //$NON-NLS-1$ + } + + byte[] cryptedToken = result.getCryptedToken(); + if (cryptedToken == null) + { + throw new SecurityException("No crypted token"); //$NON-NLS-1$ + } + + return result; + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + serverSessionProtocol = new EmbeddedServerSessionProtocol(this); + serverSessionProtocol.activate(); + } + + @Override + protected void doDeactivate() throws Exception + { + serverSessionProtocol.deactivate(); + serverSessionProtocol = null; + super.doDeactivate(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java new file mode 100644 index 0000000000..3dd8efaa73 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.embedded; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.server.ISessionProtocol; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.lifecycle.Lifecycle; + +/** + * @author Eike Stepper + */ +public class EmbeddedServerSessionProtocol extends Lifecycle implements ISessionProtocol +{ + // A separate session protocol instance is required because the getSession() methods are ambiguous! + private EmbeddedClientSessionProtocol clientSessionProtocol; + + private InternalSession session; + + public EmbeddedServerSessionProtocol(EmbeddedClientSessionProtocol clientSessionProtocol) + { + this.clientSessionProtocol = clientSessionProtocol; + } + + public EmbeddedClientSessionProtocol getClientSessionProtocol() + { + return clientSessionProtocol; + } + + public InternalSession openSession(InternalRepository repository, boolean passiveUpdateEnabled) + { + session = repository.getSessionManager().openSession(this); + session.setPassiveUpdateEnabled(passiveUpdateEnabled); + return session; + } + + public InternalSession getSession() + { + return session; + } + + public CDOAuthenticationResult sendAuthenticationChallenge(byte[] randomToken) throws Exception + { + return clientSessionProtocol.handleAuthenticationChallenge(randomToken); + } + + public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleRepositoryTypeChanged(oldType, newType); + } + + @Deprecated + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState) + { + sendRepositoryStateNotification(oldState, newState, null); + } + + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState, + CDOID rootResourceID) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleRepositoryStateChanged(oldState, newState); + } + + public void sendBranchNotification(InternalCDOBranch branch) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleBranchNotification(branch); + } + + public void sendCommitNotification(CDOCommitInfo commitInfo) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleCommitNotification(commitInfo); + } + + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleLockNotification(lockChangeInfo, null); + } + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode) + { + throw new UnsupportedOperationException(); + } + + public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) + { + throw new UnsupportedOperationException(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java new file mode 100644 index 0000000000..96f26ac283 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java @@ -0,0 +1,1310 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Simon McDuff - bug 233273 + * Eike Stepper - maintenance + * Andre Dietisheim - bug 256649 + */ +package org.eclipse.emf.cdo.internal.server.mem; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.model.CDOModelConstants; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.server.mem.IMEMStore; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.LongIDStore; +import org.eclipse.emf.cdo.spi.server.StoreAccessorPool; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader, DurableLocking2 +{ + public static final String TYPE = "mem"; //$NON-NLS-1$ + + private long creationTime; + + private Map<String, String> properties = new HashMap<String, String>(); + + private Map<Integer, BranchInfo> branchInfos = new HashMap<Integer, BranchInfo>(); + + private int lastBranchID; + + private int lastLocalBranchID; + + private Map<Object, List<InternalCDORevision>> revisions = new HashMap<Object, List<InternalCDORevision>>(); + + private List<CommitInfo> commitInfos = new ArrayList<CommitInfo>(); + + private Map<CDOID, EClass> objectTypes = new HashMap<CDOID, EClass>(); + + private Map<String, LockArea> lockAreas = new HashMap<String, LockArea>(); + + private Map<String, Object> lobs = new HashMap<String, Object>(); + + private int listLimit; + + @ExcludeFromDump + private transient EStructuralFeature resourceNameFeature; + + /** + * @param listLimit + * See {@link #setListLimit(int)}. + * @since 2.0 + */ + public MEMStore(int listLimit) + { + super(TYPE, set(ChangeFormat.REVISION, ChangeFormat.DELTA), set(RevisionTemporality.NONE, + RevisionTemporality.AUDITING), set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING)); + setRevisionTemporality(RevisionTemporality.AUDITING); + setRevisionParallelism(RevisionParallelism.BRANCHING); + this.listLimit = listLimit; + } + + public MEMStore() + { + this(UNLIMITED); + } + + @Override + public CDOID createObjectID(String val) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + byte[] decoded = CDOIDUtil.decodeUUID(val); + return CDOIDUtil.createUUID(decoded); + } + + return super.createObjectID(val); + } + + @Override + public boolean isLocal(CDOID id) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + return false; + } + + return super.isLocal(id); + } + + @Override + public void ensureLastObjectID(CDOID id) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + return; + } + + super.ensureLastObjectID(id); + } + + public synchronized Map<String, String> getPersistentProperties(Set<String> names) + { + if (names == null || names.isEmpty()) + { + return new HashMap<String, String>(properties); + } + + Map<String, String> result = new HashMap<String, String>(); + for (String name : names) + { + String value = properties.get(name); + if (value != null) + { + result.put(name, value); + } + } + + return result; + } + + public synchronized void setPersistentProperties(Map<String, String> properties) + { + this.properties.putAll(properties); + } + + public synchronized void removePersistentProperties(Set<String> names) + { + for (String name : names) + { + properties.remove(name); + } + } + + public synchronized Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + if (branchID == NEW_BRANCH) + { + branchID = ++lastBranchID; + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = --lastLocalBranchID; + } + + branchInfos.put(branchID, branchInfo); + return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp()); + } + + public synchronized BranchInfo loadBranch(int branchID) + { + return branchInfos.get(branchID); + } + + public synchronized SubBranchInfo[] loadSubBranches(int branchID) + { + List<SubBranchInfo> result = new ArrayList<SubBranchInfo>(); + for (Entry<Integer, BranchInfo> entry : branchInfos.entrySet()) + { + BranchInfo branchInfo = entry.getValue(); + if (branchInfo.getBaseBranchID() == branchID) + { + int id = entry.getKey(); + result.add(new SubBranchInfo(id, branchInfo.getName(), branchInfo.getBaseTimeStamp())); + } + } + + return result.toArray(new SubBranchInfo[result.size()]); + } + + public synchronized int loadBranches(int startID, int endID, CDOBranchHandler handler) + { + int count = 0; + InternalCDOBranchManager branchManager = getRepository().getBranchManager(); + for (Entry<Integer, BranchInfo> entry : branchInfos.entrySet()) + { + int id = entry.getKey(); + if (startID <= id && (id <= endID || endID == 0)) + { + BranchInfo branchInfo = entry.getValue(); + InternalCDOBranch branch = branchManager.getBranch(id, branchInfo); + handler.handleBranch(branch); + ++count; + } + } + + return count; + } + + public synchronized void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + InternalCDOCommitInfoManager manager = getRepository().getCommitInfoManager(); + for (int i = 0; i < commitInfos.size(); i++) + { + CommitInfo info = commitInfos.get(i); + if (startTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() < startTime) + { + continue; + } + + if (endTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() > endTime) + { + continue; + } + + if (branch != null && !ObjectUtil.equals(info.getBranch(), branch)) + { + continue; + } + + info.handle(manager, handler); + } + } + + public synchronized Set<CDOID> readChangeSet(CDOChangeSetSegment[] segments) + { + Set<CDOID> ids = new HashSet<CDOID>(); + for (CDOChangeSetSegment segment : segments) + { + for (List<InternalCDORevision> list : revisions.values()) + { + readChangeSet(segment, list, ids); + } + } + + return ids; + } + + private void readChangeSet(CDOChangeSetSegment segment, List<InternalCDORevision> list, Set<CDOID> ids) + { + long startTime = segment.getTimeStamp(); + long endTime = segment.getEndTime(); + boolean listCheckDone = false; + for (InternalCDORevision revision : list) + { + CDOID id = revision.getID(); + if (!listCheckDone) + { + if (ids.contains(id)) + { + return; + } + + if (!ObjectUtil.equals(revision.getBranch(), segment.getBranch())) + { + return; + } + + listCheckDone = true; + } + + if (CDOCommonUtil.isValidTimeStamp(revision.getTimeStamp(), startTime, endTime)) + { + ids.add(id); + } + } + } + + public synchronized void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + for (List<InternalCDORevision> list : revisions.values()) + { + for (InternalCDORevision revision : list) + { + if (!handleRevision(revision, eClass, branch, timeStamp, exactTime, handler)) + { + return; + } + } + } + } + + private boolean handleRevision(InternalCDORevision revision, EClass eClass, CDOBranch branch, long timeStamp, + boolean exactTime, CDORevisionHandler handler) + { + if (eClass != null && revision.getEClass() != eClass) + { + return true; + } + + if (branch != null && !ObjectUtil.equals(revision.getBranch(), branch)) + { + return true; + } + + if (timeStamp != CDOBranchPoint.INVALID_DATE) + { + if (exactTime) + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE && revision.getTimeStamp() != timeStamp) + { + return true; + } + } + else + { + if (!revision.isValid(timeStamp)) + { + return true; + } + } + } + + return handler.handleRevision(revision); + } + + /** + * @since 2.0 + */ + public int getListLimit() + { + return listLimit; + } + + /** + * @since 2.0 + */ + public synchronized void setListLimit(int listLimit) + { + if (listLimit != UNLIMITED && this.listLimit != listLimit) + { + for (List<InternalCDORevision> list : revisions.values()) + { + enforceListLimit(list); + } + } + + this.listLimit = listLimit; + } + + /** + * @since 2.0 + */ + public synchronized List<InternalCDORevision> getCurrentRevisions() + { + ArrayList<InternalCDORevision> simpleRevisions = new ArrayList<InternalCDORevision>(); + Iterator<List<InternalCDORevision>> itr = revisions.values().iterator(); + while (itr.hasNext()) + { + List<InternalCDORevision> list = itr.next(); + InternalCDORevision revision = list.get(list.size() - 1); + simpleRevisions.add(revision); + } + + return simpleRevisions; + } + + public synchronized InternalCDORevision getRevisionByVersion(CDOID id, CDOBranchVersion branchVersion) + { + Object listKey = getListKey(id, branchVersion.getBranch()); + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return getRevisionByVersion(list, branchVersion.getVersion()); + } + + /** + * @since 2.0 + */ + public synchronized InternalCDORevision getRevision(CDOID id, CDOBranchPoint branchPoint) + { + Object listKey = getListKey(id, branchPoint.getBranch()); + if (branchPoint.getTimeStamp() == CDORevision.UNSPECIFIED_DATE) + { + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return list.get(list.size() - 1); + } + + if (!getRepository().isSupportingAudits()) + { + throw new UnsupportedOperationException("Auditing not supported"); + } + + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return getRevision(list, branchPoint); + } + + public synchronized void addRevision(InternalCDORevision revision, boolean raw) + { + Object listKey = getListKey(revision.getID(), revision.getBranch()); + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + list = new ArrayList<InternalCDORevision>(); + revisions.put(listKey, list); + } + + addRevision(list, revision, raw); + + if (raw) + { + ensureLastObjectID(revision.getID()); + } + } + + public synchronized void addCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment) + { + int index = commitInfos.size() - 1; + while (index >= 0) + { + CommitInfo info = commitInfos.get(index); + if (timeStamp > info.getTimeStamp()) + { + break; + } + + --index; + } + + CommitInfo commitInfo = new CommitInfo(branch, timeStamp, previousTimeStamp, userID, comment); + commitInfos.add(index + 1, commitInfo); + } + + /** + * @since 2.0 + */ + public synchronized boolean rollbackRevision(InternalCDORevision revision) + { + CDOID id = revision.getID(); + CDOBranch branch = revision.getBranch(); + int version = revision.getVersion(); + + Object listKey = getListKey(id, branch); + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return false; + } + + for (Iterator<InternalCDORevision> it = list.iterator(); it.hasNext();) + { + InternalCDORevision rev = it.next(); + if (rev.getVersion() == version) + { + it.remove(); + return true; + } + else if (rev.getVersion() == version - 1) + { + rev.setRevised(CDORevision.UNSPECIFIED_DATE); + } + } + + return false; + } + + /** + * @since 3.0 + */ + public synchronized DetachedCDORevision detachObject(CDOID id, CDOBranch branch, long timeStamp) + { + Object listKey = getListKey(id, branch); + List<InternalCDORevision> list = revisions.get(listKey); + if (list != null) + { + InternalCDORevision revision = getRevision(list, branch.getHead()); + if (revision != null) + { + revision.setRevised(timeStamp - 1); + } + } + + int version; + if (list == null) + { + list = new ArrayList<InternalCDORevision>(); + revisions.put(listKey, list); + version = CDOBranchVersion.FIRST_VERSION; + } + else + { + version = getHighestVersion(list) + 1; + } + + EClass eClass = getObjectType(id); + DetachedCDORevision detached = new DetachedCDORevision(eClass, id, branch, version, timeStamp); + addRevision(list, detached, false); + return detached; + } + + /** + * @since 2.0 + */ + public synchronized void queryResources(IStoreAccessor.QueryResourcesContext context) + { + CDOID folderID = context.getFolderID(); + String name = context.getName(); + boolean exactMatch = context.exactMatch(); + for (Entry<Object, List<InternalCDORevision>> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (!ObjectUtil.equals(branch, context.getBranch())) + { + continue; + } + + List<InternalCDORevision> list = entry.getValue(); + if (list.isEmpty()) + { + continue; + } + + InternalCDORevision revision = list.get(0); + if (revision instanceof SyntheticCDORevision) + { + continue; + } + + if (!revision.isResourceNode()) + { + continue; + } + + revision = getRevision(list, context); + if (revision == null || revision instanceof DetachedCDORevision) + { + continue; + } + + CDOID revisionFolder = (CDOID)revision.data().getContainerID(); + if (!CDOIDUtil.equals(revisionFolder, folderID)) + { + continue; + } + + String revisionName = (String)revision.data().get(resourceNameFeature, 0); + boolean useEquals = exactMatch || revisionName == null || name == null; + boolean match = useEquals ? ObjectUtil.equals(revisionName, name) : revisionName.startsWith(name); + + if (match) + { + if (!context.addResource(revision.getID())) + { + // No more results allowed + break; + } + } + } + } + + public synchronized void queryXRefs(QueryXRefsContext context) + { + Set<CDOID> targetIDs = context.getTargetObjects().keySet(); + Map<EClass, List<EReference>> sourceCandidates = context.getSourceCandidates(); + + for (Entry<Object, List<InternalCDORevision>> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (!ObjectUtil.equals(branch, context.getBranch())) + { + continue; + } + + List<InternalCDORevision> list = entry.getValue(); + if (list.isEmpty()) + { + continue; + } + + InternalCDORevision revision = getRevision(list, context); + if (revision == null || revision instanceof SyntheticCDORevision) + { + continue; + } + + EClass eClass = revision.getEClass(); + CDOID sourceID = revision.getID(); + + List<EReference> eReferences = sourceCandidates.get(eClass); + if (eReferences != null) + { + for (EReference eReference : eReferences) + { + Object value = revision.getValue(eReference); + if (value != null) + { + if (eReference.isMany()) + { + @SuppressWarnings("unchecked") + List<CDOID> ids = (List<CDOID>)value; + int index = 0; + for (CDOID id : ids) + { + if (!queryXRefs(context, targetIDs, id, sourceID, eReference, index++)) + { + return; + } + } + } + else + { + CDOID id = (CDOID)value; + if (!queryXRefs(context, targetIDs, id, sourceID, eReference, 0)) + { + return; + } + } + } + } + } + } + } + + private boolean queryXRefs(QueryXRefsContext context, Set<CDOID> targetIDs, CDOID targetID, CDOID sourceID, + EReference sourceReference, int index) + { + for (CDOID id : targetIDs) + { + if (id.equals(targetID)) + { + if (!context.addXRef(targetID, sourceID, sourceReference, index)) + { + // No more results allowed + return false; + } + } + } + + return true; + } + + public synchronized void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, + long toCommitTime) + { + // TODO: implement MEMStore.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime) + throw new UnsupportedOperationException(); + } + + public synchronized void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, + long toCommitTime, OMMonitor monitor) + { + // TODO: implement MEMStore.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor) + throw new UnsupportedOperationException(); + } + + public synchronized void rawDelete(CDOID id, int version, CDOBranch branch) + { + Object listKey = getListKey(id, branch); + List<InternalCDORevision> list = revisions.get(listKey); + if (list != null) + { + for (Iterator<InternalCDORevision> it = list.iterator(); it.hasNext();) + { + InternalCDORevision rev = it.next(); + if (rev.getVersion() == version) + { + it.remove(); + break; + } + } + } + } + + public synchronized LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public synchronized LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, + boolean readOnly, Map<CDOID, LockGrade> locks) + { + if (durableLockingID != null) + { + // If the caller is specifying the ID, make sure there is no area with this ID yet + if (lockAreas.containsKey(durableLockingID)) + { + throw new LockAreaAlreadyExistsException(durableLockingID); + } + } + else + { + do + { + durableLockingID = CDOLockUtil.createDurableLockingID(); + } while (lockAreas.containsKey(durableLockingID)); + } + + LockArea area = CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + lockAreas.put(durableLockingID, area); + return area; + } + + public synchronized void updateLockArea(LockArea lockArea) + { + String durableLockingID = lockArea.getDurableLockingID(); + lockAreas.put(durableLockingID, lockArea); + } + + public synchronized LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + LockArea area = lockAreas.get(durableLockingID); + if (area == null) + { + throw new LockAreaNotFoundException(durableLockingID); + } + + return area; + } + + public synchronized void getLockAreas(String userIDPrefix, Handler handler) + { + for (LockArea area : lockAreas.values()) + { + String userID = area.getUserID(); + if (userID == null || userID.startsWith(userIDPrefix)) + { + if (!handler.handleLockArea(area)) + { + return; + } + } + } + } + + public synchronized void deleteLockArea(String durableLockingID) + { + lockAreas.remove(durableLockingID); + } + + public synchronized void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + LockArea area = getLockArea(durableLockingID); + Map<CDOID, LockGrade> locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockManager(); + for (Object objectToLock : objectsToLock) + { + CDOID id = lockManager.getLockKeyID(objectToLock); + LockGrade grade = locks.get(id); + if (grade != null) + { + grade = grade.getUpdated(type, true); + } + else + { + grade = LockGrade.get(type); + } + + locks.put(id, grade); + } + } + + public synchronized void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + LockArea area = getLockArea(durableLockingID); + Map<CDOID, LockGrade> locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockManager(); + for (Object objectToUnlock : objectsToUnlock) + { + CDOID id = lockManager.getLockKeyID(objectToUnlock); + LockGrade grade = locks.get(id); + if (grade != null) + { + grade = grade.getUpdated(type, false); + if (grade == LockGrade.NONE) + { + locks.remove(id); + } + } + } + } + + public synchronized void unlock(String durableLockingID) + { + LockArea area = getLockArea(durableLockingID); + Map<CDOID, LockGrade> locks = area.getLocks(); + locks.clear(); + } + + public synchronized void queryLobs(List<byte[]> ids) + { + for (Iterator<byte[]> it = ids.iterator(); it.hasNext();) + { + byte[] id = it.next(); + String key = HexUtil.bytesToHex(id); + if (!lobs.containsKey(key)) + { + it.remove(); + } + } + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + for (Entry<String, Object> entry : lobs.entrySet()) + { + byte[] id = HexUtil.hexToBytes(entry.getKey()); + Object lob = entry.getValue(); + if (lob instanceof byte[]) + { + byte[] blob = (byte[])lob; + ByteArrayInputStream in = new ByteArrayInputStream(blob); + OutputStream out = handler.handleBlob(id, blob.length); + if (out != null) + { + try + { + IOUtil.copyBinary(in, out, blob.length); + } + finally + { + IOUtil.close(out); + } + } + } + else + { + char[] clob = (char[])lob; + CharArrayReader in = new CharArrayReader(clob); + Writer out = handler.handleClob(id, clob.length); + if (out != null) + { + try + { + IOUtil.copyCharacter(in, out, clob.length); + } + finally + { + IOUtil.close(out); + } + } + } + } + } + + public synchronized void loadLob(byte[] id, OutputStream out) throws IOException + { + String key = HexUtil.bytesToHex(id); + Object lob = lobs.get(key); + if (lob == null) + { + throw new IOException("Lob not found: " + key); + } + + if (lob instanceof byte[]) + { + byte[] blob = (byte[])lob; + ByteArrayInputStream in = new ByteArrayInputStream(blob); + IOUtil.copyBinary(in, out, blob.length); + } + else + { + char[] clob = (char[])lob; + CharArrayReader in = new CharArrayReader(clob); + IOUtil.copyCharacter(in, new OutputStreamWriter(out), clob.length); + } + } + + public synchronized void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtil.copyBinary(inputStream, out, size); + lobs.put(HexUtil.bytesToHex(id), out.toByteArray()); + } + + public synchronized void writeClob(byte[] id, long size, Reader reader) throws IOException + { + CharArrayWriter out = new CharArrayWriter(); + IOUtil.copyCharacter(reader, out, size); + lobs.put(HexUtil.bytesToHex(id), out.toCharArray()); + } + + @Override + public MEMStoreAccessor createReader(ISession session) + { + return new MEMStoreAccessor(this, session); + } + + /** + * @since 2.0 + */ + @Override + public MEMStoreAccessor createWriter(ITransaction transaction) + { + return new MEMStoreAccessor(this, transaction); + } + + /** + * @since 2.0 + */ + public long getCreationTime() + { + return creationTime; + } + + public void setCreationTime(long creationTime) + { + this.creationTime = creationTime; + } + + public boolean isFirstStart() + { + return true; + } + + public synchronized Map<CDOBranch, List<CDORevision>> getAllRevisions() + { + Map<CDOBranch, List<CDORevision>> result = new HashMap<CDOBranch, List<CDORevision>>(); + InternalCDOBranchManager branchManager = getRepository().getBranchManager(); + result.put(branchManager.getMainBranch(), new ArrayList<CDORevision>()); + + for (Integer branchID : branchInfos.keySet()) + { + InternalCDOBranch branch = branchManager.getBranch(branchID); + result.put(branch, new ArrayList<CDORevision>()); + } + + for (List<InternalCDORevision> list : revisions.values()) + { + for (InternalCDORevision revision : list) + { + CDOBranch branch = revision.getBranch(); + List<CDORevision> resultList = result.get(branch); + resultList.add(revision); + } + } + + return result; + } + + public synchronized EClass getObjectType(CDOID id) + { + return objectTypes.get(id); + } + + /** + * @since 2.0 + */ + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + creationTime = getRepository().getTimeStamp(); + + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + setObjectIDTypes(Collections.singleton(CDOID.ObjectType.UUID)); + } + } + + @Override + protected void doDeactivate() throws Exception + { + revisions.clear(); + branchInfos.clear(); + commitInfos.clear(); + objectTypes.clear(); + properties.clear(); + resourceNameFeature = null; + lastBranchID = 0; + lastLocalBranchID = 0; + super.doDeactivate(); + } + + @Override + protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing) + { + // Pooling of store accessors not supported + return null; + } + + @Override + protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing) + { + // Pooling of store accessors not supported + return null; + } + + private Object getListKey(CDOID id, CDOBranch branch) + { + if (getRevisionParallelism() == RevisionParallelism.NONE) + { + return id; + } + + return new ListKey(id, branch); + } + + private CDOBranch getBranch(Object key) + { + if (key instanceof ListKey) + { + return ((ListKey)key).getBranch(); + } + + return getRepository().getBranchManager().getMainBranch(); + } + + private int getHighestVersion(List<InternalCDORevision> list) + { + int version = CDOBranchVersion.UNSPECIFIED_VERSION; + for (InternalCDORevision revision : list) + { + if (revision.getVersion() > version) + { + version = revision.getVersion(); + } + } + + return version; + } + + private InternalCDORevision getRevisionByVersion(List<InternalCDORevision> list, int version) + { + for (InternalCDORevision revision : list) + { + if (revision.getVersion() == version) + { + return revision; + } + } + + return null; + } + + private InternalCDORevision getRevision(List<InternalCDORevision> list, CDOBranchPoint branchPoint) + { + long timeStamp = branchPoint.getTimeStamp(); + for (InternalCDORevision revision : list) + { + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + if (!revision.isHistorical()) + { + return revision; + } + } + else + { + if (revision.isValid(timeStamp)) + { + return revision; + } + } + } + + return null; + } + + private void addRevision(List<InternalCDORevision> list, InternalCDORevision revision, boolean raw) + { + boolean resource = !(revision instanceof SyntheticCDORevision) && revision.isResource(); + if (resource && resourceNameFeature == null) + { + resourceNameFeature = revision.getEClass().getEStructuralFeature(CDOModelConstants.RESOURCE_NODE_NAME_ATTRIBUTE); + } + + if (!raw) + { + // Check version conflict + int version = revision.getVersion(); + InternalCDORevision rev = getRevisionByVersion(list, version); + if (rev != null) + { + rev = getRevisionByVersion(list, version); + throw new IllegalStateException("Concurrent modification of " + rev.getEClass().getName() + "@" + rev.getID()); + } + + // Revise old revision + int oldVersion = version - 1; + if (oldVersion >= CDORevision.UNSPECIFIED_VERSION) + { + InternalCDORevision oldRevision = getRevisionByVersion(list, oldVersion); + if (oldRevision != null) + { + if (getRepository().isSupportingAudits()) + { + oldRevision.setRevised(revision.getTimeStamp() - 1); + } + else + { + list.remove(oldRevision); + } + } + } + + // Check duplicate resource + if (resource) + { + checkDuplicateResource(revision); + } + } + + // Adjust the list + list.add(revision); + if (listLimit != UNLIMITED) + { + enforceListLimit(list); + } + + CDOID id = revision.getID(); + if (!objectTypes.containsKey(id)) + { + objectTypes.put(id, revision.getEClass()); + } + } + + private void checkDuplicateResource(InternalCDORevision revision) + { + CDOID revisionFolder = (CDOID)revision.data().getContainerID(); + String revisionName = (String)revision.data().get(resourceNameFeature, 0); + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + + CDOID resourceID = accessor.readResourceID(revisionFolder, revisionName, revision); + if (!CDOIDUtil.isNull(resourceID)) + { + throw new IllegalStateException("Duplicate resource: name=" + revisionName + ", folderID=" + revisionFolder); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + private void enforceListLimit(List<InternalCDORevision> list) + { + while (list.size() > listLimit) + { + list.remove(0); + } + } + + /** + * @author Eike Stepper + */ + private static final class ListKey + { + private CDOID id; + + private CDOBranch branch; + + public ListKey(CDOID id, CDOBranch branch) + { + this.id = id; + this.branch = branch; + } + + public CDOID getID() + { + return id; + } + + public CDOBranch getBranch() + { + return branch; + } + + @Override + public int hashCode() + { + return id.hashCode() ^ branch.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + + if (obj instanceof ListKey) + { + ListKey that = (ListKey)obj; + return ObjectUtil.equals(id, that.getID()) && ObjectUtil.equals(branch, that.getBranch()); + } + + return false; + } + + @Override + public String toString() + { + return MessageFormat.format("{0}:{1}", id, branch.getID()); + } + } + + /** + * @author Eike Stepper + */ + private static final class CommitInfo + { + private CDOBranch branch; + + private long timeStamp; + + private long previousTimeStamp; + + private String userID; + + private String comment; + + public CommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment) + { + this.branch = branch; + this.timeStamp = timeStamp; + this.previousTimeStamp = previousTimeStamp; + this.userID = userID; + this.comment = comment; + } + + public CDOBranch getBranch() + { + return branch; + } + + public long getTimeStamp() + { + return timeStamp; + } + + public void handle(InternalCDOCommitInfoManager manager, CDOCommitInfoHandler handler) + { + CDOCommitInfo commitInfo = manager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, null); + handler.handleCommitInfo(commitInfo); + } + + @Override + public String toString() + { + return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}]", branch, timeStamp, previousTimeStamp, userID, + comment); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java new file mode 100644 index 0000000000..55da263437 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java @@ -0,0 +1,502 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.internal.server.mem; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryContext; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.IStoreAccessor.Raw; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor; + +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking2 +{ + private final IQueryHandler testQueryHandler = new IQueryHandler() + { + public void executeQuery(CDOQueryInfo info, IQueryContext queryContext) + { + List<Object> filters = new ArrayList<Object>(); + Object context = info.getParameters().get("context"); //$NON-NLS-1$ + Long sleep = (Long)info.getParameters().get("sleep"); //$NON-NLS-1$ + if (context != null) + { + if (context instanceof EClass) + { + final EClass eClass = (EClass)context; + filters.add(new Object() + { + @Override + public int hashCode() + { + return eClass.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + InternalCDORevision revision = (InternalCDORevision)obj; + return revision.getEClass().equals(eClass); + } + }); + } + } + + for (InternalCDORevision revision : getStore().getCurrentRevisions()) + { + if (sleep != null) + { + try + { + Thread.sleep(sleep); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + boolean valid = true; + + for (Object filter : filters) + { + if (!filter.equals(revision)) + { + valid = false; + break; + } + } + + if (valid) + { + if (!queryContext.addResult(revision)) + { + // No more results allowed + break; + } + } + } + } + }; + + private List<InternalCDORevision> newRevisions = new ArrayList<InternalCDORevision>(); + + public MEMStoreAccessor(MEMStore store, ISession session) + { + super(store, session); + } + + /** + * @since 2.0 + */ + public MEMStoreAccessor(MEMStore store, ITransaction transaction) + { + super(store, transaction); + } + + @Override + public MEMStore getStore() + { + return (MEMStore)super.getStore(); + } + + /** + * @since 2.0 + */ + public MEMStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + return new MEMStoreChunkReader(this, revision, feature); + } + + public Collection<InternalCDOPackageUnit> readPackageUnits() + { + return Collections.emptySet(); + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + throw new UnsupportedOperationException(); + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + return getStore().createBranch(branchID, branchInfo); + } + + public BranchInfo loadBranch(int branchID) + { + return getStore().loadBranch(branchID); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + return getStore().loadSubBranches(branchID); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + return getStore().loadBranches(startID, endID, branchHandler); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + getStore().loadCommitInfos(branch, startTime, endTime, handler); + } + + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + return getStore().readChangeSet(segments); + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache) + { + return getStore().getRevision(id, branchPoint); + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache) + { + return getStore().getRevisionByVersion(id, branchVersion); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + getStore().handleRevisions(eClass, branch, timeStamp, exactTime, handler); + } + + /** + * @since 2.0 + */ + @Override + protected void doCommit(OMMonitor monitor) + { + // Do nothing + } + + @Override + public void doWrite(InternalCommitContext context, OMMonitor monitor) + { + MEMStore store = getStore(); + synchronized (store) + { + super.doWrite(context, monitor); + } + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment, OMMonitor monitor) + { + getStore().addCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment); + } + + @Override + protected void doRollback(CommitContext context) + { + MEMStore store = getStore(); + synchronized (store) + { + for (InternalCDORevision revision : newRevisions) + { + store.rollbackRevision(revision); + } + } + } + + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + // Do nothing + } + + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + for (InternalCDORevision revision : revisions) + { + writeRevision(revision); + } + } + + protected void writeRevision(InternalCDORevision revision) + { + newRevisions.add(revision); + getStore().addRevision(revision, false); + } + + /** + * @since 2.0 + */ + @Override + protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created, + OMMonitor monitor) + { + for (InternalCDORevisionDelta revisionDelta : revisionDeltas) + { + writeRevisionDelta(revisionDelta, branch, created); + } + } + + /** + * @since 2.0 + */ + protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, CDOBranch branch, long created) + { + CDOID id = revisionDelta.getID(); + CDOBranchVersion version = revisionDelta.getBranch().getVersion(revisionDelta.getVersion()); + InternalCDORevision revision = getStore().getRevisionByVersion(id, version); + if (revision.getVersion() != revisionDelta.getVersion()) + { + throw new ConcurrentModificationException("Trying to update object " + id //$NON-NLS-1$ + + " that was already modified"); //$NON-NLS-1$ + } + + InternalCDORevision newRevision = revision.copy(); + newRevision.adjustForCommit(branch, created); + + revisionDelta.apply(newRevision); + writeRevision(newRevision); + } + + @Override + protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + for (CDOID id : detachedObjects) + { + detachObject(id, branch, timeStamp); + } + } + + /** + * @since 3.0 + */ + protected void detachObject(CDOID id, CDOBranch branch, long timeStamp) + { + getStore().detachObject(id, branch, timeStamp); + } + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context) + { + getStore().queryResources(context); + } + + public void queryXRefs(QueryXRefsContext context) + { + getStore().queryXRefs(context); + } + + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + if ("TEST".equals(info.getQueryLanguage())) //$NON-NLS-1$ + { + return testQueryHandler; + } + + return null; + } + + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException + { + getStore().rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + getStore().rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + getStore().addRevision(revision, true); + } + + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException + { + writeBlob(id, size, inputStream); + } + + public void rawStore(byte[] id, long size, Reader reader) throws IOException + { + writeClob(id, size, reader); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + @Deprecated + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + + // getStore().rawDelete(id, version, branch); + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + // Do nothing + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return getStore().createLockArea(userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return getStore().createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + } + + public void updateLockArea(LockArea lockArea) + { + getStore().updateLockArea(lockArea); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + return getStore().getLockArea(durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + getStore().getLockAreas(userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + getStore().deleteLockArea(durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + getStore().lock(durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + getStore().unlock(durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + getStore().unlock(durableLockingID); + } + + public void queryLobs(List<byte[]> ids) + { + getStore().queryLobs(ids); + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + getStore().handleLobs(fromTime, toTime, handler); + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + getStore().loadLob(id, out); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + getStore().writeBlob(id, size, inputStream); + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + getStore().writeClob(id, size, reader); + } + + @Override + protected void doActivate() throws Exception + { + // Do nothing + } + + @Override + protected void doDeactivate() throws Exception + { + newRevisions.clear(); + } + + @Override + protected void doPassivate() throws Exception + { + // Pooling of store accessors not supported + } + + @Override + protected void doUnpassivate() throws Exception + { + // Pooling of store accessors not supported + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java new file mode 100644 index 0000000000..82e6c634a1 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server.mem; + +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.server.StoreChunkReader; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.List; + +/** + * @author Simon McDuff + */ +public class MEMStoreChunkReader extends StoreChunkReader +{ + /** + * @since 2.0 + */ + public MEMStoreChunkReader(IStoreAccessor accessor, CDORevision revision, EStructuralFeature feature) + { + super(accessor, revision, feature); + } + + public List<Chunk> executeRead() + { + CDOID id = getRevision().getID(); + CDOBranchVersion branchVersion = getRevision(); + + MEMStore store = getAccessor().getStore(); + List<Chunk> chunks = getChunks(); + for (Chunk chunk : chunks) + { + int startIndex = chunk.getStartIndex(); + InternalCDORevision revision = store.getRevisionByVersion(id, branchVersion); + for (int i = 0; i < chunk.size(); i++) + { + Object object = revision.get(getFeature(), startIndex + i); + chunk.add(i, object); + } + } + + return chunks; + } + + /** + * @since 2.0 + */ + @Override + public MEMStoreAccessor getAccessor() + { + return (MEMStoreAccessor)super.getAccessor(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java new file mode 100644 index 0000000000..dd48174921 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server.mem; + +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStoreFactory; + +import org.w3c.dom.Element; + +import java.util.Map; + +/** + * @author Simon McDuff + */ +public class MEMStoreFactory implements IStoreFactory +{ + public MEMStoreFactory() + { + } + + public String getStoreType() + { + return MEMStore.TYPE; + } + + public IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig) + { + return new MEMStore(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java new file mode 100644 index 0000000000..294f584138 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Victor Roldan Betancort - initial API and implementation + * Eike Stepper - maintenance + */ +package org.eclipse.emf.cdo.internal.server.messages; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * @author Victor Roldan Betancort + */ +public class Messages +{ + private static final String BUNDLE_NAME = "org.eclipse.emf.cdo.internal.server.messages.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties new file mode 100644 index 0000000000..7dc1b9ef44 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties @@ -0,0 +1,16 @@ +# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Victor Roldan Betancort - initial API and implementation +# Eike Stepper - maintenance + +CDOServerApplication.1=CDO server starting +CDOServerApplication.6=CDO server started +CDOServerApplication.7=CDO server stopping +CDOServerApplication.8=CDO server stopped +CDOServerApplication.5=CDO server configuration not found: +CDOServerApplication.3=No CDO repositories configured: diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java new file mode 100644 index 0000000000..dcfab829f1 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.syncing; + +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalFailoverParticipant; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +/** + * @author Eike Stepper + */ +public class FailoverParticipant extends SynchronizableRepository implements InternalFailoverParticipant +{ + private boolean allowBackupCommits; + + public FailoverParticipant() + { + setState(OFFLINE); + } + + public boolean isAllowBackupCommits() + { + return allowBackupCommits; + } + + public void setAllowBackupCommits(boolean allowBackupCommits) + { + this.allowBackupCommits = allowBackupCommits; + } + + @Override + public void setType(Type type) + { + checkArg(type == MASTER || type == BACKUP, "Type must be MASTER or BACKUP"); + super.setType(type); + } + + @Override + protected void changingType(Type oldType, Type newType) + { + if (isActive()) + { + if (newType == MASTER) + { + // Switch off synchronizer + doStopSynchronization(); + } + else + { + // Bug 312879 + setReplicationCountersToLatest(); + + // Switch on synchronizer + doStartSynchronization(); + } + } + + super.changingType(oldType, newType); + } + + @Override + protected void initRootResource() + { + if (getType() == BACKUP) + { + super.initRootResource(); + } + else + { + doInitRootResource(); + } + } + + protected void doStartSynchronization() + { + super.startSynchronization(); + } + + protected void doStopSynchronization() + { + super.stopSynchronization(); + } + + @Override + protected void startSynchronization() + { + if (getType() == BACKUP) + { + doStartSynchronization(); + } + } + + @Override + protected void stopSynchronization() + { + if (getType() == BACKUP) + { + doStopSynchronization(); + } + } + + @Override + public InternalCommitContext createCommitContext(InternalTransaction transaction) + { + if (getType() == BACKUP) + { + if (getState() != ONLINE) + { + throw new IllegalStateException("Backup repository is not online"); + } + + if (allowBackupCommits || transaction.getSession() == getReplicatorSession()) + { + return createWriteThroughCommitContext(transaction); + } + + throw new IllegalStateException( + "Only the repository synchronizer is allowed to commit transactions to a backup repository"); + } + + return createNormalCommitContext(transaction); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java new file mode 100644 index 0000000000..d551fc52e9 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java @@ -0,0 +1,249 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.syncing; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOChangeKind; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeKindCache; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.collection.IndexedList; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +public class OfflineClone extends SynchronizableRepository +{ + public OfflineClone() + { + setState(OFFLINE); + } + + @Override + public final Type getType() + { + return CLONE; + } + + @Override + public final void setType(Type type) + { + throw new UnsupportedOperationException(); + } + + @Override + public InternalCommitContext createCommitContext(InternalTransaction transaction) + { + CDOBranch branch = transaction.getBranch(); + if (branch.isLocal()) + { + return createNormalCommitContext(transaction); + } + + if (getState() != ONLINE) + { + return createBranchingCommitContext(transaction, branch); + } + + return createWriteThroughCommitContext(transaction); + } + + protected InternalCommitContext createBranchingCommitContext(InternalTransaction transaction, CDOBranch branch) + { + long[] times = createCommitTimeStamp(new Monitor()); + CDOBranch offlineBranch = createOfflineBranch(branch, times[0] - 1L); + transaction.setBranchPoint(offlineBranch.getHead()); + return new BranchingCommitContext(transaction, times); + } + + protected CDOBranch createOfflineBranch(CDOBranch baseBranch, long baseTimeStamp) + { + try + { + StoreThreadLocal.setSession(getReplicatorSession()); + InternalCDOBranchManager branchManager = getBranchManager(); + return branchManager.createBranch(NEW_LOCAL_BRANCH, + "Offline-" + baseTimeStamp, (InternalCDOBranch)baseBranch, baseTimeStamp); //$NON-NLS-1$ + } + finally + { + StoreThreadLocal.release(); + } + } + + /** + * @author Eike Stepper + */ + protected static final class CommitContextData implements CDOCommitData + { + private InternalCommitContext commitContext; + + private CDOChangeKindCache changeKindCache; + + public CommitContextData(InternalCommitContext commitContext) + { + this.commitContext = commitContext; + } + + public boolean isEmpty() + { + return false; + } + + public CDOChangeSetData copy() + { + throw new UnsupportedOperationException(); + } + + public void merge(CDOChangeSetData changeSetData) + { + throw new UnsupportedOperationException(); + } + + public List<CDOPackageUnit> getNewPackageUnits() + { + final InternalCDOPackageUnit[] newPackageUnits = commitContext.getNewPackageUnits(); + return new IndexedList<CDOPackageUnit>() + { + @Override + public CDOPackageUnit get(int index) + { + return newPackageUnits[index]; + } + + @Override + public int size() + { + return newPackageUnits.length; + } + }; + } + + public List<CDOIDAndVersion> getNewObjects() + { + final InternalCDORevision[] newObjects = commitContext.getNewObjects(); + return new IndexedList<CDOIDAndVersion>() + { + @Override + public CDOIDAndVersion get(int index) + { + return newObjects[index]; + } + + @Override + public int size() + { + return newObjects.length; + } + }; + } + + public List<CDORevisionKey> getChangedObjects() + { + final InternalCDORevisionDelta[] changedObjects = commitContext.getDirtyObjectDeltas(); + return new IndexedList<CDORevisionKey>() + { + @Override + public CDORevisionKey get(int index) + { + return changedObjects[index]; + } + + @Override + public int size() + { + return changedObjects.length; + } + }; + } + + public List<CDOIDAndVersion> getDetachedObjects() + { + final CDOID[] detachedObjects = commitContext.getDetachedObjects(); + return new IndexedList<CDOIDAndVersion>() + { + @Override + public CDOIDAndVersion get(int index) + { + return CDOIDUtil.createIDAndVersion(detachedObjects[index], CDOBranchVersion.UNSPECIFIED_VERSION); + } + + @Override + public int size() + { + return detachedObjects.length; + } + }; + } + + public synchronized Map<CDOID, CDOChangeKind> getChangeKinds() + { + if (changeKindCache == null) + { + changeKindCache = new CDOChangeKindCache(this); + } + + return changeKindCache; + } + + public CDOChangeKind getChangeKind(CDOID id) + { + return getChangeKinds().get(id); + } + } + + /** + * @author Eike Stepper + */ + protected final class BranchingCommitContext extends TransactionCommitContext + { + private long[] times; + + public BranchingCommitContext(InternalTransaction transaction, long[] times) + { + super(transaction); + this.times = times; + } + + @Override + protected void lockObjects() throws InterruptedException + { + // Do nothing + } + + @Override + protected long[] createTimeStamp(OMMonitor monitor) + { + return times; + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java new file mode 100644 index 0000000000..e69b5a6858 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.syncing; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.List; + +/** + * TODO Optimize createCommitInfo() + * + * @author Eike Stepper + */ +public final class ReplicatorCommitContext extends TransactionCommitContext +{ + private final CDOCommitInfo commitInfo; + + public ReplicatorCommitContext(InternalTransaction transaction, CDOCommitInfo commitInfo) + { + super(transaction); + this.commitInfo = commitInfo; + + setCommitComment(commitInfo.getComment()); + + InternalCDOPackageUnit[] newPackageUnits = getNewPackageUnits(commitInfo, getPackageRegistry()); + setNewPackageUnits(newPackageUnits); + + InternalCDORevision[] newObjects = getNewObjects(commitInfo); + setNewObjects(newObjects); + + InternalCDORevisionDelta[] dirtyObjectDeltas = getDirtyObjectDeltas(commitInfo); + setDirtyObjectDeltas(dirtyObjectDeltas); + + CDOID[] detachedObjects = getDetachedObjects(commitInfo); + setDetachedObjects(detachedObjects); + } + + @Override + public String getUserID() + { + return commitInfo.getUserID(); + } + + @Override + protected long[] createTimeStamp(OMMonitor monitor) + { + InternalRepository repository = getTransaction().getSession().getManager().getRepository(); + + long commitTimeStamp = commitInfo.getTimeStamp(); + if (commitTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + commitTimeStamp = repository.getTimeStamp(); + } + + return repository.forceCommitTimeStamp(commitInfo.getTimeStamp(), monitor); + } + + @Override + protected void adjustForCommit() + { + // Do nothing + } + + @Override + public void applyIDMappings(OMMonitor monitor) + { + monitor.done(); + } + + @Override + protected void lockObjects() throws InterruptedException + { + // Do nothing + } + + @Override + protected void checkXRefs() + { + // Do nothing + } + + private static InternalCDOPackageUnit[] getNewPackageUnits(CDOCommitInfo commitInfo, + InternalCDOPackageRegistry packageRegistry) + { + List<CDOPackageUnit> list = commitInfo.getNewPackageUnits(); + InternalCDOPackageUnit[] result = new InternalCDOPackageUnit[list.size()]; + + int i = 0; + for (CDOPackageUnit packageUnit : list) + { + result[i] = (InternalCDOPackageUnit)packageUnit; + packageRegistry.putPackageUnit(result[i]); + ++i; + } + + return result; + } + + private static InternalCDORevision[] getNewObjects(CDOCommitInfo commitInfo) + { + List<CDOIDAndVersion> list = commitInfo.getNewObjects(); + InternalCDORevision[] result = new InternalCDORevision[list.size()]; + + int i = 0; + for (CDOIDAndVersion revision : list) + { + result[i++] = (InternalCDORevision)revision; + } + + return result; + } + + private static InternalCDORevisionDelta[] getDirtyObjectDeltas(CDOCommitInfo commitInfo) + { + List<CDORevisionKey> list = commitInfo.getChangedObjects(); + InternalCDORevisionDelta[] result = new InternalCDORevisionDelta[list.size()]; + + int i = 0; + for (CDORevisionKey delta : list) + { + result[i++] = (InternalCDORevisionDelta)delta; + } + + return result; + } + + private static CDOID[] getDetachedObjects(CDOCommitInfo commitInfo) + { + List<CDOIDAndVersion> list = commitInfo.getDetachedObjects(); + CDOID[] result = new CDOID[list.size()]; + + int i = 0; + for (CDOIDAndVersion key : list) + { + result[i++] = key.getID(); + } + + return result; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java new file mode 100644 index 0000000000..dfb44b894a --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java @@ -0,0 +1,650 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.syncing; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchCreatedEvent; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.internal.common.revision.NOOPRevisionCache; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.session.CDOSessionConfiguration; +import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; +import org.eclipse.emf.cdo.session.CDOSessionInvalidationEvent; +import org.eclipse.emf.cdo.session.CDOSessionLocksChangedEvent; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache; +import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer; +import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository; + +import org.eclipse.net4j.util.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.QueueRunner; +import org.eclipse.net4j.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.lifecycle.ILifecycleEvent; +import org.eclipse.net4j.util.om.monitor.NotifyingMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; + +/** + * @author Eike Stepper + * @since 3.0 + */ +public class RepositorySynchronizer extends QueueRunner implements InternalRepositorySynchronizer +{ + public static final int DEFAULT_RETRY_INTERVAL = 3; + + public static final int DEFAULT_MAX_RECOMMITS = 10; + + public static final int DEFAULT_RECOMMIT_INTERVAL = 1; + + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REPOSITORY, RepositorySynchronizer.class); + + private static final Integer CONNECT_PRIORITY = 0; + + private static final Integer REPLICATE_PRIORITY = 1; + + private static final Integer BRANCH_PRIORITY = 2; + + private static final Integer COMMIT_PRIORITY = 3; + + private static final Integer LOCKS_PRIORITY = COMMIT_PRIORITY; + + private int retryInterval = DEFAULT_RETRY_INTERVAL; + + private Object connectLock = new Object(); + + private InternalSynchronizableRepository localRepository; + + /** + * The session that connects to the master; used passively to receive notifications, and actively to request + * replications. + */ + private InternalCDOSession remoteSession; + + private RemoteSessionListener remoteSessionListener = new RemoteSessionListener(); + + private CDOSessionConfigurationFactory remoteSessionConfigurationFactory; + + private boolean rawReplication; + + private int maxRecommits = DEFAULT_MAX_RECOMMITS; + + private int recommitInterval = DEFAULT_RECOMMIT_INTERVAL; + + private Timer recommitTimer; + + public RepositorySynchronizer() + { + setDaemon(true); + } + + public int getRetryInterval() + { + return retryInterval; + } + + public void setRetryInterval(int retryInterval) + { + this.retryInterval = retryInterval; + } + + public InternalSynchronizableRepository getLocalRepository() + { + return localRepository; + } + + public void setLocalRepository(InternalSynchronizableRepository localRepository) + { + checkInactive(); + this.localRepository = localRepository; + } + + public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory() + { + return remoteSessionConfigurationFactory; + } + + public void setRemoteSessionConfigurationFactory(CDOSessionConfigurationFactory masterSessionConfigurationFactory) + { + checkArg(masterSessionConfigurationFactory, "remoteSessionConfigurationFactory"); //$NON-NLS-1$ + remoteSessionConfigurationFactory = masterSessionConfigurationFactory; + } + + public InternalCDOSession getRemoteSession() + { + return remoteSession; + } + + public boolean isRawReplication() + { + return rawReplication; + } + + public void setRawReplication(boolean rawReplication) + { + checkInactive(); + this.rawReplication = rawReplication; + } + + public int getMaxRecommits() + { + return maxRecommits; + } + + public void setMaxRecommits(int maxRecommits) + { + this.maxRecommits = maxRecommits; + } + + public int getRecommitInterval() + { + return recommitInterval; + } + + public void setRecommitInterval(int recommitInterval) + { + this.recommitInterval = recommitInterval; + } + + @Override + protected String getThreadName() + { + return "RepositorySynchronizer"; //$NON-NLS-1$ + } + + @Override + protected BlockingQueue<Runnable> createQueue() + { + return new PriorityBlockingQueue<Runnable>(); + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + checkState(remoteSessionConfigurationFactory, "remoteSessionConfigurationFactory"); //$NON-NLS-1$ + checkState(localRepository, "localRepository"); //$NON-NLS-1$ + } + + @Override + protected void doAfterActivate() throws Exception + { + super.doAfterActivate(); + scheduleConnect(); + } + + @Override + protected void doDeactivate() throws Exception + { + if (recommitTimer != null) + { + recommitTimer.cancel(); + recommitTimer = null; + } + + if (remoteSession != null) + { + remoteSession.removeListener(remoteSessionListener); + remoteSession.getBranchManager().removeListener(remoteSessionListener); + remoteSession.close(); + remoteSession = null; + } + + super.doDeactivate(); + } + + private void handleDisconnect() + { + OM.LOG.info("Disconnected from master."); + if (localRepository.getRootResourceID() == null) + { + localRepository.setState(CDOCommonRepository.State.INITIAL); + } + else + { + localRepository.setState(CDOCommonRepository.State.OFFLINE); + } + + remoteSession.getBranchManager().removeListener(remoteSessionListener); + remoteSession.removeListener(remoteSessionListener); + remoteSession = null; + + reconnect(); + } + + private void reconnect() + { + clearQueue(); + if (isActive()) + { + scheduleConnect(); + } + } + + private void scheduleConnect() + { + synchronized (connectLock) + { + if (localRepository.getState().isConnected()) + { + return; + } + + if (isActive()) + { + addWork(new ConnectRunnable()); + } + } + } + + private void scheduleReplicate() + { + if (isActive()) + { + addWork(new ReplicateRunnable()); + } + } + + private void sleepRetryInterval() + { + long end = System.currentTimeMillis() + 1000L * retryInterval; + + for (;;) + { + long now = System.currentTimeMillis(); + if (now >= end || !isActive()) + { + break; + } + + ConcurrencyUtil.sleep(Math.min(100L, end - now)); + } + } + + /** + * @author Eike Stepper + */ + private final class RemoteSessionListener implements IListener + { + public void notifyEvent(IEvent event) + { + if (!isActive()) + { + return; + } + + if (event instanceof CDOBranchCreatedEvent) + { + CDOBranchCreatedEvent e = (CDOBranchCreatedEvent)event; + addWork(new BranchRunnable(e.getBranch())); + } + else if (event instanceof CDOSessionInvalidationEvent) + { + CDOSessionInvalidationEvent e = (CDOSessionInvalidationEvent)event; + if (e.isRemote()) + { + addWork(new CommitRunnable(e)); + } + } + else if (event instanceof CDOSessionLocksChangedEvent) + { + CDOSessionLocksChangedEvent e = (CDOSessionLocksChangedEvent)event; + addWork(new LocksRunnable(e)); + } + else if (event instanceof ILifecycleEvent) + { + ILifecycleEvent e = (ILifecycleEvent)event; + if (e.getKind() == ILifecycleEvent.Kind.DEACTIVATED && e.getSource() == remoteSession) + { + handleDisconnect(); + } + } + } + } + + /** + * @author Eike Stepper + */ + private static abstract class QueueRunnable implements Runnable, Comparable<QueueRunnable> + { + public int compareTo(QueueRunnable o) + { + return getPriority().compareTo(o.getPriority()); + } + + protected abstract Integer getPriority(); + } + + /** + * @author Eike Stepper + */ + private final class ConnectRunnable extends QueueRunnable + { + public ConnectRunnable() + { + } + + public void run() + { + synchronized (connectLock) + { + checkActive(); + if (TRACER.isEnabled()) + { + TRACER.trace("Connecting to master..."); //$NON-NLS-1$ + } + + try + { + CDOSessionConfiguration masterConfiguration = remoteSessionConfigurationFactory.createSessionConfiguration(); + masterConfiguration.setPassiveUpdateMode(PassiveUpdateMode.ADDITIONS); + masterConfiguration.setLockNotificationMode(LockNotificationMode.ALWAYS); + + remoteSession = (InternalCDOSession)masterConfiguration.openSession(); + + ensureNOOPRevisionCache(); + setRootResourceID(); + } + catch (Exception ex) + { + if (isActive()) + { + OM.LOG.warn("Connection attempt failed. Retrying in " + retryInterval + " seconds...", ex); + sleepRetryInterval(); + reconnect(); + } + + return; + } + + OM.LOG.info("Connected to master."); + scheduleReplicate(); + + remoteSession.addListener(remoteSessionListener); + remoteSession.getBranchManager().addListener(remoteSessionListener); + } + } + + @Override + protected Integer getPriority() + { + return CONNECT_PRIORITY; + } + + private void setRootResourceID() + { + if (localRepository.getState() == CDOCommonRepository.State.INITIAL) + { + CDOID rootResourceID = remoteSession.getRepositoryInfo().getRootResourceID(); + localRepository.setRootResourceID(rootResourceID); + localRepository.setState(CDOCommonRepository.State.OFFLINE); + } + } + + private void ensureNOOPRevisionCache() + { + // Ensure that incoming revisions are not cached! + InternalCDORevisionCache cache = remoteSession.getRevisionManager().getCache(); + if (!(cache instanceof NOOPRevisionCache)) + { + throw new IllegalStateException("Master session does not use a NOOPRevisionCache: " + + cache.getClass().getName()); + } + } + } + + /** + * @author Eike Stepper + */ + private final class ReplicateRunnable extends QueueRunnable + { + public ReplicateRunnable() + { + } + + public void run() + { + try + { + checkActive(); + if (TRACER.isEnabled()) + { + TRACER.trace("Synchronizing with master..."); //$NON-NLS-1$ + } + + localRepository.setState(CDOCommonRepository.State.SYNCING); + + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + OMMonitor monitor = new NotifyingMonitor("Synchronizing", getListeners()); + + if (isRawReplication()) + { + sessionProtocol.replicateRepositoryRaw(localRepository, monitor); + } + else + { + sessionProtocol.replicateRepository(localRepository, monitor); + } + + localRepository.setState(CDOCommonRepository.State.ONLINE); + OM.LOG.info("Synchronized with master."); + } + catch (RuntimeException ex) + { + if (isActive()) + { + OM.LOG.warn("Replication attempt failed. Retrying in " + retryInterval + " seconds...", ex); + sleepRetryInterval(); + handleDisconnect(); + } + } + } + + @Override + protected Integer getPriority() + { + return REPLICATE_PRIORITY; + } + } + + /** + * @author Eike Stepper + */ + private final class BranchRunnable extends QueueRunnable + { + private CDOBranch branch; + + public BranchRunnable(CDOBranch branch) + { + this.branch = branch; + } + + public void run() + { + localRepository.handleBranch(branch); + } + + @Override + public int compareTo(QueueRunnable o) + { + int result = super.compareTo(o); + if (result == 0) + { + result = branch.compareTo(((BranchRunnable)o).branch); + } + + return result; + } + + @Override + protected Integer getPriority() + { + return BRANCH_PRIORITY; + } + } + + private final class CommitRunnable extends RetryingRunnable + { + private CDOCommitInfo commitInfo; + + public CommitRunnable(CDOCommitInfo commitInfo) + { + this.commitInfo = commitInfo; + } + + @Override + protected void doRun() + { + localRepository.handleCommitInfo(commitInfo); + } + + @Override + public int compareTo(QueueRunnable o) + { + int result = super.compareTo(o); + if (result == 0) + { + Long timeStamp = commitInfo.getTimeStamp(); + Long timeStamp2 = ((CommitRunnable)o).commitInfo.getTimeStamp(); + result = timeStamp < timeStamp2 ? -1 : timeStamp == timeStamp2 ? 0 : 1; + } + + return result; + } + + @Override + protected Integer getPriority() + { + return COMMIT_PRIORITY; + } + + @Override + protected String getErrorMessage() + { + return "Replication of master commit failed:" + commitInfo; + } + } + + /** + * @author Eike Stepper + */ + private abstract class RetryingRunnable extends QueueRunnable + { + private List<Exception> failedRuns; + + protected abstract void doRun(); + + protected abstract String getErrorMessage(); + + public void run() + { + try + { + doRun(); + } + catch (Exception ex) + { + if (failedRuns == null) + { + failedRuns = new ArrayList<Exception>(); + } + + failedRuns.add(ex); + if (failedRuns.size() <= maxRecommits) + { + if (TRACER.isEnabled()) + { + TRACER.format("Replication of master commit failed. Trying again in {0} seconds...", recommitInterval); //$NON-NLS-1$ + } + + if (recommitTimer == null) + { + recommitTimer = new Timer("RecommitTimer-" + RepositorySynchronizer.this); + } + + recommitTimer.schedule(new TimerTask() + { + @Override + public void run() + { + try + { + addWork(this); + } + catch (Exception ex) + { + OM.LOG.error("CommitRunnableTask failed", ex); + } + } + }, recommitInterval * 1000L); + } + else + { + OM.LOG.error(getErrorMessage(), ex); + } + } + } + } + + /** + * @author Caspar De Groot + */ + private final class LocksRunnable extends RetryingRunnable + { + private CDOLockChangeInfo lockChangeInfo; + + public LocksRunnable(CDOLockChangeInfo lockChangeInfo) + { + this.lockChangeInfo = lockChangeInfo; + } + + @Override + protected Integer getPriority() + { + return LOCKS_PRIORITY; + } + + @Override + protected void doRun() + { + try + { + StoreThreadLocal.setSession(localRepository.getReplicatorSession()); + localRepository.handleLockChangeInfo(lockChangeInfo); + } + finally + { + StoreThreadLocal.release(); + } + } + + @Override + protected String getErrorMessage() + { + return "Replication of master lock changes failed:" + lockChangeInfo; + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java new file mode 100644 index 0000000000..2276dc9b99 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java @@ -0,0 +1,788 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server.syncing; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLob; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOException; +import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +import org.eclipse.emf.cdo.internal.server.syncing.OfflineClone.CommitContextData; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalSessionManager; +import org.eclipse.emf.cdo.spi.server.InternalStore; +import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalView; +import org.eclipse.emf.cdo.spi.server.SyncingUtil; + +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.transaction.TransactionException; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * TODO: + * <ul> + * <li>Handle new package units that had been committed during offline (testDisconnectAndCommitAndMergeWithNewPackages). + * <li>Make CDOIDs of new objects temporary when merging out of temp branch. + * <li>Provide custom branching strategies. + * <li>Consider non-auditing masters. + * <li>Test out-of-order commits. + * <li>Don't create branches table if branching not supported. + * <li>Implement raw replication for NUMERIC and DECIMAL. + * <li>Notify new branches during raw replication. + * </ul> + * + * @author Eike Stepper + */ +public abstract class SynchronizableRepository extends Repository.Default implements InternalSynchronizableRepository +{ + protected static final CDOCommonRepository.Type MASTER = CDOCommonRepository.Type.MASTER; + + protected static final CDOCommonRepository.Type BACKUP = CDOCommonRepository.Type.BACKUP; + + protected static final CDOCommonRepository.Type CLONE = CDOCommonRepository.Type.CLONE; + + protected static final CDOCommonRepository.State INITIAL = CDOCommonRepository.State.INITIAL; + + protected static final CDOCommonRepository.State OFFLINE = CDOCommonRepository.State.OFFLINE; + + protected static final CDOCommonRepository.State SYNCING = CDOCommonRepository.State.SYNCING; + + protected static final CDOCommonRepository.State ONLINE = CDOCommonRepository.State.ONLINE; + + private static final String PROP_LAST_REPLICATED_BRANCH_ID = "org.eclipse.emf.cdo.server.lastReplicatedBranchID"; //$NON-NLS-1$ + + private static final String PROP_LAST_REPLICATED_COMMIT_TIME = "org.eclipse.emf.cdo.server.lastReplicatedCommitTime"; //$NON-NLS-1$ + + private static final String PROP_GRACEFULLY_SHUT_DOWN = "org.eclipse.emf.cdo.server.gracefullyShutDown"; //$NON-NLS-1$ + + private InternalRepositorySynchronizer synchronizer; + + private InternalSession replicatorSession; + + private int lastReplicatedBranchID = CDOBranch.MAIN_BRANCH_ID; + + private long lastReplicatedCommitTime = CDOBranchPoint.UNSPECIFIED_DATE; + + private int lastTransactionID; + + private ReadLock writeThroughCommitLock; + + private WriteLock handleCommitInfoLock; + + public SynchronizableRepository() + { + ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); + writeThroughCommitLock = rwLock.readLock(); + handleCommitInfoLock = rwLock.writeLock(); + } + + public InternalRepositorySynchronizer getSynchronizer() + { + return synchronizer; + } + + public void setSynchronizer(InternalRepositorySynchronizer synchronizer) + { + checkInactive(); + this.synchronizer = synchronizer; + } + + public InternalSession getReplicatorSession() + { + return replicatorSession; + } + + @Override + public Object[] getElements() + { + List<Object> list = Arrays.asList(super.getElements()); + list.add(synchronizer); + return list.toArray(); + } + + public int getLastReplicatedBranchID() + { + return lastReplicatedBranchID; + } + + public void setLastReplicatedBranchID(int lastReplicatedBranchID) + { + if (this.lastReplicatedBranchID < lastReplicatedBranchID) + { + this.lastReplicatedBranchID = lastReplicatedBranchID; + } + } + + public long getLastReplicatedCommitTime() + { + return lastReplicatedCommitTime; + } + + public void setLastReplicatedCommitTime(long lastReplicatedCommitTime) + { + if (this.lastReplicatedCommitTime < lastReplicatedCommitTime) + { + this.lastReplicatedCommitTime = lastReplicatedCommitTime; + } + } + + public String[] getLockAreaIDs() + { + try + { + StoreThreadLocal.setSession(replicatorSession); + final List<String> areaIDs = new LinkedList<String>(); + getLockManager().getLockAreas(null, new LockArea.Handler() + { + public boolean handleLockArea(LockArea area) + { + areaIDs.add(area.getDurableLockingID()); + return true; + } + }); + return areaIDs.toArray(new String[areaIDs.size()]); + } + finally + { + StoreThreadLocal.release(); + } + } + + public void handleBranch(CDOBranch branch) + { + if (branch.isLocal()) + { + return; + } + + int branchID = branch.getID(); + String name = branch.getName(); + + CDOBranchPoint base = branch.getBase(); + InternalCDOBranch baseBranch = (InternalCDOBranch)base.getBranch(); + long baseTimeStamp = base.getTimeStamp(); + + InternalCDOBranchManager branchManager = getBranchManager(); + branchManager.createBranch(branchID, name, baseBranch, baseTimeStamp); + setLastReplicatedBranchID(branchID); + } + + public void handleCommitInfo(CDOCommitInfo commitInfo) + { + CDOBranch branch = commitInfo.getBranch(); + if (branch.isLocal()) + { + return; + } + + long timeStamp = commitInfo.getTimeStamp(); + CDOBranchPoint head = branch.getHead(); + + InternalTransaction transaction = replicatorSession.openTransaction(++lastTransactionID, head); + ReplicatorCommitContext commitContext = new ReplicatorCommitContext(transaction, commitInfo); + commitContext.preWrite(); + boolean success = false; + + try + { + handleCommitInfoLock.lock(); + + commitContext.write(new Monitor()); + commitContext.commit(new Monitor()); + + setLastCommitTimeStamp(timeStamp); + setLastReplicatedCommitTime(timeStamp); + success = true; + } + finally + { + handleCommitInfoLock.unlock(); + commitContext.postCommit(success); + transaction.close(); + } + } + + public void handleLockChangeInfo(CDOLockChangeInfo lockChangeInfo) + { + CDOLockOwner owner = lockChangeInfo.getLockOwner(); + String durableLockingID = owner.getDurableLockingID(); + CDOBranch viewedBranch = lockChangeInfo.getBranch(); + InternalLockManager lockManager = getLockManager(); + LockType lockType = lockChangeInfo.getLockType(); + + InternalView view = null; + + try + { + view = SyncingUtil.openViewWithLockArea(replicatorSession, lockManager, viewedBranch, durableLockingID); + List<Object> lockables = new LinkedList<Object>(); + + for (CDOLockState lockState : lockChangeInfo.getLockStates()) + { + lockables.add(lockState.getLockedObject()); + } + + if (lockChangeInfo.getOperation() == Operation.LOCK) + { + // If we can't lock immediately, there's a conflict, which means we're in big + // trouble: somehow locks were obtained on the clone but not on the master. What to do? + // TODO (CD) Consider this problem further + // + long timeout = 0; + super.lock(view, lockType, lockables, null, timeout); + } + else if (lockChangeInfo.getOperation() == Operation.UNLOCK) + { + super.doUnlock(view, lockType, lockables); + } + else + { + throw new IllegalStateException("Unexpected: " + lockChangeInfo.getOperation()); + } + } + finally + { + LifecycleUtil.deactivate(view); + } + } + + public boolean handleLockArea(LockArea area) + { + try + { + StoreThreadLocal.setSession(replicatorSession); + getLockManager().updateLockArea(area); + + // TODO (CD) getSessionManager().sendLockNotification(sender, lockChangeInfo); + return true; + } + finally + { + StoreThreadLocal.release(); + } + } + + public void replicateRaw(CDODataInput in, OMMonitor monitor) throws IOException + { + try + { + int fromBranchID = lastReplicatedBranchID + 1; + int toBranchID = in.readInt(); + long fromCommitTime = lastReplicatedCommitTime + 1L; + long toCommitTime = in.readLong(); + + StoreThreadLocal.setSession(replicatorSession); + IStoreAccessor.Raw accessor = (IStoreAccessor.Raw)StoreThreadLocal.getAccessor(); + accessor.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + + replicateRawReviseRevisions(); + replicateRawNotifyClients(lastReplicatedCommitTime, toCommitTime); + + setLastReplicatedBranchID(toBranchID); + setLastReplicatedCommitTime(toCommitTime); + setLastCommitTimeStamp(toCommitTime); + } + finally + { + StoreThreadLocal.release(); + } + } + + public void goOnline() + { + if (getState() == OFFLINE) + { + LifecycleUtil.activate(synchronizer); + // Do not set the state to ONLINE yet; the synchronizer will set it to SYNCING first, + // and then to ONLINE after a succesful replication. + } + } + + public void goOffline() + { + if (getState() != OFFLINE) + { + LifecycleUtil.deactivate(synchronizer); + setState(OFFLINE); + } + } + + private void replicateRawReviseRevisions() + { + InternalCDORevisionCache cache = getRevisionManager().getCache(); + for (CDORevision revision : cache.getCurrentRevisions()) + { + cache.removeRevision(revision.getID(), revision); + } + } + + private void replicateRawNotifyClients(long fromCommitTime, long toCommitTime) + { + InternalCDOCommitInfoManager manager = getCommitInfoManager(); + InternalSessionManager sessionManager = getSessionManager(); + + Map<CDOBranch, TimeRange> branches = replicateRawGetBranches(fromCommitTime, toCommitTime); + for (Entry<CDOBranch, TimeRange> entry : branches.entrySet()) + { + CDOBranch branch = entry.getKey(); + TimeRange range = entry.getValue(); + fromCommitTime = range.getTime1(); + toCommitTime = range.getTime2(); + + CDOBranchPoint startPoint = branch.getPoint(fromCommitTime); + CDOBranchPoint endPoint = branch.getPoint(toCommitTime); + CDOChangeSetData changeSet = getChangeSet(startPoint, endPoint); + + List<CDOPackageUnit> newPackages = Collections.emptyList(); // TODO Notify about new packages + List<CDOIDAndVersion> newObjects = changeSet.getNewObjects(); + List<CDORevisionKey> changedObjects = changeSet.getChangedObjects(); + List<CDOIDAndVersion> detachedObjects = changeSet.getDetachedObjects(); + CDOCommitData data = new CDOCommitDataImpl(newPackages, newObjects, changedObjects, detachedObjects); + + String comment = "<replicate raw commits>"; //$NON-NLS-1$ + CDOCommitInfo commitInfo = manager.createCommitInfo(branch, toCommitTime, fromCommitTime, SYSTEM_USER_ID, + comment, data); + sessionManager.sendCommitNotification(replicatorSession, commitInfo); + } + } + + private Map<CDOBranch, TimeRange> replicateRawGetBranches(long fromCommitTime, long toCommitTime) + { + final Map<CDOBranch, TimeRange> branches = new HashMap<CDOBranch, TimeRange>(); + CDOCommitInfoHandler handler = new CDOCommitInfoHandler() + { + public void handleCommitInfo(CDOCommitInfo commitInfo) + { + CDOBranch branch = commitInfo.getBranch(); + long timeStamp = commitInfo.getTimeStamp(); + TimeRange range = branches.get(branch); + if (range == null) + { + branches.put(branch, new TimeRange(timeStamp)); + } + else + { + range.update(timeStamp); + } + } + }; + + getCommitInfoManager().getCommitInfos(null, fromCommitTime, toCommitTime, handler); + return branches; + } + + @Override + public abstract InternalCommitContext createCommitContext(InternalTransaction transaction); + + protected InternalCommitContext createNormalCommitContext(InternalTransaction transaction) + { + return super.createCommitContext(transaction); + } + + protected InternalCommitContext createWriteThroughCommitContext(InternalTransaction transaction) + { + return new WriteThroughCommitContext(transaction); + } + + @Override + protected void doBeforeActivate() throws Exception + { + super.doBeforeActivate(); + checkState(synchronizer, "synchronizer"); //$NON-NLS-1$ + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + InternalStore store = getStore(); + if (!store.isFirstStart()) + { + Map<String, String> map = store.getPersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN)); + if (!map.containsKey(PROP_GRACEFULLY_SHUT_DOWN)) + { + setReplicationCountersToLatest(); + } + else + { + Set<String> names = new HashSet<String>(); + names.add(PROP_LAST_REPLICATED_BRANCH_ID); + names.add(PROP_LAST_REPLICATED_COMMIT_TIME); + + map = store.getPersistentProperties(names); + setLastReplicatedBranchID(Integer.valueOf(map.get(PROP_LAST_REPLICATED_BRANCH_ID))); + setLastReplicatedCommitTime(Long.valueOf(map.get(PROP_LAST_REPLICATED_COMMIT_TIME))); + } + } + + store.removePersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN)); + + if (getType() != MASTER) + { + startSynchronization(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + stopSynchronization(); + + Map<String, String> map = new HashMap<String, String>(); + map.put(PROP_LAST_REPLICATED_BRANCH_ID, Integer.toString(lastReplicatedBranchID)); + map.put(PROP_LAST_REPLICATED_COMMIT_TIME, Long.toString(lastReplicatedCommitTime)); + map.put(PROP_GRACEFULLY_SHUT_DOWN, Boolean.TRUE.toString()); + + InternalStore store = getStore(); + store.setPersistentProperties(map); + + super.doDeactivate(); + } + + protected void startSynchronization() + { + replicatorSession = getSessionManager().openSession(null); + replicatorSession.options().setPassiveUpdateEnabled(false); + replicatorSession.options().setLockNotificationMode(LockNotificationMode.OFF); + + synchronizer.setLocalRepository(this); + synchronizer.activate(); + } + + protected void stopSynchronization() + { + if (synchronizer != null) + { + synchronizer.deactivate(); + } + } + + protected void setReplicationCountersToLatest() + { + setLastReplicatedBranchID(getStore().getLastBranchID()); + setLastReplicatedCommitTime(getStore().getLastNonLocalCommitTime()); + } + + protected void doInitRootResource() + { + super.initRootResource(); + } + + @Override + protected void initRootResource() + { + setState(INITIAL); + } + + @Override + public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, long timeout) + { + if (view.getBranch().isLocal()) + { + return super.lock(view, lockType, revisionKeys, timeout); + } + + if (getState() != ONLINE) + { + throw new CDOException("Cannot lock in a non-local branch when clone is not connected to master"); + } + + return lockThrough(view, lockType, revisionKeys, timeout); + } + + private LockObjectsResult lockOnMaster(InternalView view, LockType type, List<CDORevisionKey> revKeys, long timeout) + throws InterruptedException + { + // Delegate locking to the master + InternalCDOSession remoteSession = getSynchronizer().getRemoteSession(); + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + + String areaID = view.getDurableLockingID(); + if (areaID == null) + { + throw new IllegalStateException("Durable locking is not enabled."); + } + + LockObjectsResult masterLockingResult = sessionProtocol.delegateLockObjects(areaID, revKeys, view.getBranch(), + type, timeout); + + if (masterLockingResult.isSuccessful() && masterLockingResult.isWaitForUpdate()) + { + if (!getSynchronizer().getRemoteSession().options().isPassiveUpdateEnabled()) + { + throw new AssertionError( + "Master lock result requires clone to wait, but clone does not have passiveUpdates enabled."); + } + + long requiredTimestamp = masterLockingResult.getRequiredTimestamp(); + remoteSession.waitForUpdate(requiredTimestamp); + } + + return masterLockingResult; + } + + private LockObjectsResult lockThrough(InternalView view, LockType type, List<CDORevisionKey> keys, long timeout) + { + try + { + LockObjectsResult masterLockingResult = lockOnMaster(view, type, keys, timeout); + if (!masterLockingResult.isSuccessful()) + { + return masterLockingResult; + } + + LockObjectsResult localLockingResult = super.lock(view, type, keys, timeout); + return localLockingResult; + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + @Override + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + if (view.getBranch().isLocal()) + { + super.unlock(view, lockType, objectIDs); + } + + if (getState() != ONLINE) + { + throw new CDOException("Cannot unlock in a non-local branch when clone is not connected to master"); + } + + return unlockThrough(view, lockType, objectIDs); + } + + private void unlockOnMaster(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + InternalCDOSession remoteSession = getSynchronizer().getRemoteSession(); + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + + String lockAreaID = view.getDurableLockingID(); + if (lockAreaID == null) + { + throw new IllegalStateException("Durable locking is not enabled."); + } + + sessionProtocol.delegateUnlockObjects(lockAreaID, objectIDs, lockType); + } + + private UnlockObjectsResult unlockThrough(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + unlockOnMaster(view, lockType, objectIDs); + return super.unlock(view, lockType, objectIDs); + } + + /** + * @author Eike Stepper + */ + private static final class TimeRange + { + private long time1; + + private long time2; + + public TimeRange(long time) + { + time1 = time; + time2 = time; + } + + public void update(long time) + { + if (time < time1) + { + time1 = time; + } + + if (time > time2) + { + time2 = time; + } + } + + public long getTime1() + { + return time1; + } + + public long getTime2() + { + return time2; + } + + @Override + public String toString() + { + return "[" + CDOCommonUtil.formatTimeStamp(time1) + " - " + CDOCommonUtil.formatTimeStamp(time1) + "]"; + } + } + + /** + * @author Eike Stepper + */ + protected final class WriteThroughCommitContext extends TransactionCommitContext + { + public WriteThroughCommitContext(InternalTransaction transaction) + { + super(transaction); + } + + @Override + public void preWrite() + { + // Do nothing + } + + @Override + public void write(OMMonitor monitor) + { + // Do nothing + } + + @Override + public void commit(OMMonitor monitor) + { + InternalTransaction transaction = getTransaction(); + + // Prepare commit to the master + CDOBranch branch = transaction.getBranch(); + String userID = getUserID(); + String comment = getCommitComment(); + CDOCommitData commitData = new CommitContextData(this); + Collection<CDOLob<?>> lobs = Collections.emptySet(); + + // Delegate commit to the master + CDOSessionProtocol sessionProtocol = getSynchronizer().getRemoteSession().getSessionProtocol(); + CommitTransactionResult result = sessionProtocol.commitDelegation(branch, userID, comment, commitData, + getDetachedObjectTypes(), lobs, monitor); + + // Stop if commit to master failed + String rollbackMessage = result.getRollbackMessage(); + if (rollbackMessage != null) + { + throw new TransactionException(rollbackMessage); + } + + // Prepare data needed for commit result and commit notifications + long timeStamp = result.getTimeStamp(); + setTimeStamp(timeStamp); + addIDMappings(result.getIDMappings()); + applyIDMappings(new Monitor()); + + try + { + writeThroughCommitLock.lock(); + + // Commit to the local repository + super.preWrite(); + super.write(new Monitor()); + super.commit(new Monitor()); + } + finally + { + writeThroughCommitLock.unlock(); + } + + // Remember commit time in the local repository + setLastCommitTimeStamp(timeStamp); + setLastReplicatedCommitTime(timeStamp); + + // Remember commit time in the replicator session. + getSynchronizer().getRemoteSession().setLastUpdateTime(timeStamp); + } + + @Override + protected long[] createTimeStamp(OMMonitor monitor) + { + // Already set after commit to the master. + // Do not call getTimeStamp() of the enclosing Repo class!!! + InternalRepository repository = getTransaction().getSession().getManager().getRepository(); + return repository.forceCommitTimeStamp(WriteThroughCommitContext.this.getTimeStamp(), monitor); + } + + @Override + protected void lockObjects() throws InterruptedException + { + // Do nothing + } + + private void addIDMappings(Map<CDOID, CDOID> idMappings) + { + for (Map.Entry<CDOID, CDOID> idMapping : idMappings.entrySet()) + { + CDOID oldID = idMapping.getKey(); + CDOID newID = idMapping.getValue(); + addIDMapping(oldID, newID); + } + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java new file mode 100644 index 0000000000..ed7ec5ec4c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java @@ -0,0 +1,1220 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.revision.CDOAllRevisionsProvider; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil.AllRevisionsDumper; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.Worker; +import org.eclipse.net4j.util.container.ContainerEventAdapter; +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.container.IPluginContainer; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.factory.ProductCreationException; + +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Writer; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class CDOServerBrowser extends Worker +{ + private static final String REQUEST_PREFIX = "GET "; + + private static final String REQUEST_SUFFIX = " HTTP/1.1"; + + private ThreadLocal<Map<String, String>> params = new InheritableThreadLocal<Map<String, String>>() + { + @Override + protected Map<String, String> initialValue() + { + return new HashMap<String, String>(); + } + }; + + private int port = 7777; + + private ServerSocket serverSocket; + + private Map<String, InternalRepository> repositories; + + private List<Page> pages = new ArrayList<Page>(); + + public CDOServerBrowser(Map<String, InternalRepository> repositories) + { + this.repositories = repositories; + setDaemon(true); + } + + public Map<String, InternalRepository> getRepositories() + { + return repositories; + } + + public int getPort() + { + return port; + } + + public void setPort(int port) + { + this.port = port; + } + + @Override + protected void work(WorkContext context) throws Exception + { + Socket socket = null; + + try + { + socket = serverSocket.accept(); + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + OutputStream out = new BufferedOutputStream(socket.getOutputStream()); + PrintStream pout = new PrintStream(out); + printHeader(pout); + + String line; + while ((line = in.readLine()) != null) + { + if (line.startsWith(REQUEST_PREFIX) && line.endsWith(REQUEST_SUFFIX)) + { + String request = line.substring(REQUEST_PREFIX.length(), line.length() - REQUEST_SUFFIX.length()).trim(); + String resource = request; + String params = ""; + int pos = request.indexOf('?'); + if (pos != -1) + { + resource = request.substring(0, pos); + params = request.substring(pos + 1); + } + + initParams(params); + if ("/".equals(resource)) + { + showMenu(pout); + } + else + { + String pageName = resource.substring(1); + for (Page page : pages) + { + if (page.getName().equals(pageName)) + { + showPage(pout, page); + } + } + } + } + + out.flush(); + return; + } + } + catch (Exception ex) + { + if (isActive()) + { + ex.printStackTrace(); + } + } + finally + { + params.remove(); + if (socket != null) + { + socket.close(); + } + } + } + + protected void initParams(String params) + { + Map<String, String> map = this.params.get(); + for (String param : params.split("&")) + { + if (param.length() != 0) + { + String[] keyValue = param.split("="); + map.put(keyValue[0], keyValue[1]); + } + } + } + + protected void clearParams() + { + Map<String, String> map = params.get(); + map.clear(); + } + + public void removeParam(String key) + { + Map<String, String> map = params.get(); + map.remove(key); + } + + public String getParam(String key) + { + Map<String, String> map = params.get(); + return map.get(key); + } + + public String href(String label, String resource, String... params) + { + Map<String, String> map = new HashMap<String, String>(this.params.get()); + for (int i = 0; i < params.length;) + { + map.put(params[i++], params[i++]); + } + + List<String> list = new ArrayList<String>(map.keySet()); + Collections.sort(list); + + StringBuilder builder = new StringBuilder(); + for (String key : list) + { + String value = map.get(key); + if (value != null) + { + if (builder.length() != 0) + { + builder.append("&"); + } + + builder.append(key); + builder.append("="); + builder.append(value); + } + } + + return "<a href=\"/" + escape(resource) + "?" + escape(builder.toString()) + "\">" + escape(label) + "</a>"; + } + + public String escape(String raw) + { + if (raw == null) + { + return "null"; + } + + return raw.replace("<", "<"); + } + + protected void printHeader(PrintStream pout) + { + pout.print("HTTP/1.1 200 OK\r\n"); + pout.print("Content-Type: text/html\r\n"); + pout.print("Date: " + new Date() + "\r\n"); + pout.print("Server: DBBrowser 3.0\r\n"); + pout.print("\r\n"); + } + + protected void showMenu(PrintStream pout) + { + clearParams(); + pout.print("<h1>CDO Server Browser 4.0</h1><hr>\r\n"); + + for (Page page : pages) + { + pout.println("<h3>" + href(page.getLabel(), page.getName()) + "</h3>"); + } + } + + protected void showPage(PrintStream pout, Page page) + { + String repo = getParam("repo"); + + List<String> repoNames = new ArrayList<String>(getRepositoryNames()); + Collections.sort(repoNames); + + pout.print("<h3><a href=\"/\">" + page.getLabel() + "</a>: "); + for (String repoName : repoNames) + { + InternalRepository repository = getRepository(repoName); + if (!page.canDisplay(repository)) + { + continue; + } + + if (repo == null) + { + repo = repoName; + } + + if (repoName.equals(repo)) + { + pout.print("<b>" + escape(repoName) + "</b> "); + } + else + { + pout.print(href(repoName, page.getName(), "repo", repoName) + " "); + } + } + + pout.print("</h3>"); + + InternalRepository repository = getRepository(repo); + if (repository != null) + { + pout.print("<p>\r\n"); + page.display(this, repository, pout); + } + } + + protected Set<String> getRepositoryNames() + { + return repositories.keySet(); + } + + protected InternalRepository getRepository(String name) + { + return repositories.get(name); + } + + @Override + protected String getThreadName() + { + return "DBBrowser"; + } + + protected void initPages(List<Page> pages) + { + pages.add(new PackagesPage()); + pages.add(new RevisionsPage.FromCache()); + pages.add(new RevisionsPage.FromStore()); + pages.add(new LobsPage()); + pages.add(new HistoryPage()); + + IPluginContainer container = IPluginContainer.INSTANCE; + Set<String> factoryTypes = container.getFactoryTypes(Page.PRODUCT_GROUP); + for (String factoryType : factoryTypes) + { + Page page = (Page)container.getElement(Page.PRODUCT_GROUP, factoryType, null); + pages.add(page); + } + } + + @Override + protected void doActivate() throws Exception + { + initPages(pages); + + try + { + serverSocket = new ServerSocket(port); + } + catch (Exception ex) + { + throw new IllegalStateException("Could not open socket on port " + port, ex); + } + + super.doActivate(); + } + + @Override + protected void doDeactivate() throws Exception + { + serverSocket.close(); + super.doDeactivate(); + } + + /** + * @author Eike Stepper + */ + public static class ContainerBased extends CDOServerBrowser + { + private IContainer<?> container; + + private IListener containerListener = new ContainerEventAdapter<Object>() + { + @Override + protected void onAdded(IContainer<Object> container, Object element) + { + addElement(element); + } + + @Override + protected void onRemoved(IContainer<Object> container, Object element) + { + removeElement(element); + } + }; + + public ContainerBased(IContainer<?> container) + { + super(new HashMap<String, InternalRepository>()); + this.container = container; + } + + public ContainerBased() + { + this(IPluginContainer.INSTANCE); + } + + public IContainer<?> getContainer() + { + return container; + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + for (Object element : container.getElements()) + { + addElement(element); + } + + container.addListener(containerListener); + } + + @Override + protected void doDeactivate() throws Exception + { + container.removeListener(containerListener); + super.doDeactivate(); + } + + private void addElement(Object element) + { + if (element instanceof InternalRepository) + { + InternalRepository repository = (InternalRepository)element; + getRepositories().put(repository.getName(), repository); + } + } + + private void removeElement(Object element) + { + if (element instanceof InternalRepository) + { + InternalRepository repository = (InternalRepository)element; + getRepositories().remove(repository.getName()); + } + } + + /** + * @author Eike Stepper + */ + public static class Factory extends org.eclipse.net4j.util.factory.Factory + { + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.browsers"; + + public static final String TYPE = "default"; + + private IContainer<?> container; + + public Factory() + { + this(IPluginContainer.INSTANCE); + } + + public Factory(IContainer<?> container) + { + super(PRODUCT_GROUP, TYPE); + this.container = container; + } + + public CDOServerBrowser.ContainerBased create(String description) throws ProductCreationException + { + CDOServerBrowser.ContainerBased browser = new CDOServerBrowser.ContainerBased(container); + + try + { + if (!StringUtil.isEmpty(description)) + { + browser.setPort(Integer.valueOf(description)); + } + } + catch (Exception ex) + { + OM.LOG.warn(ex); + } + + return browser; + } + } + } + + /** + * @author Eike Stepper + */ + public static interface Page + { + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.browserPages"; + + public String getName(); + + public String getLabel(); + + public boolean canDisplay(InternalRepository repository); + + public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out); + } + + /** + * @author Eike Stepper + */ + public static abstract class AbstractPage implements Page + { + private String name; + + private String label; + + public AbstractPage(String name, String label) + { + this.name = name; + this.label = label; + } + + public String getName() + { + return name; + } + + public String getLabel() + { + return label; + } + } + + /** + * @author Eike Stepper + */ + public static class PackagesPage extends AbstractPage + { + public static final String NAME = "packages"; + + public PackagesPage() + { + super(NAME, "Packages and Classes"); + } + + public boolean canDisplay(InternalRepository repository) + { + return true; + } + + public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out) + { + String param = browser.getParam("classifier"); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + for (InternalCDOPackageUnit unit : packageRegistry.getPackageUnits()) + { + param = showPackage(unit.getTopLevelPackageInfo(), packageRegistry, browser, param, out, " "); + } + } + + protected String showPackage(InternalCDOPackageInfo info, InternalCDOPackageRegistry packageRegistry, + CDOServerBrowser browser, String param, PrintStream out, String prefix) + { + EPackage ePackage = info.getEPackage(); + out.println("<h3>" + prefix + ePackage.getName() + " [" + ePackage.getNsURI() + "]</h3>"); + + for (EClassifier classifier : ePackage.getEClassifiers()) + { + String name = classifier.getName(); + if (param == null) + { + param = name; + } + + String label = name.equals(param) ? name : browser.href(name, getName(), "classifier", name); + out.print(prefix + " <b>" + 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("</b><br>"); + } + + for (EPackage sub : ePackage.getESubpackages()) + { + InternalCDOPackageInfo subInfo = packageRegistry.getPackageInfo(sub); + param = showPackage(subInfo, packageRegistry, browser, param, out, prefix + " "); + } + + return param; + } + } + + /** + * @author Eike Stepper + */ + public static abstract class RevisionsPage extends AbstractPage + { + public RevisionsPage(String name, String label) + { + super(name, label); + } + + public void display(final CDOServerBrowser browser, InternalRepository repository, PrintStream out) + { + Map<CDOBranch, List<CDORevision>> allRevisions = getAllRevisions(repository); + Map<CDOID, List<CDORevision>> ids = getAllIDs(allRevisions); + + out.print("<table border=\"0\">\r\n"); + out.print("<tr>\r\n"); + + out.print("<td valign=\"top\">\r\n"); + out.print("<table border=\"1\" cellpadding=\"2\"><tr><td>\r\n"); + final String[] revision = { browser.getParam("revision") }; + new AllRevisionsDumper.Stream.Html(allRevisions, out) + { + private StringBuilder versionsBuilder; + + private CDORevision lastRevision; + + @Override + protected void dumpEnd(List<CDOBranch> branches) + { + dumpLastRevision(); + super.dumpEnd(branches); + } + + @Override + protected void dumpBranch(CDOBranch branch) + { + dumpLastRevision(); + super.dumpBranch(branch); + } + + @Override + protected void dumpRevision(CDORevision rev) + { + CDOID id = rev.getID(); + if (lastRevision != null && !id.equals(lastRevision.getID())) + { + dumpLastRevision(); + } + + if (versionsBuilder == null) + { + versionsBuilder = new StringBuilder(); + } + else + { + versionsBuilder.append(" "); + if (versionsBuilder.length() > 64) + { + versionsBuilder.append("<br>"); + } + } + + String key = CDORevisionUtil.formatRevisionKey(rev); + if (revision[0] == null) + { + revision[0] = key; + } + + String version = getVersionPrefix(rev) + rev.getVersion(); + if (key.equals(revision[0])) + { + versionsBuilder.append("<b>" + version + "</b>"); + } + else + { + versionsBuilder.append(browser.href(version, getName(), "revision", key)); + } + + lastRevision = rev; + } + + protected void dumpLastRevision() + { + if (versionsBuilder != null) + { + PrintStream out = out(); + out.println("<tr>"); + out.println("<td valign=\"top\"> "); + out.println(getCDOIDLabel(lastRevision)); + out.println(" </td>"); + + out.println("<td>"); + out.println(versionsBuilder.toString()); + out.println("</td>"); + out.println("</tr>"); + + lastRevision = null; + versionsBuilder = null; + } + } + }.dump(); + + out.print("</td></tr></table></td>\r\n"); + out.print("<td> </td>\r\n"); + + if (revision[0] != null) + { + out.print("<td valign=\"top\">\r\n"); + showRevision(out, browser, allRevisions, ids, revision[0], repository); + out.print("</td>\r\n"); + } + + out.print("</tr>\r\n"); + out.print("</table>\r\n"); + } + + /** + * @since 4.0 + */ + protected void showRevision(PrintStream pout, CDOServerBrowser browser, + Map<CDOBranch, List<CDORevision>> allRevisions, Map<CDOID, List<CDORevision>> ids, String key, + InternalRepository repository) + { + CDORevisionKey revisionKey = CDORevisionUtil.parseRevisionKey(key, repository.getBranchManager()); + for (CDORevision revision : allRevisions.get(revisionKey.getBranch())) + { + if (revision.getVersion() == revisionKey.getVersion() && revision.getID().equals(revisionKey.getID())) + { + showRevision(pout, browser, ids, (InternalCDORevision)revision); + return; + } + } + } + + /** + * @since 4.0 + */ + protected void showRevision(PrintStream pout, CDOServerBrowser browser, Map<CDOID, List<CDORevision>> ids, + InternalCDORevision revision) + { + String className = revision.getEClass().toString(); + className = className.substring(className.indexOf(' ')); + className = StringUtil.replace(className, new String[] { "(", ")", "," }, new String[] { "<br>", "", "<br>" }); + className = className.substring("<br>".length() + 1); + + String created = CDOCommonUtil.formatTimeStamp(revision.getTimeStamp()); + String commitInfo = browser.href(created, HistoryPage.NAME, "time", String.valueOf(revision.getTimeStamp())); + + pout.print("<table border=\"1\" cellpadding=\"2\">\r\n"); + showKeyValue(pout, true, "type", "<b>" + revision.getClass().getSimpleName() + "</b>"); + showKeyValue(pout, true, "class", className); + showKeyValue(pout, true, "id", getRevisionValue(revision.getID(), browser, ids, revision)); + showKeyValue(pout, true, "branch", revision.getBranch().getName() + "[" + revision.getBranch().getID() + "]"); + showKeyValue(pout, true, "version", revision.getVersion()); + showKeyValue(pout, true, "created", commitInfo); + showKeyValue(pout, true, "revised", CDOCommonUtil.formatTimeStamp(revision.getRevised())); + if (!(revision instanceof SyntheticCDORevision)) + { + showKeyValue(pout, true, "resource", getRevisionValue(revision.getResourceID(), browser, ids, revision)); + showKeyValue(pout, true, "container", getRevisionValue(revision.getContainerID(), browser, ids, revision)); + showKeyValue(pout, true, "feature", revision.getContainingFeatureID()); + + for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) + { + Object value = revision.getValue(feature); + showKeyValue(pout, false, feature.getName(), getRevisionValue(value, browser, ids, revision)); + } + } + + pout.print("</table>\r\n"); + } + + /** + * @since 4.0 + */ + protected Object getRevisionValue(Object value, CDOServerBrowser browser, Map<CDOID, List<CDORevision>> ids, + InternalCDORevision context) + { + if (value instanceof CDOID) + { + List<CDORevision> revisions = ids.get(value); + if (revisions != null) + { + StringBuilder builder = new StringBuilder(); + builder.append(getCDOIDLabel(revisions.get(0))); + + if (browser != null) + { + builder.append(" "); + for (CDORevision revision : revisions) + { + String label = getVersionPrefix(revision) + revision.getVersion(); + String branchName = revision.getBranch().getName(); + if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName)) + { + label += "[" + branchName + "]"; + } + + builder.append(" "); + if (revision == context) + { + builder.append(label); + } + else + { + builder.append(browser.href(label, getName(), "revision", CDORevisionUtil.formatRevisionKey(revision))); + } + } + } + + return builder.toString(); + } + } + + if (value instanceof Collection) + { + StringBuilder builder = new StringBuilder(); + for (Object element : (Collection<?>)value) + { + builder.append(builder.length() == 0 ? "" : "<br>"); + builder.append(getRevisionValue(element, browser, ids, context)); + } + + return builder.toString(); + } + + return value; + } + + private String getVersionPrefix(CDORevision revision) + { + if (revision instanceof PointerCDORevision) + { + return "p"; + } + + if (revision instanceof DetachedCDORevision) + { + return "d"; + } + + return "v"; + } + + /** + * @since 4.0 + */ + protected void showKeyValue(PrintStream pout, boolean bg, String key, Object value) + { + String color = bg ? "EEEEEE" : "FFFFFF"; + pout.print("<tr bgcolor=\"" + color + "\">\r\n"); + pout.print("<td valign=\"top\"><b>" + key + "</b></td>\r\n"); + pout.print("<td valign=\"top\">"); + pout.print(value); + pout.print("</td>\r\n"); + pout.print("</tr>\r\n"); + } + + protected abstract Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository); + + private Map<CDOID, List<CDORevision>> getAllIDs(Map<CDOBranch, List<CDORevision>> allRevisions) + { + Map<CDOID, List<CDORevision>> ids = new HashMap<CDOID, List<CDORevision>>(); + for (List<CDORevision> list : allRevisions.values()) + { + for (CDORevision revision : list) + { + CDOID id = revision.getID(); + List<CDORevision> revisions = ids.get(id); + if (revisions == null) + { + revisions = new ArrayList<CDORevision>(); + ids.put(id, revisions); + } + + revisions.add(revision); + } + } + + return ids; + } + + protected String getCDOIDLabel(CDORevision revision) + { + String label = revision.toString(); + return label.substring(0, label.indexOf(':')); + } + + /** + * @author Eike Stepper + */ + public static class FromCache extends RevisionsPage + { + public static final String NAME = "crevisions"; + + public FromCache() + { + super(NAME, "Revisions From Cache"); + } + + public boolean canDisplay(InternalRepository repository) + { + return true; + } + + @Override + protected Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository) + { + return repository.getRevisionManager().getCache().getAllRevisions(); + } + } + + /** + * @author Eike Stepper + */ + public static class FromStore extends RevisionsPage + { + public static final String NAME = "srevisions"; + + public FromStore() + { + super(NAME, "Revisions From Store"); + } + + public boolean canDisplay(InternalRepository repository) + { + return repository.getStore() instanceof CDOAllRevisionsProvider; + } + + @Override + protected Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository) + { + return ((CDOAllRevisionsProvider)repository.getStore()).getAllRevisions(); + } + } + } + + /** + * @author Eike Stepper + */ + public static class LobsPage extends AbstractPage + { + public static final String NAME = "lobs"; + + public LobsPage() + { + super(NAME, "Large Objects"); + } + + public boolean canDisplay(InternalRepository repository) + { + return true; + } + + public void display(final CDOServerBrowser browser, InternalRepository repository, final PrintStream out) + { + out.print("<table border=\"0\">\r\n"); + out.print("<tr>\r\n"); + out.print("<td valign=\"top\">\r\n"); + + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + final String param = browser.getParam("id"); + final Object[] details = { null, null, null }; + + try + { + repository.handleLobs(0, 0, new CDOLobHandler() + { + public OutputStream handleBlob(byte[] id, long size) + { + if (showLob(out, "Blob", id, size, browser, param)) + { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + details[0] = result; + details[1] = param; + details[2] = size; + return result; + } + + return null; + } + + public Writer handleClob(byte[] id, long size) + { + if (showLob(out, "Clob", id, size, browser, param)) + { + CharArrayWriter result = new CharArrayWriter(); + details[0] = result; + details[1] = param; + details[2] = size; + return result; + } + + return null; + } + }); + } + catch (IOException ex) + { + throw WrappedException.wrap(ex); + } + finally + { + StoreThreadLocal.release(); + } + + out.print("</td>\r\n"); + + if (details[0] != null) + { + out.print("<td> </td>\r\n"); + out.print("<td valign=\"top\">\r\n"); + if (details[0] instanceof ByteArrayOutputStream) + { + ByteArrayOutputStream baos = (ByteArrayOutputStream)details[0]; + String hex = HexUtil.bytesToHex(baos.toByteArray()); + + out.println("<h3>Blob " + details[1] + " (" + details[2] + ")</h3>"); + out.println("<pre>\r\n"); + for (int i = 0; i < hex.length(); i++) + { + out.print(hex.charAt(i)); + if ((i + 1) % 32 == 0) + { + out.print("\r\n"); + } + else if ((i + 1) % 16 == 0) + { + out.print(" "); + } + else if ((i + 1) % 2 == 0) + { + out.print(" "); + } + } + + out.println("</pre>\r\n"); + } + else + { + CharArrayWriter caw = (CharArrayWriter)details[0]; + out.println("<h3>Clob " + details[1] + " (" + details[2] + ")</h3>"); + out.println("<pre>" + caw + "</pre>"); + } + + out.print("</td>\r\n"); + } + + out.print("</tr>\r\n"); + out.print("</table>\r\n"); + } + + protected boolean showLob(PrintStream out, String type, byte[] id, long size, CDOServerBrowser browser, String param) + { + String hex = HexUtil.bytesToHex(id); + boolean selected = hex.equals(param); + String label = selected ? hex : browser.href(hex, getName(), "id", hex); + out.println(type + " " + label + " (" + size + ")"); + return selected; + } + } + + /** + * @author Eike Stepper + */ + public static class HistoryPage extends AbstractPage + { + public static final String NAME = "history"; + + public HistoryPage() + { + super(NAME, "Commit Infos"); + } + + public boolean canDisplay(InternalRepository repository) + { + return true; + } + + public void display(final CDOServerBrowser browser, InternalRepository repository, final PrintStream out) + { + out.print("<table border=\"0\">\r\n"); + out.print("<tr>\r\n"); + out.print("<td valign=\"top\">\r\n"); + + IStoreAccessor accessor = repository.getStore().getReader(null); + StoreThreadLocal.setAccessor(accessor); + + final String param = browser.getParam("time"); + + out.print("<table border=\"1\" cellpadding=\"2\">\r\n"); + out.print("<tr>\r\n"); + out.print("<td valign=\"top\">Time</td>\r\n"); + out.print("<td valign=\"top\">Branch</td>\r\n"); + out.print("<td valign=\"top\">User</td>\r\n"); + out.print("<td valign=\"top\">Comment</td>\r\n"); + out.print("</tr>\r\n"); + + final CDOCommitInfo[] details = { null }; + + try + { + final boolean auditing = repository.isSupportingAudits(); + repository.getCommitInfoManager().getCommitInfos(null, 0L, 0L, new CDOCommitInfoHandler() + { + public void handleCommitInfo(CDOCommitInfo commitInfo) + { + if (showCommitInfo(out, commitInfo, browser, param, auditing)) + { + details[0] = commitInfo; + } + } + }); + + out.print("</table>\r\n"); + out.print("</td>\r\n"); + out.print("<td> </td>\r\n"); + out.print("<td valign=\"top\">\r\n"); + + if (auditing) + { + CDOCommitInfo commitInfo = details[0]; + if (commitInfo != null) + { + out.print("<h3>Commit Info " + commitInfo.getTimeStamp() + "</h3>\r\n"); + showCommitData(out, commitInfo, browser); + } + } + else + { + out.print("<h3>No audit data available in this repository.</h3>\r\n"); + } + + out.print("</td>\r\n"); + out.print("</tr>\r\n"); + out.print("</table>\r\n"); + } + finally + { + StoreThreadLocal.release(); + } + } + + protected boolean showCommitInfo(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser, String param, + boolean auditing) + { + String timeStamp = String.valueOf(commitInfo.getTimeStamp()); + boolean selected = timeStamp.equals(param); + + String formatted = CDOCommonUtil.formatTimeStamp(commitInfo.getTimeStamp()).replaceAll(" ", " "); + String label = formatted; + if (!selected && auditing) + { + label = browser.href(formatted, getName(), "time", timeStamp); + } + + out.print("<tr>\r\n"); + out.print("<td valign=\"top\">\r\n"); + out.print(label); + out.print("</td>\r\n"); + + CDOBranch branch = commitInfo.getBranch(); + out.print("<td valign=\"top\">\r\n"); + out.print(branch.getName() + "[" + branch.getID() + "]"); + out.print("</td>\r\n"); + + String userID = commitInfo.getUserID(); + out.print("<td valign=\"top\">\r\n"); + out.print(StringUtil.isEmpty(userID) ? " " : browser.escape(userID)); + out.print("</td>\r\n"); + + String comment = commitInfo.getComment(); + out.print("<td valign=\"top\">\r\n"); + out.print(StringUtil.isEmpty(comment) ? " " : browser.escape(comment)); + out.print("</td>\r\n"); + + out.print("</tr>\r\n"); + return selected; + } + + protected void showCommitData(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser) + { + out.print("<h4>New Objects:</h4>\r\n"); + out.print("<ul>\r\n"); + for (CDOIDAndVersion key : commitInfo.getNewObjects()) + { + CDORevision newObject = (CDORevision)key; + out.print("<li>" + + browser.href(newObject.toString(), RevisionsPage.FromStore.NAME, "revision", + CDORevisionUtil.formatRevisionKey(newObject)) + "<br>\r\n"); + } + + out.print("</ul>\r\n"); + out.print("<h4>Changed Objects:</h4>\r\n"); + out.print("<ul>\r\n"); + for (CDORevisionKey key : commitInfo.getChangedObjects()) + { + CDORevisionDelta changedObject = (CDORevisionDelta)key; + out.print("<li>" + changedObject.toString() + "<br>\r\n"); + } + + out.print("</ul>\r\n"); + out.print("<h4>Detached Objects:</h4>\r\n"); + out.print("<ul>\r\n"); + for (CDOIDAndVersion key : commitInfo.getDetachedObjects()) + { + out.print("<li>" + key.toString() + "<br>\r\n"); + } + + out.print("</ul>\r\n"); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java new file mode 100644 index 0000000000..cc9a2adedb --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java @@ -0,0 +1,695 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lob.CDOBlob; +import org.eclipse.emf.cdo.common.lob.CDOClob; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.model.CDOClassInfo; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.io.XMLOutput; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.ecore.util.FeatureMapUtil; + +import org.xml.sax.SAXException; + +import java.io.OutputStream; +import java.io.Writer; +import java.util.Date; +import java.util.List; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public abstract class CDOServerExporter<OUT> +{ + private InternalRepository repository; + + public CDOServerExporter(IRepository repository) + { + this.repository = (InternalRepository)repository; + } + + public final IRepository getRepository() + { + return repository; + } + + public final void exportRepository(OutputStream out) throws Exception + { + boolean wasActive = LifecycleUtil.isActive(repository); + if (!wasActive) + { + LifecycleUtil.activate(repository); + } + + InternalSession session = repository.getSessionManager().openSession(null); + StoreThreadLocal.setSession(session); + + try + { + OUT output = createOutput(out); + exportAll(output); + } + finally + { + StoreThreadLocal.release(); + if (!wasActive) + { + LifecycleUtil.deactivate(repository); + } + + repository = null; + } + } + + protected abstract OUT createOutput(OutputStream out) throws Exception; + + protected void exportAll(final OUT out) throws Exception + { + try + { + exportPackages(out); + exportBranches(out); + exportLobs(out); + exportCommits(out); + } + catch (WrappedException ex) + { + throw WrappedException.unwrap(ex); + } + } + + protected void exportPackages(OUT out) throws Exception + { + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + InternalCDOPackageUnit[] packageUnits = packageRegistry.getPackageUnits(false); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + String id = packageUnit.getID(); + CDOPackageUnit.Type type = packageUnit.getOriginalType(); + long time = packageUnit.getTimeStamp(); + + EPackage ePackage = packageUnit.getTopLevelPackageInfo().getEPackage(); + String data = new String(EMFUtil.getEPackageBytes(ePackage, false, packageRegistry)); + + startPackageUnit(out, id, type, time, data); + for (InternalCDOPackageInfo packageInfo : packageUnit.getPackageInfos()) + { + String packageURI = packageInfo.getPackageURI(); + exportPackageInfo(out, packageURI); + } + + endPackageUnit(out); + } + } + + protected abstract void startPackageUnit(OUT out, String id, CDOPackageUnit.Type type, long time, String data) + throws Exception; + + protected abstract void endPackageUnit(OUT out) throws Exception; + + protected abstract void exportPackageInfo(OUT out, String packageURI) throws Exception; + + protected void exportBranches(final OUT out) throws Exception + { + InternalCDOBranchManager branchManager = repository.getBranchManager(); + exportBranch(out, branchManager.getMainBranch()); + + if (repository.isSupportingBranches()) + { + branchManager.getBranches(0, 0, new CDOBranchHandler() + { + public void handleBranch(CDOBranch branch) + { + try + { + exportBranch(out, branch); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + }); + } + } + + protected void exportBranch(OUT out, CDOBranch branch) throws Exception + { + exportRevisions(out, branch); + } + + protected void exportRevisions(final OUT out, CDOBranch branch) throws Exception + { + repository.handleRevisions(null, branch, true, CDOBranchPoint.INVALID_DATE, false, + new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + try + { + exportRevision(out, revision); + return true; + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + })); + } + + protected abstract void exportRevision(OUT out, CDORevision revision) throws Exception; + + protected void exportLobs(final OUT out) throws Exception + { + repository.handleLobs(0, 0, new CDOLobHandler() + { + public OutputStream handleBlob(byte[] id, long size) + { + try + { + return startBlob(out, id, size); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + + public Writer handleClob(byte[] id, long size) + { + try + { + return startClob(out, id, size); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + }); + } + + protected abstract OutputStream startBlob(OUT out, byte[] id, long size) throws Exception; + + protected abstract Writer startClob(OUT out, byte[] id, long size) throws Exception; + + protected void exportCommits(final OUT out) throws Exception + { + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + commitInfoManager.getCommitInfos(null, 0L, 0L, new CDOCommitInfoHandler() + { + public void handleCommitInfo(CDOCommitInfo commitInfo) + { + try + { + exportCommit(out, commitInfo); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + }); + } + + protected abstract void exportCommit(OUT out, CDOCommitInfo commitInfo) throws Exception; + + /** + * @author Eike Stepper + */ + public static interface XMLConstants + { + public static final String REPOSITORY = "repository"; + + public static final String REPOSITORY_NAME = "name"; + + public static final String REPOSITORY_UUID = "uuid"; + + public static final String REPOSITORY_ROOT = "root"; + + public static final String REPOSITORY_CREATED = "created"; + + public static final String REPOSITORY_COMMITTED = "committed"; + + public static final String MODELS = "models"; + + public static final String PACKAGE_UNIT = "packageUnit"; + + public static final String PACKAGE_UNIT_ID = "id"; + + public static final String PACKAGE_UNIT_TYPE = "type"; + + public static final String PACKAGE_UNIT_TIME = "time"; + + public static final String PACKAGE_UNIT_DATA = "data"; + + public static final String PACKAGE_INFO = "packageInfo"; + + public static final String PACKAGE_INFO_URI = "uri"; + + public static final String INSTANCES = "instances"; + + public static final String BRANCH = "branch"; + + public static final String BRANCH_ID = "id"; + + public static final String BRANCH_NAME = "name"; + + public static final String BRANCH_TIME = "time"; + + public static final String BRANCH_PARENT = "parent"; + + public static final String REVISION = "revision"; + + public static final String REVISION_ID = "id"; + + public static final String REVISION_CLASS = "class"; + + public static final String REVISION_VERSION = "version"; + + public static final String REVISION_TIME = "time"; + + public static final String REVISION_REVISED = "revised"; + + public static final String REVISION_RESOURCE = "resource"; + + public static final String REVISION_CONTAINER = "container"; + + public static final String REVISION_FEATURE = "feature"; + + public static final String FEATURE = "feature"; + + public static final String FEATURE_NAME = "name"; + + public static final String FEATURE_TYPE = "type"; + + public static final String FEATURE_INNER_FEATURE = "innerFeature"; + + public static final String FEATURE_INNER_TYPE = "innerType"; + + public static final String FEATURE_VALUE = "value"; + + public static final String FEATURE_ID = "id"; + + public static final String FEATURE_SIZE = "size"; + + public static final String TYPE_BLOB = "Blob"; + + public static final String TYPE_CLOB = "Clob"; + + public static final String TYPE_FEATURE_MAP = "FeatureMap"; + + public static final String LOBS = "lobs"; + + public static final String LOB_ID = "id"; + + public static final String LOB_SIZE = "size"; + + public static final String BLOB = "blob"; + + public static final String CLOB = "clob"; + + public static final String COMMITS = "commits"; + + public static final String COMMIT = "commit"; + + public static final String COMMIT_TIME = "time"; + + public static final String COMMIT_PREVIOUS = "previous"; + + public static final String COMMIT_BRANCH = "branch"; + + public static final String COMMIT_USER = "user"; + + public static final String COMMIT_COMMENT = "comment"; + } + + /** + * @author Eike Stepper + */ + public static class XML extends CDOServerExporter<XMLOutput> implements XMLConstants + { + public XML(IRepository repository) + { + super(repository); + } + + @Override + protected final XMLOutput createOutput(OutputStream out) throws Exception + { + return new XMLOutput(out); + } + + @Override + protected void exportAll(XMLOutput out) throws Exception + { + out.element(REPOSITORY); + out.attribute(REPOSITORY_NAME, getRepository().getName()); + out.attribute(REPOSITORY_UUID, getRepository().getUUID()); + out.attribute(REPOSITORY_ROOT, str(getRepository().getRootResourceID())); + out.attribute(REPOSITORY_CREATED, getRepository().getStore().getCreationTime()); + out.attribute(REPOSITORY_COMMITTED, getRepository().getLastCommitTimeStamp()); + + out.push(); + super.exportAll(out); + out.done(); + } + + @Override + protected void exportPackages(XMLOutput out) throws Exception + { + out.element(MODELS); + + out.push(); + super.exportPackages(out); + out.pop(); + } + + @Override + protected void startPackageUnit(XMLOutput out, String id, CDOPackageUnit.Type type, long time, String data) + throws Exception + { + out.element(PACKAGE_UNIT); + out.attribute(PACKAGE_UNIT_ID, id); + out.attribute(PACKAGE_UNIT_TYPE, type); + out.attribute(PACKAGE_UNIT_TIME, time); + out.attribute(PACKAGE_UNIT_DATA, data); + out.push(); + } + + @Override + protected void endPackageUnit(XMLOutput out) throws Exception + { + out.pop(); + } + + @Override + protected void exportPackageInfo(XMLOutput out, String uri) throws Exception + { + out.element(PACKAGE_INFO); + out.attribute(PACKAGE_INFO_URI, uri); + } + + @Override + protected void exportBranches(XMLOutput out) throws Exception + { + out.element(INSTANCES); + + out.push(); + super.exportBranches(out); + out.pop(); + } + + @Override + protected void exportBranch(XMLOutput out, CDOBranch branch) throws Exception + { + out.element(BRANCH); + out.attribute(BRANCH_ID, branch.getID()); + out.attribute(BRANCH_NAME, branch.getName()); + out.attribute(BRANCH_TIME, branch.getBase().getTimeStamp()); + if (!branch.isMainBranch()) + { + out.attribute(BRANCH_PARENT, branch.getBase().getBranch().getID()); + } + + out.push(); + super.exportBranch(out, branch); + out.pop(); + + } + + @Override + protected void exportRevision(XMLOutput out, CDORevision revision) throws Exception + { + InternalCDORevision rev = (InternalCDORevision)revision; + + out.element(REVISION); + out.attribute(REVISION_ID, str(rev.getID())); + out.attribute(REVISION_CLASS, new CDOClassifierRef(rev.getEClass()).getURI()); + out.attribute(REVISION_VERSION, rev.getVersion()); + out.attribute(REVISION_TIME, rev.getTimeStamp()); + + long revised = rev.getRevised(); + if (revised != CDOBranchPoint.UNSPECIFIED_DATE) + { + out.attribute(REVISION_REVISED, revised); + } + + CDOID resourceID = rev.getResourceID(); + if (!CDOIDUtil.isNull(resourceID)) + { + out.attribute(REVISION_RESOURCE, str(resourceID)); + } + + CDOID containerID = (CDOID)rev.getContainerID(); + if (!CDOIDUtil.isNull(containerID)) + { + out.attribute(REVISION_CONTAINER, str(containerID)); + } + + int containingFeatureID = rev.getContainingFeatureID(); + if (containingFeatureID != 0) + { + out.attribute(REVISION_FEATURE, containingFeatureID); + } + + out.push(); + CDOClassInfo classInfo = rev.getClassInfo(); + for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) + { + if (feature.isMany()) + { + @SuppressWarnings("unchecked") + List<Object> list = (List<Object>)rev.getValue(feature); + if (list != null) + { + for (Object value : list) + { + exportFeature(out, feature, value); + } + } + } + else + { + Object value = rev.getValue(feature); + if (value != null) + { + exportFeature(out, feature, value); + } + } + } + + out.pop(); + } + + protected void exportFeature(XMLOutput out, EStructuralFeature feature, Object value) throws Exception + { + out.element(FEATURE); + out.attribute(FEATURE_NAME, feature.getName()); + exportFeature(out, feature, FEATURE_TYPE, value); + } + + protected void exportFeature(XMLOutput out, EStructuralFeature feature, String featureType, Object value) + throws SAXException + { + if (value instanceof CDOID) + { + out.attribute(featureType, Object.class.getSimpleName()); + out.attribute(FEATURE_VALUE, str((CDOID)value)); + } + else if (value instanceof CDOBlob) + { + CDOBlob blob = (CDOBlob)value; + out.attribute(featureType, TYPE_BLOB); + out.attribute(FEATURE_ID, HexUtil.bytesToHex(blob.getID())); + out.attribute(FEATURE_SIZE, blob.getSize()); + } + else if (value instanceof CDOClob) + { + CDOClob clob = (CDOClob)value; + out.attribute(featureType, TYPE_CLOB); + out.attribute(FEATURE_ID, HexUtil.bytesToHex(clob.getID())); + out.attribute(FEATURE_SIZE, clob.getSize()); + } + else if (value instanceof Date) + { + Date date = (Date)value; + out.attribute(featureType, Date.class.getSimpleName()); + out.attribute(FEATURE_VALUE, date.getTime()); + } + else if (FeatureMapUtil.isFeatureMap(feature)) + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature innerFeature = entry.getEStructuralFeature(); + Object innerValue = entry.getValue(); + + out.attribute(featureType, TYPE_FEATURE_MAP); + out.attribute(FEATURE_INNER_FEATURE, innerFeature.getName()); + exportFeature(out, innerFeature, FEATURE_INNER_TYPE, innerValue); + } + else + { + if (!(value instanceof String)) + { + out.attribute(featureType, type(value)); + } + + out.attributeOrNull(FEATURE_VALUE, value); + } + } + + @Override + protected void exportLobs(XMLOutput out) throws Exception + { + out.element(LOBS); + + out.push(); + super.exportLobs(out); + out.pop(); + } + + @Override + protected OutputStream startBlob(XMLOutput out, byte[] id, long size) throws Exception + { + out.element(BLOB); + out.attribute(LOB_ID, HexUtil.bytesToHex(id)); + out.attribute(LOB_SIZE, size); + return out.bytes(); + } + + @Override + protected Writer startClob(XMLOutput out, byte[] id, long size) throws Exception + { + out.element(CLOB); + out.attribute(LOB_ID, HexUtil.bytesToHex(id)); + out.attribute(LOB_SIZE, size); + return out.characters(); + } + + @Override + protected void exportCommits(XMLOutput out) throws Exception + { + out.element(COMMITS); + + out.push(); + super.exportCommits(out); + out.pop(); + } + + @Override + protected void exportCommit(XMLOutput out, CDOCommitInfo commitInfo) throws Exception + { + out.element(COMMIT); + out.attribute(COMMIT_TIME, commitInfo.getTimeStamp()); + long previous = commitInfo.getPreviousTimeStamp(); + if (previous != CDOBranchPoint.UNSPECIFIED_DATE) + { + out.attribute(COMMIT_PREVIOUS, previous); + } + + int branch = commitInfo.getBranch().getID(); + if (branch != CDOBranch.MAIN_BRANCH_ID) + { + out.attribute(COMMIT_BRANCH, branch); + } + + out.attribute(COMMIT_USER, commitInfo.getUserID()); + out.attribute(COMMIT_COMMENT, commitInfo.getComment()); + } + + protected final String str(CDOID id) + { + StringBuilder builder = new StringBuilder(); + CDOIDUtil.write(builder, id); + return builder.toString(); + } + + protected String type(Object value) + { + if (value instanceof Boolean) + { + return Boolean.class.getSimpleName(); + } + + if (value instanceof Character) + { + return Character.class.getSimpleName(); + } + + if (value instanceof Byte) + { + return Byte.class.getSimpleName(); + } + + if (value instanceof Short) + { + return Short.class.getSimpleName(); + } + + if (value instanceof Integer) + { + return Integer.class.getSimpleName(); + } + + if (value instanceof Long) + { + return Long.class.getSimpleName(); + } + + if (value instanceof Float) + { + return Float.class.getSimpleName(); + } + + if (value instanceof Double) + { + return Double.class.getSimpleName(); + } + + if (value instanceof String) + { + return String.class.getSimpleName(); + } + + throw new IllegalArgumentException("Invalid type: " + value.getClass().getName()); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java new file mode 100644 index 0000000000..2441d7138e --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java @@ -0,0 +1,663 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lob.CDOLobUtil; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit.Type; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.model.EMFUtil.ExtResourceSet; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageLoader; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.io.AsyncOutputStream; +import org.eclipse.net4j.util.io.AsyncWriter; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 4.0 + * @apiviz.has {@link CDOServerImporter.Handler} + */ +public abstract class CDOServerImporter +{ + private InternalRepository repository; + + public CDOServerImporter(IRepository repository) + { + this.repository = (InternalRepository)repository; + init(); + } + + private void init() + { + LifecycleUtil.checkInactive(repository); + repository.setSkipInitialization(true); + repository.getStore().setDropAllDataOnActivate(true); + LifecycleUtil.activate(repository); + } + + protected final InternalRepository getRepository() + { + return repository; + } + + public void importRepository(InputStream in) throws Exception + { + try + { + FlushHandler handler = new FlushHandler(); + importAll(in, handler); + handler.flush(); + } + finally + { + StoreThreadLocal.release(); + repository = null; + } + } + + protected abstract void importAll(InputStream in, Handler handler) throws Exception; + + /** + * @author Eike Stepper + */ + public static interface Handler extends CDORevisionHandler, CDOLobHandler + { + public void handleRepository(String name, String uuid, CDOID root, long created, long committed); + + public InternalCDOPackageUnit handlePackageUnit(String id, Type type, long time, String data); + + public InternalCDOPackageInfo handlePackageInfo(String packageURI); + + public InternalCDOPackageRegistry handleModels(); + + public InternalCDOBranch handleBranch(int id, String name, long time, int parentID); + + public void handleCommitInfo(long time, long previous, int branch, String user, String comment); + + public void flush(); + + } + + /** + * @author Eike Stepper + */ + private final class FlushHandler implements Handler + { + private OMMonitor monitor = new Monitor(); + + private IStoreAccessor.Raw accessor; + + private Map<String, String> models = new HashMap<String, String>(); + + private LinkedList<InternalCDOPackageUnit> packageUnits = new LinkedList<InternalCDOPackageUnit>(); + + private List<InternalCDOPackageInfo> packageInfos; + + private InternalCDOPackageRegistry packageRegistry = getRepository().getPackageRegistry(false); + + public FlushHandler() + { + } + + public void handleRepository(String name, String uuid, CDOID root, long created, long committed) + { + repository.getStore().setCreationTime(created); + repository.getStore().setLastCommitTime(committed); + + InternalCDOBranchManager branchManager = repository.getBranchManager(); + repository.initMainBranch(branchManager, created); + LifecycleUtil.activate(branchManager); + + repository.initSystemPackages(); + repository.setRootResourceID(root); + + // InternalSession session = repository.getSessionManager().openSession(null); + // StoreThreadLocal.setSession(session); + + accessor = (IStoreAccessor.Raw)repository.getStore().getWriter(null); + StoreThreadLocal.setAccessor(accessor); + } + + public InternalCDOPackageUnit handlePackageUnit(String id, Type type, long time, String data) + { + collectPackageInfos(); + + InternalCDOPackageUnit packageUnit = packageRegistry.createPackageUnit(); + packageUnit.setOriginalType(type); + packageUnit.setTimeStamp(time); + + models.put(id, data); + packageUnits.add(packageUnit); + packageInfos = new ArrayList<InternalCDOPackageInfo>(); + return packageUnit; + } + + public InternalCDOPackageInfo handlePackageInfo(String packageURI) + { + InternalCDOPackageInfo packageInfo = (InternalCDOPackageInfo)CDOModelUtil.createPackageInfo(); + packageInfo.setPackageURI(packageURI); + packageInfos.add(packageInfo); + return packageInfo; + } + + public InternalCDOPackageRegistry handleModels() + { + collectPackageInfos(); + InternalCDOPackageUnit[] array = packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]); + packageUnits = null; + + final ExtResourceSet resourceSet = EMFUtil.createExtResourceSet(packageRegistry, false, false); + + PackageLoader loader = new PackageLoader() + { + public EPackage[] loadPackages(CDOPackageUnit packageUnit) + { + String id = packageUnit.getID(); + String data = models.get(id); + + EPackage ePackage = EMFUtil.createEPackage(id, data.getBytes(), false, resourceSet, true); + return EMFUtil.getAllPackages(ePackage); + } + }; + + packageRegistry.putPackageUnits(array, CDOPackageUnit.State.PROXY); + for (InternalCDOPackageUnit packageUnit : array) + { + packageUnit.load(loader, false); + } + + // Before we resolve, we configure the resourceSet to start delegating, which means + // it will consult the packageRegistry for packages it didn't just load -- such as the Ecore + // package + resourceSet.setDelegating(true); + EMFUtil.safeResolveAll(resourceSet); + + accessor.rawStore(array, monitor); + + return packageRegistry; + } + + public InternalCDOBranch handleBranch(int id, String name, long time, int parentID) + { + InternalCDOBranchManager branchManager = repository.getBranchManager(); + if (id == CDOBranch.MAIN_BRANCH_ID) + { + return branchManager.getMainBranch(); + } + + InternalCDOBranch parent = branchManager.getBranch(parentID); + return branchManager.createBranch(id, name, parent, time); + } + + public boolean handleRevision(CDORevision revision) + { + accessor.rawStore((InternalCDORevision)revision, monitor); + return true; + } + + public OutputStream handleBlob(final byte[] id, final long size) throws IOException + { + return new AsyncOutputStream() + { + @Override + protected void asyncWrite(InputStream in) throws IOException + { + accessor.rawStore(id, size, in); + } + }; + } + + public Writer handleClob(final byte[] id, final long size) throws IOException + { + return new AsyncWriter() + { + @Override + protected void asyncWrite(Reader in) throws IOException + { + accessor.rawStore(id, size, in); + } + }; + } + + public void handleCommitInfo(long time, long previous, int branchID, String user, String comment) + { + CDOBranch branch = repository.getBranchManager().getBranch(branchID); + accessor.rawStore(branch, time, previous, user, comment, monitor); + } + + public void flush() + { + accessor.rawCommit(1.0, monitor); + } + + private void collectPackageInfos() + { + if (packageInfos != null) + { + InternalCDOPackageUnit packageUnit = packageUnits.getLast(); + packageUnit.setPackageInfos(packageInfos.toArray(new InternalCDOPackageInfo[packageInfos.size()])); + packageInfos = null; + } + } + } + + /** + * @author Eike Stepper + */ + public static class XML extends CDOServerImporter implements CDOServerExporter.XMLConstants + { + public XML(IRepository repository) + { + super(repository); + } + + @Override + protected void importAll(InputStream in, final Handler handler) throws Exception + { + DefaultHandler xmlHandler = new XMLHandler(handler); + + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser saxParser = factory.newSAXParser(); + saxParser.parse(in, xmlHandler); + } + + /** + * @author Eike Stepper + */ + private static final class XMLHandler extends DefaultHandler + { + private Handler handler; + + private InternalCDOPackageRegistry packageRegistry; + + private InternalCDOBranch branch; + + private InternalCDORevision revision; + + private Character blobChar; + + private OutputStream blob; + + private Writer clob; + + private XMLHandler(Handler handler) + { + this.handler = handler; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException + { + if (REPOSITORY.equals(qName)) + { + String name = attributes.getValue(REPOSITORY_NAME); + String uuid = attributes.getValue(REPOSITORY_UUID); + CDOID root = id(attributes.getValue(REPOSITORY_ROOT)); + long created = Long.parseLong(attributes.getValue(REPOSITORY_CREATED)); + long committed = Long.parseLong(attributes.getValue(REPOSITORY_COMMITTED)); + handler.handleRepository(name, uuid, root, created, committed); + } + else if (PACKAGE_UNIT.equals(qName)) + { + String id = attributes.getValue(PACKAGE_UNIT_ID); + Type type = CDOPackageUnit.Type.valueOf(attributes.getValue(PACKAGE_UNIT_TYPE)); + long time = Long.parseLong(attributes.getValue(PACKAGE_UNIT_TIME)); + String data = attributes.getValue(PACKAGE_UNIT_DATA); + handler.handlePackageUnit(id, type, time, data); + } + else if (PACKAGE_INFO.equals(qName)) + { + String packageURI = attributes.getValue(PACKAGE_INFO_URI); + handler.handlePackageInfo(packageURI); + } + else if (BRANCH.equals(qName)) + { + int id = Integer.parseInt(attributes.getValue(BRANCH_ID)); + String name = attributes.getValue(BRANCH_NAME); + long time = Long.parseLong(attributes.getValue(BRANCH_TIME)); + String parent = attributes.getValue(BRANCH_PARENT); + int parentID = parent == null ? 0 : Integer.parseInt(parent); + branch = handler.handleBranch(id, name, time, parentID); + } + else if (REVISION.equals(qName)) + { + CDOClassifierRef classifierRef = new CDOClassifierRef(attributes.getValue(REVISION_CLASS)); + EClass eClass = (EClass)classifierRef.resolve(packageRegistry); + revision = (InternalCDORevision)CDORevisionFactory.DEFAULT.createRevision(eClass); + revision.setID(id(attributes.getValue(REVISION_ID))); + revision.setBranchPoint(branch.getPoint(Long.parseLong(attributes.getValue(REVISION_TIME)))); + revision.setVersion(Integer.parseInt(attributes.getValue(REVISION_VERSION))); + String revised = attributes.getValue(REVISION_REVISED); + if (revised != null) + { + revision.setRevised(Long.parseLong(revised)); + } + + String resourceID = attributes.getValue(REVISION_RESOURCE); + if (resourceID != null) + { + revision.setResourceID(id(resourceID)); + } + + String containerID = attributes.getValue(REVISION_CONTAINER); + if (containerID != null) + { + revision.setContainerID(id(containerID)); + } + + String featureID = attributes.getValue(REVISION_FEATURE); + if (featureID != null) + { + revision.setContainingFeatureID(Integer.parseInt(featureID)); + } + } + else if (FEATURE.equals(qName)) + { + String name = attributes.getValue(FEATURE_NAME); + Object value = value(attributes); + + EClass eClass = revision.getEClass(); + EStructuralFeature feature = eClass.getEStructuralFeature(name); + if (feature == null) + { + throw new IllegalStateException("Feature " + name + " not found in class " + eClass.getName()); + } + + if (feature.isMany()) + { + CDOList list = revision.getList(feature); + list.add(value); + } + else + { + if (value != null) + { + revision.setValue(feature, value); + } + } + } + else if (BLOB.equals(qName)) + { + try + { + byte[] id = HexUtil.hexToBytes(attributes.getValue(LOB_ID)); + long size = Long.parseLong(attributes.getValue(LOB_SIZE)); + blob = handler.handleBlob(id, size); + } + catch (IOException ex) + { + throw WrappedException.wrap(ex); + } + } + else if (CLOB.equals(qName)) + { + try + { + byte[] id = HexUtil.hexToBytes(attributes.getValue(LOB_ID)); + long size = Long.parseLong(attributes.getValue(LOB_SIZE)); + clob = handler.handleClob(id, size); + } + catch (IOException ex) + { + throw WrappedException.wrap(ex); + } + } + else if (COMMIT.equals(qName)) + { + long time = Long.parseLong(attributes.getValue(COMMIT_TIME)); + + String value = attributes.getValue(COMMIT_PREVIOUS); + long previous = value == null ? CDOBranchPoint.UNSPECIFIED_DATE : Long.parseLong(value); + + value = attributes.getValue(COMMIT_BRANCH); + int branch = value == null ? CDOBranch.MAIN_BRANCH_ID : Integer.parseInt(value); + + String user = attributes.getValue(COMMIT_USER); + String comment = attributes.getValue(COMMIT_COMMENT); + + handler.handleCommitInfo(time, previous, branch, user, comment); + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException + { + if (blob != null) + { + try + { + if (blobChar != null) + { + char[] firstChars = { blobChar, ch[start] }; + blobChar = null; + + byte[] firstByte = HexUtil.hexToBytes(new String(firstChars)); + blob.write(firstByte, 0, 1); + + ++start; + --length; + } + + if ((length & 1) == 1) // odd length? + { + --length; + blobChar = ch[length]; + } + + if (start != 0 || length != ch.length) + { + char[] tmp = new char[length]; + System.arraycopy(ch, start, tmp, 0, length); + ch = tmp; + } + + byte[] buf = HexUtil.hexToBytes(new String(ch)); + blob.write(buf); + } + catch (IOException ex) + { + throw WrappedException.wrap(ex); + } + } + else if (clob != null) + { + try + { + clob.write(ch, start, length); + } + catch (IOException ex) + { + throw WrappedException.wrap(ex); + } + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException + { + if (MODELS.equals(qName)) + { + packageRegistry = handler.handleModels(); + } + else if (BRANCH.equals(qName)) + { + branch = null; + } + else if (REVISION.equals(qName)) + { + handler.handleRevision(revision); + revision = null; + } + else if (BLOB.equals(qName)) + { + IOUtil.close(blob); + blob = null; + } + else if (CLOB.equals(qName)) + { + IOUtil.close(clob); + clob = null; + } + } + + protected final CDOID id(String str) + { + return CDOIDUtil.read(str); + } + + protected Object value(Attributes attributes) + { + String type = attributes.getValue(FEATURE_TYPE); + + if (TYPE_BLOB.equals(type)) + { + byte[] id = HexUtil.hexToBytes(attributes.getValue(FEATURE_ID)); + long size = Long.parseLong(attributes.getValue(FEATURE_SIZE)); + return CDOLobUtil.createBlob(id, size); + } + + if (TYPE_CLOB.equals(type)) + { + byte[] id = HexUtil.hexToBytes(attributes.getValue(FEATURE_ID)); + long size = Long.parseLong(attributes.getValue(FEATURE_SIZE)); + return CDOLobUtil.createClob(id, size); + } + + if (TYPE_FEATURE_MAP.equals(type)) + { + String innerFeatureName = attributes.getValue(FEATURE_INNER_FEATURE); + EStructuralFeature innerFeature = revision.getEClass().getEStructuralFeature(innerFeatureName); + + String innerType = attributes.getValue(FEATURE_INNER_TYPE); + Object innerValue = value(attributes, innerType); + + return CDORevisionUtil.createFeatureMapEntry(innerFeature, innerValue); + } + + return value(attributes, type); + } + + protected Object value(Attributes attributes, String type) + { + String str = attributes.getValue(FEATURE_VALUE); + if (str == null) + { + return null; + } + + if (type == null || String.class.getSimpleName().equals(type)) + { + return str; + } + + if (Object.class.getSimpleName().equals(type)) + { + return id(str); + } + + if (Boolean.class.getSimpleName().equals(type)) + { + return Boolean.valueOf(str); + } + + if (Character.class.getSimpleName().equals(type)) + { + return str.charAt(0); + } + + if (Byte.class.getSimpleName().equals(type)) + { + return Byte.valueOf(str); + } + + if (Short.class.getSimpleName().equals(type)) + { + return Short.valueOf(str); + } + + if (Integer.class.getSimpleName().equals(type)) + { + return Integer.valueOf(str); + } + + if (Long.class.getSimpleName().equals(type)) + { + return Long.valueOf(str); + } + + if (Float.class.getSimpleName().equals(type)) + { + return Float.valueOf(str); + } + + if (Double.class.getSimpleName().equals(type)) + { + return Double.valueOf(str); + } + + if (Date.class.getSimpleName().equals(type)) + { + return new Date(Long.valueOf(str)); + } + + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java new file mode 100644 index 0000000000..e609c536b1 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java @@ -0,0 +1,299 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.ServerCDOView; +import org.eclipse.emf.cdo.internal.server.SessionManager; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.internal.server.embedded.EmbeddedClientSessionConfiguration; +import org.eclipse.emf.cdo.internal.server.syncing.FailoverParticipant; +import org.eclipse.emf.cdo.internal.server.syncing.OfflineClone; +import org.eclipse.emf.cdo.internal.server.syncing.RepositorySynchronizer; +import org.eclipse.emf.cdo.server.embedded.CDOSessionConfiguration; +import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; +import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; +import org.eclipse.emf.cdo.spi.common.revision.ManagedRevisionProvider; +import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalStore; +import org.eclipse.emf.cdo.spi.server.RepositoryFactory; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.OMPlatform; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + * @apiviz.exclude + */ +public final class CDOServerUtil +{ + private CDOServerUtil() + { + } + + /** + * @since 4.0 + */ + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled, + CDORevisionProvider revisionProvider) + { + return new ServerCDOView((InternalSession)session, branchPoint, legacyModeEnabled, revisionProvider); + } + + /** + * @since 4.0 + */ + public static CDOView openView(ISession session, CDOBranchPoint branchPoint, boolean legacyModeEnabled) + { + CDORevisionManager revisionManager = session.getManager().getRepository().getRevisionManager(); + CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, branchPoint); + return new ServerCDOView((InternalSession)session, branchPoint, legacyModeEnabled, revisionProvider); + } + + /** + * @since 4.0 + */ + public static CDOView openView(IView view, boolean legacyModeEnabled) + { + ISession session = view.getSession(); + CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); + return openView(session, branchPoint, legacyModeEnabled, view); + } + + /** + * @since 4.0 + */ + public static CDOView openView(IStoreAccessor.CommitContext commitContext, boolean legacyModeEnabled) + { + ISession session = commitContext.getTransaction().getSession(); + CDOBranchPoint branchPoint = commitContext.getBranchPoint(); + return openView(session, branchPoint, legacyModeEnabled, commitContext); + } + + /** + * @since 3.0 + */ + public static CDOSessionConfiguration createSessionConfiguration() + { + return new EmbeddedClientSessionConfiguration(); + } + + /** + * @since 3.0 + */ + public static ISessionManager createSessionManager() + { + return new SessionManager(); + } + + public static IRepository createRepository(String name, IStore store, Map<String, String> props) + { + Repository repository = new Repository.Default(); + initRepository(repository, name, store, props); + return repository; + } + + /** + * @since 3.0 + */ + public static IRepositorySynchronizer createRepositorySynchronizer( + CDOSessionConfigurationFactory remoteSessionConfigurationFactory) + { + RepositorySynchronizer synchronizer = new RepositorySynchronizer(); + synchronizer.setRemoteSessionConfigurationFactory(remoteSessionConfigurationFactory); + return synchronizer; + } + + /** + * @since 3.0 + */ + public static ISynchronizableRepository createOfflineClone(String name, IStore store, Map<String, String> props, + IRepositorySynchronizer synchronizer) + { + OfflineClone repository = new OfflineClone(); + initRepository(repository, name, store, props); + repository.setSynchronizer((InternalRepositorySynchronizer)synchronizer); + return repository; + } + + /** + * @since 4.0 + */ + public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, + Map<String, String> props, IRepositorySynchronizer synchronizer, boolean master, boolean allowBackupCommits) + { + FailoverParticipant repository = new FailoverParticipant(); + initRepository(repository, name, store, props); + repository.setSynchronizer((InternalRepositorySynchronizer)synchronizer); + repository.setType(master ? CDOCommonRepository.Type.MASTER : CDOCommonRepository.Type.BACKUP); + return repository; + } + + /** + * @since 3.0 + */ + public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, + Map<String, String> props, IRepositorySynchronizer synchronizer, boolean master) + { + return createFailoverParticipant(name, store, props, synchronizer, master, false); + } + + /** + * @since 4.0 + */ + public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, + Map<String, String> props, IRepositorySynchronizer synchronizer) + { + return createFailoverParticipant(name, store, props, synchronizer, false); + } + + /** + * @since 4.0 + */ + public static ISynchronizableRepository createFailoverParticipant(String name, IStore store, Map<String, String> props) + { + return createFailoverParticipant(name, store, props, null); + } + + private static void initRepository(Repository repository, String name, IStore store, Map<String, String> props) + { + repository.setName(name); + repository.setStore((InternalStore)store); + repository.setProperties(props); + } + + public static void addRepository(IManagedContainer container, IRepository repository) + { + container.putElement(RepositoryFactory.PRODUCT_GROUP, RepositoryFactory.TYPE, repository.getName(), repository); + LifecycleUtil.activate(repository); + } + + public static IRepository getRepository(IManagedContainer container, String name) + { + return RepositoryFactory.get(container, name); + } + + public static Element getRepositoryConfig(String repositoryName) throws ParserConfigurationException, SAXException, + IOException + { + File configFile = OMPlatform.INSTANCE.getConfigFile("cdo-server.xml"); //$NON-NLS-1$ + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(configFile); + NodeList elements = document.getElementsByTagName("repository"); //$NON-NLS-1$ + for (int i = 0; i < elements.getLength(); i++) + { + Node node = elements.item(i); + if (node instanceof Element) + { + Element element = (Element)node; + String name = element.getAttribute("name"); //$NON-NLS-1$ + if (ObjectUtil.equals(name, repositoryName)) + { + return element; + } + } + } + + throw new IllegalStateException("Repository config not found: " + repositoryName); //$NON-NLS-1$ + } + + /** + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ + public static abstract class RepositoryReadAccessValidator implements IRepository.ReadAccessHandler + { + public RepositoryReadAccessValidator() + { + } + + public void handleRevisionsBeforeSending(ISession session, CDORevision[] revisions, + List<CDORevision> additionalRevisions) throws RuntimeException + { + List<String> violations = new ArrayList<String>(); + for (CDORevision revision : revisions) + { + String violation = validate(session, revision); + if (violation != null) + { + violations.add(violation); + } + } + + if (!violations.isEmpty()) + { + throwException(session, violations); + } + + for (Iterator<CDORevision> it = additionalRevisions.iterator(); it.hasNext();) + { + CDORevision revision = it.next(); + String violation = validate(session, revision); + if (violation != null) + { + OM.LOG.info("Revision can not be delivered to " + session + ": " + violation); //$NON-NLS-1$ //$NON-NLS-2$ + it.remove(); + } + } + } + + protected void throwException(ISession session, List<String> violations) throws RuntimeException + { + StringBuilder builder = new StringBuilder(); + builder.append("Revisions can not be delivered to "); //$NON-NLS-1$ + builder.append(session); + builder.append(":"); //$NON-NLS-1$ + for (String violation : violations) + { + builder.append("\n- "); //$NON-NLS-1$ + builder.append(violation); + } + + throwException(builder.toString()); + } + + protected void throwException(String message) throws RuntimeException + { + throw new IllegalStateException(message); + } + + protected abstract String validate(ISession session, CDORevision revision); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java new file mode 100644 index 0000000000..0a71e68c00 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class ContainmentCycleDetectedException extends IllegalStateException +{ + private static final long serialVersionUID = 1L; + + public ContainmentCycleDetectedException() + { + } + + public ContainmentCycleDetectedException(String message, Throwable cause) + { + super(message, cause); + } + + public ContainmentCycleDetectedException(String s) + { + super(s); + } + + public ContainmentCycleDetectedException(Throwable cause) + { + super(cause); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java new file mode 100644 index 0000000000..d73f1ec0b8 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +/** + * A simple in-memory store. + * + * @author Eike Stepper + * @since 2.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @deprecated Use {@link org.eclipse.emf.cdo.server.mem.IMEMStore} + * @apiviz.exclude + */ +@Deprecated +public interface IMEMStore extends org.eclipse.emf.cdo.server.mem.IMEMStore +{ +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java new file mode 100644 index 0000000000..9752d12981 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; + +/** + * @author Eike Stepper + * @since 2.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.exclude + */ +public interface IQueryContext extends CDOBranchPoint +{ + public IView getView(); + + /** + * @since 4.0 + */ + public int getResultCount(); + + /** + * Adds the given object to the results of the associated query. + * + * @param object + * Support many primitives, CDOID and CDORevision. CDORevision are converted in CDOID and only CDOID are + * transfered to the client. + * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResult(Object object); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java new file mode 100644 index 0000000000..65e2795182 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public interface IQueryHandler +{ + /** + * @since 3.0 + */ + public void executeQuery(CDOQueryInfo info, IQueryContext context); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java new file mode 100644 index 0000000000..944d2d7251 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; + +/** + * @author Eike Stepper + * @since 2.0 + * @apiviz.uses {@link IQueryHandler} - - provides + */ +public interface IQueryHandlerProvider +{ + /** + * @since 3.0 + */ + public IQueryHandler getQueryHandler(CDOQueryInfo info); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java new file mode 100644 index 0000000000..a9d8de729f --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.branch.CDOBranchManager; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; + +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EPackage.Registry; + +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + * @apiviz.has {@link IStore} + * @apiviz.has {@link java.util.Map} oneway - - properties + * @apiviz.has {@link org.eclipse.emf.cdo.common.model.CDOPackageRegistry} + * @apiviz.has {@link org.eclipse.emf.cdo.common.branch.CDOBranchManager} + * @apiviz.has {@link org.eclipse.emf.cdo.common.revision.CDORevisionManager} + * @apiviz.has {@link org.eclipse.emf.cdo.common.lock.IDurableLockingManager} + * @apiviz.has {@link ISessionManager} + * @apiviz.has {@link IQueryHandlerProvider} + * @apiviz.composedOf {@link org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler} + * @apiviz.composedOf {@link IRepository.Handler} - - accessHandlers + */ +public interface IRepository extends CDOCommonRepository, IQueryHandlerProvider, IContainer<Object> +{ + /** + * @since 3.0 + */ + public static final String SYSTEM_USER_ID = "CDO_SYSTEM"; //$NON-NLS-1$ + + public IStore getStore(); + + public Map<String, String> getProperties(); + + /** + * Returns the EMF {@link Registry package registry} that is used by this repository. + * + * @since 2.0 + */ + public CDOPackageRegistry getPackageRegistry(); + + /** + * @since 3.0 + */ + public CDOBranchManager getBranchManager(); + + /** + * @since 3.0 + */ + public CDORevisionManager getRevisionManager(); + + public ISessionManager getSessionManager(); + + /** + * @since 4.0 + */ + public IDurableLockingManager getLockManager(); + + /** + * @since 2.0 + */ + public IQueryHandlerProvider getQueryHandlerProvider(); + + /** + * Returns the time stamp of the last commit operation. + * + * @since 3.0 + */ + public long getLastCommitTimeStamp(); + + /** + * Blocks the calling thread until the next commit operation has succeeded and returns the last (highest) commit time + * stamp. + * + * @since 3.0 + */ + public long waitForCommit(long timeout); + + /** + * Validates the given timeStamp against the repository time. + * + * @throws IllegalArgumentException + * if the given timeStamp is less than the repository creation time or greater than the current repository + * time. + * @since 2.0 + */ + public void validateTimeStamp(long timeStamp) throws IllegalArgumentException; + + /** + * @since 4.0 + */ + public void addCommitInfoHandler(CDOCommitInfoHandler handler); + + /** + * @since 4.0 + */ + public void removeCommitInfoHandler(CDOCommitInfoHandler handler); + + /** + * @since 2.0 + */ + public void addHandler(Handler handler); + + /** + * @since 2.0 + */ + public void removeHandler(Handler handler); + + /** + * @since 4.0 + */ + public void setInitialPackages(EPackage... initialPackages); + + /** + * A marker interface to indicate valid arguments to {@link IRepository#addHandler(Handler)} and + * {@link IRepository#removeHandler(Handler)}. + * + * @see ReadAccessHandler + * @see WriteAccessHandler + * @author Eike Stepper + * @since 2.0 + */ + public interface Handler + { + } + + /** + * Provides a way to handle revisions that are to be sent to the client. + * + * @author Eike Stepper + * @since 2.0 + */ + public interface ReadAccessHandler extends Handler + { + /** + * Provides a way to handle revisions that are to be sent to the client. + * + * @param session + * The session that is going to send the revisions. + * @param revisions + * The revisions that are requested by the client. If the client must not see any of these revisions an + * unchecked exception must be thrown. + * @param additionalRevisions + * The additional revisions that are to be sent to the client because internal optimizers believe that they + * will be needed soon. If the client must not see any of these revisions they should be removed from the + * list. + * @throws RuntimeException + * to indicate that none of the revisions must be sent to the client. This exception will be visible at + * the client side! + */ + public void handleRevisionsBeforeSending(ISession session, CDORevision[] revisions, + List<CDORevision> additionalRevisions) throws RuntimeException; + } + + /** + * @author Eike Stepper + * @since 2.0 + */ + public interface WriteAccessHandler extends Handler + { + /** + * Provides a way to handle transactions that are to be committed to the backend store. + * + * @param transaction + * The transaction that is going to be committed. + * @param commitContext + * The context of the commit operation that is to be executed against the backend store. The context can be + * used to introspect all aspects of the current commit operation. <b>Note that you must not alter the + * internal state of the commit context in any way!</b> + * @param monitor + * A monitor that should be used by the implementor to avoid timeouts. + * @throws RuntimeException + * to indicate that the commit operation must not be executed against the backend store. This exception + * will be visible at the client side! + */ + public void handleTransactionBeforeCommitting(ITransaction transaction, IStoreAccessor.CommitContext commitContext, + OMMonitor monitor) throws RuntimeException; + + /** + * Provides a way to handle transactions after they have been committed to the backend store. + * + * @param transaction + * The transaction that has been committed. + * @param commitContext + * The context of the commit operation that was executed against the backend store. The context can be used + * to introspect all aspects of the current commit operation. <b>Note that you must not alter the internal + * state of the commit context in any way!</b> + * @param monitor + * A monitor that should be used by the implementor to avoid timeouts. + * @since 3.0 + */ + public void handleTransactionAfterCommitted(ITransaction transaction, IStoreAccessor.CommitContext commitContext, + OMMonitor monitor); + } + + /** + * @author Eike Stepper + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @apiviz.exclude + */ + public interface Props + { + /** + * Used to override the automatic UUID generation during first startup of a repository. Passing the empty string + * causes the UUID of the repository to be set to its {@link IRepository#getName() name}. + * + * @since 2.0 + */ + public static final String OVERRIDE_UUID = "overrideUUID"; //$NON-NLS-1$ + + /** + * @since 2.0 + */ + public static final String SUPPORTING_AUDITS = "supportingAudits"; //$NON-NLS-1$ + + /** + * @since 3.0 + */ + public static final String SUPPORTING_BRANCHES = "supportingBranches"; //$NON-NLS-1$ + + /** + * @since 4.0 + */ + public static final String SUPPORTING_ECORE = "supportingEcore"; //$NON-NLS-1$ + + /** + * @since 3.0 + */ + public static final String ENSURE_REFERENTIAL_INTEGRITY = "ensureReferentialIntegrity"; //$NON-NLS-1$ + + /** + * @since 4.0 + */ + public static final String ALLOW_INTERRUPT_RUNNING_QUERIES = "allowInterruptRunningQueries"; //$NON-NLS-1$ + + /** + * @since 4.1 + */ + public static final String ID_GENERATION_LOCATION = "idGenerationLocation"; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java new file mode 100644 index 0000000000..9e2d276c5c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +/** + * @author Eike Stepper + * @apiviz.uses {@link IRepository} - - creates + */ +public interface IRepositoryFactory +{ + /** + * @since 2.0 + */ + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.repositories"; //$NON-NLS-1$ + + public String getRepositoryType(); + + public IRepository createRepository(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java new file mode 100644 index 0000000000..336775093f --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +/** + * @author Eike Stepper + * @apiviz.uses {@link IRepository} - - provides + */ +public interface IRepositoryProvider +{ + public IRepository getRepository(String name); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java new file mode 100644 index 0000000000..cae1cc836a --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; + +import org.eclipse.net4j.util.event.INotifier; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + * @apiviz.has {@link org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory} oneway - - remote + */ +public interface IRepositorySynchronizer extends INotifier +{ + public int getRetryInterval(); + + public void setRetryInterval(int retryInterval); + + public ISynchronizableRepository getLocalRepository(); + + public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory(); + + public CDOSession getRemoteSession(); + + public boolean isRawReplication(); + + /** + * @since 4.0 + */ + public void setRawReplication(boolean rawReplication); + + public int getMaxRecommits(); + + public void setMaxRecommits(int maxRecommits); + + public int getRecommitInterval(); + + public void setRecommitInterval(int recommitInterval); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java new file mode 100644 index 0000000000..386af3d069 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 230832 + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.CDOCommonSession; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.spi.server.ISessionProtocol; + +import org.eclipse.net4j.util.container.IContainer; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + * @apiviz.has {@link org.eclipse.emf.cdo.spi.server.ISessionProtocol} + * @apiviz.composedOf {@link IView} - - views + * @apiviz.composedOf {@link ITransaction} - - transactions + */ +public interface ISession extends CDOCommonSession, IContainer<IView> +{ + /** + * @since 3.0 + */ + public ISessionManager getManager(); + + /** + * @since 3.0 + */ + public ISessionProtocol getProtocol(); + + /** + * @since 4.0 + */ + public long getLastUpdateTime(); + + /** + * @since 2.0 + */ + public boolean isSubscribed(); + + /** + * @since 3.0 + */ + public IView openView(int viewID, CDOBranchPoint branchPoint); + + /** + * @since 3.0 + */ + public ITransaction openTransaction(int viewID, CDOBranchPoint branchPoint); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java new file mode 100644 index 0000000000..795a835614 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.net4j.util.container.IContainer; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + * @apiviz.composedOf {@link ISession} + */ +public interface ISessionManager extends IContainer<ISession> +{ + /** + * @since 2.0 + */ + public IRepository getRepository(); + + public ISession[] getSessions(); + + /** + * @since 2.0 + */ + public ISession getSession(int sessionID); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java new file mode 100644 index 0000000000..a94202dc8c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; + +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + * @apiviz.landmark + * @apiviz.has {@link IStore.ChangeFormat} + * @apiviz.has {@link IStore.RevisionTemporality} + * @apiviz.has {@link IStore.RevisionParallelism} + * @apiviz.uses {@link IStoreAccessor} - - creates + */ +public interface IStore +{ + /** + * @since 2.0 + */ + public IRepository getRepository(); + + /** + * @since 2.0 + */ + public String getType(); + + /** + * @since 3.0 + */ + public Set<CDOID.ObjectType> getObjectIDTypes(); + + /** + * @since 4.0 + */ + public CDOID createObjectID(String val); + + /** + * @since 2.0 + */ + public Set<ChangeFormat> getSupportedChangeFormats(); + + /** + * @since 2.0 + */ + public Set<RevisionTemporality> getSupportedRevisionTemporalities(); + + /** + * @since 2.0 + */ + public Set<RevisionParallelism> getSupportedRevisionParallelisms(); + + /** + * @since 2.0 + */ + public RevisionTemporality getRevisionTemporality(); + + /** + * @since 2.0 + */ + public RevisionParallelism getRevisionParallelism(); + + /** + * Returns <code>true</code>if this store was activated for the first time, <code>false</code> otherwise. + * + * @since 4.0 + */ + public boolean isFirstStart(); + + /** + * Returns the store creation time. + * + * @since 2.0 + */ + public long getCreationTime(); + + /** + * Returns the id of the last branch that has been created in this store. + * + * @since 3.0 + */ + public int getLastBranchID(); + + /** + * Returns the id of the last local branch that has been created in this store. + * + * @since 3.0 + */ + public int getLastLocalBranchID(); + + /** + * Returns the time stamp of the last successful commit operation. + * + * @since 3.0 + */ + public long getLastCommitTime(); + + /** + * Returns the time stamp of the last successful commit operation to a non-local {@link CDOBranch branch}. + * + * @since 3.0 + */ + public long getLastNonLocalCommitTime(); + + /** + * Returns a map filled with the property entries for the requested property <code>names</code> if names is not + * <code>null</code> and not {@link Collection#isEmpty() empty}, all existing property entries otherwise. + * + * @since 4.0 + */ + public Map<String, String> getPersistentProperties(Set<String> names); + + /** + * @since 4.0 + */ + public void setPersistentProperties(Map<String, String> properties); + + /** + * @since 4.0 + */ + public void removePersistentProperties(Set<String> names); + + /** + * Returns a reader that can be used to read from this store in the context of the given session. + * + * @param session + * The session that should be used as a context for read access or <code>null</code>. The store implementor + * is free to interpret and use the session in a manner suitable for him or ignore it at all. It is meant + * only as a hint. Implementor can use it as a key into a cache and/or register a + * {@link org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter LifecycleEventAdapter} with it to intercept + * cleanup on session close. Note however that the session can be <code>null</code>, for example during + * startup of the server while the repositories are initialized but before any user session has been opened. + * @return a reader that can be used to read from this store in the context of the given session, never + * <code>null</code>. + * @since 2.0 + */ + public IStoreAccessor getReader(ISession session); + + /** + * Returns a writer that can be used to write to this store in the context of the given view. The given view is always + * marked as a transaction. + * + * @param transaction + * The view that must be used as a context for write access. The store implementor is free to interpret and + * use the view in a manner suitable for him or ignore it at all. It is meant only as a hint. Implementor can + * use it as a key into a cache and/or register a + * {@link org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter LifecycleEventAdapter} with it to intercept + * cleanup on view close. + * @return a writer that can be used to write to this store in the context of the given view, never <code>null</code>. + * @since 2.0 + */ + public IStoreAccessor getWriter(ITransaction transaction); + + /** + * @since 2.0 + */ + public ProgressDistributor getIndicatingCommitDistributor(); + + /** + * @author Eike Stepper + * @since 2.0 + */ + public enum ChangeFormat + { + REVISION, DELTA + } + + /** + * @author Eike Stepper + * @since 2.0 + */ + public enum RevisionTemporality + { + NONE, AUDITING + } + + /** + * @author Eike Stepper + * @since 2.0 + */ + public enum RevisionParallelism + { + NONE, BRANCHING + } + + /** + * A marker interface for {@link IStore stores} that can handle {@link CDOID IDs} assigned by a + * {@link IDGenerationLocation#CLIENT client}, typically {@link ObjectType#UUID UUIDs}. + * + * @author Eike Stepper + * @since 4.1 + * @apiviz.exclude + */ + public interface CanHandleClientAssignedIDs + { + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java new file mode 100644 index 0000000000..960a813c42 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java @@ -0,0 +1,729 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lob.CDOBlob; +import org.eclipse.emf.cdo.common.lob.CDOClob; +import org.eclipse.emf.cdo.common.lob.CDOLob; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager.CommitInfoLoader; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + * @apiviz.uses {@link IStoreChunkReader} - - creates + */ +public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, CommitInfoLoader +{ + /** + * Returns the store this accessor is associated with. + */ + public IStore getStore(); + + /** + * Returns the session this accessor is associated with. + * + * @since 3.0 + */ + public InternalSession getSession(); + + /** + * Returns the transaction this accessor is associated with if {@link #isReader()} returns <code>false</code>, + * <code>null</code> otherwise. + * + * @since 2.0 + */ + public ITransaction getTransaction(); + + /** + * Returns <code>true</code> if this accessor has been configured for read-only access to the back-end, + * <code>false</code> otherwise. + * + * @since 2.0 + */ + public boolean isReader(); + + /** + * @since 2.0 + */ + public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature); + + /** + * @since 2.0 + */ + public Collection<InternalCDOPackageUnit> readPackageUnits(); + + /** + * Demand loads a given package proxy that has been created on startup of the repository. + * <p> + * This method must only load the given package, <b>not</b> possible contained packages. + * + * @since 2.0 + */ + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit); + + /** + * Reads a revision from the back-end that was valid at the given timeStamp in the given branch. + * + * @since 4.0 + */ + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache); + + /** + * Reads a revision with the given version in the given branch from the back-end. + * + * @since 4.0 + */ + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache); + + /** + * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following + * conditions are met: + * <ul> + * <li>The <code>eClass</code> parameter is <code>null</code> or equal to <code>revision.getEClass()</code>. + * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>. + * <li><b>One</b> of the following conditions is met: + * <ul> + * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#INVALID_DATE INVALID}. + * <li>The <code>exactTime</code> parameter is <code>true</code> and the <code>timeStamp</code> parameter is + * {@link CDOBranchPoint#UNSPECIFIED_DATE UNSPECIFIED} or equal to <code>revision.getTimeStamp()</code>. + * <li>The <code>exactTime</code> parameter is <code>false</code> and the <code>timeStamp</code> parameter is between + * <code>revision.getTimeStamp()</code> and <code>revision.getRevised()</code>. + * </ul> + * </ul> + * + * @since 4.0 + */ + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler); + + /** + * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges. + * DetachedCDORevisions must also be considered! + * + * @since 4.0 + */ + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments); + + /** + * Returns the <code>CDOID</code> of the resource node with the given folderID and name if a resource with this + * folderID and name exists in the store, <code>null</code> otherwise. + * + * @since 3.0 + */ + public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint); + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context); + + /** + * @since 3.0 + */ + public void queryXRefs(QueryXRefsContext context); + + /** + * Determines which of the large objects identified by the given {@link CDOLob#getID() IDs} are known in the backend + * represented by this {@link IStoreAccessor} by removing the unknown IDs from the passed collection. + * <p> + * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object. + * <p> + * <b>Usage context:</b> This method is only called in the context of a commit operation of a client transaction if + * that transaction contains additions of or changes to large objects. + * + * @param ids + * the collection of large object IDs that the unknown IDs are supposed to be removed from. + * @since 4.0 + */ + public void queryLobs(List<byte[]> ids); + + /** + * Serializes the content of the large object identified by the given {@link CDOLob#getID() ID} to the given + * <i>stream</i>. + * <p> + * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object. + * + * @param id + * the ID of the large object whose content is to be written to the <i>stream</i>. + * @throws IOException + * if the <i>stream</i> could not be written to. + * @since 4.0 + */ + public void loadLob(byte[] id, OutputStream out) throws IOException; + + /** + * @since 4.0 + */ + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException; + + /** + * @since 2.0 + */ + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor); + + /** + * Called before committing. An instance of this accessor represents an instance of a back-end transaction. Could be + * called multiple times before commit it called. {@link IStoreAccessor#commit(OMMonitor)} or + * {@link IStoreAccessor#rollback()} will be called after any numbers of + * {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)}. + * <p> + * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and + * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads. + * + * @since 3.0 + */ + public void write(InternalCommitContext context, OMMonitor monitor); + + /** + * Flushes to the back-end and makes available the data for others. + * <p> + * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and + * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads. + * <p> + * <b>Note</b>: Implementors should detect if dirty write occurred. In this case it should throw an exception. + * + * <pre> + * if (revision.getVersion() != revisionDelta.getOriginVersion()) + * { + * throw new ConcurrentModificationException("Trying to update object " + revisionDelta.getID() + * + " that was already modified"); + * } + * </pre> + * + * @since 2.0 + */ + public void commit(OMMonitor monitor); + + /** + * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and {@link IStoreAccessor#rollback()} + * could be called from different threads. + * + * @since 2.0 + */ + public void rollback(); + + public void release(); + + /** + * Represents the state of a single, logical commit operation which is driven through multiple calls to several + * methods on the {@link IStoreAccessor} API. All these method calls get the same <code>CommitContext</code> instance + * passed so that the implementor of the {@link IStoreAccessor} can track the state and progress of the commit + * operation. + * + * @author Eike Stepper + * @since 2.0 + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.exclude + */ + public interface CommitContext extends CDORevisionProvider + { + /** + * Returns the transactional view (<code>ITransaction</code>) which is the scope of the commit operation represented + * by this <code>CommitContext</code>. + * + * @since 4.0 + */ + public ITransaction getTransaction(); + + /** + * Returns the branch ID and timestamp of this commit operation. + * + * @since 3.0 + */ + public CDOBranchPoint getBranchPoint(); + + /** + * @since 4.0 + */ + public long getPreviousTimeStamp(); + + /** + * @since 3.0 + */ + public String getUserID(); + + /** + * @since 3.0 + */ + public String getCommitComment(); + + /** + * @since 3.0 + */ + public boolean isAutoReleaseLocksEnabled(); + + /** + * Returns the temporary, transactional package manager associated with the commit operation represented by this + * <code>CommitContext</code>. In addition to the packages registered with the session this package manager also + * contains the new packages that are part of this commit operation. + */ + public InternalCDOPackageRegistry getPackageRegistry(); + + /** + * Returns an array of the new package units that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDOPackageUnit[] getNewPackageUnits(); + + /** + * Returns an array of the new objects that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDORevision[] getNewObjects(); + + /** + * Returns an array of the dirty objects that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDORevision[] getDirtyObjects(); + + /** + * Returns an array of the dirty object deltas that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDORevisionDelta[] getDirtyObjectDeltas(); + + /** + * Returns an array of the removed object that are part of the commit operation represented by this + * <code>CommitContext</code>. + * + * @since 2.0 + */ + public CDOID[] getDetachedObjects(); + + /** + * Returns a map with an {@link EClass} value per {@link CDOID} type. + * + * @since 4.0 + */ + public Map<CDOID, EClass> getDetachedObjectTypes(); + + /** + * Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the + * stream is: + * <p> + * <ol> + * <li> {@link ExtendedDataInputStream#readInt() int}: the number of lobs to be read from the stream. + * <li>The following data can be read from the stream in a loop with one iteration per lob in the stream: + * <ol> + * <li> {@link ExtendedDataInputStream#readByteArray() int + byte[]}: the id of the lob (prepended by the size of the + * id). + * <li> {@link ExtendedDataInputStream#readLong() long}: the size of the lob. The foollowing interpretation applies: + * <ul> + * <li>A positive size indicates a {@link CDOBlob blob} and means the number of bytes that can be + * {@link IOUtil#copyBinary(java.io.InputStream, java.io.OutputStream) read}. + * <li>A negative size indicates a {@link CDOClob clob} and means the number of characters that can be + * {@link IOUtil#copyCharacter(java.io.Reader, java.io.Writer) read}. + * </ul> + * </ol> + * </ol> + * + * @since 4.0 + */ + public ExtendedDataInputStream getLobs(); + + /** + * Returns an unmodifiable map from all temporary IDs to their persistent counter parts. + */ + public Map<CDOID, CDOID> getIDMappings(); + + /** + * @since 4.0 + */ + public CDOCommitInfo createCommitInfo(); + + /** + * @since 3.0 + */ + public String getRollbackMessage(); + + /** + * @since 4.0 + */ + public List<CDOIDReference> getXRefs(); + + /** + * @since 4.1 + */ + public List<LockState<Object, IView>> getPostCommmitLockStates(); + } + + /** + * @author Eike Stepper + * @since 2.0 + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.exclude + */ + public interface QueryResourcesContext extends CDOBranchPoint + { + public CDOID getFolderID(); + + public String getName(); + + public boolean exactMatch(); + + /** + * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no + * limitation. + */ + public int getMaxResults(); + + /** + * Adds the CDOID of one resource to the results of the underlying query. + * + * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResource(CDOID resourceID); + + /** + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ + public interface ExactMatch extends QueryResourcesContext + { + public CDOID getResourceID(); + } + } + + /** + * @author Eike Stepper + * @since 3.0 + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.exclude + */ + public interface QueryXRefsContext extends CDOBranchPoint + { + /** + * @since 4.0 + */ + public Map<CDOID, EClass> getTargetObjects(); + + public EReference[] getSourceReferences(); + + /** + * @since 4.0 + */ + public Map<EClass, List<EReference>> getSourceCandidates(); + + /** + * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no + * limitation. + */ + public int getMaxResults(); + + /** + * Adds the data of one cross reference to the results of the underlying query. + * + * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex); + } + + /** + * @author Eike Stepper + * @since 4.0 + * @apiviz.exclude + */ + public interface Raw extends IStoreAccessor + { + /** + * Serializes all backend data within the given ranges such that it can be deserialized by the + * {@link #rawImport(CDODataInput, int, int, long, long, OMMonitor) rawImport()} method of a different instance of + * the same implementation of {@link IStoreAccessor.Raw raw store accessor}. + * <p> + * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only + * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store + * accessor}. + * <p> + * <b>Usage context:</b> This method is only called in the context of a + * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered + * from {@link IRepositorySynchronizer}. + * + * @param out + * the <i>stream</i> to serialize the data to. + * @param fromBranchID + * the {@link CDOBranch#getID() ID} of the first branch to be exported. + * @param toBranchID + * the {@link CDOBranch#getID() ID} of the last branch to be exported. + * @param fromCommitTime + * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be exported. + * @param toCommitTime + * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be exported. + * @throws IOException + * if the <i>stream</i> could not be written to. + * @throws UnsupportedOperationException + * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching. + */ + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException; + + /** + * Deserializes backend data that has been serialized by the {@link #rawExport(CDODataOutput, int, int, long, long) + * rawExport()} method of a different instance of the same implementation of {@link IStoreAccessor.Raw raw store + * accessor}. + * <p> + * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only + * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store + * accessor}. + * <p> + * <b>Usage context:</b> This method is only called in the context of a + * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered + * from {@link IRepositorySynchronizer}. + * + * @param in + * the <i>stream</i> to deserialize the data from. + * @param fromBranchID + * the {@link CDOBranch#getID() ID} of the first branch to be imported. + * @param toBranchID + * the {@link CDOBranch#getID() ID} of the last branch to be imported. + * @param fromCommitTime + * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be imported. + * @param toCommitTime + * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be imported. + * @throws IOException + * if the <i>stream</i> could not be read from. + * @throws UnsupportedOperationException + * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching. + */ + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException; + + /** + * Stores the given {@link CDOPackageUnit package units} in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor} without going through a regular + * {@link IStoreAccessor #commit(OMMonitor) commit}. A regular commit operation would assign new + * {@link CDOPackageUnit#getTimeStamp() time stamps}, which is not desired in the context of a replication + * operation. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param packageUnits + * the package units to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor}. + * @param monitor + * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and + * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly + * to prevent timeouts from expiring in the caller. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor); + + /** + * Stores the given {@link CDORevision revision} in the backend represented by this {@link IStoreAccessor.Raw raw + * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. A regular commit + * operation would assign new {@link CDORevisionKey#getID() IDs} and {@link CDOBranchPoint#getTimeStamp() time + * stamps}, which is not desired in the context of a replication operation. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param revision + * the revision to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor}. + * @param monitor + * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and + * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly + * to prevent timeouts from expiring in the caller. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(InternalCDORevision revision, OMMonitor monitor); + + /** + * Stores the given {@link CDOBlob blob} in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param id + * the {@link CDOBlob#getID() ID} of the blob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param size + * the {@link CDOBlob#getSize() size} of the blob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param inputStream + * the {@link CDOBlob#getContents() contents} of the blob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException; + + /** + * Stores the given {@link CDOClob clob} in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param id + * the {@link CDOClob#getID() ID} of the clob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param size + * the {@link CDOClob#getSize() size} of the clob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param reader + * the {@link CDOClob#getContents() contents} of the clob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(byte[] id, long size, Reader reader) throws IOException; + + /** + * Stores the given {@link CDOCommitInfo commit} in the backend represented by this {@link IStoreAccessor.Raw raw + * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param branch + * the {@link CDOCommitInfo#getBranch() branch} of the commit info to be stored in the backend represented + * by this {@link IStoreAccessor.Raw raw store accessor}. + * @param timeStamp + * the {@link CDOCommitInfo#getTimeStamp() time stamp} of the commit info to be stored in the backend + * represented by this {@link IStoreAccessor.Raw raw store accessor}. + * @param previousTimeStamp + * the {@link CDOCommitInfo#getPreviousTimeStamp() previous time stamp} of the commit info to be stored in + * the backend represented by this {@link IStoreAccessor.Raw raw store accessor}. + * @param userID + * the {@link CDOCommitInfo#getUserID() user ID} of the commit info to be stored in the backend represented + * by this {@link IStoreAccessor.Raw raw store accessor}. + * @param comment + * the {@link CDOCommitInfo#getComment() comment} of the commit info to be stored in the backend + * represented by this {@link IStoreAccessor.Raw raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor); + + /** + * Deletes the revision identified by the given {@link CDORevisionKey key} from the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor} without going through a regular + * {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @see #rawCommit(double, OMMonitor) + * @deprecated Not used anymore + */ + @Deprecated + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor); + + /** + * Atomically commits the accumulated backend changes resulting from previous calls to the rawStore() methods. + * + * @param commitWork + * the amount of work to use up from the monitor while executing the commit. + * @param monitor + * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and + * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly + * to prevent timeouts from expiring in the caller. + * @see #rawStore(InternalCDOPackageUnit[], OMMonitor) + * @see #rawStore(InternalCDORevision, OMMonitor) + * @see #rawStore(byte[], long, InputStream) + * @see #rawStore(byte[], long, Reader) + * @see #rawStore(CDOBranch, long, long, String, String, OMMonitor) + */ + public void rawCommit(double commitWork, OMMonitor monitor); + } + + /** + * @author Eike Stepper + * @since 4.0 + * @apiviz.exclude + */ + public interface DurableLocking extends IDurableLockingManager + { + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock); + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock); + + public void unlock(String durableLockingID); + } + + /** + * @author Caspar De Groot + * @since 4.1 + * @apiviz.exclude + */ + public interface DurableLocking2 extends DurableLocking + { + LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks); + + public void updateLockArea(LockArea lockArea); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java new file mode 100644 index 0000000000..a27d25fd05 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 210868 + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.revision.CDORevision; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.List; + +/** + * @author Eike Stepper + * @apiviz.uses {@link IStoreChunkReader.Chunk} - - reads + */ +public interface IStoreChunkReader +{ + /** + * @since 2.0 + */ + public IStoreAccessor getAccessor(); + + public CDORevision getRevision(); + + /** + * @since 2.0 + */ + public EStructuralFeature getFeature(); + + public void addSimpleChunk(int index); + + /** + * @param fromIndex + * Inclusive value. + * @param toIndex + * Exclusive value. + */ + public void addRangedChunk(int fromIndex, int toIndex); + + public List<Chunk> executeRead(); + + /** + * @author Eike Stepper + */ + public static class Chunk + { + private int startIndex; + + private Object ids; + + public Chunk(int startIndex) + { + this.startIndex = startIndex; + } + + public Chunk(int startIndex, int size) + { + this(startIndex); + ids = new Object[size]; + } + + public int getStartIndex() + { + return startIndex; + } + + public int size() + { + return ids instanceof Object[] ? ((Object[])ids).length : 1; + } + + /** + * @since 2.0 + */ + public Object get(int indexInChunk) + { + if (ids instanceof Object[]) + { + return ((Object[])ids)[indexInChunk]; + } + + if (indexInChunk == 0) + { + return ids; + } + + throw new ArrayIndexOutOfBoundsException(indexInChunk); + } + + /** + * @since 2.0 + */ + public void add(int indexInChunk, Object value) + { + if (ids instanceof Object[]) + { + ((Object[])ids)[indexInChunk] = value; + } + else + { + if (indexInChunk == 0) + { + ids = value; + return; + } + + throw new ArrayIndexOutOfBoundsException(indexInChunk); + } + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java new file mode 100644 index 0000000000..daaa452533 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.w3c.dom.Element; + +import java.util.Map; + +/** + * @author Eike Stepper + * @apiviz.uses {@link IStore} - - creates + */ +public interface IStoreFactory +{ + public String getStoreType(); + + /** + * @since 4.0 + */ + public IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java new file mode 100644 index 0000000000..c692607c54 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +/** + * A repository with the ability to {@link IRepositorySynchronizer synchronize} its content with another repository. + * + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + * @apiviz.has {@link IRepositorySynchronizer} + * @apiviz.has {@link ISession} oneway - - replicatorSession + */ +public interface ISynchronizableRepository extends IRepository +{ + public IRepositorySynchronizer getSynchronizer(); + + public ISession getReplicatorSession(); + + public int getLastReplicatedBranchID(); + + public long getLastReplicatedCommitTime(); + + /** + * @since 4.1 + */ + public void goOnline(); + + /** + * @since 4.1 + */ + public void goOffline(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java new file mode 100644 index 0000000000..021d214593 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.CDOCommonTransaction; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + */ +public interface ITransaction extends IView, CDOCommonTransaction +{ +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java new file mode 100644 index 0000000000..dc5eea35cd --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.CDOCommonView; + +/** + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.landmark + */ +public interface IView extends CDOCommonView +{ + /** + * @since 2.0 + */ + public IRepository getRepository(); + + public ISession getSession(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java new file mode 100644 index 0000000000..bce4b76255 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.util.CDOException; + +/** + * @author Eike Stepper + */ +public class RepositoryNotFoundException extends CDOException +{ + private static final long serialVersionUID = 1L; + + public RepositoryNotFoundException(String repositoryName) + { + super(repositoryName); + } + + public String getRepositoryName() + { + return super.getMessage(); + } + + @Override + public String getMessage() + { + return "Repository not found: " + getRepositoryName(); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java new file mode 100644 index 0000000000..dd0ac05814 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +/** + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ +public final class StoreThreadLocal +{ + private static final ThreadLocal<InternalSession> SESSION = new InheritableThreadLocal<InternalSession>(); + + private static final ThreadLocal<IStoreAccessor> ACCESSOR = new InheritableThreadLocal<IStoreAccessor>(); + + private static final ThreadLocal<IStoreAccessor.CommitContext> COMMIT_CONTEXT = new InheritableThreadLocal<IStoreAccessor.CommitContext>(); + + private StoreThreadLocal() + { + } + + /** + * @since 3.0 + */ + public static void setSession(InternalSession session) + { + SESSION.set(session); + ACCESSOR.set(null); + } + + /** + * Returns the session associated with the current thread. + * + * @return Never <code>null</code>. + * @throws IllegalStateException + * if no session is associated with the current thread. + * @since 3.0 + */ + public static InternalSession getSession() + { + InternalSession session = SESSION.get(); + if (session == null) + { + throw new IllegalStateException("session == null"); //$NON-NLS-1$ + } + + return session; + } + + public static void setAccessor(IStoreAccessor accessor) + { + // IStoreAccessor old = ACCESSOR.get(); + // if (old != null && old != accessor) + // { + // throw new IllegalStateException("Attempt to overwrite accessor"); + // } + + SESSION.set(accessor == null ? null : accessor.getSession()); + ACCESSOR.set(accessor); + } + + public static IStoreAccessor getAccessor() + { + IStoreAccessor accessor = ACCESSOR.get(); + if (accessor == null) + { + ISession session = getSession(); + IStore store = session.getManager().getRepository().getStore(); + accessor = store.getReader(session); + ACCESSOR.set(accessor); + } + + return accessor; + } + + public static void setCommitContext(IStoreAccessor.CommitContext commitContext) + { + COMMIT_CONTEXT.set(commitContext); + } + + public static IStoreAccessor.CommitContext getCommitContext() + { + return COMMIT_CONTEXT.get(); + } + + public static void release() + { + try + { + IStoreAccessor accessor = ACCESSOR.get(); + if (accessor != null) + { + if (LifecycleUtil.isActive(accessor)) + { + accessor.release(); + } + } + } + finally + { + ACCESSOR.set(null); + SESSION.set(null); + COMMIT_CONTEXT.set(null); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java new file mode 100644 index 0000000000..9d68ad8233 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.embedded; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface CDOSession extends org.eclipse.emf.cdo.session.CDOSession +{ +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java new file mode 100644 index 0000000000..4481992c2c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.embedded; + +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.server.IRepository; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface CDOSessionConfiguration extends org.eclipse.emf.cdo.session.CDOSessionConfiguration +{ + public IRepository getRepository(); + + public void setRepository(IRepository repository); + + public CDORevisionManager getRevisionManager(); + + public void setRevisionManager(CDORevisionManager revisionManager); + + public org.eclipse.emf.cdo.server.embedded.CDOSession openSession(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/package-info.java new file mode 100644 index 0000000000..e4b6b8e8e9 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/package-info.java @@ -0,0 +1,15 @@ +/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with embedded sessions.
+ */
+package org.eclipse.emf.cdo.server.embedded;
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java new file mode 100644 index 0000000000..ef73bbdbd8 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.mem; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOAllRevisionsProvider; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.emf.ecore.EClass; + +/** + * A simple in-memory store. + * + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @since 4.0 + */ +public interface IMEMStore extends IStore, CDOAllRevisionsProvider, CanHandleClientAssignedIDs +{ + public static final int UNLIMITED = -1; + + /** + * Returns the number of {@link CDORevision revisions} per {@link CDOID} that are stored. + */ + public int getListLimit(); + + /** + * Limits the number of {@link CDORevision revisions} per {@link CDOID} to the given value. + * <p> + * A value of 2, for example, stores the current and the immediately preceding revisions whereas older revisions are + * dropped from thids store. A value of 1 only stores the current revisions. A value of {@link #UNLIMITED} does not + * limit the number of revisions to be stored for any id. + * <p> + * The list limit can be set and enforced at any time before or after the {@link LifecycleUtil#activate(Object) + * activation} of this store. + */ + public void setListLimit(int listLimit); + + /** + * @since 3.0 + */ + public EClass getObjectType(CDOID id); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java new file mode 100644 index 0000000000..358a29dba0 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.mem; + +import org.eclipse.emf.cdo.internal.server.mem.MEMStore; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public final class MEMStoreUtil +{ + private MEMStoreUtil() + { + } + + /** + * @since 4.0 + */ + public static IMEMStore createMEMStore() + { + return new MEMStore(); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java new file mode 100644 index 0000000000..d6b75391c6 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java @@ -0,0 +1,15 @@ +/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with in-memory stores.
+ */
+package org.eclipse.emf.cdo.server.mem;
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java new file mode 100644 index 0000000000..af751e4894 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java @@ -0,0 +1,23 @@ +/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with repositories and stores.
+ *
+ * @apiviz.exclude .*\.CDOServerBrowser.*
+ * @apiviz.exclude .*\.CommitInfoLoader
+ * @apiviz.exclude .*\.BranchLoader
+ * @apiviz.exclude .*\.IContainer
+ * @apiviz.exclude .*\.INotifier
+ * @apiviz.exclude .*Exception
+ */
+package org.eclipse.emf.cdo.server;
+
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java new file mode 100644 index 0000000000..3d44fe9600 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IQueryHandlerProvider; + +import org.eclipse.net4j.util.container.IManagedContainer; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class ContainerQueryHandlerProvider implements IQueryHandlerProvider +{ + private IManagedContainer container; + + public ContainerQueryHandlerProvider(IManagedContainer container) + { + this.container = container; + } + + public IManagedContainer getContainer() + { + return container; + } + + /** + * @since 3.0 + */ + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + return (IQueryHandler)container.getElement(QueryHandlerFactory.PRODUCT_GROUP, info.getQueryLanguage(), null); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java new file mode 100644 index 0000000000..5323ff826d --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IRepositoryProvider; + +import org.eclipse.net4j.util.container.IManagedContainer; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class ContainerRepositoryProvider implements IRepositoryProvider +{ + private IManagedContainer container; + + public ContainerRepositoryProvider(IManagedContainer container) + { + this.container = container; + } + + public IManagedContainer getContainer() + { + return container; + } + + public IRepository getRepository(String name) + { + try + { + return RepositoryFactory.get(container, name); + } + catch (Exception ex) + { + OM.LOG.error(ex); + return null; + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java new file mode 100644 index 0000000000..40c3826844 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java @@ -0,0 +1,108 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @deprecated Use {@link CDOLockUtil#createLockArea(String, String, CDOBranchPoint, boolean, Map)} instead
+ */
+@Deprecated
+public class DurableLockArea implements LockArea
+{
+ public static final int DEFAULT_DURABLE_LOCKING_ID_BYTES = 32;
+
+ private String durableLockingID;
+
+ private String userID;
+
+ private CDOBranchPoint branchPoint;
+
+ private boolean readOnly;
+
+ private Map<CDOID, LockGrade> locks;
+
+ public DurableLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ this.durableLockingID = durableLockingID;
+ this.userID = userID;
+ this.branchPoint = branchPoint;
+ this.readOnly = readOnly;
+ this.locks = locks;
+ }
+
+ public String getDurableLockingID()
+ {
+ return durableLockingID;
+ }
+
+ public String getUserID()
+ {
+ return userID;
+ }
+
+ public CDOBranch getBranch()
+ {
+ return branchPoint.getBranch();
+ }
+
+ public long getTimeStamp()
+ {
+ return branchPoint.getTimeStamp();
+ }
+
+ public boolean isReadOnly()
+ {
+ return readOnly;
+ }
+
+ public Map<CDOID, LockGrade> getLocks()
+ {
+ return locks;
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("DurableLockArea\nid={0}\nuser={1}\nbranchPoint={2}\nreadOnly={3}\nlocks={4}",
+ durableLockingID, userID, branchPoint, readOnly, locks);
+ }
+
+ public static String createDurableLockingID()
+ {
+ return CDOLockUtil.createDurableLockingID();
+ }
+
+ public static String createDurableLockingID(int bytes)
+ {
+ return CDOLockUtil.createDurableLockingID(bytes);
+ }
+
+ /**
+ * @since 4.1
+ */
+ public boolean isMissing()
+ {
+ return false;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java new file mode 100644 index 0000000000..19f7a2b41b --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IQueryHandlerProvider; + +import org.eclipse.net4j.util.factory.IFactory; +import org.eclipse.net4j.util.registry.HashMapRegistry; +import org.eclipse.net4j.util.registry.IRegistry; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class FactoriesQueryHandlerProvider implements IQueryHandlerProvider +{ + private IRegistry<String, IFactory> registry; + + public FactoriesQueryHandlerProvider() + { + } + + public FactoriesQueryHandlerProvider(IRegistry<String, IFactory> registry) + { + setRegistry(registry); + } + + public FactoriesQueryHandlerProvider(IFactory factory) + { + addFactory(factory); + } + + public IRegistry<String, IFactory> getRegistry() + { + if (registry == null) + { + registry = new HashMapRegistry<String, IFactory>(); + } + + return registry; + } + + public void setRegistry(IRegistry<String, IFactory> registry) + { + this.registry = registry; + } + + public void addFactory(IFactory factory) + { + getRegistry().put(factory.getKey().getType(), factory); + } + + /** + * @since 3.0 + */ + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + IFactory factory = registry.get(info.getQueryLanguage()); + if (factory != null) + { + return (IQueryHandler)factory.create(null); + } + + return null; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java new file mode 100644 index 0000000000..b828bd1692 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import java.io.File; + +/** + * @author Eike Stepper + * @since 3.0 + */ +public interface IAppExtension +{ + public static final String EXT_POINT = "appExtensions"; //$NON-NLS-1$ + + public void start(File configFile) throws Exception; + + public void stop() throws Exception; +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java new file mode 100644 index 0000000000..1562ae0689 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface ISessionProtocol extends CDOProtocol +{ + /** + * @since 4.0 + */ + public CDOAuthenticationResult sendAuthenticationChallenge(byte[] randomToken) throws Exception; + + public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType) + throws Exception; + + /** + * @deprecated + */ + @Deprecated + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState) + throws Exception; + + /** + * @since 4.1 + */ + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState, + CDOID rootResourceID) throws Exception; + + public void sendBranchNotification(InternalCDOBranch branch) throws Exception; + + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception; + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception; + + public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception; + + /** + * @since 4.1 + */ + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception; +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java new file mode 100644 index 0000000000..135b6b4d47 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.ProgressDistributable; +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import org.eclipse.emf.ecore.EClass; + +import java.util.Map; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalCommitContext extends IStoreAccessor.CommitContext +{ + @SuppressWarnings("unchecked") + public static final ProgressDistributable<InternalCommitContext>[] OPS = ProgressDistributor.array( // + new ProgressDistributable.Default<InternalCommitContext>() + { + public void runLoop(int index, InternalCommitContext commitContext, OMMonitor monitor) throws Exception + { + commitContext.write(monitor.fork()); + } + }, // + + new ProgressDistributable.Default<InternalCommitContext>() + { + public void runLoop(int index, InternalCommitContext commitContext, OMMonitor monitor) throws Exception + { + if (commitContext.getRollbackMessage() == null) + { + commitContext.commit(monitor.fork()); + } + else + { + monitor.worked(); + } + } + }); + + public InternalTransaction getTransaction(); + + public void preWrite(); + + public void write(OMMonitor monitor); + + public void commit(OMMonitor monitor); + + public void rollback(String message); + + public void postCommit(boolean success); + + /** + * @since 4.0 + */ + public InternalCDORevision[] getDetachedRevisions(); + + public void setNewPackageUnits(InternalCDOPackageUnit[] newPackageUnits); + + public void setNewObjects(InternalCDORevision[] newObjects); + + public void setDirtyObjectDeltas(InternalCDORevisionDelta[] dirtyObjectDeltas); + + public void setDetachedObjects(CDOID[] detachedObjects); + + /** + * @since 4.0 + */ + public void setDetachedObjectTypes(Map<CDOID, EClass> detachedObjectTypes); + + public void setAutoReleaseLocksEnabled(boolean on); + + public void setCommitComment(String comment); + + /** + * @since 4.0 + */ + public void setLobs(ExtendedDataInputStream in); + + public void addIDMapping(CDOID oldID, CDOID newID); + + public void applyIDMappings(OMMonitor monitor); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java new file mode 100644 index 0000000000..6ebefcf239 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.concurrent.ExecutionException; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalCommitManager +{ + public InternalRepository getRepository(); + + public void setRepository(InternalRepository repository); + + /** + * Create a future to execute commitContext in a different thread. + */ + public void preCommit(InternalCommitContext commitContext, OMMonitor monitor); + + /** + * Called after a commitContext is done successfully or not. + */ + public void remove(InternalCommitContext commitContext); + + public void rollback(InternalCommitContext commitContext); + + /** + * Waiting for a commit to be done. + */ + public void waitForTermination(InternalTransaction transaction) throws InterruptedException, ExecutionException; + + public InternalCommitContext get(InternalTransaction transaction); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java new file mode 100644 index 0000000000..71a476bfac --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +/** + * @author Eike Stepper + * @since 4.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalFailoverParticipant extends InternalSynchronizableRepository +{ + public boolean isAllowBackupCommits(); + + public void setAllowBackupCommits(boolean allowBackupCommits); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java new file mode 100644 index 0000000000..ab8d9a2c37 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.concurrent.IRWOLockManager; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * The type of the to-be-locked objects is either {@link CDOIDAndBranch} or {@link CDOID}, depending on whether + * branching is supported by the repository or not. + * + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalLockManager extends IRWOLockManager<Object, IView>, IDurableLockingManager +{ + public InternalRepository getRepository(); + + public void setRepository(InternalRepository repository); + + /** + * @since 4.0 + */ + public Object getLockEntryObject(Object key); + + /** + * @since 4.0 + */ + public Object getLockKey(CDOID id, CDOBranch branch); + + /** + * @since 4.0 + */ + public CDOID getLockKeyID(Object key); + + /** + * @since 4.0 + */ + public Map<CDOID, LockGrade> getLocks(IView view); + + /** + * @since 4.0 + */ + @Deprecated + public void lock(boolean explicit, LockType type, IView context, Collection<? extends Object> objectsToLock, + long timeout) throws InterruptedException; + + /** + * @since 4.1 + */ + public List<LockState<Object, IView>> lock2(boolean explicit, LockType type, IView context, + Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException; + + /** + * Attempts to release for a given locktype, view and objects. + * + * @throws IllegalMonitorStateException + * Unlocking objects without lock. + * @since 4.0 + */ + @Deprecated + public void unlock(boolean explicit, LockType type, IView context, Collection<? extends Object> objectsToUnlock); + + /** + * @since 4.1 + */ + public List<LockState<Object, IView>> unlock2(boolean explicit, LockType type, IView context, + Collection<? extends Object> objectsToUnlock); + + /** + * Attempts to release all locks(read and write) for a given view. + * + * @since 4.0 + */ + @Deprecated + public void unlock(boolean explicit, IView context); + + /** + * @since 4.1 + */ + public List<LockState<Object, IView>> unlock2(boolean explicit, IView context); + + /** + * @since 4.0 + */ + public LockArea createLockArea(InternalView view); + + /** + * @since 4.1 + */ + public LockArea createLockArea(InternalView view, String lockAreaID); + + /** + * @since 4.1 + */ + // TODO (CD) I've also added this to DurableLocking2 Refactoring opportunity? + public void updateLockArea(LockArea lockArea); + + /** + * @since 4.0 + */ + public IView openView(ISession session, int viewID, boolean readOnly, String durableLockingID); + + /** + * @since 4.1 + */ + public LockGrade getLockGrade(Object key); + + /** + * @since 4.1 + */ + public LockState<Object, IView> getLockState(Object key); + + /** + * @since 4.1 + */ + public void setLockState(Object key, LockState<Object, IView> lockState); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java new file mode 100644 index 0000000000..633026a217 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalQueryManager +{ + public InternalRepository getRepository(); + + public void setRepository(InternalRepository repository); + + public InternalQueryResult execute(InternalView view, CDOQueryInfo queryInfo); + + public boolean isRunning(int queryID); + + public void cancel(int queryID); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java new file mode 100644 index 0000000000..6c9479cc2e --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.util.BlockingCloseableIterator; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.common.util.CDOQueryQueue; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalQueryResult extends BlockingCloseableIterator<Object> +{ + public int getQueryID(); + + public CDOQueryInfo getQueryInfo(); + + public InternalView getView(); + + public CDOQueryQueue<Object> getQueue(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java new file mode 100644 index 0000000000..eb8c0b50ce --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.server.IQueryHandlerProvider; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.CDOReplicationContext; +import org.eclipse.emf.cdo.spi.common.CDOReplicationInfo; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader; +import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager.CommitInfoLoader; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageLoader; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageProcessor; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager.RevisionLoader; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Semaphore; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalRepository extends IRepository, PackageProcessor, PackageLoader, BranchLoader, RevisionLoader, + CommitInfoLoader +{ + public void setName(String name); + + public void setType(Type type); + + public void setState(State state); + + public InternalStore getStore(); + + public void setStore(InternalStore store); + + public void setProperties(Map<String, String> properties); + + public InternalCDOBranchManager getBranchManager(); + + public void setBranchManager(InternalCDOBranchManager branchManager); + + /** + * @since 4.1 + */ + public Semaphore getPackageRegistryCommitLock(); + + /** + * Same as calling {@link #getPackageRegistry(boolean) getPackageRegistry(true)}. + */ + public InternalCDOPackageRegistry getPackageRegistry(); + + public InternalCDOPackageRegistry getPackageRegistry(boolean considerCommitContext); + + public InternalCDORevisionManager getRevisionManager(); + + public void setRevisionManager(InternalCDORevisionManager revisionManager); + + public InternalCDOCommitInfoManager getCommitInfoManager(); + + public InternalSessionManager getSessionManager(); + + public void setSessionManager(InternalSessionManager sessionManager); + + public InternalLockManager getLockManager(); + + public InternalQueryManager getQueryManager(); + + public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider); + + public InternalCommitManager getCommitManager(); + + public InternalCommitContext createCommitContext(InternalTransaction transaction); + + /** + * Returns a commit time stamp that is guaranteed to be unique in this repository. At index 1 of the returned + * <code>long</code> array is the previous commit time. + * + * @since 4.0 + */ + public long[] createCommitTimeStamp(OMMonitor monitor); + + /** + * Like {@link #createCommitTimeStamp(OMMonitor)}, but forces the repository to use the timestamp value passed in as + * the argument. This should be called only to force the timestamp of the first commit of a new repository to be equal + * to its creation time. + * + * @since 4.0 + */ + public long[] forceCommitTimeStamp(long timestamp, OMMonitor monitor); + + /** + * Notifies the repository of the completion of a commit. The value passed in must be a value obtained earlier through + * {@link #createCommitTimeStamp(OMMonitor)} + * + * @since 4.0 + */ + public void endCommit(long timeStamp); + + /** + * Notifies the repository of the failure of a commit. The value passed in must be a value obtained earlier through + * {@link #createCommitTimeStamp(OMMonitor)} + * + * @since 4.0 + */ + public void failCommit(long timeStamp); + + /** + * @since 4.0 + */ + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + + public void setRootResourceID(CDOID rootResourceID); + + /** + * @since 4.0 + */ + public void setLastCommitTimeStamp(long commitTimeStamp); + + public IStoreAccessor ensureChunk(InternalCDORevision revision, EStructuralFeature feature, int chunkStart, + int chunkEnd); + + public void notifyReadAccessHandlers(InternalSession session, CDORevision[] revisions, + List<CDORevision> additionalRevisions); + + public void notifyWriteAccessHandlers(ITransaction transaction, IStoreAccessor.CommitContext commitContext, + boolean beforeCommit, OMMonitor monitor); + + public void replicate(CDOReplicationContext context); + + public CDOReplicationInfo replicateRaw(CDODataOutput out, int lastReplicatedBranchID, long lastReplicatedCommitTime) + throws IOException; + + public CDOChangeSetData getChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint); + + /** + * @since 4.0 + */ + public Set<CDOID> getMergeData(CDORevisionAvailabilityInfo targetInfo, CDORevisionAvailabilityInfo sourceInfo, + CDORevisionAvailabilityInfo targetBaseInfo, CDORevisionAvailabilityInfo sourceBaseInfo, OMMonitor monitor); + + /** + * @since 4.0 + */ + public void queryLobs(List<byte[]> ids); + + /** + * @since 4.0 + */ + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException; + + /** + * @since 4.0 + */ + public void loadLob(byte[] id, OutputStream out) throws IOException; + + /** + * @since 4.0 + */ + public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime, + CDORevisionHandler handler); + + /** + * @since 4.0 + */ + public boolean isSkipInitialization(); + + /** + * @since 4.0 + */ + public void setSkipInitialization(boolean skipInitialization); + + /** + * @since 4.0 + */ + public void initSystemPackages(); + + /** + * @since 4.0 + */ + public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp); + + /** + * @since 4.1 + */ + public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, long timeout); + + /** + * @since 4.1 + */ + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java new file mode 100644 index 0000000000..20151f3e8e --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.server.IRepositorySynchronizer; +import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; + +import org.eclipse.net4j.util.lifecycle.ILifecycle; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalRepositorySynchronizer extends IRepositorySynchronizer, ILifecycle +{ + public InternalSynchronizableRepository getLocalRepository(); + + public void setLocalRepository(InternalSynchronizableRepository localRepository); + + public void setRemoteSessionConfigurationFactory(CDOSessionConfigurationFactory remoteSessionConfigurationFactory); + + public InternalCDOSession getRemoteSession(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java new file mode 100644 index 0000000000..9195c68c5c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.CDOCommonSession; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import java.util.List; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalSession extends ISession, CDOIDProvider, CDOCommonSession.Options +{ + public static final int TEMP_VIEW_ID = 0; + + public InternalSessionManager getManager(); + + public InternalView[] getViews(); + + public InternalView getView(int viewID); + + public InternalView openView(int viewID, CDOBranchPoint branchPoint); + + public InternalTransaction openTransaction(int viewID, CDOBranchPoint branchPoint); + + public void viewClosed(InternalView view); + + public void setSubscribed(boolean subscribed); + + public void collectContainedRevisions(InternalCDORevision revision, CDOBranchPoint branchPoint, int referenceChunk, + Set<CDOID> revisions, List<CDORevision> additionalRevisions); + + public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType) + throws Exception; + + /** + * @deprecated use + * {@link #sendRepositoryStateNotification(org.eclipse.emf.cdo.common.CDOCommonRepository.State, org.eclipse.emf.cdo.common.CDOCommonRepository.State, CDOID)} + * instead + */ + @Deprecated + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState) + throws Exception; + + /** + * @since 4.1 + */ + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState, + CDOID rootResourceID) throws Exception; + + public void sendBranchNotification(InternalCDOBranch branch) throws Exception; + + public void sendCommitNotification(CDOCommitInfo commitInfo) throws Exception; + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception; + + public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception; + + /** + * @since 4.1 + */ + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception; +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java new file mode 100644 index 0000000000..435b78e680 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.server.ISessionManager; +import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; + +import org.eclipse.net4j.util.security.IUserManager; + +import java.util.List; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalSessionManager extends ISessionManager +{ + public InternalRepository getRepository(); + + public void setRepository(InternalRepository repository); + + public void setUserManager(IUserManager userManager); + + public InternalSession[] getSessions(); + + public InternalSession getSession(int sessionID); + + /** + * @return Never <code>null</code> + */ + public InternalSession openSession(ISessionProtocol sessionProtocol); + + public void sessionClosed(InternalSession session); + + public void sendRepositoryTypeNotification(CDOCommonRepository.Type oldType, CDOCommonRepository.Type newType); + + /** + * @deprecated use + * {@link #sendRepositoryStateNotification(org.eclipse.emf.cdo.common.CDOCommonRepository.State, org.eclipse.emf.cdo.common.CDOCommonRepository.State, CDOID)} + * instead + */ + @Deprecated + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState); + + /** + * @since 4.1 + */ + public void sendRepositoryStateNotification(CDOCommonRepository.State oldState, CDOCommonRepository.State newState, + CDOID rootResourceID); + + public void sendBranchNotification(InternalSession sender, InternalCDOBranch branch); + + public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + + /** + * @since 4.1 + */ + public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo); + + public void sendRemoteSessionNotification(InternalSession sender, byte opcode); + + public List<Integer> sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, + int[] recipients); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java new file mode 100644 index 0000000000..af99668712 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStore; + +import org.eclipse.net4j.util.lifecycle.ILifecycle; + +/** + * @author Eike Stepper + * @since 3.0 + */ +public interface InternalStore extends IStore, ILifecycle +{ + public InternalRepository getRepository(); + + public void setRepository(IRepository repository); + + public void setRevisionTemporality(RevisionTemporality revisionTemporality); + + public void setRevisionParallelism(RevisionParallelism revisionParallelism); + + public int getNextBranchID(); + + public int getNextLocalBranchID(); + + public void setLastBranchID(int lastBranchID); + + public void setLastLocalBranchID(int lastLocalBranchID); + + public void setLastCommitTime(long lastCommitTime); + + public void setLastNonLocalCommitTime(long lastNonLocalCommitTime); + + /** + * @since 4.0 + */ + public boolean isLocal(CDOID id); + + /** + * @since 4.0 + */ + public boolean isDropAllDataOnActivate(); + + /** + * @since 4.0 + */ + public void setDropAllDataOnActivate(boolean dropAllDataOnActivate); + + /** + * @since 4.0 + */ + public void setCreationTime(long creationTime); + + /** + * @author Eike Stepper + * @since 4.0 + */ + public interface NoExternalReferences + { + } + + /** + * @author Eike Stepper + * @since 4.0 + */ + public interface NoQueryXRefs + { + } + + /** + * @author Eike Stepper + * @since 4.0 + */ + public interface NoLargeObjects + { + } + + /** + * @author Eike Stepper + * @since 4.0 + */ + public interface NoFeatureMaps + { + } + + /** + * @author Eike Stepper + * @since 4.0 + */ + public interface NoHandleRevisions + { + } + + /** + * @author Eike Stepper + * @since 4.0 + */ + public interface NoRawAccess + { + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java new file mode 100644 index 0000000000..f4c3298dc0 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfoHandler; +import org.eclipse.emf.cdo.server.ISynchronizableRepository; +import org.eclipse.emf.cdo.spi.common.CDORawReplicationContext; +import org.eclipse.emf.cdo.spi.common.CDOReplicationContext; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalSynchronizableRepository extends ISynchronizableRepository, InternalRepository, + CDOReplicationContext, CDORawReplicationContext, CDOLockChangeInfoHandler +{ + public InternalRepositorySynchronizer getSynchronizer(); + + public void setSynchronizer(InternalRepositorySynchronizer synchronizer); + + public InternalSession getReplicatorSession(); + + public void setLastReplicatedBranchID(int lastReplicatedBranchID); + + public void setLastReplicatedCommitTime(long lastReplicatedCommitTime); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java new file mode 100644 index 0000000000..2f51ae2db1 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.server.ITransaction; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalTransaction extends ITransaction, InternalView +{ + public InternalCommitContext createCommitContext(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java new file mode 100644 index 0000000000..8adca8fe50 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.lifecycle.ILifecycle; + +import java.util.List; + +/** + * @author Eike Stepper + * @since 3.0 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalView extends IView, ILifecycle +{ + public InternalSession getSession(); + + public InternalRepository getRepository(); + + public void setBranchPoint(CDOBranchPoint branchPoint); + + /** + * @since 4.0 + */ + public void setDurableLockingID(String durableLockingID); + + /** + * @since 4.0 + */ + public void changeTarget(CDOBranchPoint branchPoint, List<CDOID> invalidObjects, + List<CDORevisionDelta> allChangedObjects, List<CDOID> allDetachedObjects); + + public void subscribe(CDOID id); + + public void unsubscribe(CDOID id); + + public boolean hasSubscription(CDOID id); + + public void clearChangeSubscription(); + + public void doClose(); +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java new file mode 100644 index 0000000000..908e5baa5a --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.CDORevision; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class LongIDStore extends Store +{ + /** + * @since 3.0 + */ + public static final Set<ObjectType> OBJECT_ID_TYPES = Collections.singleton(CDOID.ObjectType.LONG); + + /** + * @since 3.0 + */ + public static final long NULL = CDOIDUtil.getLong(CDOID.NULL); + + @ExcludeFromDump + private transient AtomicLong lastObjectID = new AtomicLong(); + + @ExcludeFromDump + private transient AtomicLong nextLocalObjectID = new AtomicLong(Long.MAX_VALUE); + + public LongIDStore(String type, Set<ChangeFormat> supportedChangeFormats, + Set<RevisionTemporality> supportedRevisionTemporalities, Set<RevisionParallelism> supportedRevisionParallelisms) + { + super(type, OBJECT_ID_TYPES, supportedChangeFormats, supportedRevisionTemporalities, supportedRevisionParallelisms); + } + + /** + * @since 4.0 + */ + public CDOID createObjectID(String val) + { + Long id = Long.valueOf(val); + return CDOIDUtil.createLong(id); + } + + public long getLastObjectID() + { + return lastObjectID.get(); + } + + public void setLastObjectID(long lastObjectID) + { + this.lastObjectID.set(lastObjectID); + } + + /** + * @since 3.0 + */ + public long getNextLocalObjectID() + { + return nextLocalObjectID.get(); + } + + /** + * @since 3.0 + */ + public void setNextLocalObjectID(long nextLocalObjectID) + { + this.nextLocalObjectID.set(nextLocalObjectID); + } + + /** + * @since 4.0 + */ + public CDOID getNextCDOID(LongIDStoreAccessor accessor, CDORevision revision) + { + if (revision.getBranch().isLocal()) + { + return CDOIDUtil.createLong(nextLocalObjectID.getAndDecrement()); + } + + return CDOIDUtil.createLong(lastObjectID.incrementAndGet()); + } + + /** + * @since 4.0 + */ + public boolean isLocal(CDOID id) + { + long value = CDOIDUtil.getLong(id); + return value > nextLocalObjectID.get(); + } + + /** + * @since 4.0 + */ + public void ensureLastObjectID(CDOID id) + { + long addedID = CDOIDUtil.getLong(id); + if (addedID > getLastObjectID()) + { + setLastObjectID(addedID); + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java new file mode 100644 index 0000000000..803368ead7 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 201266 + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.ITransaction; + +/** + * @since 2.0 + * @author Eike Stepper + */ +public abstract class LongIDStoreAccessor extends StoreAccessor +{ + protected LongIDStoreAccessor(Store store, ISession session) + { + super(store, session); + } + + protected LongIDStoreAccessor(Store store, ITransaction transaction) + { + super(store, transaction); + } + + /** + * @since 4.0 + */ + @Override + public LongIDStore getStore() + { + return (LongIDStore)super.getStore(); + } + + @Override + protected CDOID getNextCDOID(CDORevision revision) + { + return getStore().getNextCDOID(this, revision); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java new file mode 100644 index 0000000000..8db6cb5246 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.server.CDOServerUtil; +import org.eclipse.emf.cdo.server.IRepository.WriteAccessHandler; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EObject; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class ObjectWriteAccessHandler implements WriteAccessHandler +{ + private boolean legacyModeEnabled; + + private IStoreAccessor.CommitContext commitContext; + + private CDOView view; + + private EObject[] newObjects; + + private EObject[] dirtyObjects; + + public ObjectWriteAccessHandler() + { + } + + public ObjectWriteAccessHandler(boolean legacyModeEnabled) + { + this.legacyModeEnabled = legacyModeEnabled; + } + + public final boolean isLegacyModeEnabled() + { + return legacyModeEnabled; + } + + protected final IStoreAccessor.CommitContext getCommitContext() + { + return commitContext; + } + + protected final ITransaction getTransaction() + { + return commitContext.getTransaction(); + } + + protected final CDOView getView() + { + if (view == null) + { + view = CDOServerUtil.openView(commitContext, legacyModeEnabled); + } + + return view; + } + + protected final EObject[] getNewObjects() + { + if (newObjects == null) + { + InternalCDORevision[] newRevisions = commitContext.getNewObjects(); + newObjects = new EObject[newRevisions.length]; + CDOView view = getView(); + + for (int i = 0; i < newRevisions.length; i++) + { + InternalCDORevision newRevision = newRevisions[i]; + CDOObject newObject = view.getObject(newRevision.getID()); + newObjects[i] = CDOUtil.getEObject(newObject); + } + } + + return newObjects; + } + + protected final EObject[] getDirtyObjects() + { + if (dirtyObjects == null) + { + InternalCDORevision[] dirtyRevisions = commitContext.getDirtyObjects(); + dirtyObjects = new EObject[dirtyRevisions.length]; + CDOView view = getView(); + + for (int i = 0; i < dirtyRevisions.length; i++) + { + InternalCDORevision dirtyRevision = dirtyRevisions[i]; + CDOObject dirtyObject = view.getObject(dirtyRevision.getID()); + dirtyObjects[i] = CDOUtil.getEObject(dirtyObject); + } + } + + return dirtyObjects; + } + + public final void handleTransactionBeforeCommitting(ITransaction transaction, + IStoreAccessor.CommitContext commitContext, OMMonitor monitor) throws RuntimeException + { + try + { + this.commitContext = commitContext; + handleTransactionBeforeCommitting(monitor); + } + finally + { + LifecycleUtil.deactivate(view); + view = null; + dirtyObjects = null; + newObjects = null; + this.commitContext = null; + } + } + + public final void handleTransactionAfterCommitted(ITransaction transaction, CommitContext commitContext, + OMMonitor monitor) + { + try + { + this.commitContext = commitContext; + handleTransactionAfterCommitted(monitor); + } + finally + { + LifecycleUtil.deactivate(view); + view = null; + dirtyObjects = null; + newObjects = null; + this.commitContext = null; + } + } + + protected void handleTransactionBeforeCommitting(OMMonitor monitor) throws RuntimeException + { + } + + protected void handleTransactionAfterCommitted(OMMonitor monitor) + { + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java new file mode 100644 index 0000000000..c084dce1fb --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.net4j.util.container.IPluginContainer; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public final class PluginRepositoryProvider extends ContainerRepositoryProvider +{ + public static final PluginRepositoryProvider INSTANCE = new PluginRepositoryProvider(); + + private PluginRepositoryProvider() + { + super(IPluginContainer.INSTANCE); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java new file mode 100644 index 0000000000..10aa1d8e09 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.server.IQueryHandler; + +import org.eclipse.net4j.util.factory.Factory; +import org.eclipse.net4j.util.factory.ProductCreationException; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class QueryHandlerFactory extends Factory +{ + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.queryHandlerFactories"; //$NON-NLS-1$ + + public QueryHandlerFactory(String language) + { + super(PRODUCT_GROUP, language); + } + + /** + * @since 3.0 + */ + public abstract IQueryHandler create(String description) throws ProductCreationException; +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java new file mode 100644 index 0000000000..e58dc7918c --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java @@ -0,0 +1,346 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Lothar Werzinger - support for configuring user managers + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.internal.server.SessionManager; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.CDOServerUtil; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IRepositoryFactory; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IStoreFactory; + +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.net4j.util.security.IUserManager; + +import org.eclipse.emf.ecore.EPackage; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class RepositoryConfigurator +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REPOSITORY, RepositoryConfigurator.class); + + private IManagedContainer container; + + private Map<String, IRepositoryFactory> repositoryFactories = new HashMap<String, IRepositoryFactory>(); + + private Map<String, IStoreFactory> storeFactories = new HashMap<String, IStoreFactory>(); + + public RepositoryConfigurator() + { + this(null); + } + + public RepositoryConfigurator(IManagedContainer container) + { + this.container = container; + } + + public IManagedContainer getContainer() + { + return container; + } + + public Map<String, IRepositoryFactory> getRepositoryFactories() + { + return repositoryFactories; + } + + public Map<String, IStoreFactory> getStoreFactories() + { + return storeFactories; + } + + public IRepository[] configure(File configFile) throws ParserConfigurationException, SAXException, IOException, + CoreException + { + if (TRACER.isEnabled()) + { + TRACER.trace("Configuring CDO server from " + configFile.getAbsolutePath()); //$NON-NLS-1$ + } + + List<IRepository> repositories = new ArrayList<IRepository>(); + Document document = getDocument(configFile); + NodeList elements = document.getElementsByTagName("repository"); //$NON-NLS-1$ + for (int i = 0; i < elements.getLength(); i++) + { + Element repositoryConfig = (Element)elements.item(i); + IRepository repository = getRepository(repositoryConfig); + repositories.add(repository); + + if (container != null) + { + CDOServerUtil.addRepository(container, repository); + } + } + + return repositories.toArray(new IRepository[repositories.size()]); + } + + protected Document getDocument(File configFile) throws ParserConfigurationException, SAXException, IOException + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(configFile); + } + + protected IRepositoryFactory getRepositoryFactory(String type) throws CoreException + { + IRepositoryFactory factory = repositoryFactories.get(type); + if (factory == null) + { + factory = createExecutableExtension("repositoryFactories", "repositoryFactory", //$NON-NLS-1$ //$NON-NLS-2$ + "repositoryType", type); //$NON-NLS-1$ + } + + if (factory == null) + { + throw new IllegalStateException("CDORepositoryInfo factory not found: " + type); //$NON-NLS-1$ + } + + return factory; + } + + protected IRepository getRepository(Element repositoryConfig) throws CoreException + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + if (StringUtil.isEmpty(repositoryName)) + { + throw new IllegalArgumentException("CDORepositoryInfo name is missing or empty"); //$NON-NLS-1$ + } + + String repositoryType = repositoryConfig.getAttribute("type"); //$NON-NLS-1$ + if (StringUtil.isEmpty(repositoryType)) + { + repositoryType = RepositoryFactory.TYPE; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Configuring repository {0} (type={1})", repositoryName, repositoryType); //$NON-NLS-1$ + } + + Map<String, String> properties = getProperties(repositoryConfig, 1); + + Element storeConfig = getStoreConfig(repositoryConfig); + IStore store = createStore(repositoryName, properties, storeConfig); + + InternalRepository repository = (InternalRepository)getRepository(repositoryType); + repository.setName(repositoryName); + repository.setStore((InternalStore)store); + repository.setProperties(properties); + + Element userManagerConfig = getUserManagerConfig(repositoryConfig); + if (userManagerConfig != null) + { + IUserManager userManager = getUserManager(userManagerConfig); + if (userManager != null) + { + InternalSessionManager sessionManager = repository.getSessionManager(); + if (sessionManager == null) + { + sessionManager = new SessionManager(); + repository.setSessionManager(sessionManager); + } + + sessionManager.setUserManager(userManager); + } + } + + EPackage[] initialPackages = getInitialPackages(repositoryConfig); + if (initialPackages.length != 0) + { + repository.setInitialPackages(initialPackages); + } + + return repository; + } + + protected IRepository getRepository(String repositoryType) throws CoreException + { + IRepositoryFactory factory = getRepositoryFactory(repositoryType); + return factory.createRepository(); + } + + protected Element getUserManagerConfig(Element repositoryConfig) + { + NodeList userManagerConfig = repositoryConfig.getElementsByTagName("userManager"); //$NON-NLS-1$ + if (userManagerConfig.getLength() > 1) + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + throw new IllegalStateException("At most one user manager must be configured for repository " + repositoryName); //$NON-NLS-1$ + } + + return (Element)(userManagerConfig.getLength() > 0 ? userManagerConfig.item(0) : null); + } + + protected IUserManager getUserManager(Element userManagerConfig) throws CoreException + { + String type = userManagerConfig.getAttribute("type"); //$NON-NLS-1$ + String description = userManagerConfig.getAttribute("description"); //$NON-NLS-1$ + return getUserManager(type, description); + } + + protected IUserManager getUserManager(String type, String description) throws CoreException + { + IUserManager userManager = (IUserManager)container.getElement("org.eclipse.net4j.userManagers", type, description); //$NON-NLS-1$ + if (userManager == null) + { + throw new IllegalStateException("UserManager factory not found: " + type); //$NON-NLS-1$ + } + + return userManager; + } + + protected EPackage[] getInitialPackages(Element repositoryConfig) + { + List<EPackage> result = new ArrayList<EPackage>(); + + NodeList initialPackagesConfig = repositoryConfig.getElementsByTagName("initialPackage"); //$NON-NLS-1$ + for (int i = 0; i < initialPackagesConfig.getLength(); i++) + { + Element initialPackageConfig = (Element)initialPackagesConfig.item(i); + String nsURI = initialPackageConfig.getAttribute("nsURI"); //$NON-NLS-1$ + if (nsURI == null) + { + throw new IllegalStateException("nsURI missing for initialPackage element"); //$NON-NLS-1$ + } + + EPackage initialPackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); + if (initialPackage == null) + { + throw new IllegalStateException("Initial package not found in global package registry: " + nsURI); //$NON-NLS-1$ + } + + result.add(initialPackage); + } + + return result.toArray(new EPackage[result.size()]); + } + + protected Element getStoreConfig(Element repositoryConfig) + { + NodeList storeConfigs = repositoryConfig.getElementsByTagName("store"); //$NON-NLS-1$ + if (storeConfigs.getLength() == 0) + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + throw new IllegalStateException("A store must be configured for repository " + repositoryName); //$NON-NLS-1$ + } + + return (Element)storeConfigs.item(0); + } + + protected IStoreFactory getStoreFactory(String type) throws CoreException + { + IStoreFactory factory = storeFactories.get(type); + if (factory == null) + { + factory = createExecutableExtension("storeFactories", "storeFactory", "storeType", type); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + if (factory == null) + { + throw new IllegalStateException("Store factory not found: " + type); //$NON-NLS-1$ + } + + return factory; + } + + protected IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig) + throws CoreException + { + String type = storeConfig.getAttribute("type"); //$NON-NLS-1$ + IStoreFactory storeFactory = getStoreFactory(type); + return storeFactory.createStore(repositoryName, repositoryProperties, storeConfig); + } + + public static Map<String, String> getProperties(Element element, int levels) + { + Map<String, String> properties = new HashMap<String, String>(); + collectProperties(element, "", properties, levels); //$NON-NLS-1$ + return properties; + } + + private static void collectProperties(Element element, String prefix, Map<String, String> properties, int levels) + { + if ("property".equals(element.getNodeName())) //$NON-NLS-1$ + { + String name = element.getAttribute("name"); //$NON-NLS-1$ + String value = element.getAttribute("value"); //$NON-NLS-1$ + properties.put(prefix + name, value); + prefix += name + "."; //$NON-NLS-1$ + } + + if (levels > 0) + { + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node childNode = childNodes.item(i); + if (childNode instanceof Element) + { + collectProperties((Element)childNode, prefix, properties, levels - 1); + } + } + } + } + + private static <T> T createExecutableExtension(String extPointName, String elementName, String attributeName, + String type) throws CoreException + { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, extPointName); + for (IConfigurationElement element : elements) + { + if (ObjectUtil.equals(element.getName(), elementName)) + { + String storeType = element.getAttribute(attributeName); + if (ObjectUtil.equals(storeType, type)) + { + @SuppressWarnings("unchecked") + T result = (T)element.createExecutableExtension("class"); //$NON-NLS-1$ + return result; + } + } + } + + return null; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java new file mode 100644 index 0000000000..58ea25cc69 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IRepositoryFactory; + +import org.eclipse.net4j.util.container.IManagedContainer; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class RepositoryFactory implements IRepositoryFactory +{ + public static final String TYPE = "default"; //$NON-NLS-1$ + + public RepositoryFactory() + { + } + + public String getRepositoryType() + { + return TYPE; + } + + public IRepository createRepository() + { + return new Repository.Default(); + } + + public static IRepository get(IManagedContainer container, String name) + { + return (IRepository)container.getElement(PRODUCT_GROUP, TYPE, name); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java new file mode 100644 index 0000000000..5b7d424350 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java @@ -0,0 +1,147 @@ +/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.server.CDOServerUtil;
+import org.eclipse.emf.cdo.server.IRepository;
+
+import org.eclipse.net4j.util.container.IElementProcessor;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.security.IUserManager;
+import org.eclipse.net4j.util.security.SecurityUtil;
+import org.eclipse.net4j.util.security.UserManagerFactory;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public abstract class RepositoryUserManager extends Lifecycle implements IUserManager
+{
+ private IManagedContainer container;
+
+ private String repositoryName;
+
+ protected RepositoryUserManager()
+ {
+ }
+
+ private void setContainer(IManagedContainer container)
+ {
+ this.container = container;
+ }
+
+ private void setRepositoryName(String repositoryName)
+ {
+ this.repositoryName = repositoryName;
+ }
+
+ public void addUser(String userID, char[] password)
+ {
+ // Cann be overridden in subclasses.
+ }
+
+ public void removeUser(String userID)
+ {
+ // Cann be overridden in subclasses.
+ }
+
+ public byte[] encrypt(String userID, byte[] data, String algorithmName, byte[] salt, int count)
+ throws SecurityException
+ {
+ IRepository repository = getRepository(container, repositoryName);
+ if (repository == null)
+ {
+ throw new SecurityException("Repository not found: " + repositoryName); //$NON-NLS-1$
+ }
+
+ char[] password = getPassword(repository, userID);
+ if (password == null)
+ {
+ throw new SecurityException("No such user: " + userID); //$NON-NLS-1$
+ }
+
+ try
+ {
+ return SecurityUtil.encrypt(data, password, algorithmName, salt, count);
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ throw new SecurityException(ex);
+ }
+ }
+
+ protected IRepository getRepository(IManagedContainer container, String repositoryName)
+ {
+ return CDOServerUtil.getRepository(container, repositoryName);
+ }
+
+ protected abstract char[] getPassword(IRepository repository, String userID);
+
+ public static void prepareContainer(IManagedContainer container, RepositoryUserManagerFactory factory)
+ {
+ container.registerFactory(factory);
+ container.addPostProcessor(new RepositoryInjector());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class RepositoryUserManagerFactory extends UserManagerFactory
+ {
+ protected RepositoryUserManagerFactory(String type)
+ {
+ super(type);
+ }
+
+ public final Object create(String description) throws ProductCreationException
+ {
+ RepositoryUserManager userManager = doCreate(description);
+ String repositoryName = getRepositoryName(description);
+ userManager.setRepositoryName(repositoryName);
+ return userManager;
+ }
+
+ protected String getRepositoryName(String description)
+ {
+ return description;
+ }
+
+ protected abstract RepositoryUserManager doCreate(String description) throws ProductCreationException;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class RepositoryInjector implements IElementProcessor
+ {
+ public RepositoryInjector()
+ {
+ }
+
+ public Object process(IManagedContainer container, String productGroup, String factoryType, String description,
+ Object element)
+ {
+ if (element instanceof RepositoryUserManager)
+ {
+ RepositoryUserManager userManager = (RepositoryUserManager)element;
+ userManager.setContainer(container);
+ }
+
+ return element;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java new file mode 100644 index 0000000000..407f50c409 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java @@ -0,0 +1,549 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonView; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.ISessionManager; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.container.IContainerDelta.Kind; +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.ProgressDistributor; + +import org.eclipse.emf.ecore.EClass; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class Store extends Lifecycle implements InternalStore +{ + /** + * @since 3.0 + * @deprecated Use CDOBranchPoint.UNSPECIFIED_DATE + */ + @Deprecated + public static final long UNSPECIFIED_DATE = CDOBranchPoint.UNSPECIFIED_DATE; + + private String type; + + private Set<ObjectType> objectIDTypes; + + private Set<ChangeFormat> supportedChangeFormats; + + private Set<RevisionTemporality> supportedRevisionTemporalities; + + private Set<RevisionParallelism> supportedRevisionParallelisms; + + private RevisionTemporality revisionTemporality = RevisionTemporality.NONE; + + private RevisionParallelism revisionParallelism = RevisionParallelism.NONE; + + private InternalRepository repository; + + private boolean dropAllDataOnActivate; + + /** + * Is protected against concurrent thread access through {@link Repository#createBranchLock}. + */ + @ExcludeFromDump + private transient int lastBranchID; + + /** + * Is protected against concurrent thread access through {@link Repository#createBranchLock}. + */ + @ExcludeFromDump + private transient int lastLocalBranchID; + + @ExcludeFromDump + private transient long lastCommitTime; + + @ExcludeFromDump + private transient Object lastCommitTimeLock = new Object(); + + @ExcludeFromDump + private transient long lastNonLocalCommitTime; + + @ExcludeFromDump + private transient Object lastNonLocalCommitTimeLock = new Object(); + + /** + * @since 3.0 + */ + @ExcludeFromDump + private transient ProgressDistributor indicatingCommitDistributor = new ProgressDistributor.Geometric() + { + @Override + public String toString() + { + String result = "indicatingCommitDistributor"; //$NON-NLS-1$ + if (repository != null) + { + result += ": " + repository.getName(); //$NON-NLS-1$ + } + + return result; + } + }; + + /** + * @since 3.0 + */ + public Store(String type, Set<CDOID.ObjectType> objectIDTypes, Set<ChangeFormat> supportedChangeFormats, + Set<RevisionTemporality> supportedRevisionTemporalities, Set<RevisionParallelism> supportedRevisionParallelisms) + { + checkArg(!StringUtil.isEmpty(type), "Empty type"); //$NON-NLS-1$ + this.type = type; + this.objectIDTypes = objectIDTypes; + + checkArg(supportedChangeFormats != null && !supportedChangeFormats.isEmpty(), "Empty supportedChangeFormats"); //$NON-NLS-1$ + this.supportedChangeFormats = supportedChangeFormats; + + checkArg(supportedRevisionTemporalities != null && !supportedRevisionTemporalities.isEmpty(), + "Empty supportedRevisionTemporalities"); //$NON-NLS-1$ + this.supportedRevisionTemporalities = supportedRevisionTemporalities; + + checkArg(supportedRevisionParallelisms != null && !supportedRevisionParallelisms.isEmpty(), + "Empty supportedRevisionParallelisms"); //$NON-NLS-1$ + this.supportedRevisionParallelisms = supportedRevisionParallelisms; + } + + public final String getType() + { + return type; + } + + /** + * @since 3.0 + */ + public Set<CDOID.ObjectType> getObjectIDTypes() + { + return objectIDTypes; + } + + /** + * @since 4.0 + */ + protected void setObjectIDTypes(Set<ObjectType> objectIDTypes) + { + this.objectIDTypes = objectIDTypes; + } + + public Set<ChangeFormat> getSupportedChangeFormats() + { + return supportedChangeFormats; + } + + public Set<RevisionTemporality> getSupportedRevisionTemporalities() + { + return supportedRevisionTemporalities; + } + + public final Set<RevisionParallelism> getSupportedRevisionParallelisms() + { + return supportedRevisionParallelisms; + } + + public RevisionTemporality getRevisionTemporality() + { + return revisionTemporality; + } + + public void setRevisionTemporality(RevisionTemporality revisionTemporality) + { + checkInactive(); + checkState(supportedRevisionTemporalities.contains(revisionTemporality), "Revision temporality not supported: " //$NON-NLS-1$ + + revisionTemporality); + this.revisionTemporality = revisionTemporality; + } + + public RevisionParallelism getRevisionParallelism() + { + return revisionParallelism; + } + + public void setRevisionParallelism(RevisionParallelism revisionParallelism) + { + checkInactive(); + checkState(supportedRevisionParallelisms.contains(revisionParallelism), "Revision parallelism not supported: " //$NON-NLS-1$ + + revisionParallelism); + this.revisionParallelism = revisionParallelism; + } + + /** + * @since 3.0 + */ + public InternalRepository getRepository() + { + return repository; + } + + public void setRepository(IRepository repository) + { + this.repository = (InternalRepository)repository; + } + + /** + * @since 4.0 + */ + public boolean isDropAllDataOnActivate() + { + return dropAllDataOnActivate; + } + + /** + * @since 4.0 + */ + public void setDropAllDataOnActivate(boolean dropAllDataOnActivate) + { + this.dropAllDataOnActivate = dropAllDataOnActivate; + } + + /** + * @since 3.0 + */ + public int getLastBranchID() + { + return lastBranchID; + } + + /** + * @since 3.0 + */ + public void setLastBranchID(int lastBranchID) + { + this.lastBranchID = lastBranchID; + } + + /** + * @since 3.0 + */ + public int getNextBranchID() + { + return ++lastBranchID; + } + + /** + * @since 3.0 + */ + public int getLastLocalBranchID() + { + return lastLocalBranchID; + } + + /** + * @since 3.0 + */ + public void setLastLocalBranchID(int lastLocalBranchID) + { + this.lastLocalBranchID = lastLocalBranchID; + } + + /** + * @since 3.0 + */ + public int getNextLocalBranchID() + { + return --lastLocalBranchID; + } + + /** + * @since 3.0 + */ + public long getLastCommitTime() + { + synchronized (lastCommitTimeLock) + { + return lastCommitTime; + } + } + + /** + * @since 3.0 + */ + public void setLastCommitTime(long lastCommitTime) + { + synchronized (lastCommitTimeLock) + { + if (this.lastCommitTime < lastCommitTime) + { + this.lastCommitTime = lastCommitTime; + } + } + } + + /** + * @since 3.0 + */ + public long getLastNonLocalCommitTime() + { + synchronized (lastNonLocalCommitTimeLock) + { + return lastNonLocalCommitTime; + } + } + + /** + * @since 3.0 + */ + public void setLastNonLocalCommitTime(long lastNonLocalCommitTime) + { + synchronized (lastNonLocalCommitTimeLock) + { + if (this.lastNonLocalCommitTime < lastNonLocalCommitTime) + { + this.lastNonLocalCommitTime = lastNonLocalCommitTime; + } + } + } + + public IStoreAccessor getReader(ISession session) + { + IStoreAccessor reader = null; + StoreAccessorPool pool = getReaderPool(session, false); + if (pool != null) + { + reader = pool.removeStoreAccessor(session); + } + + if (reader == null && session != null) + { + CDOCommonView[] views = session.getViews(); + for (CDOCommonView view : views) + { + pool = getWriterPool((IView)view, false); + if (pool != null) + { + reader = pool.removeStoreAccessor(view); + if (reader != null) + { + break; + } + } + } + } + + if (reader == null) + { + reader = createReader(session); + LifecycleUtil.activate(reader); + } + + return reader; + } + + public IStoreAccessor getWriter(ITransaction transaction) + { + IStoreAccessor writer = null; + StoreAccessorPool pool = getWriterPool(transaction, false); + if (pool != null) + { + writer = pool.removeStoreAccessor(transaction); + } + + if (writer == null) + { + writer = createWriter(transaction); + LifecycleUtil.activate(writer); + } + + return writer; + } + + public ProgressDistributor getIndicatingCommitDistributor() + { + return indicatingCommitDistributor; + } + + /** + * @since 3.0 + */ + public InternalCDORevision createRevision(EClass eClass, CDOID id) + { + CDORevisionFactory factory = repository.getRevisionManager().getFactory(); + InternalCDORevision revision = (InternalCDORevision)factory.createRevision(eClass); + revision.setID(id); + return revision; + } + + /** + * @since 4.0 + */ + protected void releaseAccessor(StoreAccessorBase accessor) + { + StoreAccessorPool pool = null; + if (accessor.isReader()) + { + pool = getReaderPool(accessor.getSession(), true); + } + else + { + pool = getWriterPool(accessor.getTransaction(), true); + } + + if (pool != null) + { + pool.addStoreAccessor(accessor); + } + else + { + accessor.deactivate(); + } + } + + /** + * Returns a {@link StoreAccessorPool pool} that may contain {@link IStoreAccessor} instances that are compatible with + * the given session. The implementor may return <code>null</code> to indicate that no pooling occurs. It's also left + * to the implementors choice how to determine the appropriate pool instance to be used for the given session, for + * example it could always return the same pool instance, regardless of the given session. + * <p> + * If the implementor of this method decides to create pools that are only compatible with certain sessions or views, + * then it is his responsibility to listen to {@link Kind#REMOVED REMOVED} events sent by either the + * {@link ISessionManager} (indicating that a session is closed) or any of its sessions (indicating that a view is + * closed). <b>Note:</b> Closing a session <em>implies</em> that all contained views are closed sliently without + * firing respective events! + * + * @param session + * The context which the pool must be compatible with. Must not be <code>null</code>. + * @param forReleasing + * Enables lazy pool creation. The implementor is not supposed to create a new pool if <code>false</code> is + * passed. If <code>true</code> is passed it's up to the implementor whether to create a new pool or not. + */ + protected abstract StoreAccessorPool getReaderPool(ISession session, boolean forReleasing); + + /** + * Returns a {@link StoreAccessorPool pool} that may contain {@link IStoreAccessor} instances that are compatible with + * the given session. The implementor may return <code>null</code> to indicate that no pooling occurs. It's also left + * to the implementors choice how to determine the appropriate pool instance to be used for the given session, for + * example it could always return the same pool instance, regardless of the given session. + * <p> + * If the implementor of this method decides to create pools that are only compatible with certain sessions or views, + * then it is his responsibility to listen to {@link Kind#REMOVED REMOVED} events sent by either the + * {@link ISessionManager} (indicating that a session is closed) or any of its sessions (indicating that a view is + * closed). <b>Note:</b> Closing a session <em>implies</em> that all contained views are closed sliently without + * firing respective events! + * + * @param view + * The context which the pool must be compatible with. Must not be <code>null</code>. + * @param forReleasing + * Enables lazy pool creation. The implementor is not supposed to create a new pool if <code>false</code> is + * passed. If <code>true</code> is passed it's up to the implementor whether to create a new pool or not. + */ + protected abstract StoreAccessorPool getWriterPool(IView view, boolean forReleasing); + + /** + * Creates and returns a <b>new</b> {@link IStoreAccessor} instance. The caller of this method is responsible for + * {@link Lifecycle#activate() activating} the new instance. + */ + protected abstract IStoreAccessor createReader(ISession session); + + /** + * Creates and returns a <b>new</b> {@link IStoreAccessor} instance. The caller of this method is responsible for + * {@link Lifecycle#activate() activating} the new instance. + */ + protected abstract IStoreAccessor createWriter(ITransaction transaction); + + protected static <T> Set<T> set(T... elements) + { + return Collections.unmodifiableSet(new HashSet<T>(Arrays.asList(elements))); + } + + /** + * @since 4.0 + */ + public static String idToString(CDOID id) + { + StringBuilder builder = new StringBuilder(); + CDOIDUtil.write(builder, id); + return builder.toString(); + } + + /** + * @since 4.0 + */ + public static CDOID stringToID(String string) + { + return CDOIDUtil.read(string); + } + + /** + * @since 3.0 + */ + public static IStoreAccessor.QueryResourcesContext.ExactMatch createExactMatchContext(final CDOID folderID, + final String name, final CDOBranchPoint branchPoint) + { + return new IStoreAccessor.QueryResourcesContext.ExactMatch() + { + private CDOID resourceID; + + public CDOID getResourceID() + { + return resourceID; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public CDOID getFolderID() + { + return folderID; + } + + public String getName() + { + return name; + } + + public boolean exactMatch() + { + return true; + } + + public int getMaxResults() + { + return 1; + } + + public boolean addResource(CDOID resourceID) + { + this.resourceID = resourceID; + return false; + } + }; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java new file mode 100644 index 0000000000..5b988af6e9 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 201266 + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.LimitedInputStream; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class StoreAccessor extends StoreAccessorBase +{ + protected StoreAccessor(Store store, ISession session) + { + super(store, session); + } + + protected StoreAccessor(Store store, ITransaction transaction) + { + super(store, transaction); + } + + /** + * @since 4.0 + */ + @Override + protected void doWrite(InternalCommitContext context, OMMonitor monitor) + { + CDOBranch branch = context.getBranchPoint().getBranch(); + long timeStamp = context.getBranchPoint().getTimeStamp(); + long previousTimeStamp = context.getPreviousTimeStamp(); + String userID = context.getUserID(); + String commitComment = context.getCommitComment(); + + boolean deltas = getStore().getSupportedChangeFormats().contains(IStore.ChangeFormat.DELTA); + + InternalCDOPackageUnit[] newPackageUnits = context.getNewPackageUnits(); + InternalCDORevision[] newObjects = context.getNewObjects(); + CDOID[] detachedObjects = context.getDetachedObjects(); + int dirtyCount = deltas ? context.getDirtyObjectDeltas().length : context.getDirtyObjects().length; + + try + { + monitor.begin(1 + newPackageUnits.length + 2 + newObjects.length + detachedObjects.length + dirtyCount); + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, monitor.fork()); + + if (newPackageUnits.length != 0) + { + writePackageUnits(newPackageUnits, monitor.fork(newPackageUnits.length)); + } + + if (getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + addIDMappings(context, monitor.fork()); + } + + applyIDMappings(context, monitor); + + if (detachedObjects.length != 0) + { + detachObjects(detachedObjects, branch, timeStamp, monitor.fork(detachedObjects.length)); + } + + if (newObjects.length != 0) + { + writeRevisions(newObjects, branch, monitor.fork(newObjects.length)); + } + + if (dirtyCount != 0) + { + if (deltas) + { + writeRevisionDeltas(context.getDirtyObjectDeltas(), branch, timeStamp, monitor.fork(dirtyCount)); + } + else + { + writeRevisions(context.getDirtyObjects(), branch, monitor.fork(dirtyCount)); + } + } + + ExtendedDataInputStream in = context.getLobs(); + if (in != null) + { + try + { + int count = in.readInt(); + for (int i = 0; i < count; i++) + { + byte[] id = in.readByteArray(); + long size = in.readLong(); + if (size > 0) + { + writeBlob(id, size, new LimitedInputStream(in, size)); + } + else + { + writeClob(id, -size, new InputStreamReader(new LimitedInputStream(in, -size))); + } + } + } + catch (IOException ex) + { + throw WrappedException.wrap(ex); + } + } + } + finally + { + monitor.done(); + } + } + + /** + * @since 3.0 + */ + protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor) + { + context.applyIDMappings(monitor.fork()); + } + + /** + * @since 4.0 + */ + protected abstract void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment, OMMonitor monitor); + + /** + * @since 3.0 + */ + protected abstract void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor); + + /** + * @since 3.0 + */ + protected abstract void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, + long created, OMMonitor monitor); + + /** + * @since 3.0 + */ + protected abstract void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor); + + /** + * @since 4.0 + */ + protected abstract void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException; + + /** + * @since 4.0 + */ + protected abstract void writeClob(byte[] id, long size, Reader reader) throws IOException; +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java new file mode 100644 index 0000000000..7be7a2a3a8 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java @@ -0,0 +1,508 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Simon McDuff - bug 201266 + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitData; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDTemp; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.CDOFeatureDeltaVisitorImpl; +import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; + +import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public abstract class StoreAccessorBase extends Lifecycle implements IStoreAccessor +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, StoreAccessorBase.class); + + private Store store; + + private Object context; + + private boolean reader; + + private List<CommitContext> commitContexts = new ArrayList<CommitContext>(); + + private StoreAccessorBase(Store store, Object context, boolean reader) + { + this.store = store; + this.context = context; + this.reader = reader; + } + + protected StoreAccessorBase(Store store, ISession session) + { + this(store, session, true); + } + + protected StoreAccessorBase(Store store, ITransaction transaction) + { + this(store, transaction, false); + } + + void setContext(Object context) + { + this.context = context; + } + + public Store getStore() + { + return store; + } + + public boolean isReader() + { + return reader; + } + + /** + * @since 3.0 + */ + public InternalSession getSession() + { + if (context instanceof ITransaction) + { + return (InternalSession)((ITransaction)context).getSession(); + } + + return (InternalSession)context; + } + + public ITransaction getTransaction() + { + if (context instanceof ITransaction) + { + return (ITransaction)context; + } + + return null; + } + + public void release() + { + store.releaseAccessor(this); + commitContexts.clear(); + } + + /** + * @since 3.0 + */ + public final void write(InternalCommitContext context, OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing transaction: {0}", getTransaction()); //$NON-NLS-1$ + } + + commitContexts.add(context); + doWrite(context, monitor); + } + + protected abstract void doWrite(InternalCommitContext context, OMMonitor monitor); + + /** + * @since 3.0 + */ + public final void commit(OMMonitor monitor) + { + doCommit(monitor); + + long latest = CDORevision.UNSPECIFIED_DATE; + long latestNonLocal = CDORevision.UNSPECIFIED_DATE; + for (CommitContext commitContext : commitContexts) + { + CDOBranchPoint branchPoint = commitContext.getBranchPoint(); + long timeStamp = branchPoint.getTimeStamp(); + if (timeStamp > latest) + { + latest = timeStamp; + } + + CDOBranch branch = branchPoint.getBranch(); + if (!branch.isLocal()) + { + if (timeStamp > latestNonLocal) + { + latestNonLocal = timeStamp; + } + } + } + + getStore().setLastCommitTime(latest); + getStore().setLastNonLocalCommitTime(latestNonLocal); + } + + /** + * @since 3.0 + */ + protected abstract void doCommit(OMMonitor monitor); + + public final void rollback() + { + if (TRACER.isEnabled()) + { + TRACER.format("Rolling back transaction: {0}", getTransaction()); //$NON-NLS-1$ + } + + for (CommitContext commitContext : commitContexts) + { + doRollback(commitContext); + } + } + + protected abstract void doRollback(CommitContext commitContext); + + /** + * @since 3.0 + */ + public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint) + { + QueryResourcesContext.ExactMatch context = Store.createExactMatchContext(folderID, name, branchPoint); + queryResources(context); + return context.getResourceID(); + } + + /** + * @since 3.0 + */ + public CDOCommitData loadCommitData(long timeStamp) + { + CommitDataRevisionHandler handler = new CommitDataRevisionHandler(this, timeStamp); + return handler.getCommitData(); + } + + /** + * Add ID mappings for all new objects of a transaction to the commit context. The implementor must, for each new + * object of the commit context, determine a permanent CDOID and make it known to the context by calling + * {@link InternalCommitContext#addIDMapping(CDOID, CDOID)}. + * + * @since 3.0 + */ + public void addIDMappings(InternalCommitContext commitContext, OMMonitor monitor) + { + try + { + CDORevision[] newObjects = commitContext.getNewObjects(); + monitor.begin(newObjects.length); + for (CDORevision revision : newObjects) + { + CDOID id = revision.getID(); + if (id instanceof CDOIDTemp) + { + CDOIDTemp oldID = (CDOIDTemp)id; + CDOID newID = getNextCDOID(revision); + if (CDOIDUtil.isNull(newID) || newID.isTemporary()) + { + throw new IllegalStateException("newID=" + newID); //$NON-NLS-1$ + } + + commitContext.addIDMapping(oldID, newID); + } + + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected abstract CDOID getNextCDOID(CDORevision revision); + + protected void doPassivate() throws Exception + { + } + + protected void doUnpassivate() throws Exception + { + } + + /** + * @author Eike Stepper + * @since 3.0 + */ + public static class CommitDataRevisionHandler implements CDORevisionHandler + { + private IStoreAccessor storeAccessor; + + private long timeStamp; + + private InternalCDORevisionManager revisionManager; + + private List<CDOPackageUnit> newPackageUnits = new ArrayList<CDOPackageUnit>(); + + private List<CDOIDAndVersion> newObjects = new ArrayList<CDOIDAndVersion>(); + + private List<CDORevisionKey> changedObjects = new ArrayList<CDORevisionKey>(); + + private DetachCounter detachCounter = new DetachCounter(); + + public CommitDataRevisionHandler(IStoreAccessor storeAccessor, long timeStamp) + { + this.storeAccessor = storeAccessor; + this.timeStamp = timeStamp; + + InternalStore store = (InternalStore)storeAccessor.getStore(); + InternalRepository repository = store.getRepository(); + revisionManager = repository.getRevisionManager(); + + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + InternalCDOPackageUnit[] packageUnits = packageRegistry.getPackageUnits(timeStamp, timeStamp); + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + if (!packageUnit.isSystem()) + { + newPackageUnits.add(packageUnit); + } + } + } + + public CDOCommitData getCommitData() + { + storeAccessor.handleRevisions(null, null, timeStamp, true, new CDORevisionHandler.Filtered.Undetached(this)); + + List<CDOIDAndVersion> detachedObjects = detachCounter.getDetachedObjects(); + return new CDOCommitDataImpl(newPackageUnits, newObjects, changedObjects, detachedObjects); + } + + /** + * @since 4.0 + */ + public boolean handleRevision(CDORevision rev) + { + if (rev.getTimeStamp() != timeStamp) + { + throw new IllegalArgumentException("Invalid revision time stamp: " + + CDOCommonUtil.formatTimeStamp(rev.getTimeStamp())); + } + + if (rev instanceof DetachedCDORevision) + { + // Do nothing. Detached objects are handled by detachCounter. + } + else + { + InternalCDORevision revision = (InternalCDORevision)rev; + CDOID id = revision.getID(); + CDOBranch branch = revision.getBranch(); + int version = revision.getVersion(); + if (version > CDOBranchVersion.FIRST_VERSION) + { + CDOBranchVersion oldVersion = branch.getVersion(version - 1); + InternalCDORevision oldRevision = revisionManager.getRevisionByVersion(id, oldVersion, CDORevision.UNCHUNKED, + true); + InternalCDORevisionDelta delta = revision.compare(oldRevision); + changedObjects.add(delta); + + detachCounter.update(oldRevision, delta); + } + else + { + InternalCDORevision oldRevision = getRevisionFromBase(id, branch); + if (oldRevision != null) + { + InternalCDORevisionDelta delta = revision.compare(oldRevision); + changedObjects.add(delta); + } + else + { + InternalCDORevision newRevision = revision.copy(); + newRevision.setRevised(CDOBranchPoint.UNSPECIFIED_DATE); + newObjects.add(newRevision); + } + } + } + + return true; + } + + private InternalCDORevision getRevisionFromBase(CDOID id, CDOBranch branch) + { + if (branch.isMainBranch()) + { + return null; + } + + CDOBranchPoint base = branch.getBase(); + InternalCDORevision revision = revisionManager.getRevision(id, base, CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + if (revision == null) + { + revision = getRevisionFromBase(id, base.getBranch()); + } + + return revision; + } + + /** + * @author Eike Stepper + */ + private static final class DetachCounter extends CDOFeatureDeltaVisitorImpl + { + private Map<CDOID, AtomicInteger> counters = new HashMap<CDOID, AtomicInteger>(); + + private InternalCDORevision oldRevision; + + public DetachCounter() + { + } + + public void update(InternalCDORevision oldRevision, InternalCDORevisionDelta delta) + { + try + { + this.oldRevision = oldRevision; + delta.accept(this); + } + finally + { + this.oldRevision = null; + } + } + + public List<CDOIDAndVersion> getDetachedObjects() + { + List<CDOIDAndVersion> result = new ArrayList<CDOIDAndVersion>(); + for (Entry<CDOID, AtomicInteger> entry : counters.entrySet()) + { + int value = entry.getValue().get(); + if (value == -1) + { + CDOID id = entry.getKey(); + result.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION)); + } + } + + return result; + } + + @Override + public void visit(CDOAddFeatureDelta delta) + { + if (isContainment(delta.getFeature())) + { + handleContainment(delta.getValue(), 1); + } + } + + @Override + public void visit(CDORemoveFeatureDelta delta) + { + if (isContainment(delta.getFeature())) + { + handleContainment(delta.getValue(), -1); + } + } + + @Override + public void visit(CDOSetFeatureDelta delta) + { + if (isContainment(delta.getFeature())) + { + handleContainment(delta.getValue(), 1); + } + } + + @Override + public void visit(CDOUnsetFeatureDelta delta) + { + EStructuralFeature feature = delta.getFeature(); + if (isContainment(feature)) + { + Object value = oldRevision.getValue(feature); + handleContainment(value, -1); + } + } + + @Override + public void visit(CDOClearFeatureDelta delta) + { + EStructuralFeature feature = delta.getFeature(); + if (isContainment(feature)) + { + CDOList list = oldRevision.getList(feature); + for (Object value : list) + { + handleContainment(value, -1); + } + } + } + + private void handleContainment(Object value, int delta) + { + CDOID id = (CDOID)value; + AtomicInteger counter = counters.get(id); + if (counter == null) + { + counter = new AtomicInteger(); + counters.put(id, counter); + } + + counter.addAndGet(delta); + } + + private static boolean isContainment(EStructuralFeature feature) + { + if (feature instanceof EReference) + { + EReference reference = (EReference)feature; + return reference.isContainment(); + } + + return false; + } + } + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java new file mode 100644 index 0000000000..4b71389999 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.log.OMLogger; + +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class StoreAccessorPool +{ + /** + * The {@link IStore store} instance that manages this pool. + */ + private IStore store; + + /** + * The pooling context of this pool. An instance of either {@link ISession} or {@link IView}, or <code>null</code> if + * this pool is not contextual. + */ + private Object context; + + private ConcurrentLinkedQueue<StoreAccessorBase> accessors = new ConcurrentLinkedQueue<StoreAccessorBase>(); + + public StoreAccessorPool(IStore store, Object context) + { + this.store = store; + this.context = context; + } + + public IStore getStore() + { + return store; + } + + public Object getContext() + { + return context; + } + + /** + * Passivates the given {@link StoreAccessor store accessor} and adds it to this pool. + * + * @since 4.0 + */ + public void addStoreAccessor(StoreAccessorBase storeAccessor) + { + try + { + storeAccessor.doPassivate(); + accessors.add(storeAccessor); + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + } + + /** + * Returns a {@link StoreAccessor store accessor} from this pool if one is available, or <code>null</code> otherwise. + * If a store accessor is available it is removed from this pool and its unpassivate method is called. + * + * @since 4.0 + */ + public StoreAccessorBase removeStoreAccessor(Object context) + { + StoreAccessorBase accessor = accessors.poll(); + if (accessor != null) + { + try + { + accessor.doUnpassivate(); + accessor.setContext(context); + } + catch (Exception ex) + { + OM.LOG.error(ex); + return null; + } + } + + return accessor; + } + + /** + * Deactivates all contained {@link StoreAccessor store accessors} and clears this pool. + */ + public void dispose() + { + for (;;) + { + StoreAccessorBase accessor = accessors.poll(); + if (accessor == null) + { + break; + } + + LifecycleUtil.deactivate(accessor, OMLogger.Level.WARN); + } + + context = null; + store = null; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java new file mode 100644 index 0000000000..36b7dc1a32 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreChunkReader; + +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class StoreChunkReader implements IStoreChunkReader +{ + private IStoreAccessor accessor; + + private CDORevision revision; + + private EStructuralFeature feature; + + private List<Chunk> chunks = new ArrayList<Chunk>(0); + + public StoreChunkReader(IStoreAccessor accessor, CDORevision revision, EStructuralFeature feature) + { + this.accessor = accessor; + this.revision = revision; + this.feature = feature; + } + + public IStoreAccessor getAccessor() + { + return accessor; + } + + public CDORevision getRevision() + { + return revision; + } + + public EStructuralFeature getFeature() + { + return feature; + } + + public List<Chunk> getChunks() + { + return chunks; + } + + public void addSimpleChunk(int index) + { + chunks.add(new Chunk(index)); + } + + public void addRangedChunk(int fromIndex, int toIndex) + { + chunks.add(new Chunk(fromIndex, toIndex - fromIndex)); + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java new file mode 100644 index 0000000000..a6eef7052e --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException; + +import org.eclipse.net4j.util.CheckUtil; + +/** + * Static methods that may help with classes related to repository synchronization. + * + * @author Eike Stepper + * @since 4.1 + */ +public final class SyncingUtil +{ + private SyncingUtil() + { + } + + public static InternalView openViewWithLockArea(InternalSession session, InternalLockManager lockManager, + CDOBranch viewedBranch, String lockAreaID) + { + LockArea lockArea; + InternalView view; + + try + { + lockArea = lockManager.getLockArea(lockAreaID); + + // If we get here, the lockArea already exists. + view = (InternalView)lockManager.openView(session, InternalSession.TEMP_VIEW_ID, true, lockAreaID); + } + catch (LockAreaNotFoundException e) + { + // If we get here, the lockArea does not yet exist, so we open + // a view without a lockArea first, then create a lockArea with the given ID, + // and associate it with the view. + view = session.openView(InternalSession.TEMP_VIEW_ID, viewedBranch.getHead()); + lockArea = lockManager.createLockArea(view, lockAreaID); + view.setDurableLockingID(lockAreaID); + } + + CheckUtil.checkNull(lockAreaID, "lockAreaID"); + CheckUtil.checkNull(lockArea, "lockArea"); + CheckUtil.checkState(lockAreaID.equals(lockArea.getDurableLockingID()), "lockAreaID has incorrect value"); + + return view; + } +} diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java new file mode 100644 index 0000000000..f320267eb8 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java @@ -0,0 +1,18 @@ +/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server service provider interfaces and useful base implementations.
+ *
+ * @apiviz.exclude .*
+ */
+package org.eclipse.emf.cdo.spi.server;
+
|