diff options
44 files changed, 6039 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.repository/.classpath b/bundles/org.eclipse.equinox.p2.repository/.classpath new file mode 100644 index 000000000..64c5e31b7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.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/bundles/org.eclipse.equinox.p2.repository/.project b/bundles/org.eclipse.equinox.p2.repository/.project new file mode 100644 index 000000000..e73906f61 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.equinox.p2.repository</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> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..4a6dee366 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,347 @@ +#Fri Feb 06 10:55:56 EST 2009 +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.resourceCopyExclusionFilter=*.launch +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.4 +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=1000 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning +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=ignore +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore +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.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +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=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.3 +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=0 +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=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=false +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=false +org.eclipse.jdt.core.formatter.comment.format_line_comments=false +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +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=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not 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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not 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=do not 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=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=do not 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=do not 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=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=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.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=800 +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=false +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +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/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c4540955a --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,57 @@ +#Sun Sep 23 11:57:48 EDT 2007 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_core +formatter_settings_version=11 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=; +org.eclipse.jdt.ui.ondemandthreshold=3 +org.eclipse.jdt.ui.staticondemandthreshold=3 +org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><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_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +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.format_source_code=true +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=false +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=false +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=false +sp_cleanup.remove_unused_imports=false +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=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..979e4e8b4 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ + +eclipse.preferences.version=1 +pluginProject.extensions=true +resolve.requirebundle=false diff --git a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF new file mode 100644 index 000000000..3fec7d7fa --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF @@ -0,0 +1,38 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.equinox.p2.repository;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.eclipse.equinox.internal.p2.repository.Activator +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.ecf.filetransfer, + org.eclipse.ecf, + org.eclipse.equinox.p2.core +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: J2SE-1.4, + CDC-1.1/Foundation-1.1 +Bundle-Vendor: %providerName +Import-Package: org.eclipse.equinox.internal.p2.repository.helpers, + org.eclipse.equinox.internal.provisional.p2.core, + org.eclipse.equinox.security.storage +Export-Package: org.eclipse.equinox.internal.p2.persistence;x-friends:="org.eclipse.equinox.p2.artifact.repository,org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.metadata.repository", + org.eclipse.equinox.internal.p2.repository; + x-friends:="org.eclipse.equinox.p2.artifact.repository, + org.eclipse.equinox.p2.metadata.repository, + org.eclipse.equinox.p2.updatesite"; + uses:="org.eclipse.ecf.core.security, + org.eclipse.ecf.filetransfer.events, + org.eclipse.osgi.util, + org.eclipse.core.runtime, + org.eclipse.ecf.filetransfer", + org.eclipse.equinox.internal.p2.repository.helpers; + x-friends:="org.eclipse.equinox.p2.artifact.repository, + org.eclipse.equinox.p2.exemplarysetup, + org.eclipse.equinox.p2.metadata.repository, + org.eclipse.equinox.p2.updatesite"; + uses:="org.eclipse.equinox.internal.provisional.p2.core.eventbus, + org.eclipse.equinox.internal.provisional.p2.core.repository, + org.osgi.service.prefs, + org.eclipse.core.runtime", + org.eclipse.equinox.internal.provisional.p2.repository;uses:="org.osgi.framework,org.eclipse.core.runtime", + org.eclipse.equinox.internal.provisional.spi.p2.repository diff --git a/bundles/org.eclipse.equinox.p2.repository/about.html b/bundles/org.eclipse.equinox.p2.repository/about.html new file mode 100644 index 000000000..460233046 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/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 2, 2006</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>
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.repository/build.properties b/bundles/org.eclipse.equinox.p2.repository/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/bundles/org.eclipse.equinox.p2.repository/plugin.properties b/bundles/org.eclipse.equinox.p2.repository/plugin.properties new file mode 100644 index 000000000..44b0907ee --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/plugin.properties @@ -0,0 +1,12 @@ +############################################################################### +# Copyright (c) 2007 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### +pluginName = Equinox Provisioning Metadata Repository +providerName = Eclipse.org diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeParser.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeParser.java new file mode 100644 index 000000000..0d654e3f5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeParser.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import java.io.*; +import java.net.URI; +import java.util.*; +import javax.xml.parsers.ParserConfigurationException; +import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties; +import org.eclipse.equinox.internal.provisional.p2.core.Version; +import org.eclipse.equinox.internal.provisional.p2.core.VersionRange; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.BundleContext; +import org.xml.sax.*; + +/* + * Class used to read a composite repository. + */ +public class CompositeParser extends XMLParser implements XMLConstants { + + private static final Version CURRENT_VERSION = new Version(1, 0, 0); + static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, new Version(2, 0, 0), false); + private static final String REQUIRED_CAPABILITY_ELEMENT = "required"; //$NON-NLS-1$ + private static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$ + String repositoryType; + private CompositeRepositoryState theState; + + protected class ChildrenHandler extends AbstractHandler { + private ArrayList children; + + public ChildrenHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, CHILDREN_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + children = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public URI[] getChildren() { + int size = children.size(); + URI[] result = new URI[size]; + int i = 0; + for (Iterator it = children.iterator(); it.hasNext(); i++) { + result[i] = (URI) it.next(); + } + return result; + } + + public void startElement(String name, Attributes attributes) { + if (name.equals(CHILD_ELEMENT)) { + new ChildHandler(this, attributes, children); + } else { + invalidElement(name, attributes); + } + } + } + + protected class ChildHandler extends AbstractHandler { + private final String[] required = new String[] {LOCATION_ELEMENT}; + private final String[] optional = new String[] {}; + + URI currentRepo = null; + + private List repos; + + public ChildHandler(AbstractHandler parentHandler, Attributes attributes, List repos) { + super(parentHandler, CHILD_ELEMENT); + String[] values = parseAttributes(attributes, required, optional); + this.repos = repos; + //skip entire subrepository if the location is missing + if (values[0] == null) + return; + currentRepo = checkURI(REQUIRED_CAPABILITY_ELEMENT, URI_ATTRIBUTE, values[0]); + + } + + public void startElement(String name, Attributes attributes) { + checkCancel(); + } + + protected void finished() { + if (currentRepo != null) + repos.add(currentRepo); + } + } + + private final class RepositoryDocHandler extends DocHandler { + + public RepositoryDocHandler(String rootName, RootHandler rootHandler) { + super(rootName, rootHandler); + } + + public void processingInstruction(String target, String data) throws SAXException { + if (repositoryType.equals(target)) { + Version repositoryVersion = extractPIVersion(target, data); + if (!XML_TOLERANCE.isIncluded(repositoryVersion)) { + throw new SAXException(NLS.bind(Messages.io_IncompatibleVersion, repositoryVersion, XML_TOLERANCE)); + } + } + } + } + + /* + * Handler for the "repository" attribute. + */ + private final class RepositoryHandler extends RootHandler { + + private final String[] required = new String[] {NAME_ATTRIBUTE, TYPE_ATTRIBUTE, VERSION_ATTRIBUTE}; + private final String[] optional = new String[] {DESCRIPTION_ATTRIBUTE, PROVIDER_ATTRIBUTE}; + private PropertiesHandler propertiesHandler = null; + private ChildrenHandler childrenHandler = null; + private CompositeRepositoryState state; + private String[] attrValues = new String[required.length + optional.length]; + + public RepositoryHandler() { + super(); + } + + public CompositeRepositoryState getRepository() { + return state; + } + + protected void handleRootAttributes(Attributes attributes) { + attrValues = parseAttributes(attributes, required, optional); + attrValues[2] = checkVersion(REPOSITORY_ELEMENT, VERSION_ATTRIBUTE, attrValues[2]).toString(); + } + + public void startElement(String name, Attributes attributes) { + if (PROPERTIES_ELEMENT.equals(name)) { + if (propertiesHandler == null) { + propertiesHandler = new PropertiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (CHILDREN_ELEMENT.equals(name)) { + if (childrenHandler == null) { + childrenHandler = new ChildrenHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else { + invalidElement(name, attributes); + } + } + + /* + * If we parsed valid XML then fill in our repository state object with the parsed data. + */ + protected void finished() { + if (isValidXML()) { + state = new CompositeRepositoryState(); + state.setName(attrValues[0]); + state.setType(attrValues[1]); + state.setVersion(attrValues[2]); + state.setDescription(attrValues[3]); + state.setProvider(attrValues[4]); + state.setProperties((propertiesHandler == null ? new OrderedProperties(0) // + : propertiesHandler.getProperties())); + state.setChildren((childrenHandler == null ? new URI[0] // + : childrenHandler.getChildren())); + } + } + } + + public CompositeParser(BundleContext context, String bundleId, String type) { + super(context, bundleId); + this.repositoryType = type; + } + + public void parse(File file) throws IOException { + parse(new FileInputStream(file)); + } + + public synchronized void parse(InputStream stream) throws IOException { + this.status = null; + try { + // TODO: currently not caching the parser since we make no assumptions + // or restrictions on concurrent parsing + getParser(); + RepositoryHandler repositoryHandler = new RepositoryHandler(); + xmlReader.setContentHandler(new RepositoryDocHandler(REPOSITORY_ELEMENT, repositoryHandler)); + xmlReader.parse(new InputSource(stream)); + if (isValidXML()) { + theState = repositoryHandler.getRepository(); + } + } catch (SAXException e) { + throw new IOException(e.getMessage()); + } catch (ParserConfigurationException e) { + throw new IOException(e.getMessage()); + } finally { + stream.close(); + } + } + + public CompositeRepositoryState getRepositoryState() { + return theState; + } + + //TODO what? + protected Object getRootObject() { + return null; + } + + protected String getErrorMessage() { + return Messages.io_parseError; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryIO.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryIO.java new file mode 100644 index 000000000..02319fb52 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryIO.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import java.io.*; +import java.net.URL; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.core.Activator; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException; +import org.eclipse.osgi.util.NLS; + +/** + * This class reads and writes repository metadata (e.g. table of contents files) + * for composite artifact and metadata repositories. + * <p> + * Note: This class is not used for reading or writing the actual composite repositories. + */ +public class CompositeRepositoryIO { + + /** + * Writes the given repository to the stream. + * This method performs buffering, and closes the stream when finished. + */ + public void write(CompositeRepositoryState repository, OutputStream output, String type) { + OutputStream bufferedOutput = null; + try { + try { + bufferedOutput = new BufferedOutputStream(output); + CompositeWriter repositoryWriter = new CompositeWriter(bufferedOutput, type); + repositoryWriter.write(repository); + } finally { + if (bufferedOutput != null) { + bufferedOutput.close(); + } + } + } catch (IOException ioe) { + // TODO shouldn't this throw a core exception? + ioe.printStackTrace(); + } + } + + /** + * Reads the composite repository from the given stream, + * and returns the contained array of abstract composite repositories. + * + * This method performs buffering, and closes the stream when finished. + */ + public CompositeRepositoryState read(URL location, InputStream input, String type, IProgressMonitor monitor) throws ProvisionException { + BufferedInputStream bufferedInput = null; + try { + try { + bufferedInput = new BufferedInputStream(input); + CompositeParser repositoryParser = new CompositeParser(Activator.getContext(), Activator.ID, type); + repositoryParser.parse(input); + IStatus result = repositoryParser.getStatus(); + switch (result.getSeverity()) { + case IStatus.CANCEL : + throw new OperationCanceledException(); + case IStatus.ERROR : + throw new ProvisionException(result); + case IStatus.WARNING : + case IStatus.INFO : + LogHelper.log(result); + } + CompositeRepositoryState repositoryState = repositoryParser.getRepositoryState(); + if (repositoryState == null) + throw new ProvisionException(new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, Messages.io_parseError, null)); + return repositoryState; + } finally { + if (bufferedInput != null) + bufferedInput.close(); + } + } catch (IOException ioe) { + String msg = NLS.bind(Messages.io_failedRead, location); + throw new ProvisionException(new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, msg, ioe)); + } + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryState.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryState.java new file mode 100644 index 000000000..83f7795a6 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryState.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import java.net.URI; +import java.util.Map; + +/* + * Instances of this class represent a composite repository (either metadata + * or artifact) and are used in persisting or retrieving the repository to/from disk. + */ +public class CompositeRepositoryState { + private String name; + private String type; + private String version; + private String provider; + private String description; + private URI location; + private Map properties; + private URI[] children; + + public void setName(String value) { + name = value; + } + + public String getName() { + return name; + } + + public void setType(String value) { + type = value; + } + + public String getType() { + return type; + } + + public void setVersion(String value) { + version = value; + } + + public String getVersion() { + return version; + } + + public void setProvider(String value) { + provider = value; + } + + public String getProvider() { + return provider; + } + + public void setDescription(String value) { + description = value; + } + + public String getDescription() { + return description; + } + + public void setLocation(URI value) { + location = value; + } + + public URI getLocation() { + return location; + } + + public void setProperties(Map value) { + properties = value; + } + + public Map getProperties() { + return properties; + } + + public void setChildren(URI[] value) { + children = value; + } + + public URI[] getChildren() { + return children; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeWriter.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeWriter.java new file mode 100644 index 000000000..26dd6707f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeWriter.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import org.eclipse.core.runtime.URIUtil; +import org.eclipse.equinox.internal.provisional.p2.core.Version; + +/* + * Class used to persist a composite repository. + */ +public class CompositeWriter extends XMLWriter implements XMLConstants { + + private static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$ + private static final Version CURRENT_VERSION = new Version(1, 0, 0); + + public CompositeWriter(OutputStream output, String type) throws UnsupportedEncodingException { + super(output, new XMLWriter.ProcessingInstruction[] {XMLWriter.ProcessingInstruction.makeTargetVersionInstruction(type, CURRENT_VERSION)}); + // TODO: add a processing instruction for the metadata version + } + + /** + * Writes a list of URIs referring to sub repositories + */ + protected void writeChildren(URI[] children) { + if (children == null || children.length == 0) + return; + start(CHILDREN_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, children.length); + for (int i = 0; i < children.length; i++) + writeChild(children[i]); + end(CHILDREN_ELEMENT); + } + + protected void writeChild(URI encodedURI) { + String unencodedString = URIUtil.toUnencodedString(encodedURI); + start(CHILD_ELEMENT); + attribute(LOCATION_ELEMENT, unencodedString); + end(CHILD_ELEMENT); + } + + /** + * Write the given composite repository to the output stream. + */ + public void write(CompositeRepositoryState repository) { + start(REPOSITORY_ELEMENT); + attribute(NAME_ATTRIBUTE, repository.getName()); + attribute(TYPE_ATTRIBUTE, repository.getType()); + attribute(VERSION_ATTRIBUTE, repository.getVersion()); + attributeOptional(PROVIDER_ATTRIBUTE, repository.getProvider()); + attributeOptional(DESCRIPTION_ATTRIBUTE, repository.getDescription()); // TODO: could be cdata? + writeProperties(repository.getProperties()); + writeChildren(repository.getChildren()); + end(REPOSITORY_ELEMENT); + flush(); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/Messages.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/Messages.java new file mode 100644 index 000000000..3762b39a8 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/Messages.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.persistence.messages"; //$NON-NLS-1$ + + static { + // load message values from bundle file and assign to fields below + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + public static String XMLParser_No_SAX_Parser; + public static String XMLParser_Error_At_Line; + public static String XMLParser_Error_At_Line_Column; + public static String XMLParser_Error_At_Name_Line; + public static String XMLParser_Error_At_Name_Line_Column; + public static String XMLParser_Missing_Required_Attribute; + public static String XMLParser_Illegal_Value_For_Attribute; + public static String XMLParser_Duplicate_Element; + + public static String io_failedRead; + public static String io_IncompatibleVersion; + public static String io_parseError; + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLConstants.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLConstants.java new file mode 100644 index 000000000..f7d63c58c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLConstants.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Genuitec, LLC - added license support + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +public interface XMLConstants { + + // Constants used in defining a default processing instruction + // including a class name and a version of the associated XML + // for some category of objects. + // e.g. <?category class='a.b.c.SomeClass' version='1.2.3'?> + // + public static final String PI_CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ + public static final String PI_VERSION_ATTRIBUTE = "version"; //$NON-NLS-1$ + + // Element and attribute names for a standard property collection. + // e.g. <properties size='1'> + // <property name='some_name' value='some_value'/> + // </properties> + public static final String PROPERTIES_ELEMENT = "properties"; //$NON-NLS-1$ + public static final String PROPERTY_ELEMENT = "property"; //$NON-NLS-1$ + public static final String PROPERTY_NAME_ATTRIBUTE = "name"; //$NON-NLS-1$ + public static final String PROPERTY_VALUE_ATTRIBUTE = "value"; //$NON-NLS-1$ + + // Constants for the names of common general attributes + public static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$ + public static final String PARENT_ID_ATTRIBUTE = "parentId"; //$NON-NLS-1$ + public static final String TYPE_ATTRIBUTE = "type"; //$NON-NLS-1$ + public static final String NAME_ATTRIBUTE = "name"; //$NON-NLS-1$ + public static final String VERSION_ATTRIBUTE = "version"; //$NON-NLS-1$ + public static final String VERSION_RANGE_ATTRIBUTE = "range"; //$NON-NLS-1$ + public static final String NAMESPACE_ATTRIBUTE = "namespace"; //$NON-NLS-1$ + public static final String CLASSIFIER_ATTRIBUTE = "classifier"; //$NON-NLS-1$ + public static final String DESCRIPTION_ATTRIBUTE = "description"; //$NON-NLS-1$ + public static final String PROVIDER_ATTRIBUTE = "provider"; //$NON-NLS-1$ + public static final String URL_ATTRIBUTE = "url"; //$NON-NLS-1$ + public static final String URI_ATTRIBUTE = "uri"; //$NON-NLS-1$ + + // Constants for the license and copyright elements + public static final String LICENSES_ELEMENT = "licenses"; //$NON-NLS-1$ + public static final String LICENSE_ELEMENT = "license"; //$NON-NLS-1$ + public static final String COPYRIGHT_ELEMENT = "copyright"; //$NON-NLS-1$ + + // A constant for the name of an attribute of a collection or array element + // specifying the size or length + public static final String COLLECTION_SIZE_ATTRIBUTE = "size"; //$NON-NLS-1$ + + // A constant for an empty array of attribute names + public static String[] noAttributes = new String[0]; + + //Constants for attributes of a composite repository + public static final String CHILDREN_ELEMENT = "children"; //$NON-NLS-1$ + public static final String CHILD_ELEMENT = "child"; //$NON-NLS-1$ + public static final String LOCATION_ELEMENT = "location"; //$NON-NLS-1$ +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLParser.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLParser.java new file mode 100644 index 000000000..d96221e40 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLParser.java @@ -0,0 +1,765 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import java.net.*; +import java.util.List; +import java.util.StringTokenizer; +import javax.xml.parsers.*; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.core.Activator; +import org.eclipse.equinox.internal.p2.core.StringPool; +import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties; +import org.eclipse.equinox.internal.p2.core.helpers.Tracing; +import org.eclipse.equinox.internal.provisional.p2.core.Version; +import org.eclipse.equinox.internal.provisional.p2.core.VersionRange; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; +import org.xml.sax.*; +import org.xml.sax.ContentHandler; +import org.xml.sax.helpers.DefaultHandler; + +public abstract class XMLParser extends DefaultHandler implements XMLConstants { + + // Get the root object that is being parsed. + protected abstract Object getRootObject(); + + // Get a generic parser error message for inclusion in an error status + protected abstract String getErrorMessage(); + + protected BundleContext context; // parser class bundle context + protected String bundleId; // parser class bundle id + + protected XMLReader xmlReader; // the XML reader for the parser + + protected MultiStatus status = null; // accumulation of non-fatal errors + protected Locator locator = null; // document locator, if supported by the parser + + protected StringPool stringPool = new StringPool();//used to eliminate string duplication + private IProgressMonitor monitor; + + private static ServiceTracker xmlTracker = null; + + public XMLParser(BundleContext context, String pluginId) { + super(); + this.context = context; + this.bundleId = pluginId; + } + + /** + * Non-fatal errors accumulated during parsing. + */ + public IStatus getStatus() { + return (status != null ? status : Status.OK_STATUS); + } + + /** + * Returns the canonical form of a string. Used to eliminate duplicate equal + * strings. + */ + protected String canonicalize(String string) { + return stringPool == null ? string : stringPool.add(string); + } + + public boolean isValidXML() { + return (status == null || !status.matches(IStatus.ERROR | IStatus.CANCEL)); + } + + private static SAXParserFactory acquireXMLParsing(BundleContext context) { + if (xmlTracker == null) { + xmlTracker = new ServiceTracker(context, SAXParserFactory.class.getName(), null); + xmlTracker.open(); + } + return (SAXParserFactory) xmlTracker.getService(); + } + + protected static void releaseXMLParsing() { + if (xmlTracker != null) { + xmlTracker.close(); + } + } + + protected SAXParser getParser() throws ParserConfigurationException, SAXException { + SAXParserFactory factory = acquireXMLParsing(this.context); + if (factory == null) { + throw new SAXException(Messages.XMLParser_No_SAX_Parser); + } + factory.setNamespaceAware(true); + factory.setValidating(false); + try { + factory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$ + } catch (SAXException se) { + // some parsers may not support string interning + } + SAXParser theParser = factory.newSAXParser(); + if (theParser == null) { + throw new SAXException(Messages.XMLParser_No_SAX_Parser); + } + xmlReader = theParser.getXMLReader(); + return theParser; + } + + public static String makeSimpleName(String localName, String qualifiedName) { + if (localName != null && localName.length() > 0) { + return localName; + } + int nameSpaceIndex = qualifiedName.indexOf(":"); //$NON-NLS-1$ + return (nameSpaceIndex == -1 ? qualifiedName : qualifiedName.substring(nameSpaceIndex + 1)); + } + + /** + * Set the document locator for the parser + * + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ + public void setDocumentLocator(Locator docLocator) { + locator = docLocator; + } + + /** + * Sets the progress monitor for the parser + */ + protected void setProgressMonitor(IProgressMonitor monitor) { + this.monitor = monitor; + } + + /** + * Abstract base class for content handlers + */ + protected abstract class AbstractHandler extends DefaultHandler { + + protected ContentHandler parentHandler = null; + protected String elementHandled = null; + + protected StringBuffer characters = null; // character data inside an element + + public AbstractHandler() { + // Empty constructor for a root handler + } + + public AbstractHandler(ContentHandler parentHandler) { + this.parentHandler = parentHandler; + xmlReader.setContentHandler(this); + } + + public AbstractHandler(ContentHandler parentHandler, String elementHandled) { + this.parentHandler = parentHandler; + xmlReader.setContentHandler(this); + this.elementHandled = elementHandled; + } + + /** + * Set the document locator for the parser + * + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ + public void setDocumentLocator(Locator docLocator) { + locator = docLocator; + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + finishCharacters(); + String name = makeSimpleName(localName, qName); + trace(name, attributes); + startElement(name, attributes); + } + + public abstract void startElement(String name, Attributes attributes) throws SAXException; + + public void invalidElement(String name, Attributes attributes) { + unexpectedElement(this, name, attributes); + new IgnoringHandler(this); + } + + public void endElement(String namespaceURI, String localName, String qName) { + // TODO: throw a bad state error if makeSimpleName(localName, qName) != elementHandled + finishCharacters(); + finished(); + // Restore the parent content handler + xmlReader.setContentHandler(parentHandler); + } + + /** + * An implementation for startElement when there are no sub-elements + */ + protected void noSubElements(String name, Attributes attributes) { + unexpectedElement(this, name, attributes); + // Create a new handler to ignore subsequent nested elements + new IgnoringHandler(this); + } + + /* + * Save up character data until endElement or nested startElement + * + * @see org.xml.sax.ContentHandler#characters + */ + public void characters(char[] chars, int start, int length) { + if (this.characters == null) { + this.characters = new StringBuffer(); + } + this.characters.append(chars, start, length); + } + + // Consume the characters accumulated in this.characters. + // Called before startElement or endElement + private String finishCharacters() { + // common case -- no characters or only whitespace + if (this.characters == null || this.characters.length() == 0) { + return null; + } + if (allWhiteSpace(this.characters)) { + this.characters.setLength(0); + return null; + } + + // process the characters + try { + String trimmedChars = this.characters.toString().trim(); + if (trimmedChars.length() == 0) { + // this shouldn't happen due to the test for allWhiteSpace above + System.err.println("Unexpected non-whitespace characters: " //$NON-NLS-1$ + + trimmedChars); + return null; + } + processCharacters(trimmedChars); + return trimmedChars; + } finally { + this.characters.setLength(0); + } + } + + // Method to override in the handler of an element with CDATA. + protected void processCharacters(String data) { + if (data.length() > 0) { + unexpectedCharacterData(this, data); + } + } + + private boolean allWhiteSpace(StringBuffer sb) { + int length = sb.length(); + for (int i = 0; i < length; i += 1) { + if (!Character.isWhitespace(sb.charAt(i))) { + return false; + } + } + return true; + } + + /** + * Called when this element and all elements nested into it have been + * handled. + */ + protected void finished() { + // Do nothing by default + } + + /* + * A name used to identify the handler. + */ + public String getName() { + return (elementHandled != null ? elementHandled : "NoName"); //$NON-NLS-1$ + } + + /** + * In p2 1.0 we stored URLs, in 1.1 and later we store URIs. This method will + * first check for a URI, and then resort to looking for a URL attribute for + * backwards compatibility. + * @param attributes The attributes to parse + * @param required If true, an exception is thrown if no URI or URL attribute is present + */ + protected URI parseURIAttribute(Attributes attributes, boolean required) { + String location = parseOptionalAttribute(attributes, URI_ATTRIBUTE); + try { + if (location != null) + return new URI(location); + if (required) + location = parseRequiredAttributes(attributes, new String[] {URL_ATTRIBUTE})[0]; + else + location = parseOptionalAttribute(attributes, URL_ATTRIBUTE); + if (location == null) + return null; + return URIUtil.toURI(new URL(location)); + } catch (MalformedURLException e) { + invalidAttributeValue(elementHandled, URL_ATTRIBUTE, location, e); + } catch (URISyntaxException e) { + invalidAttributeValue(elementHandled, URL_ATTRIBUTE, location, e); + } + return null; + } + + /** + * Parse the attributes of an element with two required attributes. + */ + protected String[] parseRequiredAttributes(Attributes attributes, String name1, String name2) { + return parseRequiredAttributes(attributes, new String[] {name1, name2}); + } + + /** + * Parse the attributes of an element with only required attributes. + */ + protected String[] parseRequiredAttributes(Attributes attributes, String[] required) { + return parseAttributes(attributes, required, noAttributes); + } + + /** + * Parse the attributes of an element with a single optional attribute. + */ + protected String parseOptionalAttribute(Attributes attributes, String name) { + return parseAttributes(attributes, noAttributes, new String[] {name})[0]; + } + + /** + * Parse the attributes of an element, given the list of required and optional ones. + * Return values in same order, null for those not present. + * Log warnings for extra attributes or missing required attributes. + */ + protected String[] parseAttributes(Attributes attributes, String[] required, String[] optional) { + String[] result = new String[required.length + optional.length]; + for (int i = 0; i < attributes.getLength(); i += 1) { + String name = attributes.getLocalName(i); + String value = canonicalize(attributes.getValue(i).trim()); + int j; + if ((j = indexOf(required, name)) >= 0) { + result[j] = value; + } else if ((j = indexOf(optional, name)) >= 0) { + result[required.length + j] = value; + } else { + unexpectedAttribute(elementHandled, name, value); + } + } + for (int i = 0; i < required.length; i += 1) { + checkRequiredAttribute(elementHandled, required[i], result[i]); + } + return result; + } + + } + + /** + * Handler for an XML document. + * + * Using the inelegant name 'DocHandler' to clearly distinguish + * this class from the deprecated org.xml.sax.DocumentHandler. + */ + protected class DocHandler extends AbstractHandler { + + RootHandler rootHandler; + + public DocHandler(String rootName, RootHandler rootHandler) { + super(null, rootName); + this.rootHandler = rootHandler; + } + + public void startElement(String name, Attributes attributes) { + if (name.equals(elementHandled)) { + rootHandler.initialize(this, name, attributes); + xmlReader.setContentHandler(rootHandler); + } else { + this.noSubElements(name, attributes); + } + } + + } + + /** + * Abstract handler for the root element. + */ + protected abstract class RootHandler extends AbstractHandler { + + public RootHandler() { + super(); + } + + public void initialize(DocHandler document, String rootName, Attributes attributes) { + this.parentHandler = document; + this.elementHandled = rootName; + handleRootAttributes(attributes); + } + + protected abstract void handleRootAttributes(Attributes attributes); + + } + + /** + * Handler for an ordered properties collection. + */ + protected class PropertiesHandler extends AbstractHandler { + + private OrderedProperties properties; + + public PropertiesHandler(ContentHandler parentHandler, Attributes attributes) { + super(parentHandler, PROPERTIES_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + properties = (size != null ? new OrderedProperties(new Integer(size).intValue()) : new OrderedProperties()); + } + + public OrderedProperties getProperties() { + return properties; + } + + public void startElement(String name, Attributes attributes) { + if (name.equals(PROPERTY_ELEMENT)) { + new PropertyHandler(this, attributes, properties); + } else { + invalidElement(name, attributes); + } + } + + } + + /** + * Handler for a property in an ordered properties collection. + */ + protected class PropertyHandler extends AbstractHandler { + + public PropertyHandler(ContentHandler parentHandler, Attributes attributes, OrderedProperties properties) { + super(parentHandler, PROPERTY_ELEMENT); + String[] property = parseProperty(attributes); + if (isValidProperty(property)) { + properties.setProperty(property[0], property[1]); + } + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + + private String[] parseProperty(Attributes attributes) { + return parseRequiredAttributes(attributes, PROPERTY_NAME_ATTRIBUTE, PROPERTY_VALUE_ATTRIBUTE); + } + + private boolean isValidProperty(String[] property) { + return (property.length == 2 && property[0] != null && property[1] != null); + } + } + + /** + * Handler for an element with only cdata and no sub-elements. + */ + protected class TextHandler extends AbstractHandler { + + private String text = null; + + private List texts = null; + + // Constructor for a subclass that processes the attributes + public TextHandler(AbstractHandler parent, String elementName) { + super(parent, elementName); + } + + // Constructor for a subclass with no attributes + public TextHandler(AbstractHandler parent, String elementName, Attributes attributes) { + super(parent, elementName); + parseAttributes(attributes, noAttributes, noAttributes); + } + + public TextHandler(AbstractHandler parent, String elementName, Attributes attributes, List texts) { + super(parent, elementName); + parseAttributes(attributes, noAttributes, noAttributes); + this.texts = texts; + } + + public String getText() { + return (text != null ? text : ""); //$NON-NLS-1$ + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + + protected void processCharacters(String data) { + this.text = canonicalize(data); + if (texts != null) { + texts.add(getText()); + } + } + + } + + /** + * Handler for ignoring content. + */ + protected class IgnoringHandler extends AbstractHandler { + + public IgnoringHandler(AbstractHandler parent) { + super(parent); + this.elementHandled = "IgnoringAll"; //$NON-NLS-1$ + } + + public void startElement(String name, Attributes attributes) { + noSubElements(name, attributes); + } + + } + + // Helper for processing instructions that include a Version. + public Version extractPIVersion(String target, String data) { + return checkVersion(target, PI_VERSION_ATTRIBUTE, extractPIAttribute(data, PI_VERSION_ATTRIBUTE)); + } + + private String extractPIAttribute(String data, String key) { + StringTokenizer piTokenizer = new StringTokenizer(data, " \'\""); //$NON-NLS-1$ + String[] tokens = new String[piTokenizer.countTokens()]; + int index = 0; + int valueIndex = -1; + while (piTokenizer.hasMoreTokens() && index < tokens.length) { + tokens[index] = piTokenizer.nextToken(); + if (tokens[index].equals(key + '=') && index < tokens.length) { + valueIndex = index + 1; + } + index++; + } + return (valueIndex >= 0 ? tokens[valueIndex] : ""); //$NON-NLS-1$ + } + + public void error(SAXParseException ex) { + addError(IStatus.WARNING, ex.getMessage(), ex); + } + + public void fatalError(SAXParseException ex) { + addError(IStatus.ERROR, ex.getMessage(), ex); + } + + protected String getErrorPrefix() { + return null; + } + + protected String getErrorSuffix() { + return null; + } + + /** + * Collects an error or warning that occurred during parsing. + */ + public final void addError(int severity, String msg, Throwable exception) { + int line = 0; + int column = 0; + String key = msg; + Object[] args = new Object[] {}; + String root = (getRootObject() == null ? "" //$NON-NLS-1$ + : " (" + getRootObject() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + if (this.locator != null) { + String name = this.locator.getSystemId(); + line = this.locator.getLineNumber(); + column = this.locator.getColumnNumber(); + if (line > 0) { + args = new Object[] {msg, root, name, new Integer(line), new Integer(column)}; + if (column > 0) { + key = (name != null ? Messages.XMLParser_Error_At_Name_Line_Column // + : Messages.XMLParser_Error_At_Line_Column); + } else { + key = (name != null ? Messages.XMLParser_Error_At_Name_Line // + : Messages.XMLParser_Error_At_Line); + } + } + } + String errMsg = NLS.bind(key, args); + String prefix = getErrorPrefix(); + String suffix = getErrorSuffix(); + if (prefix != null) { + errMsg = prefix + errMsg; + } + if (suffix != null) { + errMsg = errMsg + suffix; + } + IStatus currStatus = new Status(severity, Activator.ID, errMsg, exception); + if (this.status == null) { + this.status = new MultiStatus(bundleId, IStatus.OK, new IStatus[] {currStatus}, getErrorMessage(), null); + } else { + this.status.add(currStatus); + } + } + + public void trace(String element, Attributes attributes) { + // TODO: support logging + // if (!getLogger().isDebugLoggable()) { + // return; + // } + // int indentSize = (this.stateStack != null ? this.stateStack.size() - 1 : 1); + // if (attributes == null) { + // indentSize -= 1; + // } + // char[] indent = new char[2 * indentSize]; + // Arrays.fill(indent, ' '); + // StringBuffer sb = new StringBuffer(); + // sb.append(indent); + // sb.append('<'); + // if (attributes != null) { + // sb.append(element); + // toString(sb, attributes); + // } else { + // sb.append('/').append(element); + // } + // sb.append('>'); + // getLogger().debug(sb.toString()); + } + + private static String toString(Attributes attributes) { + StringBuffer result = new StringBuffer(); + toString(result, attributes); + return result.toString(); + } + + private static void toString(StringBuffer sb, Attributes attributes) { + for (int i = 0; i < attributes.getLength(); i += 1) { + String name = attributes.getLocalName(i); + String value = attributes.getValue(i).trim(); + sb.append(' ').append(name); + sb.append('=').append('"'); + sb.append(value); + sb.append('"'); + } + } + + public void checkRequiredAttribute(String element, String name, Object value) { + if (value == null) { + addError(IStatus.WARNING, NLS.bind(Messages.XMLParser_Missing_Required_Attribute, element, name), null); + } + } + + // Check the format of a required boolean attribute + public Boolean checkBoolean(String element, String attribute, String value) { + try { + return Boolean.valueOf(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } catch (NullPointerException npe) { + invalidAttributeValue(element, attribute, null); + } + return Boolean.FALSE; + } + + // Check the format of an optional boolean attribute + public Boolean checkBoolean(String element, String attribute, String value, boolean defaultValue) { + Boolean result = (defaultValue ? Boolean.TRUE : Boolean.FALSE); + if (value != null) { + try { + return Boolean.valueOf(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } + } + return result; + } + + // Check the format of a required integer attribute + public int checkInteger(String element, String attribute, String value) { + try { + return Integer.parseInt(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } + return 0; + } + + // Check the format of a required URI attribute + public URI checkURI(String element, String attribute, String value) { + try { + return URIUtil.fromString(value); + } catch (URISyntaxException e) { + invalidAttributeValue(element, attribute, value); + } + //TODO ok to return null? + return null; + } + + public void checkCancel() { + if (monitor != null && monitor.isCanceled()) + throw new OperationCanceledException(); + } + + /** + * Converts a version string to a Version object. Returns the version object, + * or {@link Version#emptyVersion} if the value was not a valid version. + */ + public Version checkVersion(String element, String attribute, String value) { + try { + if (value != null) + return new Version(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } catch (NullPointerException npe) { + invalidAttributeValue(element, attribute, null); + } + return Version.emptyVersion; + } + + public VersionRange checkVersionRange(String element, String attribute, String value) { + try { + return new VersionRange(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } catch (NullPointerException npe) { + invalidAttributeValue(element, attribute, null); + } + return VersionRange.emptyRange; + } + + public void unexpectedAttribute(String element, String attribute, String value) { + if (Tracing.DEBUG_PARSE_PROBLEMS) + Tracing.debug("Unexpected attribute for element " + element + ": " + attribute + '=' + value); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void invalidAttributeValue(String element, String attribute, String value) { + invalidAttributeValue(element, attribute, value, null); + } + + public void invalidAttributeValue(String element, String attribute, String value, Throwable exception) { + addError(IStatus.WARNING, NLS.bind(Messages.XMLParser_Illegal_Value_For_Attribute, new Object[] {attribute, element, value}), exception); + } + + public void unexpectedElement(AbstractHandler handler, String element, Attributes attributes) { + if (Tracing.DEBUG_PARSE_PROBLEMS) + Tracing.debug("Unexpected element in element " + handler.getName() + ": <" + element + toString(attributes) + '>'); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void duplicateElement(AbstractHandler handler, String element, Attributes attributes) { + addError(IStatus.WARNING, NLS.bind(Messages.XMLParser_Duplicate_Element, new Object[] {handler.getName(), element, toString(attributes)}), null); + //ignore the duplicate element entirely because we have already logged it + new IgnoringHandler(handler); + } + + public void unexpectedCharacterData(AbstractHandler handler, String cdata) { + if (Tracing.DEBUG_PARSE_PROBLEMS) + Tracing.debug("Unexpected character data in element " + handler.getName() + ": " + cdata.trim()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Find the index of the first occurrence of object in array, or -1. + * Use Arrays.binarySearch if array is big and sorted. + */ + protected static int indexOf(String[] array, String value) { + for (int i = 0; i < array.length; i += 1) { + if (value == null ? array[i] == null : value.equals(array[i])) { + return i; + } + } + return -1; + } + + // public class BadStateError extends AssertionError { + // private static final long serialVersionUID = 1L; // not serialized + // + // public BadStateError() { + // super("unexpected state" + //$NON-NLS-1$ + // (XMLParser.this.stateStack != null ? ": " + XMLParser.this.stateStack //$NON-NLS-1$ + // : "")); //$NON-NLS-1$ + // } + // + // public BadStateError(String element) { + // super("unexpected state for " + element + //$NON-NLS-1$ + // (XMLParser.this.stateStack != null ? ": " + XMLParser.this.stateStack //$NON-NLS-1$ + // : "")); //$NON-NLS-1$ + // } + // } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLWriter.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLWriter.java new file mode 100644 index 000000000..231b601c4 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLWriter.java @@ -0,0 +1,307 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.persistence; + +import java.io.*; +import java.util.*; +import org.eclipse.equinox.internal.provisional.p2.core.Version; + +public class XMLWriter implements XMLConstants { + + public static class ProcessingInstruction { + + private String target; + private String[] data; + + // The standard UTF-8 processing instruction + public static final String XML_UTF8 = "<?xml version='1.0' encoding='UTF-8'?>"; //$NON-NLS-1$ + + public ProcessingInstruction(String target, String[] attrs, String[] values) { + // Lengths of attributes and values must be the same + this.target = target; + this.data = new String[attrs.length]; + for (int i = 0; i < attrs.length; i++) { + data[i] = attributeImage(attrs[i], values[i]); + } + } + + public static ProcessingInstruction makeTargetVersionInstruction(String target, Version version) { + return new ProcessingInstruction(target, new String[] {PI_VERSION_ATTRIBUTE}, new String[] {version.toString()}); + } + + public String toString() { + StringBuffer sb = new StringBuffer("<?"); //$NON-NLS-1$ + sb.append(this.target).append(' '); + for (int i = 0; i < data.length; i++) { + sb.append(this.data[i]); + if (i < data.length - 1) { + sb.append(' '); + } + } + sb.append("?>"); //$NON-NLS-1$ + return sb.toString(); + } + } + + private Stack elements; // XML elements that have not yet been closed + private boolean open; // Can attributes be added to the current element? + private String indent; // used for each level of indentation + + private PrintWriter pw; + + public XMLWriter(OutputStream output, ProcessingInstruction[] piElements) throws UnsupportedEncodingException { + this.pw = new PrintWriter(new OutputStreamWriter(output, "UTF8"), false); //$NON-NLS-1$ + println(ProcessingInstruction.XML_UTF8); + this.elements = new Stack(); + this.open = false; + this.indent = " "; //$NON-NLS-1$ + if (piElements != null) { + for (int i = 0; i < piElements.length; i++) { + println(piElements[i].toString()); + } + } + } + + // start a new element + public void start(String name) { + if (this.open) { + println('>'); + } + indent(); + print('<'); + print(name); + this.elements.push(name); + this.open = true; + } + + // end the most recent element with this name + public void end(String name) { + if (this.elements.empty()) { + throw new EndWithoutStartError(); + } + int index = this.elements.search(name); + if (index == -1) { + throw new EndWithoutStartError(name); + } + for (int i = 0; i < index; i += 1) { + end(); + } + } + + // end the current element + public void end() { + if (this.elements.empty()) { + throw new EndWithoutStartError(); + } + String name = (String) this.elements.pop(); + if (this.open) { + println("/>"); //$NON-NLS-1$ + } else { + printlnIndented("</" + name + '>', false); //$NON-NLS-1$ + } + this.open = false; + } + + public static String escape(String txt) { + StringBuffer buffer = null; + for (int i = 0; i < txt.length(); ++i) { + String replace; + char c = txt.charAt(i); + switch (c) { + case '<' : + replace = "<"; //$NON-NLS-1$ + break; + case '>' : + replace = ">"; //$NON-NLS-1$ + break; + case '"' : + replace = """; //$NON-NLS-1$ + break; + case '\'' : + replace = "'"; //$NON-NLS-1$ + break; + case '&' : + replace = "&"; //$NON-NLS-1$ + break; + case '\t' : + replace = "	"; //$NON-NLS-1$ + break; + case '\n' : + replace = "
"; //$NON-NLS-1$ + break; + case '\r' : + replace = "
"; //$NON-NLS-1$ + break; + default : + // this is the set of legal xml scharacters in unicode excluding high surrogates since they cannot be represented with a char + // see http://www.w3.org/TR/REC-xml/#charsets + if ((c >= '\u0020' && c <= '\uD7FF') || (c >= '\uE000' && c <= '\uFFFD')) { + if (buffer != null) + buffer.append(c); + continue; + } + replace = Character.isWhitespace(c) ? " " : null; //$NON-NLS-1$ + } + if (buffer == null) { + buffer = new StringBuffer(txt.length() + 16); + buffer.append(txt.substring(0, i)); + } + if (replace != null) + buffer.append(replace); + } + + if (buffer == null) + return txt; + + return buffer.toString(); + } + + // write a boolean attribute if it doesn't have the default value + public void attribute(String name, boolean value, boolean defaultValue) { + if (value != defaultValue) { + attribute(name, value); + } + } + + public void attribute(String name, boolean value) { + attribute(name, Boolean.toString(value)); + } + + public void attribute(String name, int value) { + attribute(name, Integer.toString(value)); + } + + public void attributeOptional(String name, String value) { + if (value != null && value.length() > 0) { + attribute(name, value); + } + } + + public void attribute(String name, Object value) { + if (!this.open) { + throw new AttributeAfterNestedContentError(); + } + if (value == null) { + return; // optional attribute with no value + } + print(' '); + print(name); + print("='"); //$NON-NLS-1$ + print(escape(value.toString())); + print('\''); + } + + public void cdata(String data) { + cdata(data, true); + } + + public void cdata(String data, boolean escape) { + if (this.open) { + println('>'); + this.open = false; + } + if (data != null) { + printlnIndented(data, escape); + } + } + + public void flush() { + this.pw.flush(); + } + + public void writeProperties(Map properties) { + writeProperties(PROPERTIES_ELEMENT, properties); + } + + public void writeProperties(String propertiesElement, Map properties) { + if (properties != null && properties.size() > 0) { + start(propertiesElement); + attribute(COLLECTION_SIZE_ATTRIBUTE, properties.size()); + for (Iterator iter = properties.keySet().iterator(); iter.hasNext();) { + String name = (String) iter.next(); + writeProperty(name, (String) properties.get(name)); + } + end(propertiesElement); + } + } + + public void writeProperty(String name, String value) { + start(PROPERTY_ELEMENT); + attribute(PROPERTY_NAME_ATTRIBUTE, name); + attribute(PROPERTY_VALUE_ATTRIBUTE, value); + end(); + } + + protected static String attributeImage(String name, String value) { + if (value == null) { + return ""; // optional attribute with no value //$NON-NLS-1$ + } + return name + "='" + escape(value) + '\''; //$NON-NLS-1$ + } + + private void println(char c) { + this.pw.println(c); + } + + private void println(String s) { + this.pw.println(s); + } + + private void println() { + this.pw.println(); + } + + private void print(char c) { + this.pw.print(c); + } + + private void print(String s) { + this.pw.print(s); + } + + private void printlnIndented(String s, boolean escape) { + if (s.length() == 0) { + println(); + } else { + indent(); + println(escape ? escape(s) : s); + } + } + + private void indent() { + for (int i = this.elements.size(); i > 0; i -= 1) { + print(this.indent); + } + } + + public static class AttributeAfterNestedContentError extends Error { + private static final long serialVersionUID = 1L; // not serialized + } + + public static class EndWithoutStartError extends Error { + private static final long serialVersionUID = 1L; // not serialized + private String name; + + public EndWithoutStartError() { + super(); + } + + public EndWithoutStartError(String name) { + super(); + this.name = name; + } + + public String getName() { + return this.name; + } + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/messages.properties b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/messages.properties new file mode 100644 index 000000000..473a1d1fb --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/messages.properties @@ -0,0 +1,27 @@ +############################################################################### +# Copyright (c) 2007, 2008 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### + +# All these errors are bound to 5 args: 0-msg, 1-root, 2-name, 3-line, 4-column +XMLParser_Error_At_Line=Error at line {3}{1}: {0} +XMLParser_Error_At_Line_Column=Error at line {3}, column {4}{1}: {0} +XMLParser_Error_At_Name_Line=Error in {2} at line {3}: {0} +XMLParser_Error_At_Name_Line_Column=Error in {2} at line {3}, column {4}: {0} + +XMLParser_No_SAX_Parser=Unable to acquire a SAX parser service. +XMLParser_Missing_Required_Attribute=Missing required attribute in "{0}": {1} +XMLParser_Illegal_Value_For_Attribute=Illegal value for attribute "{0}" of element "{1}": {2} +XMLParser_Duplicate_Element=Duplicate singleton element in element "{0}": <{1}{2}> + +io_failedRead=Unable to read repository at {0} +io_IncompatibleVersion=\ +Metadata repository has incompatible version {0}; expected {1} +io_parseError=\ +Error parsing composite repository
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java new file mode 100644 index 000000000..fe8774eb8 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text of + * such license is available at www.eclipse.org. + ******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory; +import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; +import org.osgi.framework.*; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.util.tracker.ServiceTracker; + +/** + * The activator class controls the plug-in life cycle. + * This activator has helper methods to get file transfer service tracker, and + * for making sure required ECF bundles are started. + */ +public class Activator implements BundleActivator { + + public static final String ID = "org.eclipse.equinox.p2.repository"; //$NON-NLS-1$ + private static BundleContext context; + // tracker for ECF service + private ServiceTracker retrievalFactoryTracker; + + // The shared instance + private static Activator plugin; + + public void start(BundleContext aContext) throws Exception { + Activator.context = aContext; + Activator.plugin = this; + } + + public void stop(BundleContext aContext) throws Exception { + Activator.context = null; + Activator.plugin = null; + } + + public static BundleContext getContext() { + return Activator.context; + } + + /** + * Get singleton instance. + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + /** + * Returns a {@link IRetrieveFileTransferFactory} using a {@link ServiceTracker} after having attempted + * to start the bundle "org.eclipse.ecf.provider.filetransfer". If something is wrong with the configuration + * this method returns null. + * @return a factory, or null, if configuration is incorrect + */ + public IRetrieveFileTransferFactory getRetrieveFileTransferFactory() { + return (IRetrieveFileTransferFactory) getFileTransferServiceTracker().getService(); + } + + /** + * Gets the singleton ServiceTracker for the IRetrieveFileTransferFactory and starts the bundles + * "org.eclipse.ecf" and + * "org.eclipse.ecf.provider.filetransfer" on first call. + * @return ServiceTracker + */ + private synchronized ServiceTracker getFileTransferServiceTracker() { + if (retrievalFactoryTracker == null) { + retrievalFactoryTracker = new ServiceTracker(Activator.getContext(), IRetrieveFileTransferFactory.class.getName(), null); + retrievalFactoryTracker.open(); + startBundle("org.eclipse.ecf"); //$NON-NLS-1$ + startBundle("org.eclipse.ecf.provider.filetransfer"); //$NON-NLS-1$ + } + return retrievalFactoryTracker; + } + + private boolean startBundle(String bundleId) { + PackageAdmin packageAdmin = (PackageAdmin) ServiceHelper.getService(Activator.getContext(), PackageAdmin.class.getName()); + if (packageAdmin == null) + return false; + + Bundle[] bundles = packageAdmin.getBundles(bundleId, null); + if (bundles != null && bundles.length > 0) { + for (int i = 0; i < bundles.length; i++) { + try { + if ((bundles[0].getState() & Bundle.INSTALLED) == 0) { + bundles[0].start(); + return true; + } + } catch (BundleException e) { + // failed, try next bundle + } + } + } + return false; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/AuthenticationFailedException.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/AuthenticationFailedException.java new file mode 100644 index 000000000..87c432965 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/AuthenticationFailedException.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text of + * such license is available at www.eclipse.org. + ******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +import java.net.ProtocolException; + +/** + * Exception signaling that authentication failed. + * @author henrik.lindberg@cloudsmith.com + */ +public class AuthenticationFailedException extends ProtocolException { + + private static final long serialVersionUID = 1L; + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java new file mode 100644 index 000000000..141288cf8 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2009, IBM Corporation and others. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text of + * such license is available at www.eclipse.org. + * Contributors: + * IBM Corporation - Initial API and implementation + * Cloudsmith Inc - Implementation + ******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import org.eclipse.core.runtime.*; +import org.eclipse.ecf.filetransfer.UserCancelledException; +import org.eclipse.equinox.internal.provisional.p2.core.IServiceUI; +import org.eclipse.equinox.internal.provisional.p2.core.IServiceUI.AuthenticationInfo; +import org.eclipse.equinox.internal.provisional.p2.repository.IRepository; +import org.eclipse.equinox.security.storage.*; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Credentials handles AuthenticationInfo that can be used to established an + * ECF connection context. An AuthenticationInfo is obtained for a URI buy looking + * in a store, if none is provided the user is optionally prompted for the information. + * + * @author henrik.lindberg@cloudsmith.com + * + */ +public class Credentials { + /** + * Returns the AuthenticationInfo for the given URI. This may prompt the + * user for user name and password as required. + * + * If the URI is opaque, the entire URI is used as the key. For non opaque URIs, + * the key is based on the host name, using a host name of "localhost" if host name is + * missing. + * + * @param location - the file location requiring login details + * @param prompt - use <code>true</code> to prompt the user instead of + * looking at the secure preference store for login, use <code>false</code> + * to only try the secure preference store + * @throws UserCancelledException when the user cancels the login prompt + * @throws CoreException if the password cannot be read or saved + * @return The authentication info. + */ + public static AuthenticationInfo forLocation(URI location, boolean prompt) throws UserCancelledException, CoreException { + return forLocation(location, prompt, null); + } + + /** + * Returns the AuthenticationInfo for the given URI. This may prompt the + * user for user name and password as required. + * + * If the URI is opaque, the entire URI is used as the key. For non opaque URIs, + * the key is based on the host name, using a host name of "localhost" if host name is + * missing. + * + * This method allows passing a previously used AuthenticationInfo. If set, the user interface + * may present the information "on file" to the user for editing. + * + * @param location - the location for which to obtain authentication information + * @param prompt - if true, user will be prompted for information + * @param lastUsed - optional information used in an previous attempt to login + * @return AuthenticationInfo, or null if there was no information available + * @throws UserCancelledException - user canceled the prompt for name/password + * @throws CoreException if there is an error + */ + public static AuthenticationInfo forLocation(URI location, boolean prompt, AuthenticationInfo lastUsed) throws UserCancelledException, CoreException { + ISecurePreferences securePreferences = SecurePreferencesFactory.getDefault(); + + // if URI is not opaque, just getting the host may be enough + String host = location.getHost(); + if (host == null) { + String scheme = location.getScheme(); + if (URIUtil.isFileURI(location) || scheme == null) + // If the URI references a file, a password could possibly be needed for the directory + // (it could be a protected zip file representing a compressed directory) - in this + // case the key is the path without the last segment. + // Using "Path" this way may result in an empty string - which later will result in + // an invalid key. + host = new Path(location.toString()).removeLastSegments(1).toString(); + else + // it is an opaque URI - details are unknown - can only use entire string. + host = location.toString(); + } + String nodeKey; + try { + nodeKey = URLEncoder.encode(host, "UTF-8"); //$NON-NLS-1$ + } catch (UnsupportedEncodingException e2) { + // fall back to default platform encoding + try { + // Uses getProperty "file.encoding" instead of using deprecated URLEncoder.encode(String location) + // which does the same, but throws NPE on missing property. + String enc = System.getProperty("file.encoding");//$NON-NLS-1$ + if (enc == null) + throw new UnsupportedEncodingException("No UTF-8 encoding and missing system property: file.encoding"); //$NON-NLS-1$ + nodeKey = URLEncoder.encode(host, enc); + } catch (UnsupportedEncodingException e) { + throw RepositoryStatusHelper.internalError(e); + } + } + String nodeName = IRepository.PREFERENCE_NODE + '/' + nodeKey; + ISecurePreferences prefNode = null; + try { + if (securePreferences.nodeExists(nodeName)) + prefNode = securePreferences.node(nodeName); + } catch (IllegalArgumentException e) { + // if the node name is illegal/malformed (should not happen). + throw RepositoryStatusHelper.internalError(e); + } catch (IllegalStateException e) { + // thrown if preference store has been tampered with + throw RepositoryStatusHelper.internalError(e); + } + if (!prompt) { + if (prefNode == null) + return null; + try { + String username = prefNode.get(IRepository.PROP_USERNAME, null); + String password = prefNode.get(IRepository.PROP_PASSWORD, null); + // if we don't have stored connection data just return a null auth info + if (username == null || password == null) + return null; + return new IServiceUI.AuthenticationInfo(username, password, true); + } catch (StorageException e) { + throw RepositoryStatusHelper.internalError(e); + } + } + //need to prompt user for user name and password + ServiceTracker adminUITracker = new ServiceTracker(Activator.getContext(), IServiceUI.class.getName(), null); + adminUITracker.open(); + IServiceUI adminUIService = (IServiceUI) adminUITracker.getService(); + AuthenticationInfo loginDetails = null; + if (adminUIService != null) + loginDetails = adminUIService.getUsernamePassword(host); + //null result means user canceled password dialog + if (loginDetails == null) + throw new UserCancelledException(); + //save user name and password if requested by user + if (loginDetails.saveResult()) { + if (prefNode == null) + prefNode = securePreferences.node(nodeName); + try { + prefNode.put(IRepository.PROP_USERNAME, loginDetails.getUserName(), true); + prefNode.put(IRepository.PROP_PASSWORD, loginDetails.getPassword(), true); + prefNode.flush(); + } catch (StorageException e1) { + throw RepositoryStatusHelper.internalError(e1); + } catch (IOException e) { + throw RepositoryStatusHelper.internalError(e); + } + } + return loginDetails; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java new file mode 100644 index 000000000..024025e52 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import org.eclipse.core.runtime.Status; + +/** + * A status object that optionally reports additional information about the + * result of a download. + */ +public class DownloadStatus extends Status { + public static final long UNKNOWN_RATE = -1; + + private long speed = UNKNOWN_RATE; + + /** + * Constructs a new DownloadStatus with the given attributes. + */ + public DownloadStatus(int severity, String pluginId, String message) { + super(severity, pluginId, message); + } + + /** + * Constructs a new DownloadStatus with the given attributes. + */ + public DownloadStatus(int severity, String pluginId, String message, Throwable exception) { + super(severity, pluginId, message, exception); + } + + /** + * Returns the download rate in bytes per second. If the rate is unknown, + * @{link {@link #UNKNOWN_RATE}} is returned. + * @return the download rate in bytes per second + */ + public long getTransferRate() { + return speed; + } + + /** + * Sets the download rate of the transfer in bytes per second. + * @param rate The download rate in bytes per second + */ + public void setTransferRate(long rate) { + this.speed = rate; + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfo.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfo.java new file mode 100644 index 000000000..609054213 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfo.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text or + * such license is available at www.eclipse.org. + ******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import java.util.Properties; + +/** + * Carries information about a file transfer. + * @author henrik.lindberg@cloudsmith.com + * + */ +public class FileInfo { + public static final String PROPERTY_CONTENT_TYPE = "contentType"; //$NON-NLS-1$ + + public static final String PROPERTY_LAST_MODIFIED = "lastModified"; //$NON-NLS-1$ + + public static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ + + public static final String PROPERTY_SIZE = "size"; //$NON-NLS-1$ + + public static final String PROPERTY_SPEED = "speed"; //$NON-NLS-1$ + + public static final long UNKNOWN_RATE = -1L; + + private String contentType; + + private long lastModified = 0L; + + private String name; + + private long size = -1L; + + private long averageSpeed = UNKNOWN_RATE; + + public FileInfo() { + contentType = ""; //$NON-NLS-1$ + name = ""; //$NON-NLS-1$ + } + + public FileInfo(FileInfo fileInfo) { + initFrom(fileInfo); + } + + /* + * (non java doc) + * properties based method for possible use with resumable download + */ + public FileInfo(Properties properties) { + name = properties.getProperty(PROPERTY_NAME); + contentType = properties.getProperty(PROPERTY_CONTENT_TYPE); + + String v = properties.getProperty(PROPERTY_LAST_MODIFIED); + if (v != null) + lastModified = Long.parseLong(v); + + v = properties.getProperty(PROPERTY_SIZE); + if (v != null) + size = Long.parseLong(v); + v = properties.getProperty(PROPERTY_SPEED); + if (v != null) + averageSpeed = Long.parseLong(v); + } + + /* + * (non java doc) + * properties based method for possible use with resumable download + */ + public void addProperties(Properties properties) { + if (contentType != null) + properties.setProperty(PROPERTY_CONTENT_TYPE, contentType); + if (lastModified != 0L) + properties.setProperty(PROPERTY_LAST_MODIFIED, Long.toString(lastModified)); + if (name != null) + properties.setProperty(PROPERTY_NAME, name); + if (size != -1L) + properties.setProperty(PROPERTY_SIZE, Long.toString(size)); + if (averageSpeed != UNKNOWN_RATE) + properties.setProperty(PROPERTY_SPEED, Long.toString(averageSpeed)); + + } + + public final String getContentType() { + return contentType; + } + + public long getLastModified() { + return lastModified; + } + + public final String getRemoteName() { + return name; + } + + public final long getSize() { + return size; + } + + public void initFrom(FileInfo info) { + setName(info.getRemoteName()); + setContentType(info.getContentType()); + setSize(info.getSize()); + setLastModified(info.getLastModified()); + } + + public void reset() { + name = null; + contentType = null; + size = -1; + lastModified = 0; + } + + public final void setContentType(String contentType) { + this.contentType = contentType; + } + + public void setLastModified(long timestamp) { + lastModified = timestamp; + } + + public final void setName(String name) { + this.name = name; + } + + public final void setSize(long size) { + this.size = size; + } + + /** + * Set the average transfer rate measured in bytes/second + * @param averageSpeed rate in bytes/second, or {@link #UNKNOWN_RATE} + */ + public void setAverageSpeed(long averageSpeed) { + this.averageSpeed = averageSpeed; + } + + /** + * Returns the average transfer rate measured in bytes/second. + * @return transfer rate in bytes/second or {@link #UNKNOWN_RATE} + */ + public long getAverageSpeed() { + return averageSpeed; + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java new file mode 100644 index 000000000..0b0a965e7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2009, IBM Corporation, and others. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text of + * such license is available at www.eclipse.org. + * Contributors: + * IBM Corporation - initial implementation + * Cloudsmith Inc - modified API, and implementation + ******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.ecf.core.*; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.filetransfer.*; +import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemBrowseEvent; +import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemEvent; +import org.eclipse.ecf.filetransfer.identity.*; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.osgi.util.NLS; + +/** + * The FileInfoReader is a {@link Job} similar to {@link FileReader}, but without the support + * from ECF (there is currently no way to wait on a BrowseRequest job, as this is internal to + * ECF). If such support is added, this class is easily modified. + * + * @author henrik.lindberg@cloudsmith.com + */ +public class FileInfoReader extends Job implements IRemoteFileSystemListener { + private Exception exception; + private IProgressMonitor theMonitor; + private final int connectionRetryCount; + private final long connectionRetryDelay; + private final IConnectContext connectContext; + final Boolean[] barrier = new Boolean[1]; + private IRemoteFile[] remoteFiles; + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + synchronized (barrier) { + while (barrier[0] == null) { + try { + barrier.wait(); + } catch (InterruptedException e) { + //ignore + } + } + } + return Status.OK_STATUS; + } + + /** + * Waits until request is processed (barrier[0] is non null). + * This is a bit of a hack, as it would be better if the ECFBrowser worked in similar fashion to + * file transfer were a custom job can be supplied. + * TODO: log an issue for ECF. + */ + private void waitOnSelf() { + schedule(); + while (barrier[0] == null) { + boolean logged = false; + try { + join(); + } catch (InterruptedException e) { + if (!logged) + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "Unexpected interrupt while waiting on ECF browse request", e)); //$NON-NLS-1$ + } + } + } + + /** + * Create a new FileInfoReader that will retry failed connection attempts and sleep some amount of time between each + * attempt. + */ + public FileInfoReader(IConnectContext aConnectContext) { + super(Messages.repo_loading); // job label - TODO: this is a bad label + barrier[0] = null; + // Hide this job. + setSystem(true); + setUser(false); + connectionRetryCount = RepositoryPreferences.getConnectionRetryCount(); + connectionRetryDelay = RepositoryPreferences.getConnectionMsRetryDelay(); + connectContext = aConnectContext; + } + + /** + * Get the requested information. + * @return IRemoteFile[] or null if there was an error. + * @throws CoreException + * @throws FileNotFoundException + * @throws AuthenticationFailedException + */ + public IRemoteFile[] getRemoteFiles(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException { + if (monitor != null) + monitor.beginTask(location.toString(), 1); + try { + sendBrowseRequest(location, monitor); + waitOnSelf(); + return remoteFiles; + } finally { + if (monitor != null) { + monitor.done(); + } + } + + } + + public IRemoteFile getRemoteFile(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException { + + getRemoteFiles(location, monitor); + return remoteFiles != null && remoteFiles.length > 0 ? remoteFiles[0] : null; + } + + public long getLastModified(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException { + IRemoteFile file = getRemoteFile(location, monitor); + return file == null ? 0L : file.getInfo().getLastModified(); + } + + public void handleRemoteFileEvent(IRemoteFileSystemEvent event) { + exception = event.getException(); + if (exception != null) { + synchronized (barrier) { + barrier[0] = Boolean.TRUE; + barrier.notify(); + } + } else if (event instanceof IRemoteFileSystemBrowseEvent) { + IRemoteFileSystemBrowseEvent fsbe = (IRemoteFileSystemBrowseEvent) event; + remoteFiles = fsbe.getRemoteFiles(); + if (theMonitor != null) + theMonitor.worked(1); + synchronized (barrier) { + barrier[0] = Boolean.TRUE; + barrier.notify(); + } + } else { + synchronized (barrier) { + barrier[0] = Boolean.FALSE; // ended by unknown reason + barrier.notify(); + } + } + } + + protected void sendBrowseRequest(URI uri, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException { + IContainer container; + try { + container = ContainerFactory.getDefault().createContainer(); + } catch (ContainerCreateException e) { + throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error); + } + + IRemoteFileSystemBrowserContainerAdapter adapter = (IRemoteFileSystemBrowserContainerAdapter) container.getAdapter(IRemoteFileSystemBrowserContainerAdapter.class); + if (adapter == null) { + throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error); + } + + adapter.setConnectContextForAuthentication(connectContext); + + this.exception = null; + this.theMonitor = monitor; + for (int retryCount = 0;;) { + if (monitor != null && monitor.isCanceled()) + throw new OperationCanceledException(); + + try { + IFileID fileID = FileIDFactory.getDefault().createFileID(adapter.getBrowseNamespace(), uri.toString()); + adapter.sendBrowseRequest(fileID, this); + } catch (RemoteFileSystemException e) { + exception = e; + } catch (FileCreateException e) { + exception = e; + } + + // note that 'exception' could have been captured in a callback + if (exception != null) { + // if this is a authentication failure - it is not meaningful to continue + RepositoryStatusHelper.checkPermissionDenied(exception); + + Throwable t = RepositoryStatusHelper.unwind(exception); + if (t instanceof CoreException) + throw RepositoryStatusHelper.unwindCoreException((CoreException) t); + + if (t instanceof FileNotFoundException) + // + // Connection succeeded but the target doesn't exist + // + throw (FileNotFoundException) t; + + if (t instanceof IOException && retryCount < connectionRetryCount) { + // TODO: Retry only certain exceptions or filter out + // some exceptions not worth retrying + // + ++retryCount; + exception = null; + try { + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.connection_to_0_failed_on_1_retry_attempt_2, new String[] {uri.toString(), t.getMessage(), String.valueOf(retryCount)}), t)); + + Thread.sleep(connectionRetryDelay); + continue; + } catch (InterruptedException e) { + /* ignore */ + } + } + throw RepositoryStatusHelper.wrap(exception); + } + break; + } + } + + protected Exception getException() { + return exception; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java new file mode 100644 index 000000000..c71772ec3 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java @@ -0,0 +1,314 @@ +/******************************************************************************* + * Copyright (c) 2006-2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text of + * such license is available at www.eclipse.org. + ******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import java.io.*; +import java.net.URI; +import java.util.Date; +import org.eclipse.core.runtime.*; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.filetransfer.*; +import org.eclipse.ecf.filetransfer.events.*; +import org.eclipse.ecf.filetransfer.identity.*; +import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.osgi.util.NLS; + +/** + * @author Thomas Hallgren + * @author henrik.lindberg@cloudsmith.com - adaption to 1.4 and to this p2 package + */ +public class FileReader extends FileTransferJob implements IFileTransferListener { + private boolean closeStreamWhenFinished = false; + private Exception exception; + private FileInfo fileInfo; + private long lastProgressCount; + private long lastStatsCount; + private IProgressMonitor theMonitor; + private OutputStream theOutputStream; + private ProgressStatistics statistics; + private final int connectionRetryCount; + private final long connectionRetryDelay; + private final IConnectContext connectContext; + + /** + * Create a new FileReader that will retry failed connection attempts and sleep some amount of time between each + * attempt. + */ + public FileReader(IConnectContext aConnectContext) { + super(Messages.FileTransport_reader); // job label + + // Hide this job. + setSystem(true); + setUser(false); + connectionRetryCount = RepositoryPreferences.getConnectionRetryCount(); + connectionRetryDelay = RepositoryPreferences.getConnectionMsRetryDelay(); + connectContext = aConnectContext; + } + + public FileInfo getLastFileInfo() { + return fileInfo; + } + + public synchronized void handleTransferEvent(IFileTransferEvent event) { + if (event instanceof IIncomingFileTransferReceiveStartEvent) { + IIncomingFileTransfer source = ((IIncomingFileTransferEvent) event).getSource(); + try { + FileInfo fi = new FileInfo(); + Date lastModified = source.getRemoteLastModified(); + if (lastModified != null) + fi.setLastModified(lastModified.getTime()); + fi.setName(source.getRemoteFileName()); + fi.setSize(source.getFileLength()); + fileInfo = fi; + + ((IIncomingFileTransferReceiveStartEvent) event).receive(theOutputStream, this); + } catch (IOException e) { + exception = e; + return; + } + + if (theMonitor != null) { + long fileLength = source.getFileLength(); + statistics = new ProgressStatistics(source.getRemoteFileName(), fileLength); + theMonitor.beginTask(null, 1000); + theMonitor.subTask(statistics.report()); + lastStatsCount = 0; + lastProgressCount = 0; + } + } else if (event instanceof IIncomingFileTransferReceiveDataEvent) { + IIncomingFileTransfer source = ((IIncomingFileTransferEvent) event).getSource(); + if (theMonitor != null) { + if (theMonitor.isCanceled()) { + source.cancel(); + return; + } + + long br = source.getBytesReceived(); + long count = br - lastStatsCount; + lastStatsCount = br; + statistics.increase(count); + fileInfo.setAverageSpeed(statistics.getAverageSpeed()); + if (statistics.shouldReport()) { + count = br - lastProgressCount; + lastProgressCount = br; + theMonitor.subTask(statistics.report()); + theMonitor.worked((int) (1000 * count / statistics.getTotal())); + } + } + } else if (event instanceof IIncomingFileTransferReceiveDoneEvent) { + if (closeStreamWhenFinished) + hardClose(theOutputStream); + + if (exception == null) + exception = ((IIncomingFileTransferReceiveDoneEvent) event).getException(); + } + } + + public InputStream read(URI url) throws CoreException, FileNotFoundException, AuthenticationFailedException { + final PipedInputStream input = new PipedInputStream(); + PipedOutputStream output; + try { + output = new PipedOutputStream(input); + } catch (IOException e) { + throw RepositoryStatusHelper.wrap(e); + } + RepositoryTracing.debug("Downloading {0}", url); //$NON-NLS-1$ + + final IProgressMonitor cancellationMonitor = new NullProgressMonitor(); + sendRetrieveRequest(url, output, true, cancellationMonitor); + + return new InputStream() { + public int available() throws IOException { + checkException(); + return input.available(); + } + + public void close() throws IOException { + cancellationMonitor.setCanceled(true); + hardClose(input); + checkException(); + } + + public void mark(int readlimit) { + input.mark(readlimit); + } + + public boolean markSupported() { + return input.markSupported(); + } + + public int read() throws IOException { + checkException(); + return input.read(); + } + + public int read(byte b[]) throws IOException { + checkException(); + return input.read(b); + } + + public int read(byte b[], int off, int len) throws IOException { + checkException(); + return input.read(b, off, len); + } + + public void reset() throws IOException { + checkException(); + input.reset(); + } + + public long skip(long n) throws IOException { + checkException(); + return input.skip(n); + } + + private void checkException() throws IOException { + if (getException() == null) + return; + + IOException e; + Throwable t = RepositoryStatusHelper.unwind(getException()); + if (t instanceof IOException) + e = (IOException) t; + else { + e = new IOException(t.getMessage()); + e.initCause(t); + } + throw e; + } + }; + } + + /** Only request info + * @deprecated REMOVE THIS METHOD - SHOULD USE BROWSE INSTEAD TO ONLY GET HEAD - ALSO REMOVE PARAMTER ONLYHEAD + * @param uri + * @return FileInfo + * @throws CoreException + * @throws FileNotFoundException + * @throws AuthenticationFailedException + */ + public FileInfo readInfo(URI uri) throws CoreException, FileNotFoundException, AuthenticationFailedException { + sendRetrieveRequest(uri, null, false, null); + return getLastFileInfo(); + } + + public void readInto(URI uri, OutputStream anOutputStream, IProgressMonitor monitor) // + throws CoreException, FileNotFoundException, AuthenticationFailedException { + try { + sendRetrieveRequest(uri, anOutputStream, false, monitor); + join(); + } catch (InterruptedException e) { + monitor.setCanceled(true); + throw new OperationCanceledException(); + } finally { + if (monitor != null) { + if (statistics == null) + // + // Monitor was never started. See to that it's balanced + // + monitor.beginTask(null, 1); + else + statistics = null; + monitor.done(); + } + } + } + + protected void sendRetrieveRequest(URI uri, OutputStream outputStream, boolean closeStreamOnFinish, // + IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException { + + IRetrieveFileTransferFactory factory = Activator.getDefault().getRetrieveFileTransferFactory(); + if (factory == null) { + throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error); + } + IRetrieveFileTransferContainerAdapter adapter = factory.newInstance(); + + adapter.setConnectContextForAuthentication(connectContext); + + this.exception = null; + this.closeStreamWhenFinished = closeStreamOnFinish; + this.fileInfo = null; + this.statistics = null; + this.lastProgressCount = 0L; + this.lastStatsCount = 0L; + this.theMonitor = monitor; + this.theOutputStream = outputStream; + + for (int retryCount = 0;;) { + if (monitor != null && monitor.isCanceled()) + throw new OperationCanceledException(); + + try { + IFileID fileID = FileIDFactory.getDefault().createFileID(adapter.getRetrieveNamespace(), uri.toString()); + adapter.sendRetrieveRequest(fileID, this, null); + } catch (IncomingFileTransferException e) { + exception = e; + } catch (FileCreateException e) { + exception = e; + } + + // note that 'exception' could have been captured in a callback + if (exception != null) { + // if this is a authentication failure - it is not meaningful to continue + RepositoryStatusHelper.checkPermissionDenied(exception); + + Throwable t = RepositoryStatusHelper.unwind(exception); + if (t instanceof CoreException) + throw RepositoryStatusHelper.unwindCoreException((CoreException) t); + + if (t instanceof FileNotFoundException) + // + // Connection succeeded but the target doesn't exist + // + throw (FileNotFoundException) t; + + if (t instanceof IOException && retryCount < connectionRetryCount) { + // TODO: Retry only certain exceptions or filter out + // some exceptions not worth retrying + // + ++retryCount; + exception = null; + try { + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.connection_to_0_failed_on_1_retry_attempt_2, new String[] {uri.toString(), t.getMessage(), String.valueOf(retryCount)}), t)); + + Thread.sleep(connectionRetryDelay); + continue; + } catch (InterruptedException e) { + /* ignore */ + } + } + throw RepositoryStatusHelper.wrap(exception); + } + break; + } + } + + protected Exception getException() { + return exception; + } + + /** + * Flushes a Flushable, and Closes a Closeable - errors are ignored. + * @param stream + */ + public static void hardClose(Closeable stream) { + if (stream != null) { + try { + if (stream instanceof Flushable) + ((Flushable) stream).flush(); + } catch (IOException e) { /* ignore */ + } + try { + stream.close(); + } catch (IOException e) { /* ignore */ + } + } + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java new file mode 100644 index 000000000..361dcf118 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Cloudsmith Inc - additional messages + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.repository.messages"; //$NON-NLS-1$ + + public static String artifact_not_found; + public static String available_already_in; + public static String downloading; + public static String error_closing_stream; + public static String io_failedRead; + public static String ecf_configuration_error; + public static String io_incompatibleVersion; + public static String io_invalidLocation; + public static String SignatureVerification_failedRead; + public static String SignatureVerification_invalidContent; + public static String SignatureVerification_invalidFileContent; + + public static String SignatureVerifier_OutOfMemory; + public static String io_parseError; + public static String mirroring; + public static String repoMan_internalError; + public static String repoFailedWrite; + public static String repoReadOnly; + public static String repo_loading; + + public static String sar_downloading; + public static String sar_downloadJobName; + public static String sar_failedMkdir; + public static String sar_reportStatus; + + public static String mirror_alreadyExists; + public static String message_mirroringStatus; + public static String message_childrenRepos; + public static String exception_comparatorNotFound; + public static String exception_noComparators; + public static String exception_destinationNotModifiable; + public static String exception_needSourceDestination; + public static String exception_malformedRepoURI; + public static String exception_unsupportedAddToComposite; + + public static String exception_unsupportedGetOutputStream; + public static String exception_unsupportedRemoveFromComposite; + + public static String Mirroring_NO_MATCHING_DESCRIPTOR; + + public static String TransportErrorTranslator_400; + public static String TransportErrorTranslator_401; + public static String TransportErrorTranslator_402; + public static String TransportErrorTranslator_403; + public static String TransportErrorTranslator_404; + public static String TransportErrorTranslator_405; + public static String TransportErrorTranslator_406; + public static String TransportErrorTranslator_407; + public static String TransportErrorTranslator_408; + public static String TransportErrorTranslator_409; + public static String TransportErrorTranslator_410; + public static String TransportErrorTranslator_411; + public static String TransportErrorTranslator_412; + public static String TransportErrorTranslator_413; + public static String TransportErrorTranslator_414; + public static String TransportErrorTranslator_415; + public static String TransportErrorTranslator_416; + public static String TransportErrorTranslator_417; + public static String TransportErrorTranslator_418; + public static String TransportErrorTranslator_422; + public static String TransportErrorTranslator_423; + public static String TransportErrorTranslator_424; + public static String TransportErrorTranslator_425; + public static String TransportErrorTranslator_426; + public static String TransportErrorTranslator_449; + public static String TransportErrorTranslator_450; + public static String TransportErrorTranslator_500; + public static String TransportErrorTranslator_501; + public static String TransportErrorTranslator_502; + public static String TransportErrorTranslator_503; + public static String TransportErrorTranslator_504; + public static String TransportErrorTranslator_505; + public static String TransportErrorTranslator_506; + public static String TransportErrorTranslator_507; + public static String TransportErrorTranslator_508; + public static String TransportErrorTranslator_510; + public static String TransportErrorTranslator_MalformedRemoteFileReference; + public static String TransportErrorTranslator_UnableToConnectToRepository_0; + + public static String TransportErrorTranslator_UnknownErrorCode; + public static String TransportErrorTranslator_UnknownHost; + + public static String fetching_0_1_at_2; + public static String fetching_0_1_of_2_at_3; + public static String connection_to_0_failed_on_1_retry_attempt_2; + + public static String FileTransport_reader; + + public static String UnableToRead_0_TooManyAttempts; + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java new file mode 100644 index 000000000..839dbb1bd --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2006-2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text or + * such license is available at www.eclipse.org. + ******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import java.util.*; +import java.util.Map.Entry; +import org.eclipse.osgi.util.NLS; + +/** + * Converts progress of file download to average download speed and keeps track of + * when it is suitable to update a progress monitor. A suitably scaled and formatted string for use + * in progress monitoring is provided. + * + * @author thomas.hallgren@cloudsmith.com + */ +public class ProgressStatistics { + private static final int DEFAULT_REPORT_INTERVAL = 1000; + + private static final int SPEED_INTERVAL = 5000; + + private static final int SPEED_RESOLUTION = 1000; + + private static String convert(long amount) { + if (amount < 1024) + return String.format(Locale.US, "%dB", new Object[] {Long.valueOf(amount)}); //$NON-NLS-1$ + + if (amount < 1024 * 1024) + return String.format(Locale.US, "%.2fkB", new Object[] {Double.valueOf(((double) amount) / 1024)}); //$NON-NLS-1$ + + return String.format(Locale.US, "%.2fMB", new Object[] {Double.valueOf(((double) amount) / (1024 * 1024))}); //$NON-NLS-1$ + } + + private final String m_fileName; + + private final long m_total; + + private final long m_startTime; + + private long m_current; + + private long m_lastReportTime; + + private int m_reportInterval; + + private SortedMap m_recentSpeedMap; + + private long m_recentSpeedMapKey; + + public ProgressStatistics(String fileName, long total) { + m_startTime = System.currentTimeMillis(); + m_fileName = fileName; + + m_total = total; + + m_current = 0; + m_lastReportTime = 0; + m_reportInterval = DEFAULT_REPORT_INTERVAL; + m_recentSpeedMap = new TreeMap(); + m_recentSpeedMapKey = 0L; + } + + public long getAverageSpeed() { + long dur = getDuration(); + + if (dur >= 1000) + return m_current / (dur / 1000); + + return 0L; + } + + public long getDuration() { + return System.currentTimeMillis() - m_startTime; + } + + public double getPercentage() { + if (m_total > 0) + return ((double) m_current) / ((double) m_total); + + return 0.0; + } + + synchronized public long getRecentSpeed() { + removeObsoleteRecentSpeedData(getDuration() / SPEED_RESOLUTION); + long dur = 0L; + long amount = 0L; + SortedMap relevantData = m_recentSpeedMap.headMap(Long.valueOf(m_recentSpeedMapKey)); + + Iterator itor = relevantData.entrySet().iterator(); + while (itor.hasNext()) { + Entry entry = (Entry) itor.next(); + dur += SPEED_RESOLUTION; + amount += ((Long) entry.getValue()).longValue(); + } + + if (dur >= 1000) + return amount / (dur / 1000); + + return 0L; + } + + public int getReportInterval() { + return m_reportInterval; + } + + public long getTotal() { + return m_total; + } + + public void increase(long inc) { + registerRecentSpeed(getDuration() / SPEED_RESOLUTION, inc); + m_current += inc; + } + + public synchronized String report() { + return m_total != -1 ? NLS.bind(Messages.fetching_0_1_of_2_at_3, new String[] {m_fileName, convert(m_current), convert(m_total), convert(getRecentSpeed())}) : NLS.bind(Messages.fetching_0_1_at_2, new String[] {m_fileName, convert(m_current), convert(getRecentSpeed())}); + } + + public void setReportInterval(int reportInterval) { + m_reportInterval = reportInterval; + } + + public boolean shouldReport() { + long currentTime = System.currentTimeMillis(); + if (m_lastReportTime == 0 || currentTime - m_lastReportTime >= m_reportInterval) { + m_lastReportTime = currentTime; + return true; + } + return false; + } + + public String toString() { + return report(); + } + + synchronized private void registerRecentSpeed(long key, long inc) { + Long keyL = Long.valueOf(key); + Long currentValueL = (Long) m_recentSpeedMap.get(keyL); + long currentValue = 0L; + if (currentValueL != null) + currentValue = currentValueL.longValue(); + + m_recentSpeedMap.put(keyL, Long.valueOf(inc + currentValue)); + + if (m_recentSpeedMapKey != key) { + m_recentSpeedMapKey = key; + removeObsoleteRecentSpeedData(key); + } + } + + synchronized private void removeObsoleteRecentSpeedData(long lastKey) { + long threshold = lastKey - SPEED_INTERVAL / SPEED_RESOLUTION; + m_recentSpeedMap.headMap(Long.valueOf(threshold)).clear(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryPreferences.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryPreferences.java new file mode 100644 index 000000000..4715b78d2 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryPreferences.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2006-2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text or + * such license is available at www.eclipse.org. + ******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +/** + * Holds various preferences for repository. + * TODO: if values should be configurable, they need to be persisted and read back. + * @author henrik.lindberg@cloudsmith.com + * + */ +public class RepositoryPreferences { + + /** + * Number of attempts to connect (with same credentials) before giving up. + * Note that newer ECF using apache HTTPclient has retry by default. + * TODO - make this configurable via a property. + * TODO - consider removing this option + * @return the value 1 + */ + public static int getConnectionRetryCount() { + return 1; + } + + /** + * Reconnect delay after failure to connect (with same credentials)- in milliseconds. + * Current implementation returns 200ms. + * TODO - make this configurable via a property + * @return the value 200 + */ + public static long getConnectionMsRetryDelay() { + return 200; + } + + /** + * Number of attempts to connect (with different credentials) before giving up. + * TODO - make this configurable via a property. + * @return the value 3 + */ + public static int getLoginRetryCount() { + return 3; + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java new file mode 100644 index 000000000..36a511ec5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2009 Cloudsmith Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Cloudsmith Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +import java.io.FileNotFoundException; +import java.net.*; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ecf.core.identity.IDCreateException; +import org.eclipse.ecf.filetransfer.IncomingFileTransferException; +import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException; +import org.eclipse.osgi.util.NLS; + +/** + * Utility class to transform transport errors into error messages. + * @author henrik.lindberg@cloudsmith.com + * + */ +public class RepositoryStatus { + + public static String codeToMessage(int code, String toDownload) { + switch (code) { + case 400 : + return NLS.bind(Messages.TransportErrorTranslator_400, toDownload); + case 401 : + return NLS.bind(Messages.TransportErrorTranslator_401, toDownload); + case 402 : + return NLS.bind(Messages.TransportErrorTranslator_402, toDownload); + case 403 : + return NLS.bind(Messages.TransportErrorTranslator_403, toDownload); + case 404 : + return NLS.bind(Messages.TransportErrorTranslator_404, toDownload); + case 405 : + return NLS.bind(Messages.TransportErrorTranslator_405, toDownload); + case 406 : + return NLS.bind(Messages.TransportErrorTranslator_406, toDownload); + case 407 : + return NLS.bind(Messages.TransportErrorTranslator_407, toDownload); + case 408 : + return NLS.bind(Messages.TransportErrorTranslator_408, toDownload); + case 409 : + return NLS.bind(Messages.TransportErrorTranslator_409, toDownload); + case 410 : + return NLS.bind(Messages.TransportErrorTranslator_410, toDownload); + case 411 : + return NLS.bind(Messages.TransportErrorTranslator_411, toDownload); + case 412 : + return NLS.bind(Messages.TransportErrorTranslator_412, toDownload); + case 413 : + return NLS.bind(Messages.TransportErrorTranslator_413, toDownload); + case 414 : + return NLS.bind(Messages.TransportErrorTranslator_414, toDownload); + case 415 : + return NLS.bind(Messages.TransportErrorTranslator_415, toDownload); + case 416 : + return NLS.bind(Messages.TransportErrorTranslator_416, toDownload); + case 417 : + return NLS.bind(Messages.TransportErrorTranslator_417, toDownload); + case 418 : + return NLS.bind(Messages.TransportErrorTranslator_418, toDownload); + case 422 : + return NLS.bind(Messages.TransportErrorTranslator_422, toDownload); + case 423 : + return NLS.bind(Messages.TransportErrorTranslator_423, toDownload); + case 424 : + return NLS.bind(Messages.TransportErrorTranslator_424, toDownload); + case 425 : + return NLS.bind(Messages.TransportErrorTranslator_425, toDownload); + case 426 : + return NLS.bind(Messages.TransportErrorTranslator_426, toDownload); + case 449 : + return NLS.bind(Messages.TransportErrorTranslator_449, toDownload); + case 450 : + return NLS.bind(Messages.TransportErrorTranslator_450, toDownload); + + case 500 : + return NLS.bind(Messages.TransportErrorTranslator_500, toDownload); + case 501 : + return NLS.bind(Messages.TransportErrorTranslator_501, toDownload); + case 502 : + return NLS.bind(Messages.TransportErrorTranslator_502, toDownload); + case 503 : + return NLS.bind(Messages.TransportErrorTranslator_503, toDownload); + case 504 : + return NLS.bind(Messages.TransportErrorTranslator_504, toDownload); + case 505 : + return NLS.bind(Messages.TransportErrorTranslator_505, toDownload); + case 506 : + return NLS.bind(Messages.TransportErrorTranslator_506, toDownload); + case 507 : + return NLS.bind(Messages.TransportErrorTranslator_507, toDownload); + case 508 : + return NLS.bind(Messages.TransportErrorTranslator_508, toDownload); + case 510 : + return NLS.bind(Messages.TransportErrorTranslator_510, toDownload); + + default : + return NLS.bind(Messages.TransportErrorTranslator_UnknownErrorCode + Integer.toString(code), toDownload); + } + } + + public static IStatus forStatus(IStatus original, URI toDownload) { + Throwable t = original.getException(); + return forException(t, toDownload); + } + + public static IStatus forException(Throwable t, URI toDownload) { + if (t instanceof FileNotFoundException || (t instanceof IncomingFileTransferException && ((IncomingFileTransferException) t).getErrorCode() == 404)) + return new Status(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_NOT_FOUND, NLS.bind(Messages.artifact_not_found, toDownload), t); + if (t instanceof ConnectException) + return new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, NLS.bind(Messages.TransportErrorTranslator_UnableToConnectToRepository_0, toDownload), t); + if (t instanceof UnknownHostException) + return new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_UnknownHost, toDownload), t); + if (t instanceof IDCreateException) { + IStatus status = ((IDCreateException) t).getStatus(); + if (status != null && status.getException() != null) + t = status.getException(); + + return new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_MalformedRemoteFileReference, toDownload), t); + } + if (t instanceof IncomingFileTransferException) { + int code = ((IncomingFileTransferException) t).getErrorCode(); + // Switch on error codes in the HTTP error code range + if (code >= 400 && code <= 600) { + int provisionCode = ProvisionException.REPOSITORY_FAILED_READ; + if (code == 401 || code == 500) + provisionCode = ProvisionException.REPOSITORY_FAILED_AUTHENTICATION; + else if (code == 404) + provisionCode = ProvisionException.ARTIFACT_NOT_FOUND; + return new Status(IStatus.ERROR, Activator.ID, provisionCode, // + codeToMessage(code, toDownload.toString()), t); + } + } + // Add more specific translation here + return new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.io_failedRead, toDownload), t); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java new file mode 100644 index 000000000..6313a06d1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java @@ -0,0 +1,270 @@ +/******************************************************************************* + * Copyright (c) 2009 Cloudsmith Inc, and other. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the individual + * copyright holders listed above, as Initial Contributors under such license. + * The text of such license is available at www.eclipse.org. + * Contributors: + * Cloudsmith Inc. - Initial API and implementation + * IBM Corporation - Original Implementation of checkPermissionDenied + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import org.eclipse.core.runtime.*; +import org.eclipse.ecf.filetransfer.IncomingFileTransferException; +import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException; +import org.eclipse.osgi.util.NLS; + +/** + * RepositoryStatusHelper is a utility class for processing of exceptions and status. + * @author thomas.hallgren@cloudsmith.com + * @author henrik.lindberg@cloudsmith.com - adaption to java 1.4, and Repository helper methods + */ +public abstract class RepositoryStatusHelper { + + private static final long serialVersionUID = 1L; + protected static final String SERVER_REDIRECT = "Server redirected too many times"; //$NON-NLS-1$ + + public static IStatus createStatus(String nlsMessage, Object arg) { + return createExceptionStatus(null, nlsMessage, new Object[] {arg}); + } + + public static IStatus createStatus(String nlsMessage, Object arg1, Object arg2) { + return createExceptionStatus(null, nlsMessage, new Object[] {arg1, arg2}); + } + + public static IStatus createStatus(String nlsMessage, Object arg1, Object arg2, Object arg3) { + return createExceptionStatus(null, nlsMessage, new Object[] {arg1, arg2, arg3}); + } + + public static IStatus createStatus(String nlsMessage, Object[] args) { + return createExceptionStatus(null, nlsMessage, args); + } + + public static IStatus createStatus(String nlsMessage) { + return createExceptionStatus(null, nlsMessage, new Object[] {}); + } + + public static IStatus createExceptionStatus(Throwable cause) { + return (cause instanceof CoreException) ? ((CoreException) cause).getStatus() : new Status(IStatus.ERROR, Activator.ID, IStatus.OK, cause.getMessage(), cause); + } + + public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object[] args) { + if (args != null && args.length > 0) + nlsMessage = NLS.bind(nlsMessage, args); + return new Status(IStatus.ERROR, Activator.ID, IStatus.OK, nlsMessage, cause); + } + + public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object arg1, Object arg2, Object arg3) { + return createExceptionStatus(cause, nlsMessage, new Object[] {arg1, arg2, arg3}); + } + + public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object arg1, Object arg2) { + return createExceptionStatus(cause, nlsMessage, new Object[] {arg1, arg2}); + } + + public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object arg1) { + return createExceptionStatus(cause, nlsMessage, new Object[] {arg1}); + } + + public static IStatus createExceptionStatus(Throwable cause, String nlsMessage) { + return createExceptionStatus(cause, nlsMessage, new Object[] {}); + } + + public static void deeplyPrint(Throwable e, PrintStream strm, boolean stackTrace) { + deeplyPrint(e, strm, stackTrace, 0); + } + + public static CoreException fromMessage(String nlsMessage, Object[] args) { + return fromExceptionMessage(null, nlsMessage, args); + } + + public static CoreException fromMessage(String nlsMessage, Object arg1) { + return fromExceptionMessage(null, nlsMessage, new Object[] {arg1}); + } + + public static CoreException fromMessage(String nlsMessage, Object arg1, Object arg2) { + return fromExceptionMessage(null, nlsMessage, new Object[] {arg1, arg2}); + } + + public static CoreException fromMessage(String nlsMessage, Object arg1, Object arg2, Object arg3) { + return fromExceptionMessage(null, nlsMessage, new Object[] {arg1, arg2, arg3}); + } + + public static CoreException fromMessage(String nlsMessage) { + return fromExceptionMessage(null, nlsMessage, new Object[] {}); + } + + public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object[] args) { + CoreException ce = new CoreException(createExceptionStatus(cause, nlsMessage, args)); + if (cause != null) + ce.initCause(cause); + return ce; + } + + public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object arg1, Object arg2, Object arg3) { + return fromExceptionMessage(cause, nlsMessage, new Object[] {arg1, arg2, arg3}); + } + + public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object arg1, Object arg2) { + return fromExceptionMessage(cause, nlsMessage, new Object[] {arg1, arg2}); + } + + public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object arg1) { + return fromExceptionMessage(cause, nlsMessage, new Object[] {arg1}); + } + + public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage) { + return fromExceptionMessage(cause, nlsMessage, new Object[] {}); + } + + public static Throwable unwind(Throwable t) { + for (;;) { + Class tc = t.getClass(); + + // We don't use instanceof operator since we want + // the explicit class, not subclasses. + // + if (tc != RuntimeException.class && tc != InvocationTargetException.class && tc != IOException.class) + break; + + Throwable cause = t.getCause(); + if (cause == null) + break; + + String msg = t.getMessage(); + if (msg != null && !msg.equals(cause.toString())) + break; + + t = cause; + } + return t; + } + + public static CoreException unwindCoreException(CoreException exception) { + IStatus status = exception.getStatus(); + while (status != null && status.getException() instanceof CoreException) { + exception = (CoreException) status.getException(); + status = exception.getStatus(); + } + return exception; + } + + public static CoreException wrap(IStatus status) { + CoreException e = new CoreException(status); + Throwable t = status.getException(); + if (t != null) + e.initCause(t); + return e; + } + + public static CoreException wrap(Throwable t) { + t = unwind(t); + if (t instanceof CoreException) + return unwindCoreException((CoreException) t); + + if (t instanceof OperationCanceledException || t instanceof InterruptedException) + return new CoreException(Status.CANCEL_STATUS); + + String msg = t.toString(); + return fromExceptionMessage(t, msg); + } + + private static void appendLevelString(PrintStream strm, int level) { + if (level > 0) { + strm.print("[0"); //$NON-NLS-1$ + for (int idx = 1; idx < level; ++idx) { + strm.print('.'); + strm.print(level); + } + strm.print(']'); + } + } + + private static void deeplyPrint(CoreException ce, PrintStream strm, boolean stackTrace, int level) { + appendLevelString(strm, level); + if (stackTrace) + ce.printStackTrace(strm); + deeplyPrint(ce.getStatus(), strm, stackTrace, level); + } + + private static void deeplyPrint(IStatus status, PrintStream strm, boolean stackTrace, int level) { + appendLevelString(strm, level); + String msg = status.getMessage(); + strm.println(msg); + Throwable cause = status.getException(); + if (cause != null) { + strm.print("Caused by: "); //$NON-NLS-1$ + if (stackTrace || !(msg.equals(cause.getMessage()) || msg.equals(cause.toString()))) + deeplyPrint(cause, strm, stackTrace, level); + } + + if (status.isMultiStatus()) { + IStatus[] children = status.getChildren(); + for (int i = 0; i < children.length; i++) + deeplyPrint(children[i], strm, stackTrace, level + 1); + } + } + + private static void deeplyPrint(Throwable t, PrintStream strm, boolean stackTrace, int level) { + if (t instanceof CoreException) + deeplyPrint((CoreException) t, strm, stackTrace, level); + else { + appendLevelString(strm, level); + if (stackTrace) + t.printStackTrace(strm); + else { + strm.println(t.toString()); + Throwable cause = t.getCause(); + if (cause != null) { + strm.print("Caused by: "); //$NON-NLS-1$ + deeplyPrint(cause, strm, stackTrace, level); + } + } + } + } + + /** + * Check if the given exception represents a permission failure (401 for HTTP), + * and throw a AuthenticationFailedException if a permission failure was encountered. + */ + public static void checkPermissionDenied(Throwable t) throws AuthenticationFailedException { + if (t instanceof IncomingFileTransferException) { + if (((IncomingFileTransferException) t).getErrorCode() == 401) + throw new AuthenticationFailedException(); + IStatus status = ((IncomingFileTransferException) t).getStatus(); + t = status.getException(); + } + + if (t == null || !(t instanceof IOException)) + return; + + // try to figure out if we have a 401 by parsing the exception message + // There is unfortunately no specific exception for "redirected too many times" - which is commonly + // caused by a failed login. + // + String m = t.getMessage(); + if (m != null && (m.indexOf(" 401 ") != -1 || m.indexOf(SERVER_REDIRECT) != -1)) //$NON-NLS-1$ + throw new AuthenticationFailedException(); + + } + + /** + * Get default "InternalError" ProvisionException. + * @param t + * @return a default "InternalError" + */ + public static ProvisionException internalError(Throwable t) { + return new ProvisionException(new Status(IStatus.ERROR, Activator.ID, // + ProvisionException.INTERNAL_ERROR, Messages.repoMan_internalError, t)); + } + + public static IStatus malformedAddressStatus(String address, Throwable t) { + return new Status(IStatus.ERROR, Activator.ID, // + ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.exception_malformedRepoURI, address), t); + + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTracing.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTracing.java new file mode 100644 index 000000000..aa45e7107 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTracing.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2006-2009, Cloudsmith Inc. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text or + * such license is available at www.eclipse.org. + ******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +/** + * TODO: Placeholder class - should have link to p2 tracing for repository debug + * @author henrik.lindberg@cloudsmith.com + * + */ +public class RepositoryTracing { + + public static void debug(String string, Object arg) { + // TODO Fix logging if debug is turned on + // NLS.bind(string, new Object[] {arg}); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java new file mode 100644 index 000000000..3da43c765 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2006-2009, IBM Corporation and other. + * The code, documentation and other materials contained herein have been + * licensed under the Eclipse Public License - v 1.0 by the copyright holder + * listed above, as the Initial Contributor under such license. The text of + * such license is available at www.eclipse.org. + * + * Contributors + * IBM Corporation - Initial API and implementation. + * Cloudsmith Inc - Implementation + ******************************************************************************/ + +package org.eclipse.equinox.internal.p2.repository; + +import java.io.FileNotFoundException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import org.eclipse.core.runtime.*; +import org.eclipse.ecf.core.security.ConnectContextFactory; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.filetransfer.UserCancelledException; +import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException; +import org.eclipse.equinox.internal.provisional.p2.core.IServiceUI.AuthenticationInfo; +import org.eclipse.equinox.internal.provisional.p2.repository.IStateful; +import org.eclipse.osgi.util.NLS; + +/** + * RepositoryTransport adapts p2 to ECF file download and file browsing. + * Download is performed by {@link FileReader}, and file browsing is performed by + * {@link FileInfoReader}. + * + * @author henrik.lindberg@cloudsmith.com + * + */ +public class RepositoryTransport extends Transport { + private static RepositoryTransport instance; + + /** + * Returns an shared instance of Generic Transport + */ + public static synchronized RepositoryTransport getInstance() { + if (instance == null) { + instance = new RepositoryTransport(); + } + return instance; + } + + /** + * @deprecated - use {@link #download(URI, OutputStream, IProgressMonitor) } + * @returns status with {@link URISyntaxException} on malformed <code>toDownload</code> + */ + public IStatus download(String toDownload, OutputStream target, IProgressMonitor monitor) { + // It is not meaningful to continue if the toDownload string is not a valid URI + // so deal with this here immediately instead of getting deep exceptions + // + URI uri = null; + try { + uri = new URI(toDownload); // URIUtil.fromString(toDownload); + + } catch (URISyntaxException e1) { + return RepositoryStatusHelper.malformedAddressStatus(toDownload, e1); + } + return download(uri, target, monitor); + } + + /** + * Perform a download, writing into the target output stream. Progress is reported on the + * monitor. If the <code>target</code> is an instance of {@link IStateful} the resulting status + * is also set on the target. + * + * @returns IStatus, that is a {@link DownloadStatus} on success. + * @param toDownload URI of file to download + * @param target OutputStream where result is written + * @param monitor where progress should be reported + */ + public IStatus download(URI toDownload, OutputStream target, IProgressMonitor monitor) { + boolean promptUser = false; + AuthenticationInfo loginDetails = null; + for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i++) { + try { + loginDetails = Credentials.forLocation(toDownload, promptUser, loginDetails); + IConnectContext context = (loginDetails == null) ? null : ConnectContextFactory.createUsernamePasswordConnectContext(loginDetails.getUserName(), loginDetails.getPassword()); + + // perform the download + FileReader reader = new FileReader(context); + reader.readInto(toDownload, target, monitor); + // Download status is expected on success + DownloadStatus status = new DownloadStatus(IStatus.OK, Activator.ID, Status.OK_STATUS.getMessage()); + status.setTransferRate(reader.getLastFileInfo().getAverageSpeed()); + return statusOn(target, status); + } catch (UserCancelledException e) { + return statusOn(target, Status.CANCEL_STATUS); + } catch (CoreException e) { + return statusOn(target, RepositoryStatus.forStatus(e.getStatus(), toDownload)); + } catch (FileNotFoundException e) { + return statusOn(target, RepositoryStatus.forException(e, toDownload)); + } catch (AuthenticationFailedException e) { + promptUser = true; + } + } + // reached maximum number of retries without success + IStatus status = new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_AUTHENTICATION, // + NLS.bind(Messages.UnableToRead_0_TooManyAttempts, toDownload), null); + return statusOn(target, status); + } + + private static IStatus statusOn(OutputStream target, IStatus status) { + if (target instanceof IStateful) + ((IStateful) target).setStatus(status); + return status; + } + + /** + * Returns the last modified date for a URI, or 0 on any error. + * (If more control over errors is needed, use {@link FileInfoReader#getRemoteFiles(URI, IProgressMonitor)}). + * @param toDownload + * @param monitor + * @return last modified date or 0 on any error + */ + public long getLastModified(URI toDownload, IProgressMonitor monitor) throws UserCancelledException, CoreException, FileNotFoundException, AuthenticationFailedException { + boolean promptUser = false; + AuthenticationInfo loginDetails = null; + for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i++) { + try { + loginDetails = Credentials.forLocation(toDownload, promptUser, loginDetails); + IConnectContext context = (loginDetails == null) ? null : ConnectContextFactory.createUsernamePasswordConnectContext(loginDetails.getUserName(), loginDetails.getPassword()); + + // get the remote info + FileInfoReader reader = new FileInfoReader(context); + return reader.getLastModified(toDownload, monitor); + // } catch (UserCancelledException e) { + // return 0; + } catch (CoreException e) { + // must translate this core exception as it is most likely not informative to a user + throw new CoreException(RepositoryStatus.forStatus(e.getStatus(), toDownload)); + // } catch (FileNotFoundException e) { + // return 0; + } catch (AuthenticationFailedException e) { + promptUser = true; + } + } + // reached maximum number of authentication retries without success + // return 0; + throw new AuthenticationFailedException(); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Transport.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Transport.java new file mode 100644 index 000000000..9b2defb5e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Transport.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import java.io.OutputStream; +import java.net.URI; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; + +public abstract class Transport { + /** + * @deprecated Use {@link #download(URI, OutputStream, IProgressMonitor)} instead + * @param toDownload + * @param target + * @param pm + * @return IStatus describing outcome of the download + */ + public abstract IStatus download(String toDownload, OutputStream target, IProgressMonitor pm); + + /** + * @param toDownload + * @param target + * @param pm + * @return IStatus describing outcome of the download + */ + public abstract IStatus download(URI toDownload, OutputStream target, IProgressMonitor pm); +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java new file mode 100644 index 000000000..167332f72 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java @@ -0,0 +1,956 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository.helpers; + +import java.lang.ref.SoftReference; +import java.net.*; +import java.util.*; +import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.preferences.IPreferencesService; +import org.eclipse.equinox.internal.p2.core.Activator; +import org.eclipse.equinox.internal.p2.core.helpers.*; +import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException; +import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus; +import org.eclipse.equinox.internal.provisional.p2.core.eventbus.ProvisioningListener; +import org.eclipse.equinox.internal.provisional.p2.repository.*; +import org.eclipse.osgi.util.NLS; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.prefs.Preferences; + +/** + * Common code shared between artifact and metadata repository managers. + */ +public abstract class AbstractRepositoryManager implements IRepositoryManager, ProvisioningListener { + protected static class RepositoryInfo { + public String description; + public boolean isEnabled = true; + public boolean isSystem = false; + public URI location; + public String name; + public String nickname; + public SoftReference repository; + public String suffix; + + public RepositoryInfo() { + super(); + } + } + + public static final String ATTR_SUFFIX = "suffix"; //$NON-NLS-1$ + public static final String EL_FACTORY = "factory"; //$NON-NLS-1$ + public static final String EL_FILTER = "filter"; //$NON-NLS-1$ + public static final String KEY_DESCRIPTION = "description"; //$NON-NLS-1$ + public static final String KEY_ENABLED = "enabled"; //$NON-NLS-1$ + public static final String KEY_NAME = "name"; //$NON-NLS-1$ + public static final String KEY_NICKNAME = "nickname"; //$NON-NLS-1$ + public static final String KEY_PROVIDER = "provider"; //$NON-NLS-1$ + public static final String KEY_SUFFIX = "suffix"; //$NON-NLS-1$ + public static final String KEY_SYSTEM = "isSystem"; //$NON-NLS-1$ + public static final String KEY_TYPE = "type"; //$NON-NLS-1$ + public static final String KEY_URI = "uri"; //$NON-NLS-1$ + public static final String KEY_URL = "url"; //$NON-NLS-1$ + public static final String KEY_VERSION = "version"; //$NON-NLS-1$ + + public static final String NODE_REPOSITORIES = "repositories"; //$NON-NLS-1$ + + /** + * Map of String->RepositoryInfo, where String is the repository key + * obtained via getKey(URI). + */ + protected Map repositories = null; + + //lock object to be held when referring to the repositories field + protected final Object repositoryLock = new Object(); + + /** + * Cache List of repositories that are not reachable. Maintain cache + * for short duration because repository may become available at any time. + */ + protected SoftReference unavailableRepositories; + + /** + * Set used to manage exclusive load locks on repository locations. + */ + private Map loadLocks = new HashMap(); + + protected AbstractRepositoryManager() { + IProvisioningEventBus bus = (IProvisioningEventBus) ServiceHelper.getService(Activator.getContext(), IProvisioningEventBus.SERVICE_NAME); + if (bus != null) + bus.addListener(this); + } + + /** + * Adds a repository to the list of known repositories + * @param repository the repository object to add + * @param signalAdd whether a repository change event should be fired + * @param suffix the suffix used to load the repository, or <code>null</code> if unknown + */ + protected void addRepository(IRepository repository, boolean signalAdd, String suffix) { + boolean added = false; + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + String key = getKey(repository.getLocation()); + RepositoryInfo info = (RepositoryInfo) repositories.get(key); + if (info == null) { + info = new RepositoryInfo(); + added = true; + repositories.put(key, info); + } + info.repository = new SoftReference(repository); + info.name = repository.getName(); + info.description = repository.getDescription(); + info.location = repository.getLocation(); + String value = (String) repository.getProperties().get(IRepository.PROP_SYSTEM); + if (value != null) + info.isSystem = Boolean.valueOf(value).booleanValue(); + info.suffix = suffix; + } + // save the given repository in the preferences. + remember(repository, suffix); + if (added && signalAdd) + broadcastChangeEvent(repository.getLocation(), IRepository.TYPE_METADATA, RepositoryEvent.ADDED, true); + } + + /* (non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#addRepository(java.net.URI) + */ + public void addRepository(URI location) { + //add the repository, or enable it if already known + if (!addRepository(location, true, true)) + setEnabled(location, true); + } + + /** + * Adds the repository to the list of known repositories. + * @param location The repository location + * @param isEnabled Whether the repository should be enabled + * @param signalAdd Whether a repository add event should be broadcast + * @return <code>true</code> if the repository was actually added, and + * <code>false</code> otherwise. + */ + private boolean addRepository(URI location, boolean isEnabled, boolean signalAdd) { + RepositoryInfo info = new RepositoryInfo(); + info.location = location; + info.isEnabled = isEnabled; + boolean added = true; + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + if (contains(location)) + return false; + added = repositories.put(getKey(location), info) == null; + // save the given repository in the preferences. + remember(info, true); + } + if (added && signalAdd) + broadcastChangeEvent(location, getRepositoryType(), RepositoryEvent.ADDED, isEnabled); + return added; + } + + protected IRepository basicGetRepository(URI location) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + RepositoryInfo info = (RepositoryInfo) repositories.get(getKey(location)); + if (info == null || info.repository == null) + return null; + IRepository repo = (IRepository) info.repository.get(); + //update our repository info because the repository may have changed + if (repo != null) + addRepository(repo, false, null); + return repo; + } + } + + public IRepository basicRefreshRepository(URI location, IProgressMonitor monitor) throws ProvisionException { + clearNotFound(location); + boolean wasEnabled = isEnabled(location); + //remove the repository so event is broadcast and repositories can clear their caches + if (!removeRepository(location)) + fail(location, ProvisionException.REPOSITORY_NOT_FOUND); + boolean loaded = false; + try { + IRepository result = loadRepository(location, monitor, null, 0); + loaded = true; + setEnabled(location, wasEnabled); + return result; + } finally { + //if we failed to load, make sure the repository is not lost + if (!loaded) + addRepository(location, wasEnabled, true); + } + } + + private void broadcastChangeEvent(URI location, int repositoryType, int kind, boolean isEnabled) { + IProvisioningEventBus bus = (IProvisioningEventBus) ServiceHelper.getService(Activator.getContext(), IProvisioningEventBus.class.getName()); + if (bus != null) + bus.publishEvent(new RepositoryEvent(location, repositoryType, kind, isEnabled)); + } + + /** + * Check if we recently attempted to load the given location and failed + * to find anything. Returns <code>true</code> if the repository was not + * found, and <code>false</code> otherwise. + */ + private boolean checkNotFound(URI location) { + if (unavailableRepositories == null) + return false; + List badRepos = (List) unavailableRepositories.get(); + if (badRepos == null) + return false; + return badRepos.contains(location); + } + + /** + * Clear the fact that we tried to load a repository at this location and did not find anything. + */ + protected void clearNotFound(URI location) { + List badRepos; + if (unavailableRepositories != null) { + badRepos = (List) unavailableRepositories.get(); + if (badRepos != null) { + badRepos.remove(location); + return; + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#contains(java.net.URI) + */ + public boolean contains(URI location) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + return repositories.containsKey(getKey(location)); + } + } + + /* (non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepositoryManager#createRepository(java.net.URL, java.lang.String, java.lang.String, java.util.Map) + */ + protected IRepository doCreateRepository(URI location, String name, String type, Map properties) throws ProvisionException { + Assert.isNotNull(name); + Assert.isNotNull(type); + IRepository result = null; + try { + enterLoad(location); + boolean loaded = false; + try { + //repository should not already exist + loadRepository(location, (IProgressMonitor) null, type, 0); + loaded = true; + } catch (ProvisionException e) { + //expected - fall through and create the new repository + } + if (loaded) + fail(location, ProvisionException.REPOSITORY_EXISTS); + + IExtension extension = RegistryFactory.getRegistry().getExtension(getRepositoryProviderExtensionPointId(), type); + if (extension == null) + fail(location, ProvisionException.REPOSITORY_UNKNOWN_TYPE); + // MetadataRepositoryFactory factory = (MetadataRepositoryFactory) createExecutableExtension(extension, EL_FACTORY); + // if (factory == null) + // fail(location, ProvisionException.REPOSITORY_FAILED_READ); + result = factoryCreate(location, name, type, properties, extension); + if (result == null) + fail(location, ProvisionException.REPOSITORY_FAILED_READ); + clearNotFound(location); + addRepository(result, false, null); + } finally { + exitLoad(location); + } + //fire event after releasing load lock + broadcastChangeEvent(location, getRepositoryType(), RepositoryEvent.ADDED, true); + return result; + } + + /** + * Returns the executable extension, or <code>null</code> if there + * was no corresponding extension, or an error occurred loading it + */ + protected Object createExecutableExtension(IExtension extension, String element) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + CoreException failure = null; + for (int i = 0; i < elements.length; i++) { + if (elements[i].getName().equals(element)) { + try { + return elements[i].createExecutableExtension("class"); //$NON-NLS-1$ + } catch (CoreException e) { + log("Error loading repository extension: " + extension.getUniqueIdentifier(), failure); //$NON-NLS-1$ + return null; + } + } + } + log("Malformed repository extension: " + extension.getUniqueIdentifier(), null); //$NON-NLS-1$ + return null; + } + + /** + * Obtains an exclusive right to load a repository at the given location. Blocks + * if another thread is currently loading at that location. Invocation of this + * method must be followed by a subsequent call to {@link #exitLoad(URI)}. + * + * To avoid deadlock between the loadLock and repositoryLock, this method + * must not be called when repositoryLock is held. + * + * @param location The location to lock + */ + private void enterLoad(URI location) { + Thread current = Thread.currentThread(); + synchronized (loadLocks) { + while (true) { + Thread owner = (Thread) loadLocks.get(location); + if (owner == null || current.equals(owner)) + break; + try { + loadLocks.wait(); + } catch (InterruptedException e) { + //keep trying + } + } + loadLocks.put(location, current); + } + } + + /** + * Relinquishes the exclusive right to load a repository at the given location. Unblocks + * other threads waiting to load at that location. + * @param location The location to unlock + */ + private void exitLoad(URI location) { + synchronized (loadLocks) { + loadLocks.remove(location); + loadLocks.notifyAll(); + } + } + + /** + * Creates and returns a repository using the given repository factory extension. Returns + * null if no factory could be found associated with that extension. + */ + protected abstract IRepository factoryCreate(URI location, String name, String type, Map properties, IExtension extension) throws ProvisionException; + + /** + * Loads and returns a repository using the given repository factory extension. Returns + * null if no factory could be found associated with that extension. + */ + protected abstract IRepository factoryLoad(URI location, IExtension extension, int flags, SubMonitor monitor) throws ProvisionException; + + protected void fail(URI location, int code) throws ProvisionException { + String msg = null; + switch (code) { + case ProvisionException.REPOSITORY_EXISTS : + msg = NLS.bind(Messages.repoMan_exists, location); + break; + case ProvisionException.REPOSITORY_UNKNOWN_TYPE : + msg = NLS.bind(Messages.repoMan_unknownType, location); + break; + case ProvisionException.REPOSITORY_FAILED_READ : + msg = NLS.bind(Messages.repoMan_failedRead, location); + break; + case ProvisionException.REPOSITORY_NOT_FOUND : + msg = NLS.bind(Messages.repoMan_notExists, location); + break; + } + if (msg == null) + msg = Messages.repoMan_internalError; + throw new ProvisionException(new Status(IStatus.ERROR, getBundleId(), code, msg, null)); + } + + protected IExtension[] findMatchingRepositoryExtensions(String suffix, String type) { + IConfigurationElement[] elt = null; + if (type != null && type.length() > 0) { + IExtension ext = RegistryFactory.getRegistry().getExtension(getRepositoryProviderExtensionPointId(), type); + elt = (ext != null) ? ext.getConfigurationElements() : new IConfigurationElement[0]; + } else { + elt = RegistryFactory.getRegistry().getConfigurationElementsFor(getRepositoryProviderExtensionPointId()); + } + int count = 0; + for (int i = 0; i < elt.length; i++) { + if (EL_FILTER.equals(elt[i].getName())) { + if (!suffix.equals(elt[i].getAttribute(ATTR_SUFFIX))) { + elt[i] = null; + } else { + count++; + } + } else { + elt[i] = null; + } + } + IExtension[] results = new IExtension[count]; + for (int i = 0; i < elt.length; i++) { + if (elt[i] != null) + results[--count] = elt[i].getDeclaringExtension(); + } + return results; + } + + protected String[] getAllSuffixes() { + IConfigurationElement[] elements = RegistryFactory.getRegistry().getConfigurationElementsFor(getRepositoryProviderExtensionPointId()); + ArrayList result = new ArrayList(elements.length); + result.add(getDefaultSuffix()); + for (int i = 0; i < elements.length; i++) { + if (elements[i].getName().equals(EL_FILTER)) { + String suffix = elements[i].getAttribute(ATTR_SUFFIX); + if (!result.contains(suffix)) + result.add(suffix); + } + } + return (String[]) result.toArray(new String[result.size()]); + } + + /** + * Returns the bundle id of the bundle that provides the concrete repository manager + * @return a symbolic bundle id + */ + protected abstract String getBundleId(); + + /** + * Returns the default repository suffix. This is used to ensure a particular + * repository type is preferred over all others. + */ + protected abstract String getDefaultSuffix(); + + /* + * Return a string key based on the given repository location which + * is suitable for use as a preference node name. + * TODO: convert local file system URI to canonical form + */ + private String getKey(URI location) { + String key = location.toString().replace('/', '_'); + //remove trailing slash + if (key.endsWith("_")) //$NON-NLS-1$ + key = key.substring(0, key.length() - 1); + return key; + } + + /* (non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#getKnownRepositories(int) + */ + public URI[] getKnownRepositories(int flags) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + ArrayList result = new ArrayList(); + int i = 0; + for (Iterator it = repositories.values().iterator(); it.hasNext(); i++) { + RepositoryInfo info = (RepositoryInfo) it.next(); + if (matchesFlags(info, flags)) + result.add(info.location); + } + return (URI[]) result.toArray(new URI[result.size()]); + } + } + + /** + * Return the preference node which is the root for where we store the repository information. + */ + Preferences getPreferences() { + IPreferencesService prefService = (IPreferencesService) ServiceHelper.getService(Activator.getContext(), IPreferencesService.class.getName()); + + try { + return prefService.getRootNode().node("/profile/_SELF_/" + getBundleId() + "/" + NODE_REPOSITORIES); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IllegalArgumentException e) { + return null; + } + } + + /** + * Restores a repository location from the preferences. + */ + private URI getRepositoryLocation(Preferences node) { + //prefer the location stored in URI form + String locationString = node.get(KEY_URI, null); + try { + if (locationString != null) + return new URI(locationString); + } catch (URISyntaxException e) { + log("Error while restoring repository: " + locationString, e); //$NON-NLS-1$ + } + //we used to store the repository as a URL, so try old key for backwards compatibility + locationString = node.get(KEY_URL, null); + try { + if (locationString != null) + return URIUtil.toURI(new URL(locationString)); + } catch (MalformedURLException e) { + log("Error while restoring repository: " + locationString, e); //$NON-NLS-1$ + } catch (URISyntaxException e) { + log("Error while restoring repository: " + locationString, e); //$NON-NLS-1$ + } + return null; + } + + /*(non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#getRepositoryProperty(java.net.URI, java.lang.String) + */ + public String getRepositoryProperty(URI location, String key) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + RepositoryInfo info = (RepositoryInfo) repositories.get(getKey(location)); + if (info == null) + return null;// Repository not found + if (IRepository.PROP_DESCRIPTION.equals(key)) + return info.description; + else if (IRepository.PROP_NAME.equals(key)) + return info.name; + else if (IRepository.PROP_SYSTEM.equals(key)) + return Boolean.toString(info.isSystem); + else if (IRepository.PROP_NICKNAME.equals(key)) + return info.nickname; + // Key not known, return null + return null; + } + } + + /*(non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#getRepositoryProperty(java.net.URI, java.lang.String) + */ + public void setRepositoryProperty(URI location, String key, String value) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + RepositoryInfo info = (RepositoryInfo) repositories.get(getKey(location)); + if (info == null) + return;// Repository not found + if (IRepository.PROP_DESCRIPTION.equals(key)) + info.description = value; + else if (IRepository.PROP_NAME.equals(key)) + info.name = value; + else if (IRepository.PROP_NICKNAME.equals(key)) + info.nickname = value; + else if (IRepository.PROP_SYSTEM.equals(key)) + //only true if value.equals("true") which is OK because a repository is only system if it's explicitly set to system. + info.isSystem = Boolean.valueOf(value).booleanValue(); + remember(info, true); + } + } + + /** + * Returns the fully qualified id of the repository provider extension point. + */ + protected abstract String getRepositoryProviderExtensionPointId(); + + /** + * Returns the system property used to specify additional repositories to be + * automatically added to the list of known repositories. + */ + protected abstract String getRepositorySystemProperty(); + + /** + * Returns the repository type stored in this manager. + */ + protected abstract int getRepositoryType(); + + /* (non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#isEnabled(java.net.URI) + */ + public boolean isEnabled(URI location) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + RepositoryInfo info = (RepositoryInfo) repositories.get(getKey(location)); + if (info != null) + return info.isEnabled; + // Repository not found, return false + return false; + } + } + + protected IRepository loadRepository(URI location, IProgressMonitor monitor, String type, int flags) throws ProvisionException { + boolean added = false; + IRepository result = null; + + try { + enterLoad(location); + result = basicGetRepository(location); + if (result != null) + return result; + if (checkNotFound(location)) + fail(location, ProvisionException.REPOSITORY_NOT_FOUND); + //add the repository first so that it will be enabled, but don't send add event until after the load + added = addRepository(location, true, false); + String[] suffixes = sortSuffixes(getAllSuffixes(), location); + SubMonitor sub = SubMonitor.convert(monitor, NLS.bind(Messages.repoMan_adding, location), suffixes.length * 100); + ProvisionException failure = null; + try { + for (int i = 0; i < suffixes.length; i++) { + if (sub.isCanceled()) + throw new OperationCanceledException(); + try { + result = loadRepository(location, suffixes[i], type, flags, sub.newChild(100)); + } catch (ProvisionException e) { + failure = e; + break; + } + if (result != null) { + addRepository(result, false, suffixes[i]); + break; + } + } + } finally { + sub.done(); + } + if (result == null) { + //if we just added the repository, remove it because it cannot be loaded + if (added) + removeRepository(location, false); + //eagerly cleanup missing system repositories + if (Boolean.valueOf(getRepositoryProperty(location, IRepository.PROP_SYSTEM)).booleanValue()) + removeRepository(location); + else + rememberNotFound(location); + if (failure != null) + throw failure; + fail(location, ProvisionException.REPOSITORY_NOT_FOUND); + } + } finally { + exitLoad(location); + } + //broadcast the add event after releasing lock + if (added) + broadcastChangeEvent(location, IRepository.TYPE_METADATA, RepositoryEvent.ADDED, true); + return result; + } + + private IRepository loadRepository(URI location, String suffix, String type, int flags, SubMonitor monitor) throws ProvisionException { + IExtension[] providers = findMatchingRepositoryExtensions(suffix, type); + // Loop over the candidates and return the first one that successfully loads + monitor.beginTask("", providers.length * 10); //$NON-NLS-1$ + for (int i = 0; i < providers.length; i++) + try { + IRepository repo = factoryLoad(location, providers[i], flags, monitor); + if (repo != null) + return repo; + } catch (ProvisionException e) { + if (e.getStatus().getCode() != ProvisionException.REPOSITORY_NOT_FOUND) + throw e; + } catch (Exception e) { + //catch and log unexpected errors and move onto the next factory + log("Unexpected error loading extension: " + providers[i].getUniqueIdentifier(), e); //$NON-NLS-1$ + } catch (LinkageError e) { + //catch and log unexpected errors and move onto the next factory + log("Unexpected error loading extension: " + providers[i].getUniqueIdentifier(), e); //$NON-NLS-1$ + } + return null; + } + + protected void log(String message, Throwable t) { + LogHelper.log(new Status(IStatus.ERROR, getBundleId(), message, t)); + } + + private boolean matchesFlags(RepositoryInfo info, int flags) { + if ((flags & REPOSITORIES_SYSTEM) == REPOSITORIES_SYSTEM) + if (!info.isSystem) + return false; + if ((flags & REPOSITORIES_NON_SYSTEM) == REPOSITORIES_NON_SYSTEM) + if (info.isSystem) + return false; + if ((flags & REPOSITORIES_DISABLED) == REPOSITORIES_DISABLED) { + if (info.isEnabled) + return false; + } else { + //ignore disabled repositories for all other flag types + if (!info.isEnabled) + return false; + } + if ((flags & REPOSITORIES_LOCAL) == REPOSITORIES_LOCAL) + return "file".equals(info.location.getScheme()); //$NON-NLS-1$ + return true; + } + + /*(non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.eventbus.ProvisioningListener#notify(java.util.EventObject) + */ + public void notify(EventObject o) { + if (o instanceof RepositoryEvent) { + RepositoryEvent event = (RepositoryEvent) o; + if (event.getKind() == RepositoryEvent.DISCOVERED && event.getRepositoryType() == getRepositoryType()) + addRepository(event.getRepositoryLocation(), event.isRepositoryEnabled(), true); + } + } + + /** + * Sets a preference and returns <code>true</code> if the preference + * was actually changed. + */ + protected boolean putValue(Preferences node, String key, String newValue) { + String oldValue = node.get(key, null); + if (oldValue == newValue || (oldValue != null && oldValue.equals(newValue))) + return false; + if (newValue == null) + node.remove(key); + else + node.put(key, newValue); + return true; + } + + /* + * Add the given repository object to the preferences and save. + */ + private void remember(IRepository repository, String suffix) { + boolean changed = false; + Preferences node = getPreferences(); + // Ensure we retrieved preferences + if (node == null) + return; + node = node.node(getKey(repository.getLocation())); + + try { + changed |= putValue(node, KEY_URI, repository.getLocation().toString()); + changed |= putValue(node, KEY_URL, null); + changed |= putValue(node, KEY_DESCRIPTION, repository.getDescription()); + changed |= putValue(node, KEY_NAME, repository.getName()); + changed |= putValue(node, KEY_PROVIDER, repository.getProvider()); + changed |= putValue(node, KEY_TYPE, repository.getType()); + changed |= putValue(node, KEY_VERSION, repository.getVersion()); + changed |= putValue(node, KEY_SYSTEM, (String) repository.getProperties().get(IRepository.PROP_SYSTEM)); + changed |= putValue(node, KEY_SUFFIX, suffix); + if (changed) + saveToPreferences(); + } catch (IllegalStateException e) { + //the repository was removed concurrently, so we don't need to save it + } + } + + /** + * Writes the state of the repository information into the appropriate preference node. + * + * @param info The info to write to the preference node + * @param flush <code>true</code> if the preference node should be flushed to + * disk, and <code>false</code> otherwise + */ + private boolean remember(RepositoryInfo info, boolean flush) { + boolean changed = false; + Preferences node = getPreferences(); + // Ensure we retrieved preferences + if (node == null) + return changed; + node = node.node(getKey(info.location)); + try { + changed |= putValue(node, KEY_URI, info.location.toString()); + changed |= putValue(node, KEY_URL, null); + changed |= putValue(node, KEY_SYSTEM, Boolean.toString(info.isSystem)); + changed |= putValue(node, KEY_DESCRIPTION, info.description); + changed |= putValue(node, KEY_NAME, info.name); + changed |= putValue(node, KEY_NICKNAME, info.nickname); + changed |= putValue(node, KEY_SUFFIX, info.suffix); + changed |= putValue(node, KEY_ENABLED, Boolean.toString(info.isEnabled)); + if (changed && flush) + saveToPreferences(); + return changed; + } catch (IllegalStateException e) { + //the repository was removed concurrently, so we don't need to save it + return false; + } + } + + /** + * Cache the fact that we tried to load a repository at this location and did not find anything. + */ + private void rememberNotFound(URI location) { + List badRepos; + if (unavailableRepositories != null) { + badRepos = (List) unavailableRepositories.get(); + if (badRepos != null) { + badRepos.add(location); + return; + } + } + badRepos = new ArrayList(); + badRepos.add(location); + unavailableRepositories = new SoftReference(badRepos); + } + + public boolean removeRepository(URI toRemove) { + return removeRepository(toRemove, true); + } + + private boolean removeRepository(URI toRemove, boolean signalRemove) { + Assert.isNotNull(toRemove); + final String repoKey = getKey(toRemove); + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + if (repositories.remove(repoKey) == null) + return false; + } + // remove the repository from the preference store + try { + if (Tracing.DEBUG_REMOVE_REPO) { + String msg = "Removing repository: " + toRemove; //$NON-NLS-1$ + Tracing.debug(msg); + new Exception(msg).printStackTrace(); + } + Preferences node = getPreferences(); + if (node != null) { + getPreferences().node(repoKey).removeNode(); + saveToPreferences(); + } + clearNotFound(toRemove); + } catch (BackingStoreException e) { + log("Error saving preferences", e); //$NON-NLS-1$ + } + //TODO: compute and pass appropriate isEnabled flag + if (signalRemove) + broadcastChangeEvent(toRemove, getRepositoryType(), RepositoryEvent.REMOVED, true); + return true; + } + + /* + * Load the list of repositories from the preferences. + */ + private void restoreFromPreferences() { + // restore the list of repositories from the preference store + Preferences node = getPreferences(); + if (node == null) + return; + String[] children; + try { + children = node.childrenNames(); + } catch (BackingStoreException e) { + log("Error restoring repositories from preferences", e); //$NON-NLS-1$ + return; + } + for (int i = 0; i < children.length; i++) { + Preferences child = node.node(children[i]); + URI location = getRepositoryLocation(child); + if (location == null) + continue; + RepositoryInfo info = new RepositoryInfo(); + info.location = location; + info.name = child.get(KEY_NAME, null); + info.nickname = child.get(KEY_NICKNAME, null); + info.description = child.get(KEY_DESCRIPTION, null); + info.isSystem = child.getBoolean(KEY_SYSTEM, false); + info.isEnabled = child.getBoolean(KEY_ENABLED, true); + info.suffix = child.get(KEY_SUFFIX, null); + repositories.put(getKey(info.location), info); + } + // now that we have loaded everything, remember them + saveToPreferences(); + } + + private void restoreFromSystemProperty() { + String locationString = Activator.getContext().getProperty(getRepositorySystemProperty()); + if (locationString != null) { + StringTokenizer tokenizer = new StringTokenizer(locationString, ","); //$NON-NLS-1$ + while (tokenizer.hasMoreTokens()) { + try { + addRepository(new URI(tokenizer.nextToken()), true, true); + } catch (URISyntaxException e) { + log("Error while restoring repository " + locationString, e); //$NON-NLS-1$ + } + } + } + } + + /** + * Restores the repository list. + */ + private void restoreRepositories() { + synchronized (repositoryLock) { + repositories = new HashMap(); + restoreSpecialRepositories(); + restoreFromSystemProperty(); + restoreFromPreferences(); + } + } + + /** + * Hook method to restore special additional repositories. + */ + protected void restoreSpecialRepositories() { + //by default no special repositories + } + + /* + * Save the list of repositories to the file-system. + */ + private void saveToPreferences() { + try { + Preferences node = getPreferences(); + if (node != null) + node.flush(); + } catch (BackingStoreException e) { + log("Error while saving repositories in preferences", e); //$NON-NLS-1$ + } + } + + /* (non-Javadoc) + * @see org.eclipse.equinox.internal.provisional.p2.core.repository.IRepositoryManager#setEnabled(java.net.URI, boolean) + */ + public void setEnabled(URI location, boolean enablement) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + RepositoryInfo info = (RepositoryInfo) repositories.get(getKey(location)); + if (info == null || info.isEnabled == enablement) + return; + info.isEnabled = enablement; + remember(info, true); + } + broadcastChangeEvent(location, getRepositoryType(), RepositoryEvent.ENABLEMENT, enablement); + } + + /** + * Shuts down the repository manager. + */ + public void shutdown() { + IProvisioningEventBus bus = (IProvisioningEventBus) ServiceHelper.getService(Activator.getContext(), IProvisioningEventBus.SERVICE_NAME); + if (bus != null) + bus.removeListener(this); + //ensure all repository state in memory is written to disk + boolean changed = false; + synchronized (repositoryLock) { + if (repositories != null) { + for (Iterator it = repositories.values().iterator(); it.hasNext();) { + RepositoryInfo info = (RepositoryInfo) it.next(); + changed |= remember(info, false); + } + } + } + if (changed) { + if (Tracing.DEBUG) + Tracing.debug("Unsaved preferences when shutting down " + getClass().getName()); //$NON-NLS-1$ + saveToPreferences(); + } + repositories = null; + unavailableRepositories = null; + } + + /** + * Optimize the order in which repository suffixes are searched by trying + * the last successfully loaded suffix first. + */ + private String[] sortSuffixes(String[] suffixes, URI location) { + synchronized (repositoryLock) { + if (repositories == null) + restoreRepositories(); + RepositoryInfo info = (RepositoryInfo) repositories.get(getKey(location)); + if (info == null || info.suffix == null) + return suffixes; + //move lastSuffix to the front of the list but preserve order of remaining entries + String lastSuffix = info.suffix; + for (int i = 0; i < suffixes.length; i++) { + if (lastSuffix.equals(suffixes[i])) { + System.arraycopy(suffixes, 0, suffixes, 1, i); + suffixes[0] = lastSuffix; + return suffixes; + } + } + } + return suffixes; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java new file mode 100644 index 000000000..fbf79029c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository.helpers; + +import org.eclipse.osgi.util.NLS; + +class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.repository.helpers.messages"; //$NON-NLS-1$ + + static { + // load message values from bundle file and assign to fields below + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } + + public static String repoMan_adding; + public static String repoMan_exists; + public static String repoMan_failedRead; + public static String repoMan_internalError; + public static String repoMan_notExists; + public static String repoMan_save; + public static String repoMan_unknownType; + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties new file mode 100644 index 000000000..aefad68d0 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties @@ -0,0 +1,18 @@ +############################################################################### +# Copyright (c) 2007, 2008 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### + +repoMan_adding = Adding repository {0} +repoMan_exists=Repository already exists at {0}. +repoMan_failedRead=The repository could not be read: {0}. +repoMan_internalError=Internal error. +repoMan_notExists=No repository found at {0}. +repoMan_save=Saving repository settings +repoMan_unknownType=Unknown repository type at {0}.
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties new file mode 100644 index 000000000..546ed87d6 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties @@ -0,0 +1,102 @@ +############################################################################### +# Copyright (c) 2007, 2008 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# IBM Corporation - initial API and implementation +# Cloudsmith Inc - additional messages +############################################################################### +artifact_not_found=Artifact not found: {0}. +available_already_in=The artifact is already available in the repository {0}. +downloading=Downloading {0} +error_closing_stream=Error closing the output stream for {0} on repository {1}. + +io_failedRead=Unable to read repository at {0}. +io_parseError=\ + Error parsing simple artifact repository. +ecf_configuration_error=Transport initialization error. +io_incompatibleVersion=\ + Simple artifact repository has incompatible version {0}; expected {1}. +io_invalidLocation=Invalid repository location: {0} +mirroring=Mirroring: + +repoFailedWrite=Unable to write to repository: {0}. +repoReadOnly=Cannot write because repository is read only: {0} + +repoMan_internalError=Internal error. +repo_loading = Loading the repository {0} + +SignatureVerification_failedRead=Error reading signed content: +SignatureVerification_invalidContent=Invalid content: +SignatureVerification_invalidFileContent=File has invalid content: +SignatureVerifier_OutOfMemory=Out of memory: Cannot verify signed content. + +sar_downloading=Download {0} artifacts +sar_downloadJobName=Install download +sar_failedMkdir=Failed to create directory {0}. +sar_reportStatus=Problems downloading artifact: {0}. + +mirror_alreadyExists=Artifact: {0} already exists in repository: {1}. + +message_mirroringStatus = Messages while mirroring artifact descriptors. +message_childrenRepos = Messages while trying children repositories. + +exception_comparatorNotFound = The Artifact Comparator {0} was not found. +exception_noComparators = No Artifact Comparators are available. +exception_destinationNotModifiable = The destination repository must be modifiable: {0}. +exception_needSourceDestination = Must specify a source and destination. +exception_malformedRepoURI = The repository location ({0}) must be a URI. +Mirroring_NO_MATCHING_DESCRIPTOR=Could not match descriptor for compare + +exception_unsupportedAddToComposite = Cannot add descriptors to a composite repository. +exception_unsupportedGetOutputStream=Cannot write artifacts to a composite repository. +exception_unsupportedRemoveFromComposite = Cannot remove descriptors from a composite repository. +TransportErrorTranslator_400=Bad HTTP Request: {0} +TransportErrorTranslator_401=Authentication Failed - Unauthorized: {0} +TransportErrorTranslator_402=HTTP Payment Required: {0} +TransportErrorTranslator_403=HTTP Access Forbidden: {0} +TransportErrorTranslator_404=HTTP Remote File Not Found: {0} +TransportErrorTranslator_405=HTTP Method Not Allowed: {0} +TransportErrorTranslator_406=HTTP Request Not Acceptable: {0} +TransportErrorTranslator_407=HTTP Proxy Authentication Required: {0} +TransportErrorTranslator_408=HTTP Request Timeout: {0} +TransportErrorTranslator_409=HTTP Conflict In Request: {0} +TransportErrorTranslator_410=HTTP Remote File Permanently Removed: {0} +TransportErrorTranslator_411=HTTP Length Required: {0} +TransportErrorTranslator_412=HTTP Precondition Failed: {0} +TransportErrorTranslator_413=HTTP Requested Entity Too Large: {0} +TransportErrorTranslator_414=HTTP Request URI Too Long: {0} +TransportErrorTranslator_415=HTTP Unsupported Media Type: {0} +TransportErrorTranslator_416=HTTP Requested Range Not Satisfiable: {0} +TransportErrorTranslator_417=HTTP Expectation Failed: {0} +TransportErrorTranslator_418=HTTP April Fool's Teapot Error: {0} +TransportErrorTranslator_422=HTTP (WebDav) Unprocessable Entity: {0} +TransportErrorTranslator_423=HTTP (WebDAV) Locked: {0} +TransportErrorTranslator_424=HTTP (WebDAV) Failed Dependency: {0} +TransportErrorTranslator_425=HTTP Unordered Collection: {0} +TransportErrorTranslator_426=HTTP Upgrade Required: {0} +TransportErrorTranslator_449=HTTP Retry With Response: {0} +TransportErrorTranslator_450=HTTP Blocked By Parental Control: {0} +TransportErrorTranslator_500=HTTP Server 'Internal Error': {0} +TransportErrorTranslator_501=HTTP Server 'Not Implemented': {0} +TransportErrorTranslator_502=HTTP Server 'Bad Gateway' : {0} +TransportErrorTranslator_503=HTTP Server 'Service Unavailable': {0} +TransportErrorTranslator_504=HTTP Server 'Gateway Timeout': {0} +TransportErrorTranslator_505=HTTP Server 'HTTP Version Not Supported: {0} +TransportErrorTranslator_506=HTTP Server 'Variant Also Negotiates': {0} +TransportErrorTranslator_507=HTTP (WebDAV) 'Insufficient Storage': {0} +TransportErrorTranslator_508=HTTP Server 'Bandwidth Limit Exceeded': {0} +TransportErrorTranslator_510=HTTP Server 'Not Extended': {0} +TransportErrorTranslator_MalformedReference=Malformed reference to remote file: {0} +TransportErrorTranslator_MalformedRemoteFileReference=Malformed reference to remote file: {0} +TransportErrorTranslator_UnableToConnectToRepository_0=Unable to connect to repository {0} +TransportErrorTranslator_UnknownErrorCode=HTTP Server Unknown HTTP Response Code ({0}):{1} +TransportErrorTranslator_UnknownHost=Unknown Host: {0} +fetching_0_1_at_2=Fetching {0} ({1} at {2}/s) +fetching_0_1_of_2_at_3=Fetching {0} ({1} of {2} at {3}/s) +FileTransport_reader=File Transport Reader +connection_to_0_failed_on_1_retry_attempt_2=Connection to {0} failed on {1}. Retry attempt {2} started +UnableToRead_0_TooManyAttempts=Unable to read repository at: {0}. Too many failed attempts. diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/ICompositeRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/ICompositeRepository.java new file mode 100644 index 000000000..2e11dcf0e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/ICompositeRepository.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import java.net.URI; +import java.util.List; + +public interface ICompositeRepository extends IRepository { + /** + * + * @return a list of URIs containing the locations of the children repositories + */ + public abstract List getChildren(); + + /** + * Removes all child repositories + */ + public abstract void removeAllChildren(); + + /** + * Removes specified URI from list of child repositories. + * Does nothing if specified URI is not a child repository + * @param child + */ + public abstract void removeChild(URI child); + + /** + * Adds a specified URI to list of child repositories. + * Does nothing if URI is a duplicate of an existing child repository. + * @param child + */ + public abstract void addChild(URI child); +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepository.java new file mode 100644 index 000000000..68113b6be --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepository.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import java.net.URI; +import java.util.Map; +import org.eclipse.core.runtime.IAdaptable; + +/** + * Base interface that defines common properties that may be provided by + * various kinds of repositories. + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IRepository extends IAdaptable { + /** + * The key for a boolean property indicating that the repository + * is a system repository. System repositories are implementation details + * that are not subject to general access, hidden from the typical user, etc. + */ + public static final String PROP_SYSTEM = "p2.system"; //$NON-NLS-1$ + + /** + * The key for a boolean property indicating that repository metadata is + * stored in compressed form. A compressed repository will have lower + * bandwidth cost to read when remote, but higher processing cost to + * uncompress when reading. + */ + public static final String PROP_COMPRESSED = "p2.compressed"; //$NON-NLS-1$ + + /** + * The key for a string property providing a human-readable name for the repository. + */ + public static final String PROP_NAME = "name"; //$NON-NLS-1$ + + /** + * The key for a string property providing a user-defined name for the repository. + * This property is never stored in the repository itself, but is instead tracked and managed + * by an {@link IRepositoryManager}. + */ + public static final String PROP_NICKNAME = "p2.nickname"; //$NON-NLS-1$ + + /** + * The key for a string property providing a human-readable description for the repository. + */ + public static final String PROP_DESCRIPTION = "description"; //$NON-NLS-1$ + + /** + * The key for a string property providing the common base URL that should + * be replaced with the mirror URL. + */ + public static final String PROP_MIRRORS_BASE_URL = "p2.mirrorsBaseURL"; //$NON-NLS-1$ + + /** + * The key for a string property providing a URL that can return mirrors of this + * repository. + */ + public static final String PROP_MIRRORS_URL = "p2.mirrorsURL"; //$NON-NLS-1$ + + /** + * The key for a string property containing the time when the repository was last modified. + */ + public static final String PROP_TIMESTAMP = "p2.timestamp"; //$NON-NLS-1$ + + /** + * The key for a string property providing the user name to an authenticated + * URL. This key is used in the secure preference store for repository data. + * @see #PREFERENCE_NODE + */ + public static final String PROP_USERNAME = "username"; //$NON-NLS-1$ + + /** + * The key for a string property providing the password to an authenticated + * URL. This key is used in the secure preference store for repository data. + * @see #PREFERENCE_NODE + */ + public static final String PROP_PASSWORD = "password"; //$NON-NLS-1$ + + /** + * The node identifier for repository secure preference store. + */ + public static final String PREFERENCE_NODE = "org.eclipse.equinox.p2.repository"; //$NON-NLS-1$ + + /** + * A repository type constant (value 0) representing a metadata repository. + */ + public static final int TYPE_METADATA = 0; + + /** + * A repository type constant (value 1) representing an artifact repository. + */ + public static final int TYPE_ARTIFACT = 1; + + /** + * General purpose zero-valued bit mask constant. Useful whenever you need to + * supply a bit mask with no bits set. + */ + public static final int NONE = 0; + + /** + * An option flag constant (value 1) indicating an enabled repository. + */ + public static final int ENABLED = 1; + + /** + * Returns the URL of the repository. + * TODO: Should we use URL or URI? URL requires a protocol handler + * to be installed in Java. Can the URL have any protocol? + * @return the URL of the repository. + */ + public URI getLocation(); + + /** + * Returns the name of the repository. + * @return the name of the repository. + */ + public String getName(); + + /** + * Returns a string representing the type of the repository. + * @return the type of the repository. + */ + public String getType(); + + /** + * Returns a string representing the version for the repository type. + * @return the version of the type of the repository. + */ + public String getVersion(); + + /** + * Returns a brief description of the repository. + * @return the description of the repository. + */ + public String getDescription(); + + /** + * Returns the name of the provider of the repository. + * @return the provider of this repository. + */ + public String getProvider(); + + /** + * Returns a read-only collection of the properties of the repository. + * @return the properties of this repository. + */ + public Map getProperties(); + + /** + * Returns <code>true</code> if this repository can be modified. + * @return whether or not this repository can be modified + */ + public boolean isModifiable(); + + /** + * Set the name of the repository. + */ + public void setName(String name); + + /** + * Sets the description of the repository. + */ + public void setDescription(String description); + + /** + * Sets the value of the property with the given key. Returns the old property + * associated with that key, if any. Setting a value of <code>null</code> will + * remove the corresponding key from the properties of this repository. + * + * @param key The property key + * @param value The new property value, or <code>null</code> to remove the key + * @return The old property value, or <code>null</code> if there was no old value + */ + public String setProperty(String key, String value); + + /** + * Sets the name of the provider of the repository. + */ + public void setProvider(String provider); +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepositoryManager.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepositoryManager.java new file mode 100644 index 000000000..69ec21076 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepositoryManager.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import java.net.URI; + +/** + * The common base class for metadata and artifact repository managers. + * <p> + * A repository manager keeps track of a set of known repositories, and provides + * caching of these known repositories to avoid unnecessary loading of repositories + * from the disk or network. The manager fires {@link RepositoryEvent}s when the + * set of known repositories changes. + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IRepositoryManager { + /** + * Constant used to indicate that all enabled repositories are of interest. + */ + public static final int REPOSITORIES_ALL = 0; + + /** + * Constant used to indicate that disabled repositories are of interest. + * @see #getKnownRepositories(int) + */ + public static final int REPOSITORIES_DISABLED = 1 << 3; + + /** + * Constant used to indicate that local repositories are of interest. + * @see #getKnownRepositories(int) + */ + public static final int REPOSITORIES_LOCAL = 1 << 2; + + /** + * Constant used to indicate that non-system repositories are of interest. + * @see IRepository#PROP_SYSTEM + * @see #getKnownRepositories(int) + */ + public static final int REPOSITORIES_NON_SYSTEM = 1 << 1; + + /** + * Constant used to indicate that system repositories are of interest. + * @see IRepository#PROP_SYSTEM + * @see #getKnownRepositories(int) + */ + public static final int REPOSITORIES_SYSTEM = 1 << 0; + + /** + * Constant used to indicate that a repository manager should only load the + * repository if the repository is modifiable. + * @see IRepository#isModifiable() + */ + public static final int REPOSITORY_HINT_MODIFIABLE = 1 << 0; + + /** + * Adds the repository at the given location to the list of repositories tracked by + * this repository manager. + * <p> + * If there is a known disabled repository at the given location, it will become + * enabled as a result of this method. Thus the caller can be guaranteed that + * there is a known, enabled repository at the given location when this method returns. + * + * @param location The location of the repository to add + * @see #isEnabled(URI) + */ + public void addRepository(URI location); + + /** + * Returns whether a repository at the given location is in the list of repositories + * tracked by this repository manager. + * + * @param location The location of the repository to look for + * @return <code>true</code> if the repository is known to this manager, + * and <code>false</code> otherwise + */ + public boolean contains(URI location); + + /** + * Returns the artifact repository locations known to the repository manager. + * <p> + * Note that the repository manager does not guarantee that a valid repository + * exists at any of the returned locations at any particular moment in time. + * A subsequent attempt to load a repository at any of the given locations may + * or may not succeed. + * + * @param flags an integer bit-mask indicating which repositories should be + * returned. <code>REPOSITORIES_ALL</code> can be used as the mask when + * all enabled repositories should be returned. + * @return the locations of the repositories managed by this repository manager. + * + * @see #REPOSITORIES_ALL + * @see #REPOSITORIES_SYSTEM + * @see #REPOSITORIES_NON_SYSTEM + * @see #REPOSITORIES_LOCAL + * @see #REPOSITORIES_DISABLED + */ + public URI[] getKnownRepositories(int flags); + + /** + * Returns the property associated with the repository at the given URI, + * without loading the repository. + * <p> + * Note that some properties for a repository can only be + * determined when that repository is loaded. This method will return <code>null</code> + * for such properties. Only values for the properties that are already + * known by a repository manager will be returned. + * <p> + * If a client wishes to retrieve a property value from a repository + * regardless of the cost of retrieving it, the client should load the + * repository and then retrieve the property from the repository itself. + * + * @param location the URI of the repository in question + * @param key the String key of the property desired + * @return the value of the property, or <code>null</code> if the repository + * does not exist, the value does not exist, or the property value + * could not be determined without loading the repository. + * + * @see IRepository#getProperties() + * @see #setRepositoryProperty(URI, String, String) + */ + public String getRepositoryProperty(URI location, String key); + + /** + * Sets the property associated with the repository at the given URI, + * without loading the repository. + * <p> + * This method stores properties in a cache in the repository manager and does + * not write the property to the backing repository. This is useful for making + * repository properties available without incurring the cost of loading the repository. + * When the repository is loaded, it will overwrite any conflicting properties that + * have been set using this method. + * </p> + * <p> + * To persistently set a property on a repository, clients must load + * the repository and call {@link IRepository#setProperty(String, String)}. + * </p> + * + * @param location the URI of the repository in question + * @param key the String key of the property desired + * @param value the value to set the property to + * @see #getRepositoryProperty(URI, String) + * @see IRepository#setProperty(String, String) + */ + public void setRepositoryProperty(URI location, String key, String value); + + /** + * Returns the enablement value of a repository. Disabled repositories are known + * to the repository manager, but are never used in the context of provisioning + * operations. Disabled repositories are useful as a form of bookmark to indicate that a + * repository location is of interest, but not currently used. + * <p> + * Note that enablement is a property of the repository manager and not a property + * of the affected repository. The enablement of the repository is discarded when + * a repository is removed from the repository manager. + * + * @param location The location of the repository whose enablement is requested + * @return <code>true</code> if the repository is enabled, and + * <code>false</code> if it is not enabled, or if the repository location + * is not known to the repository manager. + * @see #REPOSITORIES_DISABLED + * @see #setEnabled(URI, boolean) + */ + public boolean isEnabled(URI location); + + /** + * Removes the repository at the given location from the list of + * repositories known to this repository manager. The underlying + * repository is not deleted. This method has no effect if the given + * repository is not already known to this repository manager. + * + * @param location The location of the repository to remove + * @return <code>true</code> if a repository was removed, and + * <code>false</code> otherwise. + */ + public boolean removeRepository(URI location); + + /** + * Sets the enablement of a repository. Disabled repositories are known + * to the repository manager, but are never used in the context of provisioning + * operation. Disabled repositories are useful as a form of bookmark to indicate that a + * repository location is of interest, but not currently used. + * <p> + * Note that enablement is a property of the repository manager and not a property + * of the affected repository. The enablement of the repository is discarded when + * a repository is removed from the repository manager. + * <p> + * This method has no effect if the given repository location is not known to the + * repository manager. + * + * @param location The location of the repository to enable or disable + * @param enablement <code>true</code>to enable the repository, and + * <code>false</code> to disable the repository + * @see #REPOSITORIES_DISABLED + * @see #isEnabled(URI) + */ + public void setEnabled(URI location, boolean enablement); + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IStateful.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IStateful.java new file mode 100644 index 000000000..49210e95e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IStateful.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007 compeople AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * compeople AG (Stefan Liebig) - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import org.eclipse.core.runtime.IStatus; + +/** + * Implementing <code>IStateful</code> adds the ability to store status information. + */ +public interface IStateful { + + /** + * Set the status. + * + * @param status if status equals null => getStatus().isOK + */ + void setStatus(IStatus status); + + /** + * Get status. + * @return status + */ + public IStatus getStatus(); + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryCreationException.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryCreationException.java new file mode 100644 index 000000000..77d1a549b --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryCreationException.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +public class RepositoryCreationException extends Exception { + + private static final long serialVersionUID = -5648382121963317100L; + + public RepositoryCreationException(Throwable e) { + super(e); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryEvent.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryEvent.java new file mode 100644 index 000000000..965836aca --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryEvent.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import java.net.URI; +import java.util.EventObject; + +/** + * An event indicating a repository was added, removed, changed, + * or discovered. + * + * @noextend This class is not intended to be subclassed by clients. + */ +public class RepositoryEvent extends EventObject { + private static final long serialVersionUID = 3082402920617281765L; + + /** + * A change kind constant (value 0), indicating a repository was added to the + * list of repositories known to a repository manager. + */ + public static final int ADDED = 0; + + /** + * A change kind constant (value 1), indicating a repository was removed from + * the list of repositories known to a repository manager. + */ + public static final int REMOVED = 1; + + /** + * A change kind constant (value 2), indicating a repository known to a + * repository manager was modified. + */ + public static final int CHANGED = 2; + + /** + * A change kind constant (value 4), indicating a new repository was discovered. + * This event is a way to notify repository managers in a generic way about + * a newly discovered repository. The repository manager will typically receive + * this event, add the repository to its list of known repositories, and issue + * a subsequent {@link #ADDED} event. Other clients should not typically + * listen for this kind of event. + */ + public static final int DISCOVERED = 4; + + /** + * A change kind constant (value 8), indicating the repository's enablement + * was changed. The {{@link #isRepositoryEnabled()} method can be used + * to find out the new enablement state of the repository, and to deduce + * the previous enablement state. + */ + public static final int ENABLEMENT = 8; + + private final int kind, type; + private boolean isEnabled; + private String nickname; + + /** + * Creates and returns a new repository discovery event. + * @param location the location of the repository that changed. + * @param nickname the repository nickname + * @param repositoryType the type of repository that was changed + * @param enabled whether the repository is enabled + * @return A new repository discovery event + * @see IRepository#PROP_NICKNAME + */ + public static RepositoryEvent newDiscoveryEvent(URI location, String nickname, int repositoryType, boolean enabled) { + RepositoryEvent event = new RepositoryEvent(location, repositoryType, DISCOVERED, enabled); + event.nickname = nickname; + return event; + } + + /** + * Creates a new repository event. + * + * @param location the location of the repository that changed. + * @param repositoryType the type of repository that was changed + * @param kind the kind of change that occurred. + * @param enabled whether the repository is enabled + */ + public RepositoryEvent(URI location, int repositoryType, int kind, boolean enabled) { + super(location); + this.kind = kind; + this.type = repositoryType; + isEnabled = enabled; + } + + /** + * Returns the kind of change that occurred. + * + * @return the kind of change that occurred. + * @see #ADDED + * @see #REMOVED + * @see #CHANGED + * @see #DISCOVERED + * @see #ENABLEMENT + */ + public int getKind() { + return kind; + } + + /** + * Returns the nickname of the repository. This method is only applicable + * for the {@link #DISCOVERED} event type. For other event types this + * method returns <code>null</code>. + */ + public String getRepositoryNickname() { + return nickname; + } + + /** + * Returns the location of the repository associated with this event. + * + * @return the location of the repository associated with this event. + */ + public URI getRepositoryLocation() { + return (URI) getSource(); + } + + /** + * Returns the type of repository associated with this event. Clients + * should not assume that the set of possible repository types is closed; + * clients should ignore events from repository types they don't know about. + * + * @return the type of repository associated with this event. + * ({@link IRepository#TYPE_METADATA} or {@link IRepository#TYPE_ARTIFACT}). + */ + public int getRepositoryType() { + return type; + } + + /** + * Returns whether the affected repository is enabled. + * + * @return <code>true</code> if the repository is enabled, + * and <code>false</code> otherwise. + */ + public boolean isRepositoryEnabled() { + return isEnabled; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/spi/p2/repository/AbstractRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/spi/p2/repository/AbstractRepository.java new file mode 100644 index 000000000..03a5baf74 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/spi/p2/repository/AbstractRepository.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.spi.p2.repository; + +import org.eclipse.equinox.internal.provisional.p2.repository.IRepository; + +import java.net.URI; +import java.util.Map; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties; + +/** +* AbstractRepository defines common properties that may be provided by various kinds +* of repositories. +* <p> +* Clients may extend this class. +* </p> +*/ +public abstract class AbstractRepository extends PlatformObject implements IRepository { + protected String description; + protected transient URI location; + protected String name; + protected Map properties = new OrderedProperties(); + protected String provider; + protected String type; + protected String version; + + protected AbstractRepository(String name, String type, String version, URI location, String description, String provider, Map properties) { + this.name = name; + this.type = type; + this.version = version; + this.location = location; + this.description = description == null ? "" : description; //$NON-NLS-1$ + this.provider = provider == null ? "" : provider; //$NON-NLS-1$ + if (properties != null) + this.properties.putAll(properties); + } + + /** + * Asserts that this repository is modifiable, throwing a runtime exception if + * it is not. This is suitable for use by subclasses when an attempt is made + * to write to a repository. + */ + protected void assertModifiable() { + if (!isModifiable()) + throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$ + } + + /** + * Returns a brief description of the repository. + * @return the description of the repository. + */ + public synchronized String getDescription() { + return description; + } + + /** + * Returns the location of this repository. + * TODO: Should we use URL or URI? URL requires a protocol handler + * to be installed in Java. Can the URL have any protocol? + * @return the URL of the repository. + */ + public synchronized URI getLocation() { + return location; + } + + /** + * Returns the name of the repository. + * @return the name of the repository. + */ + public synchronized String getName() { + return name; + } + + /** + * Returns a read-only collection of the properties of the repository. + * @return the properties of this repository. + */ + public synchronized Map getProperties() { + return OrderedProperties.unmodifiableProperties(properties); + } + + /** + * Returns the name of the provider of the repository. + * @return the provider of this repository. + */ + public synchronized String getProvider() { + return provider; + } + + /** + * Returns a string representing the type of the repository. + * @return the type of the repository. + */ + public synchronized String getType() { + return type; + } + + /** + * Returns a string representing the version for the repository type. + * @return the version of the type of the repository. + */ + public synchronized String getVersion() { + return version; + } + + public boolean isModifiable() { + return false; + } + + public synchronized void setDescription(String description) { + this.description = description; + } + + public synchronized void setName(String value) { + this.name = value; + } + + public synchronized String setProperty(String key, String value) { + assertModifiable(); + return (String) (value == null ? properties.remove(key) : properties.put(key, value)); + } + + public synchronized void setProvider(String provider) { + this.provider = provider; + } +} |