diff --git a/.editorconfig b/.editorconfig index bc2270d..9eb33c7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -30,7 +30,7 @@ ij_css_hex_color_upper_case = false ij_css_keep_blank_lines_in_code = 2 ij_css_keep_indents_on_empty_lines = false ij_css_keep_single_line_blocks = false -ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow ij_css_space_after_colon = true ij_css_space_before_opening_brace = true ij_css_use_double_quotes = true @@ -142,7 +142,7 @@ ij_java_for_statement_wrap = off ij_java_generate_final_locals = false ij_java_generate_final_parameters = false ij_java_if_brace_force = never -ij_java_imports_layout = *,|,javax.**,java.**,|,$* +ij_java_imports_layout = *, |, javax.**, java.**, |, $* ij_java_indent_case_from_switch = true ij_java_insert_inner_class_imports = false ij_java_insert_override_annotation = true @@ -181,7 +181,7 @@ ij_java_names_count_to_use_import_on_demand = 3 ij_java_new_line_after_lparen_in_annotation = false ij_java_new_line_after_lparen_in_deconstruction_pattern = true ij_java_new_line_after_lparen_in_record_header = false -ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.* +ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.* ij_java_parameter_annotation_wrap = off ij_java_parentheses_expression_new_line_after_left_paren = false ij_java_parentheses_expression_right_paren_on_new_line = false @@ -320,7 +320,7 @@ ij_less_keep_indents_on_empty_lines = false ij_less_keep_single_line_blocks = false ij_less_line_comment_add_space = false ij_less_line_comment_at_first_column = false -ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_less_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow ij_less_space_after_colon = true ij_less_space_before_opening_brace = true ij_less_use_double_quotes = true @@ -354,7 +354,7 @@ ij_sass_keep_indents_on_empty_lines = false ij_sass_keep_single_line_blocks = false ij_sass_line_comment_add_space = false ij_sass_line_comment_at_first_column = false -ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow ij_sass_space_after_colon = true ij_sass_space_before_opening_brace = true ij_sass_use_double_quotes = true @@ -377,7 +377,7 @@ ij_scss_keep_indents_on_empty_lines = false ij_scss_keep_single_line_blocks = false ij_scss_line_comment_add_space = false ij_scss_line_comment_at_first_column = false -ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow ij_scss_space_after_colon = true ij_scss_space_before_opening_brace = true ij_scss_use_double_quotes = true @@ -439,7 +439,7 @@ ij_typescript_array_initializer_wrap = off ij_typescript_assignment_wrap = off ij_typescript_binary_operation_sign_on_next_line = false ij_typescript_binary_operation_wrap = off -ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** ij_typescript_blank_lines_after_imports = 1 ij_typescript_blank_lines_around_class = 1 ij_typescript_blank_lines_around_field = 0 @@ -602,6 +602,8 @@ ij_shell_switch_cases_indented = false ij_shell_use_unix_line_separator = true [{*.cjs,*.js}] +indent_size = 2 +tab_width = 2 ij_javascript_align_imports = false ij_javascript_align_multiline_array_initializer_expression = false ij_javascript_align_multiline_binary_operation = false @@ -620,7 +622,7 @@ ij_javascript_array_initializer_wrap = off ij_javascript_assignment_wrap = off ij_javascript_binary_operation_sign_on_next_line = false ij_javascript_binary_operation_wrap = off -ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** ij_javascript_blank_lines_after_imports = 1 ij_javascript_blank_lines_around_class = 1 ij_javascript_blank_lines_around_field = 0 @@ -835,7 +837,7 @@ ij_groovy_ginq_on_wrap_policy = 1 ij_groovy_ginq_space_after_keyword = true ij_groovy_if_brace_force = never ij_groovy_import_annotation_wrap = 2 -ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* +ij_groovy_imports_layout = *, |, javax.**, java.**, |, $* ij_groovy_indent_case_from_switch = true ij_groovy_indent_label_blocks = true ij_groovy_insert_inner_class_imports = false @@ -866,7 +868,7 @@ ij_groovy_method_parameters_right_paren_on_new_line = false ij_groovy_method_parameters_wrap = off ij_groovy_modifier_list_wrap = false ij_groovy_names_count_to_use_import_on_demand = 3 -ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.* +ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.* ij_groovy_parameter_annotation_wrap = off ij_groovy_parentheses_expression_new_line_after_left_paren = false ij_groovy_parentheses_expression_right_paren_on_new_line = false @@ -1000,7 +1002,7 @@ ij_kotlin_field_annotation_wrap = split_into_lines ij_kotlin_finally_on_new_line = false ij_kotlin_if_rparen_on_new_line = false ij_kotlin_import_nested_classes = false -ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ +ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ ij_kotlin_insert_whitespaces_in_simple_one_line_method = true ij_kotlin_keep_blank_lines_before_right_brace = 2 ij_kotlin_keep_blank_lines_in_code = 2 @@ -1020,7 +1022,7 @@ ij_kotlin_method_parameters_right_paren_on_new_line = false ij_kotlin_method_parameters_wrap = off ij_kotlin_name_count_to_use_star_import = 5 ij_kotlin_name_count_to_use_star_import_for_members = 3 -ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.** +ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** ij_kotlin_parameter_annotation_wrap = off ij_kotlin_space_after_comma = true ij_kotlin_space_after_extend_colon = true @@ -1068,23 +1070,23 @@ ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false [{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] -ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 +ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 ij_html_align_attributes = true ij_html_align_text = false ij_html_attribute_wrap = normal ij_html_block_comment_add_space = false ij_html_block_comment_at_first_column = true ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p -ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot +ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p +ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot ij_html_enforce_quotes = false -ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var +ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var ij_html_keep_blank_lines = 2 ij_html_keep_indents_on_empty_lines = false ij_html_keep_line_breaks = true ij_html_keep_line_breaks_in_text = true ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span,pre,textarea +ij_html_keep_whitespaces_inside = span, pre, textarea ij_html_line_comment_at_first_column = true ij_html_new_line_after_last_attribute = never ij_html_new_line_before_first_attribute = never diff --git a/.github/workflows/buildspec.yml b/.github/workflows/buildspec.yml index 4265b88..f07ce60 100644 --- a/.github/workflows/buildspec.yml +++ b/.github/workflows/buildspec.yml @@ -47,7 +47,7 @@ jobs: - name: Build with Maven # mvn -B: Batch mode causes Maven to not display "Progress: 125/150kB" style lines when running. # Must be 'clean package', instead of 'clean test', otherwise this error will occur: "Artifact has not been packaged yet!" - run: mvn -B clean package + run: mvn -P generate-openapi-sources -B clean package - name: Create Tag Version run: mvn -B scm:tag -Dusername=${{ github.actor }} -Dpassword=${{ github.token }} diff --git a/.runconfig/common-web-notification.run.xml b/.runconfig/common-web-notification.run.xml new file mode 100644 index 0000000..6b79a10 --- /dev/null +++ b/.runconfig/common-web-notification.run.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 652ea70..42db3ae 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Commons Herd.io --------------- [![Build Status](https://github.com/desiderati/commons/workflows/Build/badge.svg)](https://github.com/desiderati/commons/actions) -[![Version](https://img.shields.io/badge/Version-3.3.0.RC2-red.svg)](https://github.com/desiderati/commons/releases) +[![Version](https://img.shields.io/badge/Version-3.3.0.RC3-red.svg)](https://github.com/desiderati/commons/releases) [![GitHub Stars](https://img.shields.io/github/stars/desiderati/commons.svg?label=GitHub%20Stars)](https://github.com/desiderati/commons/) [![LICENSE](https://img.shields.io/badge/License-MIT-lightgrey.svg)](https://github.com/desiderati/commons/blob/master/LICENSE) @@ -24,7 +24,7 @@ Changelog All project changes will be documented in this file. -#### [3.3.0.RC2] - 2024-01-12 +#### [3.3.0.RC3] - 2024-01-12 - Added support to retrieve the authorities passed by the delegated authentication provider. - Added support for pre/post authorizations on GraphQL calls. - Added Hamkrest and Spek as test dependencies. @@ -32,7 +32,7 @@ All project changes will be documented in this file. - Added the pageable functionality for GraphQL. - Removed dependency with org.reflections. - Updated some dependency versions. -- Update the copyrigth. +- Update the copyright. #### [3.2.1.RELEASE] - 2023-12-13 - Added support to create request scoped beans within async threads. @@ -369,8 +369,8 @@ All project changes will be documented in this file. - Creation of CPF/CNPJ field validations. - Creation of the generic exception handling functionality. (**ExceptionHandlingController**) - Creation of JPA repositories to handle null method parameters (_Null-Safe Parameters_), including MongoDB. -- Creation of **AbstractRepository** class to assist in the creation of personalized data repositories. -- Creation of **AbstractPersistableIdentity** class to assist in the creation of JPA entities. +- Creation of **AbstractRepository** class to help in the creation of personalized data repositories. +- Creation of **AbstractPersistableIdentity** class to help in the creation of JPA entities. - Creation of the functionality responsible for defining the name strategy to use with databases. (_Naming Strategy_) - Creation of classes to use with automated tests. - Added support for internationalization. diff --git a/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractEntity.kt b/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractIdentity.kt similarity index 91% rename from common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractEntity.kt rename to common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractIdentity.kt index 242c2e4..3824a84 100644 --- a/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractEntity.kt +++ b/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractIdentity.kt @@ -21,10 +21,10 @@ package io.herd.common.data.jpa import org.springframework.data.util.ProxyUtils import java.io.Serializable -abstract class AbstractEntity : Identity { +abstract class AbstractIdentity : Identity { @Override - abstract override fun getId() : I? + abstract override fun getId(): I? @Override override fun equals(other: Any?): Boolean { @@ -36,11 +36,11 @@ abstract class AbstractEntity : Identity { return true } - if (javaClass != ProxyUtils.getUserClass(other) && other !is AbstractEntity<*>) { + if (javaClass != ProxyUtils.getUserClass(other) && other !is AbstractIdentity<*>) { return false } - val that = other as AbstractEntity<*> + val that = other as AbstractIdentity<*> return this.id == that.id } diff --git a/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractPersistableIdentity.java b/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractPersistableIdentity.java index 59f6b18..6a7a73c 100644 --- a/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractPersistableIdentity.java +++ b/common-data-jpa/src/main/java/io/herd/common/data/jpa/AbstractPersistableIdentity.java @@ -18,10 +18,10 @@ */ package io.herd.common.data.jpa; +import jakarta.persistence.*; import org.springframework.data.domain.Persistable; import org.springframework.data.jpa.domain.AbstractPersistable; -import jakarta.persistence.*; import java.io.Serializable; /** @@ -35,7 +35,7 @@ */ @MappedSuperclass @SuppressWarnings("unused") -public abstract class AbstractPersistableIdentity extends AbstractEntity implements Persistable, Identity { +public abstract class AbstractPersistableIdentity extends AbstractIdentity implements Persistable, Identity { public AbstractPersistableIdentity() { this(null); @@ -54,7 +54,7 @@ public final I getId() { return id; } - protected final void setId(final I id) { + protected void setId(final I id) { this.id = id; } diff --git a/common-data-jpa/src/main/java/io/herd/common/data/multitenant/MultiTenantContext.java b/common-data-jpa/src/main/java/io/herd/common/data/multitenant/MultiTenantContext.java index 10e1530..4ceadd8 100644 --- a/common-data-jpa/src/main/java/io/herd/common/data/multitenant/MultiTenantContext.java +++ b/common-data-jpa/src/main/java/io/herd/common/data/multitenant/MultiTenantContext.java @@ -37,7 +37,8 @@ public class MultiTenantContext { private static String defaultTenant; @Autowired - @SuppressWarnings("squid:S3010") // We may ignore the error because it will have only one instance by application context. + @SuppressWarnings("squid:S3010") + // We may ignore the error because it will have only one instance by application context. public MultiTenantContext(@Value("${app.database.multitenant.default-tenant:public}") String defaultTenant) { MultiTenantContext.defaultTenant = defaultTenant; } diff --git a/common-data-jpa/src/test/java/io/herd/common/data/jpa/AbstractEntityTest.kt b/common-data-jpa/src/test/java/io/herd/common/data/jpa/AbstractEntityTest.kt index c722662..1cf2357 100644 --- a/common-data-jpa/src/test/java/io/herd/common/data/jpa/AbstractEntityTest.kt +++ b/common-data-jpa/src/test/java/io/herd/common/data/jpa/AbstractEntityTest.kt @@ -93,7 +93,7 @@ class AbstractEntityTest { } } -internal open class ClassePai(private var id: Long? = null) : AbstractEntity() { +internal open class ClassePai(private var id: Long? = null) : AbstractIdentity() { override fun getId(): Long? { return id } diff --git a/common-google/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/common-google/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 5b8b20f..3f04988 100644 --- a/common-google/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/common-google/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -67,8 +67,7 @@ { "name": "google.captcha.secret-key", "description": "Google Captcha service secret key.", - "type": "java.lang.String", - "defaultValue": "/google-calendar-api-secret.json" + "type": "java.lang.String" } ] } diff --git a/common-jms/README.md b/common-jms/README.md index 68b6808..f88356a 100644 --- a/common-jms/README.md +++ b/common-jms/README.md @@ -1,7 +1,7 @@ Support for JMS Queues ---------------------- -It will be necessary to copy the file `jms.properties` contained in the directory `/src/main/resources` -of this project, to your application's `/src/main/resources` directory, changing the values +It will be necessary to copy the file `jms.properties` contained in the directory `/src/main/resources` +of this project, to your application's `/src/main/resources` directory, changing the values of its properties according to the necessity. -Only copy this properties file, in case there is a change in the properties. \ No newline at end of file +Only copy this properties file, in case there is a change in the properties. diff --git a/common-jms/src/main/java/io/herd/common/jms/AbstractAsyncMessage.java b/common-jms/src/main/java/io/herd/common/jms/AbstractAsyncMessage.java new file mode 100644 index 0000000..6f02896 --- /dev/null +++ b/common-jms/src/main/java/io/herd/common/jms/AbstractAsyncMessage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.jms; + +import java.util.UUID; + +@SuppressWarnings("unused") +public abstract class AbstractAsyncMessage implements AsyncMessage { + + private final UUID msgId; + + public AbstractAsyncMessage() { + this.msgId = UUID.randomUUID(); + } + + @Override + public UUID getMsgId() { + return msgId; + } + + @Override + public String toString() { + return "AbstractAsyncMessage{mgId=" + msgId + "}"; + } +} diff --git a/common-jms/src/main/java/io/herd/common/jms/AbstractAsyncMessageListener.java b/common-jms/src/main/java/io/herd/common/jms/AbstractAsyncMessageListener.java new file mode 100644 index 0000000..bea2e9a --- /dev/null +++ b/common-jms/src/main/java/io/herd/common/jms/AbstractAsyncMessageListener.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.jms; + +import io.herd.common.exception.ApplicationException; +import io.herd.common.jms.configuration.JmsAutoConfiguration; +import jakarta.jms.Message; +import jakarta.validation.ConstraintViolationException; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jms.annotation.JmsListener; + +/** + * {@link jakarta.jms.MessageListener} responsible for reading messages sent to the standard queue. + * + * @see JmsAutoConfiguration#queue(String) + */ +@Slf4j +public abstract class AbstractAsyncMessageListener { + + @SneakyThrows + @JmsListener(destination = "${jms.default-queue.name}") + public void onMessage(Message jmsMessage, M message) { + try { + log.info("Message '{}' received!", message.getMsgId()); + log.debug("{}", jmsMessage); + receive(message); + log.info("Message '{}' received and processed with success!", message.getMsgId()); + jmsMessage.acknowledge(); + + } catch (ApplicationException applicationException) { + // It means is an application (business) exception and there's nothing else to do about it. + handleApplicationException(jmsMessage, message, applicationException); + + } catch (ConstraintViolationException constraintViolationException) { + // Thrown when used the @{link jakarta.validation.Valid} annotation. + StringBuilder validationErrorsMsg = new StringBuilder("Constraint Violations:"); + constraintViolationException.getConstraintViolations().forEach( + constraintViolation -> validationErrorsMsg + .append(System.lineSeparator()) + .append(constraintViolation.getPropertyPath()) + .append(":") + .append(constraintViolation.getMessage()) + ); + handleApplicationException(jmsMessage, message, new ApplicationException(validationErrorsMsg.toString())); + + } catch (Throwable throwable) { + throw new JmsApplicationException(jmsMessage, throwable.getMessage(), throwable); + } + } + + /** + * Method responsible for processing the message received from the queue. + * + * @param message The message received from the queue. + * @throws Exception If any error occurs during the processing of the message. + */ + protected abstract void receive(M message) throws Exception; + + /** + * Handles an application (business) exception that occurred while processing a message. + * + * @param jmsMessage The JMS message that triggered the exception. + * @param message The message being processed when the exception occurred. + * @param applicationException The application exception that occurred. + */ + @SuppressWarnings("unused") + protected void handleApplicationException( + Message jmsMessage, + M message, + ApplicationException applicationException + ) { + log.error("Error while processing message '{}': ", message.getMsgId(), applicationException); + } +} diff --git a/common-jms/src/main/java/io/herd/common/jms/AbstractMessageListener.java b/common-jms/src/main/java/io/herd/common/jms/AbstractMessageListener.java deleted file mode 100644 index 84567a5..0000000 --- a/common-jms/src/main/java/io/herd/common/jms/AbstractMessageListener.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2024 - Felipe Desiderati - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.herd.common.jms; - -import io.herd.common.jms.configuration.JmsAutoConfiguration; -import lombok.extern.slf4j.Slf4j; -import org.springframework.jms.annotation.JmsListener; - -/** - * {@link jakarta.jms.MessageListener} responsible for reading messages sent to the standard queue. - * - * @see JmsAutoConfiguration#queue(String) - */ -@Slf4j -public abstract class AbstractMessageListener { - - @JmsListener(destination = "${jms.default-queue.name}") - public void receive(M message) throws Exception { - log.info("Message {} received!", message.getId()); - log.debug("{}", message); - doReceive(message); - log.info("Message {} received and processed with success!", message.getId()); - } - - @SuppressWarnings({"squid:S00112"}) // Must have thrown a generic exception. - protected abstract void doReceive(M message) throws Exception; -} diff --git a/common-jms/src/main/java/io/herd/common/jms/Message.java b/common-jms/src/main/java/io/herd/common/jms/AsyncMessage.java similarity index 88% rename from common-jms/src/main/java/io/herd/common/jms/Message.java rename to common-jms/src/main/java/io/herd/common/jms/AsyncMessage.java index 7a4b691..ee95808 100644 --- a/common-jms/src/main/java/io/herd/common/jms/Message.java +++ b/common-jms/src/main/java/io/herd/common/jms/AsyncMessage.java @@ -18,16 +18,11 @@ */ package io.herd.common.jms; -import lombok.Getter; - +import java.io.Serializable; import java.util.UUID; -@Getter -public abstract class Message { +public interface AsyncMessage extends Serializable { - private final UUID id; + UUID getMsgId(); - public Message() { - id = UUID.randomUUID(); - } } diff --git a/common-jms/src/main/java/io/herd/common/jms/MessagePublisher.java b/common-jms/src/main/java/io/herd/common/jms/AsyncMessagePublisher.java similarity index 86% rename from common-jms/src/main/java/io/herd/common/jms/MessagePublisher.java rename to common-jms/src/main/java/io/herd/common/jms/AsyncMessagePublisher.java index 019a291..6b77ee1 100644 --- a/common-jms/src/main/java/io/herd/common/jms/MessagePublisher.java +++ b/common-jms/src/main/java/io/herd/common/jms/AsyncMessagePublisher.java @@ -19,13 +19,12 @@ package io.herd.common.jms; import io.herd.common.jms.configuration.JmsAutoConfiguration; +import jakarta.jms.Queue; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsTemplate; import org.springframework.stereotype.Component; -import jakarta.jms.Queue; - /** * Class responsible for publishing messages to the standard queue. *

@@ -36,23 +35,23 @@ */ @Slf4j @Component -public class MessagePublisher { +public class AsyncMessagePublisher { private final JmsTemplate jmsTemplate; private final Queue queue; @Autowired - public MessagePublisher(JmsTemplate jmsTemplate, Queue queue) { + public AsyncMessagePublisher(JmsTemplate jmsTemplate, Queue queue) { this.jmsTemplate = jmsTemplate; this.queue = queue; } @SuppressWarnings("unused") - public void publish(M message) { - log.info("Publishing message {} ...", message.getId()); + public void publish(M message) { + log.info("Publishing message '{}' ...", message.getMsgId()); log.debug("{}", message); jmsTemplate.convertAndSend(queue, message); - log.info("Message {} published!", message.getId()); + log.info("Message '{}' published!", message.getMsgId()); } } diff --git a/common-jms/src/main/java/io/herd/common/jms/JmsApplicationException.java b/common-jms/src/main/java/io/herd/common/jms/JmsApplicationException.java new file mode 100644 index 0000000..bb8da27 --- /dev/null +++ b/common-jms/src/main/java/io/herd/common/jms/JmsApplicationException.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.jms; + +import io.herd.common.exception.ApplicationException; +import jakarta.jms.Message; +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; + +@Getter +@SuppressWarnings("unused") +public class JmsApplicationException extends ApplicationException { + + @Serial + private static final long serialVersionUID = 1L; + + private final Message failedMessage; + + public JmsApplicationException(Message failedMessage, String description) { + super(description); + this.failedMessage = failedMessage; + } + + public JmsApplicationException(Message failedMessage, String description, Serializable... args) { + super(description, args); + this.failedMessage = failedMessage; + } + + public JmsApplicationException(Message failedMessage, String description, Throwable cause) { + super(description, cause); + this.failedMessage = failedMessage; + } + + public JmsApplicationException( + Message failedMessage, + String description, + Throwable cause, + Serializable... args + ) { + super(description, cause, args); + this.failedMessage = failedMessage; + } +} diff --git a/common-jms/src/main/java/io/herd/common/jms/JmsErrorHandler.java b/common-jms/src/main/java/io/herd/common/jms/JmsErrorHandler.java index 8e564e3..f384cbe 100644 --- a/common-jms/src/main/java/io/herd/common/jms/JmsErrorHandler.java +++ b/common-jms/src/main/java/io/herd/common/jms/JmsErrorHandler.java @@ -18,17 +18,49 @@ */ package io.herd.common.jms; +import jakarta.jms.JMSException; +import jakarta.jms.Message; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.springframework.stereotype.Component; import org.springframework.util.ErrorHandler; @Slf4j -@Component public class JmsErrorHandler implements ErrorHandler { + private final Integer maxDeliveryAttempts; + + public JmsErrorHandler(Integer maxDeliveryAttempts) { + this.maxDeliveryAttempts = maxDeliveryAttempts; + } + @Override - public void handleError(@NotNull Throwable t) { - log.error("An error occurred while processing JMS message!", t); + public void handleError(@NotNull Throwable throwable) { + try { + if (throwable.getCause() != null && throwable.getCause() instanceof JmsApplicationException exception) { + Message message = exception.getFailedMessage(); + int deliveryCount = message.getIntProperty("JMSXDeliveryCount"); + if (deliveryCount >= maxDeliveryAttempts) { + handleLastError(exception.getCause()); + } else { + log.warn( + "An error occurred while processing JMS message [{}/{}]!", + deliveryCount, + maxDeliveryAttempts, + throwable + ); + } + } + } catch (JMSException ex) { + log.error("It was not possible to extract the delivery count from the message!", ex); + } + } + + protected void handleLastError(@NotNull Throwable throwable) { + log.error( + "All attempts [{}/{}] to process the message have been exhausted!", + maxDeliveryAttempts, + maxDeliveryAttempts, + throwable + ); } } diff --git a/common-jms/src/main/java/io/herd/common/jms/configuration/JmsAutoConfiguration.java b/common-jms/src/main/java/io/herd/common/jms/configuration/JmsAutoConfiguration.java index 2a7585d..02701c6 100644 --- a/common-jms/src/main/java/io/herd/common/jms/configuration/JmsAutoConfiguration.java +++ b/common-jms/src/main/java/io/herd/common/jms/configuration/JmsAutoConfiguration.java @@ -18,24 +18,22 @@ */ package io.herd.common.jms.configuration; -import jakarta.jms.ConnectionFactory; +import io.herd.common.jms.JmsErrorHandler; import jakarta.jms.Queue; import lombok.extern.slf4j.Slf4j; import org.apache.activemq.artemis.jms.client.ActiveMQQueue; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration; import org.springframework.context.annotation.*; import org.springframework.jms.annotation.EnableJms; -import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.converter.MessageType; -import org.springframework.util.ErrorHandler; @Slf4j @EnableJms @@ -51,55 +49,44 @@ public class JmsAutoConfiguration { public static final String TYPE_ID_PROPERTY_NAME = "_type"; + public JmsAutoConfiguration(@Value("${jms.dlq.enabled:false}") Boolean jmsDefaultQueueDlqEnabled) { + log.info("JMS default queue DLQ enabled? {}", jmsDefaultQueueDlqEnabled); + } + @Bean public Queue queue(@Value("${jms.default-queue.name}") String queueName) { return new ActiveMQQueue(queueName); } @Bean - @ConditionalOnProperty(name = "jms.dlq.enabled", havingValue = "true") + @ConditionalOnExpression("${jms.dlq.enabled:false}") public Queue dlqQueue( - @Value("${jms.dlq.queue-prefix}") String queueDlqPrefix, + @Value("${jms.dlq.prefix}") String queueDlqPrefix, @Value("${jms.default-queue.name}") String queueName ) { return new ActiveMQQueue(queueDlqPrefix + queueName); } @Bean + @ConditionalOnProperty(name = "jms.default-response-queue.name") public Queue responseQueue(@Value("${jms.default-response-queue.name}") String responseQueueName) { return new ActiveMQQueue(responseQueueName); } @Bean - @ConditionalOnProperty(name = "jms.dlq.enabled", havingValue = "true") + @ConditionalOnExpression("${jms.dlq.enabled:false} and !'${jms.default-response-queue.name}'.empty()") public Queue responseDlqQueue( - @Value("${jms.dlq.queue-prefix}") String queueDlqPrefix, + @Value("${jms.dlq.prefix}") String queueDlqPrefix, @Value("${jms.default-response-queue.name}") String responseQueueName ) { return new ActiveMQQueue(queueDlqPrefix + responseQueueName); } - /** - * We need to redefine a {@link DefaultJmsListenerContainerFactory}, because we need to register - * a custom {@link ErrorHandler}. We also had to register a redelivery policy. - */ - @Bean - public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( - DefaultJmsListenerContainerFactoryConfigurer configurer, - @Qualifier("jmsConnectionFactory") ConnectionFactory connectionFactory, - @Qualifier("jmsErrorHandler") ErrorHandler errorHandler - ) { - - DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); - configurer.configure(factory, connectionFactory); - factory.setErrorHandler(errorHandler); - return factory; - } - /** * We registered a converter for JSON. */ @Bean + @ConditionalOnMissingBean(MessageConverter.class) public MessageConverter jacksonJmsMessageConverter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); converter.setTargetType(MessageType.TEXT); @@ -109,4 +96,12 @@ public MessageConverter jacksonJmsMessageConverter() { converter.setTypeIdPropertyName(TYPE_ID_PROPERTY_NAME); return converter; } + + @Bean + @ConditionalOnMissingBean(JmsErrorHandler.class) + public JmsErrorHandler jmsErrorHandler( + @Value("${jms.default-queue.max-delivery-attempts:10}") Integer maxDeliveryAttempts + ) { + return new JmsErrorHandler(maxDeliveryAttempts); + } } diff --git a/common-jms/src/main/java/io/herd/common/jms/configuration/JmsListenerContainerFactoryPostProcessor.java b/common-jms/src/main/java/io/herd/common/jms/configuration/JmsListenerContainerFactoryPostProcessor.java new file mode 100644 index 0000000..1016972 --- /dev/null +++ b/common-jms/src/main/java/io/herd/common/jms/configuration/JmsListenerContainerFactoryPostProcessor.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.jms.configuration; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.jms.config.AbstractJmsListenerContainerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.ErrorHandler; + +/** + * A {@link BeanPostProcessor} that sets the {@link ErrorHandler} on all {@link AbstractJmsListenerContainerFactory} + * beans to the provided {@link ErrorHandler}. + */ +@Component +public class JmsListenerContainerFactoryPostProcessor implements BeanPostProcessor { + + private final ErrorHandler errorHandler; + + public JmsListenerContainerFactoryPostProcessor(ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + + @Override + public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) { + if (bean instanceof AbstractJmsListenerContainerFactory) { + ((AbstractJmsListenerContainerFactory) bean).setErrorHandler(errorHandler); + } + return bean; + } + + @Override + public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName) { + return bean; + } +} diff --git a/common-jms/src/main/resources/application-common-jms.properties b/common-jms/src/main/resources/application-common-jms.properties index 5af589c..26e9144 100644 --- a/common-jms/src/main/resources/application-common-jms.properties +++ b/common-jms/src/main/resources/application-common-jms.properties @@ -29,7 +29,7 @@ spring.jms.template.default-destination=${jms.default-queue.name} spring.jms.template.delivery-mode=persistent # Priority of a message when sending. Enable QoS when set. -spring.jms.template.priority=100 +spring.jms.template.priority=0 # Enable explicit QoS when sending a message. spring.jms.template.qos-enabled=true @@ -50,10 +50,10 @@ spring.jms.template.time-to-live=5m spring.artemis.broker-url=tcp://localhost:61616 # Login user of the broker. -#spring.artemis.user= +spring.artemis.user=artemis # Login password of the broker. -#spring.artemis.password= +spring.artemis.password=artemis # Block when a connection is requested and the pool is full. # Set it false, to throw a "JMSException" instead. diff --git a/common-jms/src/main/resources/jms.properties b/common-jms/src/main/resources/jms.properties index dbcd583..cd352e2 100644 --- a/common-jms/src/main/resources/jms.properties +++ b/common-jms/src/main/resources/jms.properties @@ -1,3 +1,6 @@ # suppress inspection "UnusedProperty" for whole file +jms.dlq.enabled=false +jms.dlq.prefix=dlq- jms.default-queue.name=default-queue +jms.default-queue.max-delivery-attempts=10 jms.default-response-queue.name=default-response-queue diff --git a/common-logging-test/src/main/resources/logback-test.xml b/common-logging-test/src/main/resources/logback-test.xml index 2119731..2362700 100644 --- a/common-logging-test/src/main/resources/logback-test.xml +++ b/common-logging-test/src/main/resources/logback-test.xml @@ -23,8 +23,8 @@ - + value="%yellow(%d{HH:mm:ss.SSS}) %highlight([%-5level]) %cyan(%-40.40logger{39}) : %white(%m%n%wEx)"/> + diff --git a/common-logging/README.md b/common-logging/README.md index e620d27..9c89cd2 100644 --- a/common-logging/README.md +++ b/common-logging/README.md @@ -2,7 +2,7 @@ Instructions ------------ Add the following dependency to your application's `pom.xml` file. In this way, all -log information will be recorded following the same formatting pattern. To use with tests only! +log information will be recorded following the same formatting pattern. To use with tests only! ``` @@ -19,16 +19,16 @@ log information will be recorded following the same formatting pattern. To use w In addition to the console (standard output), such information will be recorded in a Log file, according to the following rule: - 1) `${LOG_FILE}`, system variable that defines the full path (including the name) to the Log file. +1) `${LOG_FILE}`, system variable that defines the full path (including the name) to the Log file. - 2) If the variable above is not defined, the following rule will be applied: `${LOG_PATH}/app.log`, - where `${LOG_PATH}` is the system variable that defines where the Log file **app.log** will be stored. +2) If the variable above is not defined, the following rule will be applied: `${LOG_PATH}/app.log`, + where `${LOG_PATH}` is the system variable that defines where the Log file **app.log** will be stored. - 3) If the variable above is not defined, the following rule will be applied: `${LOG_TEMP}/app.log`, - where `${LOG_TEMP}` is the system variable that defines the temporary directory where the Log file - **app.log** will be stored. +3) If the variable above is not defined, the following rule will be applied: `${LOG_TEMP}/app.log`, + where `${LOG_TEMP}` is the system variable that defines the temporary directory where the Log file + **app.log** will be stored. - 4) If the variable above is not defined, the following rule will be applied: `${java.io.tmpdir}/app.log`, - where `${java.io.tmpdir}` is the system variable that defines the temporary directory for Java applications. +4) If the variable above is not defined, the following rule will be applied: `${java.io.tmpdir}/app.log`, + where `${java.io.tmpdir}` is the system variable that defines the temporary directory for Java applications. - 5) If the variable above is not defined, the following rule will be applied: `/tmp/app.log`. \ No newline at end of file +5) If the variable above is not defined, the following rule will be applied: `/tmp/app.log`. diff --git a/common-logging/src/main/resources/logback-spring.xml b/common-logging/src/main/resources/logback-spring.xml index 8494c59..20af512 100644 --- a/common-logging/src/main/resources/logback-spring.xml +++ b/common-logging/src/main/resources/logback-spring.xml @@ -23,14 +23,16 @@ - + - + - + value="${CONSOLE_LOG_PATTERN:-%yellow(%d{HH:mm:ss.SSS}) %highlight([%-5level]) %cyan(%-40.40logger{39}) : %white(%m%n)}"/> + @@ -39,8 +41,8 @@ - - + + diff --git a/common-parent-info/pom.xml b/common-parent-info/pom.xml index 7e76131..d7ae09a 100644 --- a/common-parent-info/pom.xml +++ b/common-parent-info/pom.xml @@ -82,7 +82,7 @@ - 3.3.0.RC2 + 3.3.0.RC3 ${revision} @@ -113,7 +113,7 @@ If not specified, the first compiled class found that contains a 'main' method will be used. --> - + diff --git a/common-parent-openapi-client/pom.xml b/common-parent-openapi-client/pom.xml index 3358435..0008f17 100644 --- a/common-parent-openapi-client/pom.xml +++ b/common-parent-openapi-client/pom.xml @@ -38,6 +38,7 @@ --> true + 9111 20 15000 --spring.main.banner-mode=off,--server.port=12345 @@ -216,6 +217,7 @@ ${openapi.thin-server.arguments} ${openapi.thin-server.max-attempts} ${openapi.thin-server.timeout-between-attempts} + ${openapi.thin-server.jmx-port} diff --git a/common-parent-static/assembly.xml b/common-parent-static/assembly.xml index 7159ceb..8ad781e 100644 --- a/common-parent-static/assembly.xml +++ b/common-parent-static/assembly.xml @@ -29,7 +29,9 @@ - ${project.properties.npm.dist-dir}/${project.artifactId}/${project.properties.npm.dist-environment-dir} + + ${project.properties.npm.dist-dir}/${project.artifactId}/${project.properties.npm.dist-environment-dir} + / **/* diff --git a/common-parent/pom.xml b/common-parent/pom.xml index 9292e0f..16c6cc7 100644 --- a/common-parent/pom.xml +++ b/common-parent/pom.xml @@ -97,7 +97,7 @@ 2.12.2 0.12.6 3.0.2 - 5.10.2 + 5.10.3 4.27.0 + + + 4.0.0 + + io.herd.common + common-parent + ${revision} + + ../common-parent + + common-web-client + + + + io.herd.common + common + + + + diff --git a/common-web/src/main/java/io/herd/common/web/configuration/OpenApiClientProperties.java b/common-web-client/src/main/java/io/herd/common/web/client/configuration/OpenApiClientProperties.java similarity index 97% rename from common-web/src/main/java/io/herd/common/web/configuration/OpenApiClientProperties.java rename to common-web-client/src/main/java/io/herd/common/web/client/configuration/OpenApiClientProperties.java index 7077333..1bfc798 100644 --- a/common-web/src/main/java/io/herd/common/web/configuration/OpenApiClientProperties.java +++ b/common-web-client/src/main/java/io/herd/common/web/client/configuration/OpenApiClientProperties.java @@ -16,7 +16,7 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.herd.common.web.configuration; +package io.herd.common.web.client.configuration; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; diff --git a/common-web-notification-client/pom.xml b/common-web-notification-client/pom.xml new file mode 100644 index 0000000..f8d0a77 --- /dev/null +++ b/common-web-notification-client/pom.xml @@ -0,0 +1,57 @@ + + + + + 4.0.0 + + io.herd.common + common-parent-openapi-client + ${revision} + + ../common-parent-openapi-client + + common-web-notification-client + + + false + io.herd.common.web.notification.WebTempApplication + + + false + api-docs + notification-openapi-client.json + + false + + + + + io.herd.common + common-web-client + + + io.herd.common + common-web-notification + true + + + + diff --git a/common-web-notification-client/src/main/java/io/herd/common/web/notification/client/NotificationClient.java b/common-web-notification-client/src/main/java/io/herd/common/web/notification/client/NotificationClient.java new file mode 100644 index 0000000..8dd7f33 --- /dev/null +++ b/common-web-notification-client/src/main/java/io/herd/common/web/notification/client/NotificationClient.java @@ -0,0 +1,49 @@ +package io.herd.common.web.notification.client; + +import io.openapi.client.ApiException; +import io.openapi.client.api.BroadcastControllerApi; +import io.openapi.client.api.model.BroadcastMessageObject; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@SuppressWarnings("unused") +public class NotificationClient { + + private final BroadcastControllerApi broadcastControllerApi; + + public NotificationClient(BroadcastControllerApi broadcastControllerApi) { + this.broadcastControllerApi = broadcastControllerApi; + } + + public void broadcastToAll(M message) { + try { + BroadcastMessageObject broadcastMessage = new BroadcastMessageObject(); + broadcastMessage.setPayload(message); + broadcastControllerApi.broadcast(broadcastMessage); + } catch (ApiException ex) { + log.error("It was not possible to broadcast message!", ex); + } + } + + public void broadcastToSpecificBroadcaster(String broadcasterId, M message) { + try { + BroadcastMessageObject broadcastMessage = new BroadcastMessageObject(); + broadcastMessage.setBroadcastId(broadcasterId); + broadcastMessage.setPayload(message); + broadcastControllerApi.broadcast(broadcastMessage); + } catch (ApiException ex) { + log.error("It was not possible to broadcast message!", ex); + } + } + + public void broadcastToSpecificResource(String resourceId, M message) { + try { + BroadcastMessageObject broadcastMessage = new BroadcastMessageObject(); + broadcastMessage.setResourceId(resourceId); + broadcastMessage.setPayload(message); + broadcastControllerApi.broadcast(broadcastMessage); + } catch (ApiException ex) { + log.error("It was not possible to broadcast message!", ex); + } + } +} diff --git a/common-web-notification-client/src/main/java/io/herd/common/web/notification/client/configuration/WebNotificationClientAutoConfiguration.java b/common-web-notification-client/src/main/java/io/herd/common/web/notification/client/configuration/WebNotificationClientAutoConfiguration.java new file mode 100644 index 0000000..79589de --- /dev/null +++ b/common-web-notification-client/src/main/java/io/herd/common/web/notification/client/configuration/WebNotificationClientAutoConfiguration.java @@ -0,0 +1,40 @@ +package io.herd.common.web.notification.client.configuration; + +import io.herd.common.web.client.configuration.OpenApiClientProperties; +import io.herd.common.web.notification.client.NotificationClient; +import io.openapi.client.ApiClient; +import io.openapi.client.api.BroadcastControllerApi; +import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.context.annotation.RequestScope; + +@Configuration(proxyBeanMethods = false) +@ComponentScan(basePackages = "io.herd.common.web.notification.client", + // Do not add the auto-configured classes, otherwise the auto-configuration will not work as expected. + excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) +) +@PropertySource("classpath:notification-openapi-client.properties") +public class WebNotificationClientAutoConfiguration { + + @Bean + @RequestScope + public ApiClient notificationOpenApiClient(OpenApiClientProperties notificationOpenApiClientProperties) { + ApiClient apiClient = io.openapi.client.Configuration.getDefaultApiClient(); + apiClient.setBasePath(notificationOpenApiClientProperties.getBasePath()); + return apiClient; + } + + @Bean + @Validated + @ConfigurationProperties("notification.openapi.client") + public OpenApiClientProperties notificationOpenApiClientProperties() { + return new OpenApiClientProperties(); + } + + @Bean + public NotificationClient notificationClient(ApiClient apiClient) { + return new NotificationClient(new BroadcastControllerApi(apiClient)); + } +} diff --git a/common-web-notification-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/common-web-notification-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..b3716b5 --- /dev/null +++ b/common-web-notification-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,15 @@ +{ + "properties": [ + { + "name": "notification.openapi.client.host", + "description": "It defines the host of the Notification server.", + "type": "java.lang.String", + "defaultValue": "http://localhost:9090" + }, + { + "name": "notification.openapi.client.base-path", + "description": "It defines the base path of the Notification server.", + "type": "java.lang.String" + } + ] +} diff --git a/common-web-notification-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common-web-notification-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..1d50a19 --- /dev/null +++ b/common-web-notification-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +io.herd.common.web.notification.client.configuration.WebNotificationClientAutoConfiguration diff --git a/common-web-notification-client/src/main/resources/notification-openapi-client.json b/common-web-notification-client/src/main/resources/notification-openapi-client.json new file mode 100644 index 0000000..9af370d --- /dev/null +++ b/common-web-notification-client/src/main/resources/notification-openapi-client.json @@ -0,0 +1,67 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "SpringDoc Sample Application API", + "description": "SpringDoc Sample Application", + "license": { + "name": "The MIT License", + "url": "https://opensource.org/license/mit/" + }, + "version": "v0.0.1" + }, + "externalDocs": { + "description": "SpringDoc Sample Application WiKi", + "url": "https://github.com/springdoc/wiki" + }, + "servers": [ + { + "url": "/api" + } + ], + "paths": { + "/api/v1/broadcast": { + "post": { + "tags": [ + "broadcast-controller" + ], + "operationId": "broadcast", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BroadcastMessageObject" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "BroadcastMessageObject": { + "required": [ + "payload" + ], + "type": "object", + "properties": { + "broadcastId": { + "type": "string" + }, + "resourceId": { + "type": "string" + }, + "payload": { + "type": "object" + } + } + } + } + } +} diff --git a/common-web-notification-client/src/main/resources/notification-openapi-client.properties b/common-web-notification-client/src/main/resources/notification-openapi-client.properties new file mode 100644 index 0000000..6acb7b5 --- /dev/null +++ b/common-web-notification-client/src/main/resources/notification-openapi-client.properties @@ -0,0 +1,5 @@ +# It defines the host of the Notification server. +notification.openapi.client.host=http://localhost:9090 + +# It defines the base path of the Notification server. +notification.openapi.client.base-path=${notification.openapi.client.host} diff --git a/common-web-notification/pom.xml b/common-web-notification/pom.xml index 402dadd..7d38002 100644 --- a/common-web-notification/pom.xml +++ b/common-web-notification/pom.xml @@ -23,10 +23,10 @@ 4.0.0 io.herd.common - common-parent + common-starter-web ${revision} - ../common-parent + ../common-starter-web common-web-notification diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/WebTempApplication.java b/common-web-notification/src/main/java/io/herd/common/web/notification/WebTempApplication.java new file mode 100644 index 0000000..ab9d81e --- /dev/null +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/WebTempApplication.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.web.notification; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +/** + * We defined as a temporary application to get the OpenAPI documentation, + * while building the common-web-notification-client. + */ +@SpringBootApplication +public class WebTempApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder(WebTempApplication.class).run(args); + } +} diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/controller/BroadcastController.java b/common-web-notification/src/main/java/io/herd/common/web/notification/controller/BroadcastController.java new file mode 100644 index 0000000..8657251 --- /dev/null +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/controller/BroadcastController.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.web.notification.controller; + +import io.herd.common.web.notification.domain.BroadcastMessage; +import io.herd.common.web.notification.service.NotificationService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/v1/broadcast") +public class BroadcastController { + + private final NotificationService notificationService; + + @Autowired + public BroadcastController(NotificationService notificationService) { + this.notificationService = notificationService; + } + + @PostMapping + public void broadcast(@RequestBody @Valid BroadcastMessage broadcastMessage) { + if (broadcastMessage.getResourceId() != null) { + notificationService.broadcastToSpecificResource( + broadcastMessage.getResourceId(), + broadcastMessage.getPayload() + ); + } else if (broadcastMessage.getBroadcastId() != null) { + notificationService.broadcastToSpecificBroadcaster( + broadcastMessage.getBroadcastId(), + broadcastMessage.getPayload() + ); + } else { + notificationService.broadcastToAll( + broadcastMessage.getPayload() + ); + } + } +} diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationController.java b/common-web-notification/src/main/java/io/herd/common/web/notification/controller/NotificationController.java similarity index 75% rename from common-web-notification/src/main/java/io/herd/common/web/notification/NotificationController.java rename to common-web-notification/src/main/java/io/herd/common/web/notification/controller/NotificationController.java index b3aeca6..8c72b33 100644 --- a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationController.java +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/controller/NotificationController.java @@ -16,8 +16,11 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.herd.common.web.notification; +package io.herd.common.web.notification.controller; +import io.herd.common.web.notification.domain.NotificationMessage; +import io.herd.common.web.notification.domain.NotificationMessageDecoder; +import io.herd.common.web.notification.domain.NotificationMessageEncoder; import jakarta.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -29,7 +32,7 @@ // Cannot be singleton! @Slf4j @Getter -@ManagedService(path = "/{notification}/{user}") +@ManagedService(path = "/{notificationPath}/{broadcaster}") public class NotificationController { /** @@ -40,9 +43,13 @@ public class NotificationController { @Inject private BroadcasterFactory broadcasterFactory; - @PathParam("user") + @PathParam("notificationPath") @SuppressWarnings("unused") - private String user; + private String notificationPath; + + @PathParam("broadcaster") + @SuppressWarnings("unused") + private String broadcaster; /** * Executed when the connection was fully established, in other words, ready to receive messages. @@ -57,7 +64,7 @@ public class NotificationController { */ @Ready public void onReady(AtmosphereResource resource) { - log.info("Client {} connected on Broadcast {}", resource.uuid(), resource.getBroadcaster().getID()); + log.info("Client '{}' connected on Broadcast '{}'", resource.uuid(), resource.getBroadcaster().getID()); } /** @@ -65,7 +72,7 @@ public void onReady(AtmosphereResource resource) { */ @Disconnect public void onDisconnect(AtmosphereResourceEvent event) { - log.info("Client {} disconnected [{}]", event.getResource().uuid(), + log.info("Client '{}' disconnected [{}]", event.getResource().uuid(), (event.isCancelled() ? "cancelled" : "closed") ); } @@ -74,15 +81,15 @@ public void onDisconnect(AtmosphereResourceEvent event) { * Necessary method for: 1) to receive the message sent by a client, * 2) to perform some type of treatment and 3) to resend it to all listeners. */ - @Message(encoders = NotificationJacksonEncoder.class, decoders = NotificationJacksonDecoder.class) - public Notification onMessage(AtmosphereResource resource, Notification notification) { + @Message(encoders = NotificationMessageEncoder.class, decoders = NotificationMessageDecoder.class) + public NotificationMessage onMessage(AtmosphereResource resource, NotificationMessage notification) { if (notification.getUuid() == null) { log.trace("Notification message '{}' sent to all clients of broadcast '{}'", - notification.getMessage(), resource.getBroadcaster().getID() + notification.getPayload(), resource.getBroadcaster().getID() ); } else { - log.trace("Notification message '{}' sent to client {} of broadcast '{}'", - notification.getMessage(), notification.getUuid(), resource.getBroadcaster().getID() + log.trace("Notification message '{}' sent to client '{}' of broadcast '{}'", + notification.getPayload(), notification.getUuid(), resource.getBroadcaster().getID() ); } return notification; diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/domain/BroadcastMessage.java b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/BroadcastMessage.java new file mode 100644 index 0000000..c76f53c --- /dev/null +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/BroadcastMessage.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.web.notification.domain; + +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * This class represents a message to be sent to the notification server first, + * before being broadcast to the clientes. + */ +@Getter +@Setter +@NoArgsConstructor +@ToString +public class BroadcastMessage { + + private String broadcastId; + private String resourceId; + + @NotNull + private M payload; + +} diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/Notification.java b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessage.java similarity index 87% rename from common-web-notification/src/main/java/io/herd/common/web/notification/Notification.java rename to common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessage.java index 1d6abeb..3933a2a 100644 --- a/common-web-notification/src/main/java/io/herd/common/web/notification/Notification.java +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessage.java @@ -16,18 +16,21 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.herd.common.web.notification; +package io.herd.common.web.notification.domain; import lombok.*; import java.io.Serial; import java.io.Serializable; +/** + * This class represents a message to be sent to the client. + */ @Getter @NoArgsConstructor @AllArgsConstructor @RequiredArgsConstructor -public class Notification implements Serializable { +public class NotificationMessage implements Serializable { @Serial private static final long serialVersionUID = 1L; @@ -38,6 +41,6 @@ public class Notification implements Serializable { private String uuid; @NonNull - private M message; + private M payload; } diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationJacksonDecoder.java b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessageDecoder.java similarity index 86% rename from common-web-notification/src/main/java/io/herd/common/web/notification/NotificationJacksonDecoder.java rename to common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessageDecoder.java index 6ae3b1d..63ddc64 100644 --- a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationJacksonDecoder.java +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessageDecoder.java @@ -16,7 +16,7 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.herd.common.web.notification; +package io.herd.common.web.notification.domain; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -25,14 +25,14 @@ import org.atmosphere.config.managed.Decoder; @Slf4j -public class NotificationJacksonDecoder implements Decoder> { +public class NotificationMessageDecoder implements Decoder> { private final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); @Override - public Notification decode(String string) { + public NotificationMessage decode(String string) { try { - return mapper.readValue(string, Notification.class); + return mapper.readValue(string, NotificationMessage.class); } catch (Exception ex) { String errorMsg = "Unable to deserialize JSON object: " + string; log.error(ex.getMessage(), ex); diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationJacksonEncoder.java b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessageEncoder.java similarity index 90% rename from common-web-notification/src/main/java/io/herd/common/web/notification/NotificationJacksonEncoder.java rename to common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessageEncoder.java index a7196d9..06eec43 100644 --- a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationJacksonEncoder.java +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/domain/NotificationMessageEncoder.java @@ -16,7 +16,7 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.herd.common.web.notification; +package io.herd.common.web.notification.domain; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -25,12 +25,12 @@ import org.atmosphere.config.managed.Encoder; @Slf4j -public class NotificationJacksonEncoder implements Encoder, String> { +public class NotificationMessageEncoder implements Encoder, String> { private final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); @Override - public String encode(Notification object) { + public String encode(NotificationMessage object) { try { return mapper.writeValueAsString(object); } catch (Exception ex) { diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/service/NotificationService.java b/common-web-notification/src/main/java/io/herd/common/web/notification/service/NotificationService.java new file mode 100644 index 0000000..e649914 --- /dev/null +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/service/NotificationService.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 - Felipe Desiderati + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.herd.common.web.notification.service; + +public interface NotificationService { + +

void broadcastToAll(P payload); + +

void broadcastToSpecificBroadcaster(String broadcasterId, P payload); + +

void broadcastToSpecificResource(String resourceId, P payload); + +} diff --git a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationService.java b/common-web-notification/src/main/java/io/herd/common/web/notification/service/NotificationServiceImpl.java similarity index 80% rename from common-web-notification/src/main/java/io/herd/common/web/notification/NotificationService.java rename to common-web-notification/src/main/java/io/herd/common/web/notification/service/NotificationServiceImpl.java index bcc7c96..f309808 100644 --- a/common-web-notification/src/main/java/io/herd/common/web/notification/NotificationService.java +++ b/common-web-notification/src/main/java/io/herd/common/web/notification/service/NotificationServiceImpl.java @@ -16,9 +16,10 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.herd.common.web.notification; +package io.herd.common.web.notification.service; import io.herd.common.web.UrlUtils; +import io.herd.common.web.notification.domain.NotificationMessage; import lombok.extern.slf4j.Slf4j; import org.atmosphere.cpr.AtmosphereResource; import org.atmosphere.cpr.AtmosphereResourceFactory; @@ -31,7 +32,7 @@ @Slf4j @Service -public class NotificationService { +public class NotificationServiceImpl implements NotificationService { @Value("${spring.web.atmosphere.url.mapping:/atmosphere}") private String atmosphereUrlMapping; @@ -41,7 +42,7 @@ public class NotificationService { private final AtmosphereResourceFactory atmosphereResourceFactory; @Autowired - public NotificationService( + public NotificationServiceImpl( @Lazy BroadcasterFactory atmosphereBroadcasterFactory, @Lazy AtmosphereResourceFactory atmosphereResourceFactory ) { @@ -49,24 +50,26 @@ public NotificationService( this.atmosphereResourceFactory = atmosphereResourceFactory; } - public void broadcast(M message) { + @Override + public

void broadcastToAll(P payload) { for (Broadcaster broadcaster : atmosphereBroadcasterFactory.lookupAll()) { - broadcaster.broadcast(new Notification<>(message)); + broadcaster.broadcast(new NotificationMessage<>(payload)); } } - public void broadcastToSpecificBroadcaster(String broadcasterId, M message) { + @Override + public

void broadcastToSpecificBroadcaster(String broadcasterId, P payload) { String prefix = UrlUtils.appendSlash(atmosphereUrlMapping); Broadcaster broadcaster = atmosphereBroadcasterFactory.lookup(prefix + broadcasterId); if (broadcaster == null) { log.warn("Atmosphere Broadcaster '{}' not found!", prefix + broadcasterId); return; } - - broadcaster.broadcast(new Notification<>(message)); + broadcaster.broadcast(new NotificationMessage<>(payload)); } - public void broadcastToSpecificResource(String resourceId, M message) { + @Override + public

void broadcastToSpecificResource(String resourceId, P payload) { AtmosphereResource atmosphereResource = atmosphereResourceFactory.find(resourceId); if (atmosphereResource == null) { log.warn("Atmosphere Resource '{}' not found!", resourceId); @@ -74,7 +77,7 @@ public void broadcastToSpecificResource(String resourceId, M message) { } for (Broadcaster broadcaster : atmosphereResource.broadcasters()) { - broadcaster.broadcast(new Notification<>(resourceId, message), atmosphereResource); + broadcaster.broadcast(new NotificationMessage<>(resourceId, payload), atmosphereResource); } } } diff --git a/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java b/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java index 2e533ed..57e5196 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java @@ -230,8 +230,8 @@ public void configureArgumentResolvers( } } - @Bean("webSecurityCorsProperties") @Validated + @Bean("webSecurityCorsProperties") @ConfigurationProperties("spring.web.security.jwt.authentication.cors") public CorsProperties webSecurityCorsProperties() { return new CorsProperties(); diff --git a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/DefaultJwtAuthenticationTokenConfigurer.java b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/DefaultJwtAuthenticationTokenConfigurer.java index c191ebf..ab1c907 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/DefaultJwtAuthenticationTokenConfigurer.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/DefaultJwtAuthenticationTokenConfigurer.java @@ -21,11 +21,11 @@ import io.herd.common.web.security.MultiTenantSupport; import io.herd.common.web.security.jwt.JwtService; import io.herd.common.web.security.jwt.JwtTokenConfigurer; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; -import jakarta.servlet.http.HttpServletRequest; import java.util.stream.Collectors; public class DefaultJwtAuthenticationTokenConfigurer implements JwtAuthenticationTokenConfigurer { diff --git a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/JwtAuthenticationTokenConfigurer.java b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/JwtAuthenticationTokenConfigurer.java index 2c875f3..62cf5a0 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/JwtAuthenticationTokenConfigurer.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authentication/JwtAuthenticationTokenConfigurer.java @@ -19,9 +19,8 @@ package io.herd.common.web.security.jwt.authentication; import io.herd.common.web.security.jwt.JwtTokenConfigurer; -import org.springframework.security.core.Authentication; - import jakarta.servlet.http.HttpServletRequest; +import org.springframework.security.core.Authentication; public interface JwtAuthenticationTokenConfigurer { diff --git a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/DefaultJwtTokenExtractor.java b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/DefaultJwtTokenExtractor.java index 4cb9277..3fc46b6 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/DefaultJwtTokenExtractor.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/DefaultJwtTokenExtractor.java @@ -51,6 +51,6 @@ public Authentication extract(Claims tokenPayload) { new UserWithMultiTenantSupport(username, "****", authorities, tenant) : new User(username, "****", authorities); - return new UsernamePasswordAuthenticationToken(principal,null, authorities); + return new UsernamePasswordAuthenticationToken(principal, null, authorities); } } diff --git a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/JwtAuthorizationService.java b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/JwtAuthorizationService.java index 107bc13..a22929c 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/JwtAuthorizationService.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/jwt/authorization/JwtAuthorizationService.java @@ -20,6 +20,7 @@ import io.herd.common.web.security.jwt.JwtService; import io.herd.common.web.security.jwt.JwtTokenExtractor; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -27,8 +28,6 @@ import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; -import jakarta.servlet.http.HttpServletRequest; - @Slf4j @Service @ConditionalOnProperty(name = "spring.web.security.jwt.authorization.enabled", havingValue = "true") diff --git a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestAuthorizationClientProperties.java b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestAuthorizationClientProperties.java index a4fe405..e5d5103 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestAuthorizationClientProperties.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestAuthorizationClientProperties.java @@ -30,8 +30,8 @@ @Getter @Setter // Never forget to put the setXXX (...) for configuration files! -@Component @Validated +@Component @ConfigurationProperties("spring.web.security.sign-request.authorization.client") public class SignRequestAuthorizationClientProperties { @@ -50,7 +50,6 @@ public interface SignValidation { * bean class io.herd.common.web.security.sign_request.authorization.SignRequestAuthorizationClientProperties * contains validation constraints but had not been annotated with @Validated." *

- * * This way, when we annotate a class with {@link Validated}, Spring Boot will create a Proxy for this class * and thus the validation will not work correctly if the validation annotations are not in the methods. */ diff --git a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestServletInputStream.java b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestServletInputStream.java index df73dad..5051a61 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestServletInputStream.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestServletInputStream.java @@ -20,6 +20,7 @@ import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; + import java.io.IOException; import java.io.InputStream; diff --git a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestSigner.java b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestSigner.java index 5b8f7fa..f649d22 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestSigner.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestSigner.java @@ -84,7 +84,9 @@ private String generateStringToSign(HttpServletRequest request) { request.getHeader(HEADER_DATE); } - /** Instead of signing the whole request content directly, first we need to generate the hash. */ + /** + * Instead of signing the whole request content directly, first we need to generate the hash. + */ @SneakyThrows({IOException.class, NoSuchAlgorithmException.class}) private String computeRequestBodyHash(InputStream fis) { MessageDigest digest = MessageDigest.getInstance("MD5"); diff --git a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestWrapper.java b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestWrapper.java index b25f3e3..40972cf 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestWrapper.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/sign_request/authorization/SignRequestWrapper.java @@ -18,11 +18,11 @@ */ package io.herd.common.web.security.sign_request.authorization; -import org.apache.commons.io.IOUtils; - import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; + import java.io.*; /** diff --git a/common-web-security/src/test/java/io/herd/common/web/security/sign_request/authorization/SignRequestSignerTest.java b/common-web-security/src/test/java/io/herd/common/web/security/sign_request/authorization/SignRequestSignerTest.java index c5bb47d..908dc9a 100644 --- a/common-web-security/src/test/java/io/herd/common/web/security/sign_request/authorization/SignRequestSignerTest.java +++ b/common-web-security/src/test/java/io/herd/common/web/security/sign_request/authorization/SignRequestSignerTest.java @@ -18,13 +18,13 @@ */ package io.herd.common.web.security.sign_request.authorization; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.DelegatingServletInputStream; -import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -39,8 +39,8 @@ void shouldSignStringWithSuccess() throws IOException { HttpServletRequest servletRequest = mock(HttpServletRequest.class); when(servletRequest.getMethod()).thenReturn("POST"); doReturn(new DelegatingServletInputStream( - IOUtils.toInputStream( "Testing Request Signature!", StandardCharsets.UTF_8))) - .when(servletRequest).getInputStream(); + IOUtils.toInputStream("Testing Request Signature!", StandardCharsets.UTF_8) + )).when(servletRequest).getInputStream(); when(servletRequest.getHeader(SignRequestSigner.HEADER_DATE)).thenReturn("Fri, 17 Jan 2020 18:30:00 GMT"); String secretKey = "!A%D*G-KaPdSgVkXp2s5v8y/B?E(H+MbQeThWmZq3t6w9z$C&F)J@NcRfUjXn2r5"; diff --git a/common-web/README.md b/common-web/README.md index 8c42bd5..88b5fe1 100644 --- a/common-web/README.md +++ b/common-web/README.md @@ -1,7 +1,7 @@ SpringFox Support --------------- -It will be necessary to copy the file `springdoc.properties` contained in the directory `/src/main/resources` -of this project, to your application's `/src/main/resources` directory, changing the values +It will be necessary to copy the file `springdoc.properties` contained in the directory `/src/main/resources` +of this project, to your application's `/src/main/resources` directory, changing the values of its properties according to the necessity. Only copy this properties file, in case there is a change in the properties. diff --git a/common-web/pom.xml b/common-web/pom.xml index 286e671..543abd6 100644 --- a/common-web/pom.xml +++ b/common-web/pom.xml @@ -35,12 +35,15 @@ io.herd.common common-data-jpa - io.herd.common common-google true + + io.herd.common + common-web-client + diff --git a/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java b/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java index 6dcad52..332a629 100644 --- a/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java +++ b/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java @@ -25,8 +25,8 @@ import io.herd.common.web.UrlUtils; import io.herd.common.web.configuration.async.AsyncWebConfiguration; import io.herd.common.web.graphql.PageableTypeDefinitionConnectionFactory; -import io.herd.common.web.rest.exception.ResponseExceptionDTOHttpMessageConverter; import io.herd.common.web.graphql.exception.GraphQLExceptionHandler; +import io.herd.common.web.rest.exception.ResponseExceptionDTOHttpMessageConverter; import jakarta.persistence.EntityManager; import jakarta.persistence.metamodel.Type; import lombok.extern.slf4j.Slf4j; @@ -235,7 +235,7 @@ public void configureRepositoryRestConfiguration( RepositoryDetectionStrategy.RepositoryDetectionStrategies.ANNOTATED ); - // Felipe Desiderati: Spring Data Rest should allow to define the complete path + // Felipe Desiderati: Spring Data Rest should allow defining the complete path // on annotation @RepositoryRestResource. // See: https://stackoverflow.com/questions/30396953/how-to-customize-spring-data-rest-to-use-a-multi-segment-path-for-a-repository-r // Configures the Base Path. It can be redefined using property: spring.data.rest.base-path diff --git a/common-web/src/main/java/io/herd/common/web/rest/CaptchaRest.kt b/common-web/src/main/java/io/herd/common/web/rest/CaptchaController.kt similarity index 92% rename from common-web/src/main/java/io/herd/common/web/rest/CaptchaRest.kt rename to common-web/src/main/java/io/herd/common/web/rest/CaptchaController.kt index 38d3b5f..4fbc284 100644 --- a/common-web/src/main/java/io/herd/common/web/rest/CaptchaRest.kt +++ b/common-web/src/main/java/io/herd/common/web/rest/CaptchaController.kt @@ -12,13 +12,13 @@ import org.springframework.web.bind.annotation.RestController @RestController @ConditionalOnProperty("google.captcha.secret-key") @Suppress("SpringJavaInjectionPointsAutowiringInspection") -class CaptchaRest( +class CaptchaController( private val objectMapper: ObjectMapper, private val googleCaptchaService: GoogleCaptchaService, -) : PublicRest { +) : PublicController { @PostMapping - @RequestMapping("/captcha") + @RequestMapping("/v1/captcha") fun validateCaptcha(@RequestBody payload: String): Boolean { val payloadNode: JsonNode = objectMapper.readTree(payload) val captchaResponse = payloadNode.get("captchaResponse").asText() diff --git a/common-web/src/main/java/io/herd/common/web/rest/PublicRest.kt b/common-web/src/main/java/io/herd/common/web/rest/PublicController.kt similarity index 82% rename from common-web/src/main/java/io/herd/common/web/rest/PublicRest.kt rename to common-web/src/main/java/io/herd/common/web/rest/PublicController.kt index 80b91ae..0b53d5c 100644 --- a/common-web/src/main/java/io/herd/common/web/rest/PublicRest.kt +++ b/common-web/src/main/java/io/herd/common/web/rest/PublicController.kt @@ -3,4 +3,4 @@ package io.herd.common.web.rest import org.springframework.web.bind.annotation.RequestMapping @RequestMapping("/public") -interface PublicRest +interface PublicController diff --git a/common-web/src/main/java/io/herd/common/web/rest/exception/ExceptionHandlingController.java b/common-web/src/main/java/io/herd/common/web/rest/exception/ExceptionHandlingController.java index ab46d51..a8efcae 100644 --- a/common-web/src/main/java/io/herd/common/web/rest/exception/ExceptionHandlingController.java +++ b/common-web/src/main/java/io/herd/common/web/rest/exception/ExceptionHandlingController.java @@ -399,7 +399,7 @@ public ResponseEntity handleJavaxConstraintViolationExcept } /** - * Thrown when used the @{@link jakarta.validation.Valid} annotation. + * Thrown when used the @{@link jakarta.validation.Valid} annotation. */ @NotNull @Override diff --git a/common-web/src/main/java/io/herd/common/web/rest/exception/ResponseExceptionDTOHttpMessageConverter.java b/common-web/src/main/java/io/herd/common/web/rest/exception/ResponseExceptionDTOHttpMessageConverter.java index 87a7b67..b5ff0b4 100644 --- a/common-web/src/main/java/io/herd/common/web/rest/exception/ResponseExceptionDTOHttpMessageConverter.java +++ b/common-web/src/main/java/io/herd/common/web/rest/exception/ResponseExceptionDTOHttpMessageConverter.java @@ -50,8 +50,11 @@ protected boolean supports(@NotNull Class clazz) { } @Override - protected void addDefaultHeaders(HttpHeaders headers, @NotNull ResponseExceptionDTO responseExceptionDTO, - MediaType contentType) throws IOException { + protected void addDefaultHeaders( + HttpHeaders headers, + @NotNull ResponseExceptionDTO responseExceptionDTO, + MediaType contentType + ) throws IOException { // It doesn't matter what happened, if there's an error always return as application/json. headers.setContentType(MediaType.APPLICATION_JSON); @@ -60,14 +63,20 @@ protected void addDefaultHeaders(HttpHeaders headers, @NotNull ResponseException @NotNull @Override - protected ResponseExceptionDTO readInternal(@NotNull Class clazz, - @NotNull HttpInputMessage inputMessage) throws IOException { + protected ResponseExceptionDTO readInternal( + @NotNull Class clazz, + @NotNull HttpInputMessage inputMessage + ) throws IOException { + return (ResponseExceptionDTO) httpMessageConverter.read(clazz, inputMessage); } @Override - protected void writeInternal(@NotNull ResponseExceptionDTO responseExceptionDTO, - @NotNull HttpOutputMessage outputMessage) throws IOException { + protected void writeInternal( + @NotNull ResponseExceptionDTO responseExceptionDTO, + @NotNull HttpOutputMessage outputMessage + ) throws IOException { + httpMessageConverter.write(responseExceptionDTO, MediaType.APPLICATION_JSON, outputMessage); } } diff --git a/common-web/src/main/java/io/herd/common/web/rest/exception/RestApiException.java b/common-web/src/main/java/io/herd/common/web/rest/exception/RestApiException.java index a1dd4a3..a2ba3c7 100644 --- a/common-web/src/main/java/io/herd/common/web/rest/exception/RestApiException.java +++ b/common-web/src/main/java/io/herd/common/web/rest/exception/RestApiException.java @@ -38,7 +38,7 @@ public RestApiException(String message) { super(message); } - public RestApiException(String message, Serializable...args) { + public RestApiException(String message, Serializable... args) { super(message); this.args = args; } @@ -47,7 +47,7 @@ public RestApiException(String message, Throwable cause) { super(message, cause); } - public RestApiException(String message, Throwable cause, Serializable...args) { + public RestApiException(String message, Throwable cause, Serializable... args) { super(message, cause); this.args = args; } diff --git a/common-web/src/main/resources/openapi-client.properties.example b/common-web/src/main/resources/openapi-client.properties.example index 1ef93c2..b184d7e 100644 --- a/common-web/src/main/resources/openapi-client.properties.example +++ b/common-web/src/main/resources/openapi-client.properties.example @@ -1,5 +1,4 @@ # suppress inspection "UnusedProperty" for whole file - openapi.client.host=http://localhost:9090 openapi.client.base-path=${openapi.client.host} openapi.client.auth-user= diff --git a/common-web/src/main/resources/springdoc.properties b/common-web/src/main/resources/springdoc.properties index a0089f5..80a3300 100644 --- a/common-web/src/main/resources/springdoc.properties +++ b/common-web/src/main/resources/springdoc.properties @@ -1,5 +1,4 @@ # suppress inspection "UnusedProperty" for whole file - springdoc.api-info.title=SpringDoc Sample Application API springdoc.api-info.description=SpringDoc Sample Application springdoc.api-info.version=v0.0.1 diff --git a/common/README.md b/common/README.md index e511fde..032faaf 100644 --- a/common/README.md +++ b/common/README.md @@ -32,12 +32,12 @@ Internationalization Support By default, the files: `i18n/exceptions`, `i18n/http-exceptions`, `i18n/default-validation-messages` and `i18n/validation-messages` are already loaded within the application context when presented in classpath. -Do not forget that these files have their respective location suffixes. -Eg: `i18n/exceptions_en_US.properties` or `i18n/exceptions_pt_BR.properties`. +Remember that these files have their respective location suffixes. +E.g.: `i18n/exceptions_en_US.properties` or `i18n/exceptions_pt_BR.properties`. > **Note**: > ONLY reconfigure the `i18n/exceptions` and `i18n/validation-messages` files to add support for -internationalization in your application, as shown below: +> internationalization in your application, as shown below: #### i18n/exceptions_en_US.properties @@ -55,12 +55,12 @@ track.duration.minvalue=Duration can not be less than {value} seconds. ``` > **Note**: -> IF YOU DO NOT WISH TO USE THE INTERNATIONALIZATION FILES STANDARD NAMES, -it will be necessary to copy the file `i18n.properties` contained in the directory `/src/main/resources` -of this project, to your application's `/src/main/resources` directory, setting the` i18n.files` property -with the internationalization filenames separated by commas to be loaded within the application context. -Use this feature only for internationalized properties. For other cases, use the default Spring behavior, -as shown below: +> IF YOU DO NOT WISH TO USE THE INTERNATIONALIZATION FILES STANDARD NAMES, +> it will be necessary to copy the file `i18n.properties` contained in the directory `/src/main/resources` +> of this project, to your application's `/src/main/resources` directory, setting the` i18n.files` property +> with the internationalization filenames separated by commas to be loaded within the application context. +> Use this feature only for internationalized properties. For other cases, use the default Spring behavior, +> as shown below: ``` @Component @@ -90,7 +90,7 @@ with the following name: `templates/text/{filename}.txt`. If an HTML template is used, create a file inside the `/src/main/resources` folder of your application, with the following name: `templates/html/{filename}.html`. -In the class responsible for obtaining the content generated by the Thymeleaf Framework, add a dependency +In the class responsible for obtaining the content generated by the Thymeleaf Framework, add a dependency with the `TemplateEngine` class. Use the code below as a reference: ``` diff --git a/common/src/main/java/io/herd/common/exception/ApplicationException.java b/common/src/main/java/io/herd/common/exception/ApplicationException.java index f06ad9a..f1c9d24 100644 --- a/common/src/main/java/io/herd/common/exception/ApplicationException.java +++ b/common/src/main/java/io/herd/common/exception/ApplicationException.java @@ -37,7 +37,7 @@ public ApplicationException(String message) { super(message); } - public ApplicationException(String message, Serializable...args) { + public ApplicationException(String message, Serializable... args) { super(message); this.args = args; } @@ -46,7 +46,7 @@ public ApplicationException(String message, Throwable cause) { super(message, cause); } - public ApplicationException(String message, Throwable cause, Serializable...args) { + public ApplicationException(String message, Throwable cause, Serializable... args) { super(message, cause); this.args = args; } diff --git a/common/src/main/java/io/herd/common/mapper/Default.kt b/common/src/main/java/io/herd/common/mapper/Default.kt index dbe8a6d..09c34fd 100644 --- a/common/src/main/java/io/herd/common/mapper/Default.kt +++ b/common/src/main/java/io/herd/common/mapper/Default.kt @@ -17,6 +17,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @file:Suppress("unused") + package io.herd.common.mapper /** diff --git a/common/src/main/java/io/herd/common/validation/AbstractCpfCpnpjValidator.java b/common/src/main/java/io/herd/common/validation/AbstractCpfCnpjValidator.java similarity index 95% rename from common/src/main/java/io/herd/common/validation/AbstractCpfCpnpjValidator.java rename to common/src/main/java/io/herd/common/validation/AbstractCpfCnpjValidator.java index efd2f1c..b244269 100644 --- a/common/src/main/java/io/herd/common/validation/AbstractCpfCpnpjValidator.java +++ b/common/src/main/java/io/herd/common/validation/AbstractCpfCnpjValidator.java @@ -26,7 +26,7 @@ import java.lang.annotation.Annotation; import java.util.regex.Pattern; -public class AbstractCpfCpnpjValidator implements ConstraintValidator { +public class AbstractCpfCnpjValidator implements ConstraintValidator { private static final Pattern DIGITS_ONLY = Pattern.compile("\\d+"); diff --git a/common/src/main/java/io/herd/common/validation/CnpjValidator.java b/common/src/main/java/io/herd/common/validation/CnpjValidator.java index 57efb1c..42ee0f5 100644 --- a/common/src/main/java/io/herd/common/validation/CnpjValidator.java +++ b/common/src/main/java/io/herd/common/validation/CnpjValidator.java @@ -27,7 +27,7 @@ * * @see org.hibernate.validator.internal.constraintvalidators.hv.br.CNPJValidator */ -public class CnpjValidator extends AbstractCpfCpnpjValidator { +public class CnpjValidator extends AbstractCpfCnpjValidator { @Override public void initialize(Cnpj constraintAnnotation) { diff --git a/common/src/main/java/io/herd/common/validation/CpfValidator.java b/common/src/main/java/io/herd/common/validation/CpfValidator.java index e65ad97..e81da1b 100644 --- a/common/src/main/java/io/herd/common/validation/CpfValidator.java +++ b/common/src/main/java/io/herd/common/validation/CpfValidator.java @@ -27,7 +27,7 @@ * * @see org.hibernate.validator.internal.constraintvalidators.hv.br.CPFValidator */ -public class CpfValidator extends AbstractCpfCpnpjValidator { +public class CpfValidator extends AbstractCpfCnpjValidator { @Override public void initialize(Cpf constraintAnnotation) { diff --git a/pom.xml b/pom.xml index 729f9df..70e05ae 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,9 @@ ./common-parent-openapi-client ./common-scanner ./common-web + ./common-web-client ./common-web-notification + ./common-web-notification-client ./common-web-security ./common-starter ./common-starter-openapi-client