Merge changes Ic8f371d9,Iae4cffcd,I4213004f

* changes:
  Implement DynamicSet<T>, DynamicMap<T> to provide bindings in Guice
  Automatically register plugin bindings
  Define gerrit-extension-api module
This commit is contained in:
Martin Fick
2012-05-10 16:29:08 -07:00
committed by gerrit code review
36 changed files with 2256 additions and 71 deletions

6
gerrit-extension-api/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/target
/.classpath
/.project
/.settings/org.maven.ide.eclipse.prefs
/.settings/org.eclipse.m2e.core.prefs
/gerrit-extension-api.iml

View File

@@ -0,0 +1,5 @@
#Thu Jul 28 11:02:36 PDT 2011
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8

View File

@@ -0,0 +1,3 @@
#Tue Sep 02 16:59:24 PDT 2008
eclipse.preferences.version=1
line.separator=\n

View File

@@ -0,0 +1,269 @@
#Thu Jul 28 11:02:36 PDT 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6
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=16
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=16
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
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=0
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
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=2
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=true
org.eclipse.jdt.core.formatter.comment.format_header=true
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=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_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=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=do not 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=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.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=true
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=80
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=3
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
org.eclipse.jdt.core.formatter.tabulation.char=space
org.eclipse.jdt.core.formatter.tabulation.size=2
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true

View File

@@ -0,0 +1,61 @@
#Wed Jul 29 11:31:38 PDT 2009
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_Google Format
formatter_settings_version=11
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
org.eclipse.jdt.ui.ondemandthreshold=99
org.eclipse.jdt.ui.staticondemandthreshold=99
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=false
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=false
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.make_local_variable_final=true
sp_cleanup.make_parameters_final=true
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=false
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=false
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=false
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

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
<version>2.5-SNAPSHOT</version>
</parent>
<artifactId>gerrit-extension-api</artifactId>
<name>Gerrit Code Review - Extension API</name>
<description>
Interfaces describing the extension API
</description>
<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<createSourcesJar>true</createSourcesJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.annotations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Annotation applied to auto-registered, exported types.
* <p>
* Plugins or extensions using auto-registration should apply this annotation to
* any non-abstract class they want exported for access.
* <p>
* For SSH commands the @Export annotation names the subcommand:
*
* <pre>
* @Export("print")
* class MyCommand extends SshCommand {
* </pre>
*
* For HTTP servlets, the @Export annotation names the URL the servlet is bound
* to, relative to the plugin or extension's namespace within the Gerrit
* container.
*
* <pre>
* @Export("/index.html")
* class ShowIndexHtml extends HttpServlet {
* </pre>
*/
@Target({ElementType.TYPE})
@Retention(RUNTIME)
@BindingAnnotation
public @interface Export {
String value();
}

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.annotations;
import java.io.Serializable;
import java.lang.annotation.Annotation;
final class ExportImpl implements Export, Serializable {
private static final long serialVersionUID = 0;
private final String value;
ExportImpl(String value) {
this.value = value;
}
@Override
public Class<? extends Annotation> annotationType() {
return Export.class;
}
@Override
public String value() {
return value;
}
@Override
public int hashCode() {
return (127 * "value".hashCode()) ^ value.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof Export && value.equals(((Export) o).value());
}
@Override
public String toString() {
return "@" + Export.class.getName() + "(value=" + value + ")";
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.annotations;
/** Static constructors for {@link Export} annotations. */
public final class Exports {
/** Create an annotation to export under a specific name. */
public static Export named(String name) {
return new ExportImpl(name);
}
private Exports() {
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.annotations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Annotation for interfaces that accept auto-registered implementations.
* <p>
* Interfaces that accept automatically registered implementations into their
* {@link DynamicSet} must be tagged with this annotation.
* <p>
* Plugins or extensions that implement an {@code @ExtensionPoint} interface
* should use the {@link Listen} annotation to automatically register.
*
* @see Listen
*/
@Target({ElementType.TYPE})
@Retention(RUNTIME)
@BindingAnnotation
public @interface ExtensionPoint {
}

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.annotations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Annotation for auto-registered extension point implementations.
* <p>
* Plugins or extensions using auto-registration should apply this annotation to
* any non-abstract class that implements an unnamed extension point, such as a
* notification listener. Gerrit will automatically determine which extension
* points to apply based on the interfaces the type implements.
*
* @see Export
*/
@Target({ElementType.TYPE})
@Retention(RUNTIME)
@BindingAnnotation
public @interface Listen {
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.plugins;
package com.google.gerrit.extensions.annotations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -22,6 +22,19 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Annotation applied to a String containing the plugin or extension name.
* <p>
* A plugin or extension may receive this string by Guice injection to discover
* the name that an administrator has installed the plugin or extension under:
*
* <pre>
* @Inject
* MyType(@PluginName String myName) {
* ...
* }
* </pre>
*/
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RUNTIME)
@BindingAnnotation

View File

@@ -0,0 +1,155 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.registration;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Types;
import java.util.Collections;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A map of members that can be modified as plugins reload.
* <p>
* Maps index their members by plugin name and export name.
* <p>
* DynamicMaps are always mapped as singletons in Guice, and only may contain
* singletons, as providers are resolved to an instance before the member is
* added to the map.
*/
public abstract class DynamicMap<T> {
/**
* Declare a singleton {@code DynamicMap<T>} with a binder.
* <p>
* Maps must be defined in a Guice module before they can be bound:
*
* <pre>
* DynamicMap.mapOf(binder(), Interface.class);
* bind(Interface.class)
* .annotatedWith(Exports.named(&quot;foo&quot;))
* .to(Impl.class);
* </pre>
*
* @param binder a new binder created in the module.
* @param member type of value in the map.
*/
public static <T> void mapOf(Binder binder, Class<T> member) {
mapOf(binder, TypeLiteral.get(member));
}
/**
* Declare a singleton {@code DynamicMap<T>} with a binder.
* <p>
* Maps must be defined in a Guice module before they can be bound:
*
* <pre>
* DynamicMap.mapOf(binder(), new TypeLiteral<Thing<Bar>>(){});
* bind(new TypeLiteral<Thing<Bar>>() {})
* .annotatedWith(Exports.named(&quot;foo&quot;))
* .to(Impl.class);
* </pre>
*
* @param binder a new binder created in the module.
* @param member type of value in the map.
*/
public static <T> void mapOf(Binder binder, TypeLiteral<T> member) {
@SuppressWarnings("unchecked")
Key<DynamicMap<T>> key = (Key<DynamicMap<T>>) Key.get(
Types.newParameterizedType(DynamicMap.class, member.getType()));
binder.bind(key)
.toProvider(new DynamicMapProvider<T>(member))
.in(Scopes.SINGLETON);
}
final ConcurrentMap<NamePair, T> items;
DynamicMap() {
items = new ConcurrentHashMap<NamePair, T>(16, 0.75f, 1);
}
/**
* Lookup an implementation by name.
*
* @param pluginName local name of the plugin providing the item.
* @param exportName name the plugin exports the item as.
* @return the implementation. Null if the plugin is not running, or if the
* plugin does not export this name.
*/
public T get(String pluginName, String exportName) {
return items.get(new NamePair(pluginName, exportName));
}
/**
* Get the names of all running plugins supplying this type.
*
* @return sorted set of active plugins that supply at least one item.
*/
public SortedSet<String> plugins() {
SortedSet<String> r = new TreeSet<String>();
for (NamePair p : items.keySet()) {
r.add(p.pluginName);
}
return Collections.unmodifiableSortedSet(r);
}
/**
* Get the items exported by a single plugin.
*
* @param pluginName name of the plugin.
* @return items exported by a plugin, keyed by the export name.
*/
public SortedMap<String, T> byPlugin(String pluginName) {
SortedMap<String, T> r = new TreeMap<String, T>();
for (Map.Entry<NamePair, T> e : items.entrySet()) {
if (e.getKey().pluginName.equals(pluginName)) {
r.put(e.getKey().exportName, e.getValue());
}
}
return Collections.unmodifiableSortedMap(r);
}
static class NamePair {
private final String pluginName;
private final String exportName;
NamePair(String pn, String en) {
this.pluginName = pn;
this.exportName = en;
}
@Override
public int hashCode() {
return pluginName.hashCode() * 31 + exportName.hashCode();
}
@Override
public boolean equals(Object other) {
if (other instanceof NamePair) {
NamePair np = (NamePair) other;
return pluginName.equals(np) && exportName.equals(np);
}
return false;
}
}
}

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.registration;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import java.util.List;
class DynamicMapProvider<T> implements Provider<DynamicMap<T>> {
private final TypeLiteral<T> type;
@Inject
private Injector injector;
DynamicMapProvider(TypeLiteral<T> type) {
this.type = type;
}
public DynamicMap<T> get() {
PrivateInternals_DynamicMapImpl<T> m =
new PrivateInternals_DynamicMapImpl<T>();
List<Binding<T>> bindings = injector.findBindingsByType(type);
if (bindings != null) {
for (Binding<T> b : bindings) {
m.put("gerrit", b.getKey(), b.getProvider().get());
}
}
return m;
}
}

View File

@@ -0,0 +1,231 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.registration;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.name.Named;
import com.google.inject.util.Types;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
/**
* A set of members that can be modified as plugins reload.
* <p>
* DynamicSets are always mapped as singletons in Guice, and only may contain
* singletons, as providers are resolved to an instance before the member is
* added to the set.
*/
public class DynamicSet<T> implements Iterable<T> {
/**
* Declare a singleton {@code DynamicSet<T>} with a binder.
* <p>
* Sets must be defined in a Guice module before they can be bound:
* <pre>
* DynamicSet.setOf(binder(), Interface.class);
* DynamicSet.bind(binder(), Interface.class).to(Impl.class);
* </pre>
*
* @param binder a new binder created in the module.
* @param member type of entry in the set.
*/
public static <T> void setOf(Binder binder, Class<T> member) {
setOf(binder, TypeLiteral.get(member));
}
/**
* Declare a singleton {@code DynamicSet<T>} with a binder.
* <p>
* Sets must be defined in a Guice module before they can be bound:
* <pre>
* DynamicSet.setOf(binder(), new TypeLiteral<Thing<Foo>>() {});
* </pre>
*
* @param binder a new binder created in the module.
* @param member type of entry in the set.
*/
public static <T> void setOf(Binder binder, TypeLiteral<T> member) {
@SuppressWarnings("unchecked")
Key<DynamicSet<T>> key = (Key<DynamicSet<T>>) Key.get(
Types.newParameterizedType(DynamicSet.class, member.getType()));
binder.bind(key)
.toProvider(new DynamicSetProvider<T>(member))
.in(Scopes.SINGLETON);
}
/**
* Bind one implementation into the set using a unique annotation.
*
* @param binder a new binder created in the module.
* @param type type of entries in the set.
* @return a binder to continue configuring the new set member.
*/
public static <T> LinkedBindingBuilder<T> bind(Binder binder, Class<T> type) {
return bind(binder, TypeLiteral.get(type));
}
/**
* Bind one implementation into the set using a unique annotation.
*
* @param binder a new binder created in the module.
* @param type type of entries in the set.
* @return a binder to continue configuring the new set member.
*/
public static <T> LinkedBindingBuilder<T> bind(Binder binder, TypeLiteral<T> type) {
return binder.bind(type).annotatedWith(UniqueAnnotations.create());
}
/**
* Bind a named implementation into the set.
*
* @param binder a new binder created in the module.
* @param type type of entries in the set.
* @param name {@code @Named} annotation to apply instead of a unique
* annotation.
* @return a binder to continue configuring the new set member.
*/
public static <T> LinkedBindingBuilder<T> bind(Binder binder,
Class<T> type,
Named name) {
return bind(binder, TypeLiteral.get(type));
}
/**
* Bind a named implementation into the set.
*
* @param binder a new binder created in the module.
* @param type type of entries in the set.
* @param name {@code @Named} annotation to apply instead of a unique
* annotation.
* @return a binder to continue configuring the new set member.
*/
public static <T> LinkedBindingBuilder<T> bind(Binder binder,
TypeLiteral<T> type,
Named name) {
return binder.bind(type).annotatedWith(name);
}
private final CopyOnWriteArrayList<AtomicReference<T>> items;
DynamicSet(Collection<AtomicReference<T>> base) {
items = new CopyOnWriteArrayList<AtomicReference<T>>(base);
}
@Override
public Iterator<T> iterator() {
final Iterator<AtomicReference<T>> itr = items.iterator();
return new Iterator<T>() {
private T next;
@Override
public boolean hasNext() {
while (next == null && itr.hasNext()) {
next = itr.next().get();
}
return next != null;
}
@Override
public T next() {
if (hasNext()) {
T result = next;
next = null;
return result;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Add one new element to the set.
*
* @param item the item to add to the collection. Must not be null.
* @return handle to remove the item at a later point in time.
*/
public RegistrationHandle add(final T item) {
final AtomicReference<T> ref = new AtomicReference<T>(item);
items.add(ref);
return new RegistrationHandle() {
@Override
public void remove() {
if (ref.compareAndSet(item, null)) {
items.remove(ref);
}
}
};
}
/**
* Add one new element that may be hot-replaceable in the future.
*
* @param key unique description from the item's Guice binding. This can be
* later obtained from the registration handle to facilitate matching
* with the new equivalent instance during a hot reload.
* @param item the item to add to the collection right now. Must not be null.
* @return a handle that can remove this item later, or hot-swap the item
* without it ever leaving the collection.
*/
public ReloadableRegistrationHandle<T> add(Key<T> key, T item) {
AtomicReference<T> ref = new AtomicReference<T>(item);
items.add(ref);
return new ReloadableHandle(ref, key, item);
}
private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
private final AtomicReference<T> ref;
private final Key<T> key;
private final T item;
ReloadableHandle(AtomicReference<T> ref, Key<T> key, T item) {
this.ref = ref;
this.key = key;
this.item = item;
}
@Override
public void remove() {
if (ref.compareAndSet(item, null)) {
items.remove(ref);
}
}
@Override
public Key<T> getKey() {
return key;
}
@Override
public ReloadableHandle replace(Key<T> newKey, T newItem) {
if (ref.compareAndSet(item, newItem)) {
return new ReloadableHandle(ref, newKey, newItem);
}
return null;
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.registration;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
class DynamicSetProvider<T> implements Provider<DynamicSet<T>> {
private final TypeLiteral<T> type;
@Inject
private Injector injector;
DynamicSetProvider(TypeLiteral<T> type) {
this.type = type;
}
public DynamicSet<T> get() {
return new DynamicSet<T>(find(injector, type));
}
private static <T> List<AtomicReference<T>> find(
Injector src,
TypeLiteral<T> type) {
List<Binding<T>> bindings = src.findBindingsByType(type);
int cnt = bindings != null ? bindings.size() : 0;
if (cnt == 0) {
return Collections.emptyList();
}
List<AtomicReference<T>> r = new ArrayList<AtomicReference<T>>(cnt);
for (Binding<T> b : bindings) {
r.add(new AtomicReference<T>(b.getProvider().get()));
}
return r;
}
}

View File

@@ -0,0 +1,96 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.registration;
import com.google.gerrit.extensions.annotations.Export;
import com.google.inject.Key;
/** <b>DO NOT USE</b> */
public class PrivateInternals_DynamicMapImpl<T> extends DynamicMap<T> {
PrivateInternals_DynamicMapImpl() {
}
/**
* Store one new element into the map.
*
* @param pluginName unique name of the plugin providing the export.
* @param exportName name the plugin has exported the item as.
* @param item the item to add to the collection. Must not be null.
* @return handle to remove the item at a later point in time.
*/
public RegistrationHandle put(
String pluginName, String exportName,
final T item) {
final NamePair key = new NamePair(pluginName, exportName);
items.put(key, item);
return new RegistrationHandle() {
@Override
public void remove() {
items.remove(key, item);
}
};
}
/**
* Store one new element that may be hot-replaceable in the future.
*
* @param pluginName unique name of the plugin providing the export.
* @param key unique description from the item's Guice binding. This can be
* later obtained from the registration handle to facilitate matching
* with the new equivalent instance during a hot reload. The key must
* use an {@link @Export} annotation.
* @param item the item to add to the collection right now. Must not be null.
* @return a handle that can remove this item later, or hot-swap the item
* without it ever leaving the collection.
*/
public ReloadableRegistrationHandle<T> put(
String pluginName, Key<T> key,
T item) {
String exportName = ((Export) key.getAnnotation()).value();
NamePair np = new NamePair(pluginName, exportName);
items.put(np, item);
return new ReloadableHandle(np, key, item);
}
private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
private final NamePair np;
private final Key<T> key;
private final T item;
ReloadableHandle(NamePair np, Key<T> key, T item) {
this.np = np;
this.key = key;
this.item = item;
}
@Override
public void remove() {
items.remove(np, item);
}
@Override
public Key<T> getKey() {
return key;
}
@Override
public ReloadableHandle replace(Key<T> newKey, T newItem) {
if (items.replace(np, item, newItem)) {
return new ReloadableHandle(np, newKey, newItem);
}
return null;
}
}
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.plugins;
package com.google.gerrit.extensions.registration;
/** Handle for registered information. */
public interface RegistrationHandle {

View File

@@ -0,0 +1,23 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.registration;
import com.google.inject.Key;
public interface ReloadableRegistrationHandle<T> extends RegistrationHandle {
public Key<T> getKey();
public RegistrationHandle replace(Key<T> key, T item);
}

View File

@@ -0,0 +1,69 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd.plugins;
import com.google.common.collect.Maps;
import com.google.gerrit.extensions.annotations.Export;
import com.google.gerrit.server.plugins.InvalidPluginException;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.inject.Module;
import com.google.inject.Scopes;
import com.google.inject.servlet.ServletModule;
import java.util.Map;
import javax.servlet.http.HttpServlet;
class HttpAutoRegisterModuleGenerator extends ServletModule
implements ModuleGenerator {
private final Map<String, Class<HttpServlet>> serve = Maps.newHashMap();
@Override
protected void configureServlets() {
for (Map.Entry<String, Class<HttpServlet>> e : serve.entrySet()) {
bind(e.getValue()).in(Scopes.SINGLETON);
serve(e.getKey()).with(e.getValue());
}
}
@Override
public void setPluginName(String name) {
}
@SuppressWarnings("unchecked")
@Override
public void export(Export export, Class<?> type)
throws InvalidPluginException {
if (HttpServlet.class.isAssignableFrom(type)) {
Class<HttpServlet> old = serve.get(export.value());
if (old != null) {
throw new InvalidPluginException(String.format(
"@Export(\"%s\") has duplicate bindings:\n %s\n %s",
export.value(), old.getName(), type.getName()));
}
serve.put(export.value(), (Class<HttpServlet>) type);
} else {
throw new InvalidPluginException(String.format(
"Class %s with @Export(\"%s\") must extend %s",
type.getName(), export.value(),
HttpServlet.class.getName()));
}
}
@Override
public Module create() throws InvalidPluginException {
return this;
}
}

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.httpd.plugins;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
import com.google.inject.internal.UniqueAnnotations;
@@ -32,5 +33,8 @@ public class HttpPluginModule extends ServletModule {
bind(ReloadPluginListener.class)
.annotatedWith(UniqueAnnotations.create())
.to(HttpPluginServlet.class);
bind(ModuleGenerator.class)
.to(HttpAutoRegisterModuleGenerator.class);
}
}

View File

@@ -17,9 +17,9 @@ package com.google.gerrit.httpd.plugins;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
import com.google.gerrit.server.plugins.Plugin;
import com.google.gerrit.server.plugins.RegistrationHandle;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
import com.google.inject.Inject;

View File

@@ -121,6 +121,12 @@ limitations under the License.
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-util-cli</artifactId>

View File

@@ -0,0 +1,392 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.plugins;
import static com.google.gerrit.server.plugins.PluginGuiceEnvironment.is;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.annotations.Export;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.extensions.annotations.Listen;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
import org.eclipse.jgit.util.IO;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
class AutoRegisterModules {
private static final int SKIP_ALL = ClassReader.SKIP_CODE
| ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
private final String pluginName;
private final PluginGuiceEnvironment env;
private final JarFile jarFile;
private final ClassLoader classLoader;
private final ModuleGenerator sshGen;
private final ModuleGenerator httpGen;
private Set<Class<?>> sysSingletons;
private Map<TypeLiteral<?>, Class<?>> sysListen;
Module sysModule;
Module sshModule;
Module httpModule;
AutoRegisterModules(String pluginName,
PluginGuiceEnvironment env,
JarFile jarFile,
ClassLoader classLoader) {
this.pluginName = pluginName;
this.env = env;
this.jarFile = jarFile;
this.classLoader = classLoader;
this.sshGen = env.hasSshModule() ? env.newSshModuleGenerator() : null;
this.httpGen = env.hasHttpModule() ? env.newHttpModuleGenerator() : null;
}
AutoRegisterModules discover() throws InvalidPluginException {
sysSingletons = Sets.newHashSet();
sysListen = Maps.newHashMap();
if (sshGen != null) {
sshGen.setPluginName(pluginName);
}
if (httpGen != null) {
httpGen.setPluginName(pluginName);
}
scan();
if (!sysSingletons.isEmpty() || !sysListen.isEmpty()) {
sysModule = makeSystemModule();
}
if (sshGen != null) {
sshModule = sshGen.create();
}
if (httpGen != null) {
httpModule = httpGen.create();
}
return this;
}
private Module makeSystemModule() {
return new AbstractModule() {
@Override
protected void configure() {
for (Class<?> clazz : sysSingletons) {
bind(clazz).in(Scopes.SINGLETON);
}
for (Map.Entry<TypeLiteral<?>, Class<?>> e : sysListen.entrySet()) {
@SuppressWarnings("unchecked")
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
@SuppressWarnings("unchecked")
Class<Object> impl = (Class<Object>) e.getValue();
Annotation n = impl.getAnnotation(Export.class);
if (n == null) {
n = impl.getAnnotation(javax.inject.Named.class);
}
if (n == null) {
n = impl.getAnnotation(com.google.inject.name.Named.class);
}
if (n == null) {
n = UniqueAnnotations.create();
}
bind(type).annotatedWith(n).to(impl);
}
}
};
}
private void scan() throws InvalidPluginException {
Enumeration<JarEntry> e = jarFile.entries();
while (e.hasMoreElements()) {
JarEntry entry = e.nextElement();
if (skip(entry)) {
continue;
}
ClassData def = new ClassData();
try {
new ClassReader(read(entry)).accept(def, SKIP_ALL);
} catch (IOException err) {
throw new InvalidPluginException("Cannot auto-register", err);
} catch (RuntimeException err) {
PluginLoader.log.warn(String.format(
"Plugin %s has invaild class file %s inside of %s",
pluginName, entry.getName(), jarFile.getName()), err);
continue;
}
if (def.exportedAsName != null) {
if (def.isConcrete()) {
export(def);
} else {
PluginLoader.log.warn(String.format(
"Plugin %s tries to @Export(\"%s\") abstract class %s",
pluginName, def.exportedAsName, def.className));
}
} else if (def.listen) {
if (def.isConcrete()) {
listen(def);
} else {
PluginLoader.log.warn(String.format(
"Plugin %s tries to @Listen abstract class %s",
pluginName, def.className));
}
}
}
}
private void export(ClassData def) throws InvalidPluginException {
Class<?> clazz;
try {
clazz = Class.forName(def.className, false, classLoader);
} catch (ClassNotFoundException err) {
throw new InvalidPluginException(String.format(
"Cannot load %s with @Export(\"%s\")",
def.className, def.exportedAsName), err);
}
Export export = clazz.getAnnotation(Export.class);
if (export == null) {
PluginLoader.log.warn(String.format(
"In plugin %s asm incorrectly parsed %s with @Export(\"%s\")",
pluginName, clazz.getName(), def.exportedAsName));
return;
}
if (is("org.apache.sshd.server.Command", clazz)) {
if (sshGen != null) {
sshGen.export(export, clazz);
}
} else if (is("javax.servlet.http.HttpServlet", clazz)) {
if (httpGen != null) {
httpGen.export(export, clazz);
listen(clazz, clazz);
}
} else {
int cnt = sysListen.size();
listen(clazz, clazz);
if (cnt == sysListen.size()) {
// If no bindings were recorded, the extension isn't recognized.
throw new InvalidPluginException(String.format(
"Class %s with @Export(\"%s\") not supported",
clazz.getName(), export.value()));
}
}
}
private void listen(ClassData def) throws InvalidPluginException {
Class<?> clazz;
try {
clazz = Class.forName(def.className, false, classLoader);
} catch (ClassNotFoundException err) {
throw new InvalidPluginException(String.format(
"Cannot load %s with @Listen",
def.className), err);
}
Listen listen = clazz.getAnnotation(Listen.class);
if (listen != null) {
listen(clazz, clazz);
} else {
PluginLoader.log.warn(String.format(
"In plugin %s asm incorrectly parsed %s with @Listen",
pluginName, clazz.getName()));
}
}
private void listen(java.lang.reflect.Type type, Class<?> clazz)
throws InvalidPluginException {
while (type != null) {
Class<?> rawType;
if (type instanceof ParameterizedType) {
rawType = (Class<?>) ((ParameterizedType) type).getRawType();
} else if (type instanceof Class) {
rawType = (Class<?>) type;
} else {
return;
}
if (rawType.getAnnotation(ExtensionPoint.class) != null) {
TypeLiteral<?> tl = TypeLiteral.get(type);
if (env.hasDynamicSet(tl)) {
sysSingletons.add(clazz);
sysListen.put(tl, clazz);
} else if (env.hasDynamicMap(tl)) {
if (clazz.getAnnotation(Export.class) == null) {
throw new InvalidPluginException(String.format(
"Class %s requires @Export(\"name\") annotation for %s",
clazz.getName(), rawType.getName()));
}
sysSingletons.add(clazz);
sysListen.put(tl, clazz);
} else {
throw new InvalidPluginException(String.format(
"Cannot register %s, server does not accept %s",
clazz.getName(), rawType.getName()));
}
return;
}
java.lang.reflect.Type[] interfaces = rawType.getGenericInterfaces();
if (interfaces != null) {
for (java.lang.reflect.Type i : interfaces) {
listen(i, clazz);
}
}
type = rawType.getGenericSuperclass();
}
}
private static boolean skip(JarEntry entry) {
if (!entry.getName().endsWith(".class")) {
return true; // Avoid non-class resources.
}
if (entry.getSize() <= 0) {
return true; // Directories have 0 size.
}
if (entry.getSize() >= 1024 * 1024) {
return true; // Do not scan huge class files.
}
return false;
}
private byte[] read(JarEntry entry) throws IOException {
byte[] data = new byte[(int) entry.getSize()];
InputStream in = jarFile.getInputStream(entry);
try {
IO.readFully(in, data, 0, data.length);
} finally {
in.close();
}
return data;
}
private static class ClassData implements ClassVisitor {
private static final String EXPORT = Type.getType(Export.class).getDescriptor();
private static final String LISTEN = Type.getType(Listen.class).getDescriptor();
String className;
int access;
String exportedAsName;
boolean listen;
boolean isConcrete() {
return (access & Opcodes.ACC_ABSTRACT) == 0
&& (access & Opcodes.ACC_INTERFACE) == 0;
}
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
this.className = Type.getObjectType(name).getClassName();
this.access = access;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (visible && EXPORT.equals(desc)) {
return new AbstractAnnotationVisitor() {
@Override
public void visit(String name, Object value) {
exportedAsName = (String) value;
}
};
}
if (visible && LISTEN.equals(desc)) {
listen = true;
return null;
}
return null;
}
@Override
public void visitSource(String arg0, String arg1) {
}
@Override
public void visitOuterClass(String arg0, String arg1, String arg2) {
}
@Override
public MethodVisitor visitMethod(int arg0, String arg1, String arg2,
String arg3, String[] arg4) {
return null;
}
@Override
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
@Override
public FieldVisitor visitField(int arg0, String arg1, String arg2,
String arg3, Object arg4) {
return null;
}
@Override
public void visitEnd() {
}
@Override
public void visitAttribute(Attribute arg0) {
}
}
private static abstract class AbstractAnnotationVisitor implements
AnnotationVisitor {
@Override
public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
return null;
}
@Override
public AnnotationVisitor visitArray(String arg0) {
return null;
}
@Override
public void visitEnum(String arg0, String arg1, String arg2) {
}
@Override
public void visitEnd() {
}
}
}

View File

@@ -0,0 +1,27 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.plugins;
public class InvalidPluginException extends Exception {
private static final long serialVersionUID = 1L;
public InvalidPluginException(String message) {
super(message);
}
public InvalidPluginException(String message, Throwable why) {
super(message, why);
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.plugins;
import com.google.gerrit.extensions.annotations.Export;
import com.google.inject.Module;
public interface ModuleGenerator {
void setPluginName(String name);
void export(Export export, Class<?> type) throws InvalidPluginException;
Module create() throws InvalidPluginException;
}

View File

@@ -15,17 +15,22 @@
package com.google.gerrit.server.plugins;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.servlet.GuiceFilter;
import org.eclipse.jgit.storage.file.FileSnapshot;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
@@ -36,7 +41,7 @@ public class Plugin {
static {
// Guice logs warnings about multiple injectors being created.
// Silence this in case HTTP plugins are used.
java.util.logging.Logger.getLogger(GuiceFilter.class.getName())
java.util.logging.Logger.getLogger("com.google.inject.servlet.GuiceFilter")
.setLevel(java.util.logging.Level.OFF);
}
@@ -45,6 +50,7 @@ public class Plugin {
private final FileSnapshot snapshot;
private final JarFile jarFile;
private final Manifest manifest;
private final ClassLoader classLoader;
private Class<? extends Module> sysModule;
private Class<? extends Module> sshModule;
private Class<? extends Module> httpModule;
@@ -53,12 +59,14 @@ public class Plugin {
private Injector sshInjector;
private Injector httpInjector;
private LifecycleManager manager;
private List<ReloadableRegistrationHandle<?>> reloadableHandles;
public Plugin(String name,
File srcJar,
FileSnapshot snapshot,
JarFile jarFile,
Manifest manifest,
ClassLoader classLoader,
@Nullable Class<? extends Module> sysModule,
@Nullable Class<? extends Module> sshModule,
@Nullable Class<? extends Module> httpModule) {
@@ -67,6 +75,7 @@ public class Plugin {
this.snapshot = snapshot;
this.jarFile = jarFile;
this.manifest = manifest;
this.classLoader = classLoader;
this.sysModule = sysModule;
this.sshModule = sshModule;
this.httpModule = httpModule;
@@ -108,25 +117,48 @@ public class Plugin {
Injector root = newRootInjector(env);
manager = new LifecycleManager();
AutoRegisterModules auto = null;
if (sysModule == null && sshModule == null && httpModule == null) {
auto = new AutoRegisterModules(name, env, jarFile, classLoader);
auto.discover();
}
if (sysModule != null) {
sysInjector = root.createChildInjector(root.getInstance(sysModule));
manager.add(sysInjector);
} else if (auto != null && auto.sysModule != null) {
sysInjector = root.createChildInjector(auto.sysModule);
manager.add(sysInjector);
} else {
sysInjector = root;
}
if (sshModule != null && env.hasSshModule()) {
sshInjector = sysInjector.createChildInjector(
env.getSshModule(),
sysInjector.getInstance(sshModule));
manager.add(sshInjector);
if (env.hasSshModule()) {
if (sshModule != null) {
sshInjector = sysInjector.createChildInjector(
env.getSshModule(),
sysInjector.getInstance(sshModule));
manager.add(sshInjector);
} else if (auto != null && auto.sshModule != null) {
sshInjector = sysInjector.createChildInjector(
env.getSshModule(),
auto.sshModule);
manager.add(sshInjector);
}
}
if (httpModule != null && env.hasHttpModule()) {
httpInjector = sysInjector.createChildInjector(
env.getHttpModule(),
sysInjector.getInstance(httpModule));
manager.add(httpInjector);
if (env.hasHttpModule()) {
if (httpModule != null) {
httpInjector = sysInjector.createChildInjector(
env.getHttpModule(),
sysInjector.getInstance(httpModule));
manager.add(httpInjector);
} else if (auto != null && auto.httpModule != null) {
httpInjector = sysInjector.createChildInjector(
env.getHttpModule(),
auto.httpModule);
manager.add(httpInjector);
}
}
manager.start();
@@ -159,6 +191,10 @@ public class Plugin {
return jarFile;
}
public Injector getSysInjector() {
return sysInjector;
}
@Nullable
public Injector getSshInjector() {
return sshInjector;
@@ -170,6 +206,13 @@ public class Plugin {
}
public void add(final RegistrationHandle handle) {
if (handle instanceof ReloadableRegistrationHandle) {
if (reloadableHandles == null) {
reloadableHandles = Lists.newArrayList();
}
reloadableHandles.add((ReloadableRegistrationHandle<?>) handle);
}
add(new LifecycleListener() {
@Override
public void start() {
@@ -186,6 +229,13 @@ public class Plugin {
manager.add(listener);
}
List<ReloadableRegistrationHandle<?>> getReloadableHandles() {
if (reloadableHandles != null) {
return reloadableHandles;
}
return Collections.emptyList();
}
@Override
public String toString() {
return "Plugin [" + name + "]";

View File

@@ -14,21 +14,36 @@
package com.google.gerrit.server.plugins;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import javax.inject.Inject;
/**
@@ -44,10 +59,22 @@ public class PluginGuiceEnvironment {
private final CopyConfigModule copyConfigModule;
private final List<StartPluginListener> onStart;
private final List<ReloadPluginListener> onReload;
private Module sysModule;
private Module sshModule;
private Module httpModule;
private Provider<ModuleGenerator> sshGen;
private Provider<ModuleGenerator> httpGen;
private Map<TypeLiteral<?>, DynamicSet<?>> sysSets;
private Map<TypeLiteral<?>, DynamicSet<?>> sshSets;
private Map<TypeLiteral<?>, DynamicSet<?>> httpSets;
private Map<TypeLiteral<?>, DynamicMap<?>> sysMaps;
private Map<TypeLiteral<?>, DynamicMap<?>> sshMaps;
private Map<TypeLiteral<?>, DynamicMap<?>> httpMaps;
@Inject
PluginGuiceEnvironment(Injector sysInjector, CopyConfigModule ccm) {
this.sysInjector = sysInjector;
@@ -58,6 +85,21 @@ public class PluginGuiceEnvironment {
onReload = new CopyOnWriteArrayList<ReloadPluginListener>();
onReload.addAll(listeners(sysInjector, ReloadPluginListener.class));
sysSets = dynamicSetsOf(sysInjector);
sysMaps = dynamicMapsOf(sysInjector);
}
boolean hasDynamicSet(TypeLiteral<?> type) {
return sysSets.containsKey(type)
|| (sshSets != null && sshSets.containsKey(type))
|| (httpSets != null && httpSets.containsKey(type));
}
boolean hasDynamicMap(TypeLiteral<?> type) {
return sysMaps.containsKey(type)
|| (sshMaps != null && sshMaps.containsKey(type))
|| (httpMaps != null && httpMaps.containsKey(type));
}
Module getSysModule() {
@@ -79,6 +121,9 @@ public class PluginGuiceEnvironment {
public void setSshInjector(Injector injector) {
sshModule = copy(injector);
sshGen = injector.getProvider(ModuleGenerator.class);
sshSets = dynamicSetsOf(injector);
sshMaps = dynamicMapsOf(injector);
onStart.addAll(listeners(injector, StartPluginListener.class));
onReload.addAll(listeners(injector, ReloadPluginListener.class));
}
@@ -91,8 +136,15 @@ public class PluginGuiceEnvironment {
return sshModule;
}
ModuleGenerator newSshModuleGenerator() {
return sshGen.get();
}
public void setHttpInjector(Injector injector) {
httpModule = copy(injector);
httpGen = injector.getProvider(ModuleGenerator.class);
httpSets = dynamicSetsOf(injector);
httpMaps = dynamicMapsOf(injector);
onStart.addAll(listeners(injector, StartPluginListener.class));
onReload.addAll(listeners(injector, ReloadPluginListener.class));
}
@@ -105,31 +157,265 @@ public class PluginGuiceEnvironment {
return httpModule;
}
ModuleGenerator newHttpModuleGenerator() {
return httpGen.get();
}
void onStartPlugin(Plugin plugin) {
for (StartPluginListener l : onStart) {
l.onStartPlugin(plugin);
}
attachSet(sysSets, plugin.getSysInjector(), plugin);
attachSet(sshSets, plugin.getSshInjector(), plugin);
attachSet(httpSets, plugin.getHttpInjector(), plugin);
attachMap(sysMaps, plugin.getSysInjector(), plugin);
attachMap(sshMaps, plugin.getSshInjector(), plugin);
attachMap(httpMaps, plugin.getHttpInjector(), plugin);
}
private void attachSet(Map<TypeLiteral<?>, DynamicSet<?>> sets,
@Nullable Injector src,
Plugin plugin) {
if (src != null && sets != null && !sets.isEmpty()) {
for (Map.Entry<TypeLiteral<?>, DynamicSet<?>> e : sets.entrySet()) {
@SuppressWarnings("unchecked")
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
@SuppressWarnings("unchecked")
DynamicSet<Object> set = (DynamicSet<Object>) e.getValue();
for (Binding<Object> b : bindings(src, type)) {
plugin.add(set.add(b.getKey(), b.getProvider().get()));
}
}
}
}
private void attachMap(Map<TypeLiteral<?>, DynamicMap<?>> maps,
@Nullable Injector src,
Plugin plugin) {
if (src != null && maps != null && !maps.isEmpty()) {
for (Map.Entry<TypeLiteral<?>, DynamicMap<?>> e : maps.entrySet()) {
@SuppressWarnings("unchecked")
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
@SuppressWarnings("unchecked")
PrivateInternals_DynamicMapImpl<Object> set =
(PrivateInternals_DynamicMapImpl<Object>) e.getValue();
for (Binding<Object> b : bindings(src, type)) {
plugin.add(set.put(
plugin.getName(),
b.getKey(),
b.getProvider().get()));
}
}
}
}
void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) {
for (ReloadPluginListener l : onReload) {
l.onReloadPlugin(oldPlugin, newPlugin);
}
// Index all old registrations by the raw type. These may be replaced
// during the reattach calls below. Any that are not replaced will be
// removed when the old plugin does its stop routine.
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> old =
LinkedListMultimap.create();
for (ReloadableRegistrationHandle<?> h : oldPlugin.getReloadableHandles()) {
old.put(h.getKey().getTypeLiteral(), h);
}
reattachMap(old, sysMaps, newPlugin.getSysInjector(), newPlugin);
reattachMap(old, sshMaps, newPlugin.getSshInjector(), newPlugin);
reattachMap(old, httpMaps, newPlugin.getHttpInjector(), newPlugin);
reattachSet(old, sysSets, newPlugin.getSysInjector(), newPlugin);
reattachSet(old, sshSets, newPlugin.getSshInjector(), newPlugin);
reattachSet(old, httpSets, newPlugin.getHttpInjector(), newPlugin);
}
private static <T> List<T> listeners(Injector src, Class<T> type) {
List<Binding<T>> bindings = src.findBindingsByType(TypeLiteral.get(type));
List<T> found = Lists.newArrayListWithCapacity(bindings.size());
for (Binding<T> b : bindings) {
found.add(b.getProvider().get());
private void reattachMap(
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
Map<TypeLiteral<?>, DynamicMap<?>> maps,
@Nullable Injector src,
Plugin newPlugin) {
if (src == null || maps == null || maps.isEmpty()) {
return;
}
for (Map.Entry<TypeLiteral<?>, DynamicMap<?>> e : maps.entrySet()) {
@SuppressWarnings("unchecked")
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
@SuppressWarnings("unchecked")
PrivateInternals_DynamicMapImpl<Object> map =
(PrivateInternals_DynamicMapImpl<Object>) e.getValue();
Map<Annotation, ReloadableRegistrationHandle<?>> am = Maps.newHashMap();
for (ReloadableRegistrationHandle<?> h : oldHandles.get(type)) {
Annotation a = h.getKey().getAnnotation();
if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
am.put(a, h);
}
}
for (Binding<?> binding : bindings(src, e.getKey())) {
@SuppressWarnings("unchecked")
Binding<Object> b = (Binding<Object>) binding;
Key<Object> key = b.getKey();
@SuppressWarnings("unchecked")
ReloadableRegistrationHandle<Object> h =
(ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
if (h != null) {
replace(newPlugin, h, b);
oldHandles.remove(type, h);
} else {
newPlugin.add(map.put(
newPlugin.getName(),
b.getKey(),
b.getProvider().get()));
}
}
}
}
/** Type used to declare unique annotations. Guice hides this, so extract it. */
private static final Class<?> UNIQUE_ANNOTATION =
UniqueAnnotations.create().getClass();
private void reattachSet(
ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
Map<TypeLiteral<?>, DynamicSet<?>> sets,
@Nullable Injector src,
Plugin newPlugin) {
if (src == null || sets == null || sets.isEmpty()) {
return;
}
for (Map.Entry<TypeLiteral<?>, DynamicSet<?>> e : sets.entrySet()) {
@SuppressWarnings("unchecked")
TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
@SuppressWarnings("unchecked")
DynamicSet<Object> set = (DynamicSet<Object>) e.getValue();
// Index all old handles that match this DynamicSet<T> keyed by
// annotations. Ignore the unique annotations, thereby favoring
// the @Named annotations or some other non-unique naming.
Map<Annotation, ReloadableRegistrationHandle<?>> am = Maps.newHashMap();
List<ReloadableRegistrationHandle<?>> old = oldHandles.get(type);
Iterator<ReloadableRegistrationHandle<?>> oi = old.iterator();
while (oi.hasNext()) {
ReloadableRegistrationHandle<?> h = oi.next();
Annotation a = h.getKey().getAnnotation();
if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
am.put(a, h);
oi.remove();
}
}
// Replace old handles with new bindings, favoring cases where there
// is an exact match on an @Named annotation. If there is no match
// pick any handle and replace it. We generally expect only one
// handle of each DynamicSet type when using unique annotations, but
// possibly multiple ones if @Named was used. Plugin authors that want
// atomic replacement across reloads should use @Named annotations with
// stable names that do not change across plugin versions to ensure the
// handles are swapped correctly.
oi = old.iterator();
for (Binding<?> binding : bindings(src, type)) {
@SuppressWarnings("unchecked")
Binding<Object> b = (Binding<Object>) binding;
Key<Object> key = b.getKey();
@SuppressWarnings("unchecked")
ReloadableRegistrationHandle<Object> h1 =
(ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
if (h1 != null) {
replace(newPlugin, h1, b);
} else if (oi.hasNext()) {
@SuppressWarnings("unchecked")
ReloadableRegistrationHandle<Object> h2 =
(ReloadableRegistrationHandle<Object>) oi.next();
oi.remove();
replace(newPlugin, h2, b);
} else {
newPlugin.add(set.add(b.getKey(), b.getProvider().get()));
}
}
}
}
private static <T> void replace(Plugin newPlugin,
ReloadableRegistrationHandle<T> h, Binding<T> b) {
RegistrationHandle n = h.replace(b.getKey(), b.getProvider().get());
if (n != null){
newPlugin.add(n);
}
}
static <T> List<T> listeners(Injector src, Class<T> type) {
List<Binding<T>> bindings = bindings(src, TypeLiteral.get(type));
int cnt = bindings != null ? bindings.size() : 0;
List<T> found = Lists.newArrayListWithCapacity(cnt);
if (bindings != null) {
for (Binding<T> b : bindings) {
found.add(b.getProvider().get());
}
}
return found;
}
private static <T> List<Binding<T>> bindings(Injector src, TypeLiteral<T> type) {
return src.findBindingsByType(type);
}
private static Map<TypeLiteral<?>, DynamicSet<?>> dynamicSetsOf(Injector src) {
Map<TypeLiteral<?>, DynamicSet<?>> m = Maps.newHashMap();
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
TypeLiteral<?> type = e.getKey().getTypeLiteral();
if (type.getRawType() == DynamicSet.class) {
ParameterizedType p = (ParameterizedType) type.getType();
m.put(TypeLiteral.get(p.getActualTypeArguments()[0]),
(DynamicSet<?>) e.getValue().getProvider().get());
}
}
return m;
}
private static Map<TypeLiteral<?>, DynamicMap<?>> dynamicMapsOf(Injector src) {
Map<TypeLiteral<?>, DynamicMap<?>> m = Maps.newHashMap();
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
TypeLiteral<?> type = e.getKey().getTypeLiteral();
if (type.getRawType() == DynamicMap.class) {
ParameterizedType p = (ParameterizedType) type.getType();
m.put(TypeLiteral.get(p.getActualTypeArguments()[0]),
(DynamicMap<?>) e.getValue().getProvider().get());
}
}
return m;
}
private static Module copy(Injector src) {
Set<TypeLiteral<?>> dynamicTypes = Sets.newHashSet();
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
TypeLiteral<?> type = e.getKey().getTypeLiteral();
if (type.getRawType() == DynamicSet.class
|| type.getRawType() == DynamicMap.class) {
ParameterizedType t = (ParameterizedType) type.getType();
dynamicTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
}
}
final Map<Key<?>, Binding<?>> bindings = Maps.newLinkedHashMap();
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
if (shouldCopy(e.getKey())) {
if (!dynamicTypes.contains(e.getKey().getTypeLiteral())
&& shouldCopy(e.getKey())) {
bindings.put(e.getKey(), e.getValue());
}
}
@@ -202,22 +488,22 @@ public class PluginGuiceEnvironment {
return true;
}
private static boolean is(String name, Class<?> type) {
Class<?> p = type;
while (p != null) {
if (name.equals(p.getName())) {
static boolean is(String name, Class<?> type) {
while (type != null) {
if (name.equals(type.getName())) {
return true;
}
p = p.getSuperclass();
}
Class<?>[] interfaces = type.getInterfaces();
if (interfaces != null) {
for (Class<?> i : interfaces) {
if (is(name, i)) {
return true;
Class<?>[] interfaces = type.getInterfaces();
if (interfaces != null) {
for (Class<?> i : interfaces) {
if (is(name, i)) {
return true;
}
}
}
type = type.getSuperclass();
}
return false;
}

View File

@@ -337,6 +337,7 @@ public class PluginLoader implements LifecycleListener {
return new Plugin(name,
srcJar, snapshot,
jarFile, manifest,
pluginLoader,
sysModule, sshModule, httpModule);
} finally {
if (!keep) {

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.sshd;
import com.google.common.collect.Maps;
import com.google.gerrit.server.plugins.RegistrationHandle;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;

View File

@@ -0,0 +1,74 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.sshd;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.gerrit.extensions.annotations.Export;
import com.google.gerrit.server.plugins.InvalidPluginException;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.apache.sshd.server.Command;
import java.util.Map;
class SshAutoRegisterModuleGenerator
extends AbstractModule
implements ModuleGenerator {
private final Map<String, Class<Command>> commands = Maps.newHashMap();
private CommandName command;
@Override
protected void configure() {
bind(Commands.key(command))
.toProvider(new DispatchCommandProvider(command));
for (Map.Entry<String, Class<Command>> e : commands.entrySet()) {
bind(Commands.key(command, e.getKey())).to(e.getValue());
}
}
public void setPluginName(String name) {
command = Commands.named(name);
}
@SuppressWarnings("unchecked")
@Override
public void export(Export export, Class<?> type)
throws InvalidPluginException {
Preconditions.checkState(command != null, "pluginName must be provided");
if (Command.class.isAssignableFrom(type)) {
Class<Command> old = commands.get(export.value());
if (old != null) {
throw new InvalidPluginException(String.format(
"@Export(\"%s\") has duplicate bindings:\n %s\n %s",
export.value(), old.getName(), type.getName()));
}
commands.put(export.value(), (Class<Command>) type);
} else {
throw new InvalidPluginException(String.format(
"Class %s with @Export(\"%s\") must extend %s or implement %s",
type.getName(), export.value(),
SshCommand.class.getName(), Command.class.getName()));
}
}
@Override
public Module create() throws InvalidPluginException {
Preconditions.checkState(command != null, "pluginName must be provided");
return this;
}
}

View File

@@ -31,6 +31,7 @@ import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.git.QueueProvider;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
import com.google.gerrit.server.project.ProjectControl;
@@ -94,6 +95,7 @@ public class SshModule extends FactoryModule {
install(new LifecycleModule() {
@Override
protected void configure() {
bind(ModuleGenerator.class).to(SshAutoRegisterModuleGenerator.class);
bind(SshPluginStarterCallback.class);
bind(StartPluginListener.class)
.annotatedWith(UniqueAnnotations.create())

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.sshd.commands;
import com.google.common.base.Preconditions;
import com.google.gerrit.server.plugins.PluginName;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.sshd.CommandName;
import com.google.gerrit.sshd.Commands;
import com.google.gerrit.sshd.DispatchCommandProvider;

View File

@@ -87,6 +87,7 @@ limitations under the License.
<module>gerrit-gwtdebug</module>
<module>gerrit-war</module>
<module>gerrit-extension-api</module>
<module>gerrit-plugin-api</module>
<module>gerrit-gwtui</module>

39
tools/deploy_api.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
SRC=$(ls gerrit-plugin-api/target/gerrit-plugin-api-*-sources.jar)
VER=${SRC#gerrit-plugin-api/target/gerrit-plugin-api-}
VER=${VER%-sources.jar}
type=release
case $VER in
*-SNAPSHOT)
echo >&2 "fatal: Cannot deploy $VER"
echo >&2 " Use ./tools/version.sh --release && mvn clean package"
exit 1
;;
*-[0-9]*-g*) type=snapshot ;;
esac
URL=s3://gerrit-api@commondatastorage.googleapis.com/$type
echo "Deploying API $VER to $URL"
for module in gerrit-extension-api gerrit-plugin-api
do
mvn deploy:deploy-file \
-DgroupId=com.google.gerrit \
-DartifactId=$module \
-Dversion=$VER \
-Dpackaging=jar \
-Dfile=$module/target/$module-$VER.jar \
-DrepositoryId=gerrit-api-repository \
-Durl=$URL
mvn deploy:deploy-file \
-DgroupId=com.google.gerrit \
-DartifactId=$module \
-Dversion=$VER \
-Dpackaging=java-source \
-Dfile=$module/target/$module-$VER-sources.jar \
-Djava-source=false \
-DrepositoryId=gerrit-api-repository \
-Durl=$URL
done

View File

@@ -1,37 +0,0 @@
#!/bin/sh
SRC=$(ls gerrit-plugin-api/target/gerrit-plugin-api-*-sources.jar)
VER=${SRC#gerrit-plugin-api/target/gerrit-plugin-api-}
VER=${VER%-sources.jar}
JAR=gerrit-plugin-api/target/gerrit-plugin-api-$VER.jar
type=release
case $VER in
*-SNAPSHOT)
echo >&2 "fatal: Cannot deploy $VER"
echo >&2 " Use ./tools/version.sh --release && mvn clean package"
exit 1
;;
*-[0-9]*-g*) type=snapshot ;;
esac
URL=s3://gerrit-api@commondatastorage.googleapis.com/$type
echo "Deploying gerrit-plugin-api $VER to $URL"
mvn deploy:deploy-file \
-DgroupId=com.google.gerrit \
-DartifactId=gerrit-plugin-api \
-Dversion=$VER \
-Dpackaging=jar \
-Dfile=$JAR \
-DrepositoryId=gerrit-api-repository \
-Durl=$URL
mvn deploy:deploy-file \
-DgroupId=com.google.gerrit \
-DartifactId=gerrit-plugin-api \
-Dversion=$VER \
-Dpackaging=java-source \
-Dfile=$SRC \
-Djava-source=false \
-DrepositoryId=gerrit-api-repository \
-Durl=$URL