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:
6
gerrit-extension-api/.gitignore
vendored
Normal file
6
gerrit-extension-api/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/target
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings/org.maven.ide.eclipse.prefs
|
||||
/.settings/org.eclipse.m2e.core.prefs
|
||||
/gerrit-extension-api.iml
|
@@ -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
|
@@ -0,0 +1,3 @@
|
||||
#Tue Sep 02 16:59:24 PDT 2008
|
||||
eclipse.preferences.version=1
|
||||
line.separator=\n
|
269
gerrit-extension-api/.settings/org.eclipse.jdt.core.prefs
Normal file
269
gerrit-extension-api/.settings/org.eclipse.jdt.core.prefs
Normal 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
|
61
gerrit-extension-api/.settings/org.eclipse.jdt.ui.prefs
Normal file
61
gerrit-extension-api/.settings/org.eclipse.jdt.ui.prefs
Normal 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
|
71
gerrit-extension-api/pom.xml
Normal file
71
gerrit-extension-api/pom.xml
Normal 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>
|
@@ -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();
|
||||
}
|
@@ -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 + ")";
|
||||
}
|
||||
}
|
@@ -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() {
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
}
|
@@ -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 {
|
||||
}
|
@@ -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
|
@@ -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("foo"))
|
||||
* .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("foo"))
|
||||
* .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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 {
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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>
|
||||
|
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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 + "]";
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -337,6 +337,7 @@ public class PluginLoader implements LifecycleListener {
|
||||
return new Plugin(name,
|
||||
srcJar, snapshot,
|
||||
jarFile, manifest,
|
||||
pluginLoader,
|
||||
sysModule, sshModule, httpModule);
|
||||
} finally {
|
||||
if (!keep) {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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())
|
||||
|
@@ -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;
|
||||
|
1
pom.xml
1
pom.xml
@@ -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
39
tools/deploy_api.sh
Executable 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
|
@@ -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
|
Reference in New Issue
Block a user