Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/.classpath7
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/.project28
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.core.prefs347
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.jdt.ui.prefs57
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/.settings/org.eclipse.pde.core.prefs4
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF38
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/about.html28
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/build.properties4
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/plugin.properties12
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeParser.java214
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryIO.java87
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeRepositoryState.java93
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/CompositeWriter.java68
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/Messages.java37
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLConstants.java63
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLParser.java765
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/XMLWriter.java307
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/persistence/messages.properties27
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java102
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/AuthenticationFailedException.java21
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java160
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java54
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfo.java148
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java220
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java314
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java118
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java159
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryPreferences.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java147
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java270
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTracing.java23
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java148
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Transport.java35
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java956
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java36
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties18
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties102
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/ICompositeRepository.java41
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepository.java188
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IRepositoryManager.java207
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/IStateful.java33
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryCreationException.java21
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/RepositoryEvent.java149
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/spi/p2/repository/AbstractRepository.java135
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 (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ 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 = "&lt;"; //$NON-NLS-1$
+ break;
+ case '>' :
+ replace = "&gt;"; //$NON-NLS-1$
+ break;
+ case '"' :
+ replace = "&quot;"; //$NON-NLS-1$
+ break;
+ case '\'' :
+ replace = "&apos;"; //$NON-NLS-1$
+ break;
+ case '&' :
+ replace = "&amp;"; //$NON-NLS-1$
+ break;
+ case '\t' :
+ replace = "&#x9;"; //$NON-NLS-1$
+ break;
+ case '\n' :
+ replace = "&#xA;"; //$NON-NLS-1$
+ break;
+ case '\r' :
+ replace = "&#xD;"; //$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;
+ }
+}

Back to the top