mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
Compare commits
164 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81a4b36c08 | ||
|
|
bf154cf83d | ||
|
|
0d1234ca4b | ||
|
|
a1c42f2709 | ||
|
|
7608921684 | ||
|
|
24f2b17416 | ||
|
|
33eb469520 | ||
|
|
90eef904f9 | ||
|
|
d1f72ee53a | ||
|
|
e0e212dfc4 | ||
|
|
ef0a03cb3b | ||
|
|
221eeddab8 | ||
|
|
1076527b62 | ||
|
|
1e13c11061 | ||
|
|
440922380d | ||
|
|
969a199a8e | ||
|
|
21b0176a49 | ||
|
|
06a996cd81 | ||
|
|
e1fc33626e | ||
|
|
b331626e8f | ||
|
|
95d4f725f9 | ||
|
|
45b54a75db | ||
|
|
e000bb05c4 | ||
|
|
d66ca05dca | ||
|
|
321260b0a5 | ||
|
|
5ef8fd18ca | ||
|
|
5b5d5cca1c | ||
|
|
51ac16a9e1 | ||
|
|
cf185c3877 | ||
|
|
71368fba62 | ||
|
|
6bae50a56a | ||
|
|
27681603cd | ||
|
|
e1be05711b | ||
|
|
7b1bb9072e | ||
|
|
a58b0a0806 | ||
|
|
e26950671c | ||
|
|
0d730128f7 | ||
|
|
174664619b | ||
|
|
f8738f10af | ||
|
|
677fb87f71 | ||
|
|
c980e5dd67 | ||
|
|
474995c8dd | ||
|
|
74ee810757 | ||
|
|
c5b56b47ae | ||
|
|
dccaca4972 | ||
|
|
92f53a0034 | ||
|
|
15eb00b1ba | ||
|
|
041b5ad2c0 | ||
|
|
4520ef4078 | ||
|
|
701a1903ba | ||
|
|
ff7458dfc1 | ||
|
|
a72e08c0c6 | ||
|
|
2bff335698 | ||
|
|
b8a256ac7d | ||
|
|
2168c0039a | ||
|
|
d225884ec3 | ||
|
|
9934c4a169 | ||
|
|
9e75f23d8f | ||
|
|
f36471bbf3 | ||
|
|
4664bef4d8 | ||
|
|
71403d4174 | ||
|
|
cb1b99815c | ||
|
|
5668efc8a8 | ||
|
|
d3223ec8b4 | ||
|
|
bfbe39993f | ||
|
|
e90747fd08 | ||
|
|
8926f9784d | ||
|
|
0ff1d58dfb | ||
|
|
8df587aaad | ||
|
|
10fdffc378 | ||
|
|
a7d7335970 | ||
|
|
bb5244c118 | ||
|
|
f20a5e92e2 | ||
|
|
5ce0428b15 | ||
|
|
a43e738365 | ||
|
|
f4a4eab32d | ||
|
|
7ffc58892a | ||
|
|
8f85637bb8 | ||
|
|
a37925396a | ||
|
|
b66749264a | ||
|
|
6dcf2aabd1 | ||
|
|
71bb33d710 | ||
|
|
365c235e1f | ||
|
|
da65e85081 | ||
|
|
7497b88c26 | ||
|
|
5d5c955451 | ||
|
|
c17cc5bd1c | ||
|
|
54e5621267 | ||
|
|
13534b5f44 | ||
|
|
43a0c7be81 | ||
|
|
8ec9705dd6 | ||
|
|
a82e6f3402 | ||
|
|
704081656e | ||
|
|
201a3ae96f | ||
|
|
2d54ec9efb | ||
|
|
c1ac273749 | ||
|
|
9bc5fdf02f | ||
|
|
8d340e0f52 | ||
|
|
8628ac9e9a | ||
|
|
737e24e7dc | ||
|
|
f5943889ec | ||
|
|
61bdd484d3 | ||
|
|
0ed901ffb6 | ||
|
|
5fe5b97130 | ||
|
|
ef79cf1748 | ||
|
|
0f00161b93 | ||
|
|
0809021c25 | ||
|
|
ad28b26e72 | ||
|
|
7827cf49d6 | ||
|
|
8ed58a8aa5 | ||
|
|
068bb1a0d8 | ||
|
|
4f1b458458 | ||
|
|
7ad9c24879 | ||
|
|
e3e476555a | ||
|
|
223c2f464e | ||
|
|
6d396e1982 | ||
|
|
60bf96411c | ||
|
|
3dd4f140e2 | ||
|
|
1131d70645 | ||
|
|
4b080510e7 | ||
|
|
37437877e1 | ||
|
|
da94880c53 | ||
|
|
68ad6d8b55 | ||
|
|
080c0b48d0 | ||
|
|
e8bfecc07d | ||
|
|
8e43a7fa00 | ||
|
|
fa45d1bfad | ||
|
|
9cdc364fde | ||
|
|
6f29af1710 | ||
|
|
e77787e2cd | ||
|
|
84159a3a2d | ||
|
|
68e531ed0c | ||
|
|
72bdf2573c | ||
|
|
00159ce1c5 | ||
|
|
d293e972f2 | ||
|
|
c618e22c52 | ||
|
|
73f2871235 | ||
|
|
bdb30a60c3 | ||
|
|
7da630ed6d | ||
|
|
8845c54d0c | ||
|
|
02f1090fe7 | ||
|
|
9bac3f424f | ||
|
|
db2264023f | ||
|
|
ec8eb4bd1f | ||
|
|
ed5596636a | ||
|
|
dd0fdfc89e | ||
|
|
d212e96664 | ||
|
|
1a720e6a29 | ||
|
|
7316a6e07d | ||
|
|
84a75db464 | ||
|
|
dde3d8e405 | ||
|
|
9e0a39981f | ||
|
|
dab9f53743 | ||
|
|
645164997d | ||
|
|
c2b53b117c | ||
|
|
6e52f60e85 | ||
|
|
1498e24037 | ||
|
|
fdacac74cc | ||
|
|
3defd982e7 | ||
|
|
f4eb9e2a09 | ||
|
|
08693e16f0 | ||
|
|
d95e1522d8 | ||
|
|
150920e0c8 | ||
|
|
074ecbf159 |
11
README.md
11
README.md
@@ -6,15 +6,12 @@ Google Reader inspired self-hosted RSS reader, based on JAX-RS, Wicket and Angul
|
||||
|
||||
Deploy on your own server (using TomEE, a lightweight JavaEE6 container based on Tomcat) or even in the cloud for free on OpenShift.
|
||||
|
||||
[Android app](https://github.com/doomrobo/CommaFeed-Android-Reader)
|
||||
Related open-source projects
|
||||
----------------------------
|
||||
|
||||
[Chrome extension](https://github.com/Athou/commafeed-chrome)
|
||||
Android apps: [News+ extension](https://github.com/Athou/commafeed-newsplus) - [Android app](https://github.com/doomrobo/CommaFeed-Android-Reader)
|
||||
|
||||
[Firefox extension](https://github.com/Athou/commafeed-firefox)
|
||||
|
||||
[Opera extension](https://github.com/Athou/commafeed-opera)
|
||||
|
||||
[Safari extension](https://github.com/Athou/commafeed-safari)
|
||||
Browser extensions: [Chrome](https://github.com/Athou/commafeed-chrome) - [Firefox](https://github.com/Athou/commafeed-firefox) - [Opera](https://github.com/Athou/commafeed-opera) - [Safari](https://github.com/Athou/commafeed-safari)
|
||||
|
||||
Deployment on OpenShift
|
||||
-----------------------
|
||||
|
||||
283
dev/EclipseCodeFormatter.xml
Normal file
283
dev/EclipseCodeFormatter.xml
Normal file
@@ -0,0 +1,283 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="12">
|
||||
<profile kind="CodeFormatterProfile" name="Eclipse [built-in] 140 chars" version="12">
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.source" value="1.5"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.5"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.5"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
267
dev/EclipseJavascriptCodeFormatter.xml
Normal file
267
dev/EclipseJavascriptCodeFormatter.xml
Normal file
@@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="11">
|
||||
<profile kind="CodeFormatterProfile" name="Eclipse [built-in] 140 chars" version="11">
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_compact_if" value="52"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_block_comments" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_object_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_between_type_declarations" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_default" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.continuation_indentation_for_objlit_initializer" value="1"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.compiler.compliance" value="1.5"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_closing_brace_in_objlit_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_after_package" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_comma_in_objlit_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_objlit_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.compiler.source" value="1.5"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_conditional_expression" value="48"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_after_imports" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="64"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_member_type" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_enum_constants" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_imports" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.lineSplit" value="140"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_method" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_superclass_in_type_declaration" value="64"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.compiler.codegen.targetPlatform" value="1.5"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_empty_objlit_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="64"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_object_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.line_length" value="80"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_new_chunk" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_opening_brace_in_objlit_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
101
pom.xml
101
pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.2.0</version>
|
||||
<packaging>war</packaging>
|
||||
<name>CommaFeed</name>
|
||||
|
||||
@@ -46,9 +46,8 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<compilerArgument>-proc:none</compilerArgument>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -65,6 +64,13 @@
|
||||
<include>**/beans.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>target/generated-sources/api-docs/</directory>
|
||||
<targetPath>api/api-docs</targetPath>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -138,12 +144,25 @@
|
||||
<outputDirectory>target/generated-sources/metamodel</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>doc</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<processors>
|
||||
<processor>com.commafeed.frontend.APIGenerator</processor>
|
||||
</processors>
|
||||
<outputDirectory>target/generated-sources/api-docs</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
<version>1.2.0.Final</version>
|
||||
<version>1.3.0.Final</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
@@ -167,6 +186,12 @@
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>0.12.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec</groupId>
|
||||
<artifactId>jboss-javaee-6.0</artifactId>
|
||||
@@ -237,16 +262,6 @@
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.lambdaj</groupId>
|
||||
<artifactId>lambdaj</artifactId>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.java.dev.rome</groupId>
|
||||
@@ -270,11 +285,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.uci.ics</groupId>
|
||||
<artifactId>crawler4j</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jdom</groupId>
|
||||
<artifactId>jdom</artifactId>
|
||||
@@ -296,17 +306,6 @@
|
||||
<version>0.9.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client-servlet</artifactId>
|
||||
<version>1.14.1-beta</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.http-client</groupId>
|
||||
<artifactId>google-http-client-jackson2</artifactId>
|
||||
<version>1.14.1-beta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
@@ -319,9 +318,9 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||
<version>2.1.4</version>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -337,23 +336,23 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-core</artifactId>
|
||||
<version>6.8.0</version>
|
||||
<version>6.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-auth-roles</artifactId>
|
||||
<version>6.8.0</version>
|
||||
<version>6.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-extensions</artifactId>
|
||||
<version>6.8.0</version>
|
||||
<version>6.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-cdi</artifactId>
|
||||
<version>6.8.0</version>
|
||||
<version>6.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ro.isdc.wro4j</groupId>
|
||||
@@ -361,28 +360,16 @@
|
||||
<version>1.6.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.wordnik</groupId>
|
||||
<artifactId>swagger-annotations_2.9.1</artifactId>
|
||||
<version>1.2.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.wordnik</groupId>
|
||||
<artifactId>swagger-jaxrs_2.9.1</artifactId>
|
||||
<version>1.2.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jersey-servlet</artifactId>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jersey-client</artifactId>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jersey-core</artifactId>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -518,7 +505,7 @@
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>execute</goal>
|
||||
</goals>
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
package com.commafeed.backend;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
|
||||
/**
|
||||
* Contains utility methods for cleaning the database
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class DatabaseCleaner {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(DatabaseCleaner.class);
|
||||
|
||||
@Inject
|
||||
FeedDAO feedDAO;
|
||||
|
||||
@@ -29,6 +34,12 @@ public class DatabaseCleaner {
|
||||
@Inject
|
||||
FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryContentDAO feedEntryContentDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryStatusDAO feedEntryStatusDAO;
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@@ -45,16 +56,16 @@ public class DatabaseCleaner {
|
||||
return total;
|
||||
}
|
||||
|
||||
public long cleanEntriesWithoutFeeds() {
|
||||
public long cleanContentsWithoutEntries() {
|
||||
|
||||
long total = 0;
|
||||
int deleted = -1;
|
||||
do {
|
||||
deleted = feedEntryDAO.deleteWithoutFeeds(100);
|
||||
deleted = feedEntryContentDAO.deleteWithoutEntries(10);
|
||||
total += deleted;
|
||||
log.info("removed {} entries without feeds", total);
|
||||
log.info("removed {} feeds without subscriptions", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} entries without feeds deleted", total);
|
||||
log.info("cleanup done: {} feeds without subscriptions deleted", total);
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -83,9 +94,14 @@ public class DatabaseCleaner {
|
||||
sub.setFeed(into);
|
||||
}
|
||||
feedSubscriptionDAO.saveOrUpdate(subs);
|
||||
feedDAO.deleteRelationships(feed);
|
||||
feedDAO.delete(feed);
|
||||
}
|
||||
feedDAO.saveOrUpdate(into);
|
||||
}
|
||||
|
||||
public void cleanStatusesOlderThan(Date olderThan) {
|
||||
log.info("cleaning old read statuses");
|
||||
int deleted = feedEntryStatusDAO.deleteOldStatuses(olderThan);
|
||||
log.info("cleaned {} read statuses", deleted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ import liquibase.structure.DatabaseObject;
|
||||
|
||||
import com.commafeed.backend.services.ApplicationPropertiesService;
|
||||
|
||||
/**
|
||||
* Executes needed liquibase database schema upgrades
|
||||
*
|
||||
*/
|
||||
@Stateless
|
||||
@TransactionManagement(TransactionManagementType.BEAN)
|
||||
public class DatabaseUpdater {
|
||||
@@ -33,18 +37,14 @@ public class DatabaseUpdater {
|
||||
try {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader classLoader = currentThread.getContextClassLoader();
|
||||
ResourceAccessor accessor = new ClassLoaderResourceAccessor(
|
||||
classLoader);
|
||||
ResourceAccessor accessor = new ClassLoaderResourceAccessor(classLoader);
|
||||
|
||||
context = new InitialContext();
|
||||
DataSource dataSource = (DataSource) context
|
||||
.lookup(datasourceName);
|
||||
DataSource dataSource = (DataSource) context.lookup(datasourceName);
|
||||
connection = dataSource.getConnection();
|
||||
JdbcConnection jdbcConnection = new JdbcConnection(connection);
|
||||
|
||||
Database database = DatabaseFactory.getInstance()
|
||||
.findCorrectDatabaseImplementation(
|
||||
jdbcConnection);
|
||||
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcConnection);
|
||||
|
||||
if (database instanceof PostgresDatabase) {
|
||||
database = new PostgresDatabase() {
|
||||
@@ -56,9 +56,7 @@ public class DatabaseUpdater {
|
||||
database.setConnection(jdbcConnection);
|
||||
}
|
||||
|
||||
Liquibase liq = new Liquibase(
|
||||
"changelogs/db.changelog-master.xml", accessor,
|
||||
database);
|
||||
Liquibase liq = new Liquibase("changelogs/db.changelog-master.xml", accessor, database);
|
||||
liq.update("prod");
|
||||
} finally {
|
||||
if (context != null) {
|
||||
|
||||
@@ -1,62 +1,49 @@
|
||||
package com.commafeed.backend;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
/**
|
||||
* List wrapper that sorts its elements in the order provided by given comparator and ensure a maximum capacity.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class FixedSizeSortedSet<E> {
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class FixedSizeSortedSet<E> extends TreeSet<E> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private List<E> inner;
|
||||
|
||||
private final Comparator<? super E> comparator;
|
||||
private final int maxSize;
|
||||
private final int capacity;
|
||||
|
||||
public FixedSizeSortedSet(int maxSize, Comparator<? super E> comparator) {
|
||||
super(comparator);
|
||||
this.maxSize = maxSize;
|
||||
public FixedSizeSortedSet(int capacity, Comparator<? super E> comparator) {
|
||||
this.inner = new ArrayList<E>(Math.max(0, capacity));
|
||||
this.capacity = capacity < 0 ? Integer.MAX_VALUE : capacity;
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
public void add(E e) {
|
||||
int position = Math.abs(Collections.binarySearch(inner, e, comparator) + 1);
|
||||
if (isFull()) {
|
||||
E last = last();
|
||||
int comparison = comparator.compare(e, last);
|
||||
if (comparison < 0) {
|
||||
remove(last);
|
||||
return super.add(e);
|
||||
} else {
|
||||
return false;
|
||||
if (position < inner.size()) {
|
||||
inner.remove(inner.size() - 1);
|
||||
inner.add(position, e);
|
||||
}
|
||||
} else {
|
||||
return super.add(e);
|
||||
inner.add(position, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> c) {
|
||||
if (CollectionUtils.isEmpty(c)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
for (E e : c) {
|
||||
success &= add(e);
|
||||
}
|
||||
return success;
|
||||
public E last() {
|
||||
return inner.get(inner.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
public boolean isFull() {
|
||||
return size() == maxSize;
|
||||
return inner.size() == capacity;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<E> asList() {
|
||||
return (List<E>) Lists.newArrayList(toArray());
|
||||
return inner;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
@@ -38,13 +40,14 @@ import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.apache.http.params.HttpProtocolParams;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Smart HTTP getter: handles gzip, ssl, last modified and etag headers
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpGetter {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(HttpGetter.class);
|
||||
|
||||
private static final String USER_AGENT = "CommaFeed/1.0 (http://www.commafeed.com)";
|
||||
private static final String ACCEPT_LANGUAGE = "en";
|
||||
private static final String PRAGMA_NO_CACHE = "No-cache";
|
||||
@@ -56,9 +59,7 @@ public class HttpGetter {
|
||||
static {
|
||||
try {
|
||||
SSL_CONTEXT = SSLContext.getInstance("TLS");
|
||||
SSL_CONTEXT.init(new KeyManager[0],
|
||||
new TrustManager[] { new DefaultTrustManager() },
|
||||
new SecureRandom());
|
||||
SSL_CONTEXT.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() }, new SecureRandom());
|
||||
} catch (Exception e) {
|
||||
log.error("Could not configure ssl context");
|
||||
}
|
||||
@@ -66,8 +67,7 @@ public class HttpGetter {
|
||||
|
||||
private static final X509HostnameVerifier VERIFIER = new DefaultHostnameVerifier();
|
||||
|
||||
public HttpResult getBinary(String url, int timeout) throws ClientProtocolException,
|
||||
IOException, NotModifiedException {
|
||||
public HttpResult getBinary(String url, int timeout) throws ClientProtocolException, IOException, NotModifiedException {
|
||||
return getBinary(url, null, null, timeout);
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ public class HttpGetter {
|
||||
* @throws NotModifiedException
|
||||
* if the url hasn't changed since we asked for it last time
|
||||
*/
|
||||
public HttpResult getBinary(String url, String lastModified, String eTag, int timeout)
|
||||
throws ClientProtocolException, IOException, NotModifiedException {
|
||||
public HttpResult getBinary(String url, String lastModified, String eTag, int timeout) throws ClientProtocolException, IOException,
|
||||
NotModifiedException {
|
||||
HttpResult result = null;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
@@ -110,27 +110,23 @@ public class HttpGetter {
|
||||
response = client.execute(httpget);
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
if (code == HttpStatus.SC_NOT_MODIFIED) {
|
||||
throw new NotModifiedException("304 http code");
|
||||
throw new NotModifiedException("'304 - not modified' http code received");
|
||||
} else if (code >= 300) {
|
||||
throw new HttpResponseException(code,
|
||||
"Server returned HTTP error code " + code);
|
||||
throw new HttpResponseException(code, "Server returned HTTP error code " + code);
|
||||
}
|
||||
|
||||
} catch (HttpResponseException e) {
|
||||
if (e.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
|
||||
throw new NotModifiedException("304 http code");
|
||||
throw new NotModifiedException("'304 - not modified' http code received");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
Header lastModifiedHeader = response
|
||||
.getFirstHeader(HttpHeaders.LAST_MODIFIED);
|
||||
Header lastModifiedHeader = response.getFirstHeader(HttpHeaders.LAST_MODIFIED);
|
||||
Header eTagHeader = response.getFirstHeader(HttpHeaders.ETAG);
|
||||
|
||||
String lastModifiedResponse = lastModifiedHeader == null ? null
|
||||
: StringUtils.trimToNull(lastModifiedHeader.getValue());
|
||||
if (lastModified != null
|
||||
&& StringUtils.equals(lastModified, lastModifiedResponse)) {
|
||||
String lastModifiedResponse = lastModifiedHeader == null ? null : StringUtils.trimToNull(lastModifiedHeader.getValue());
|
||||
if (lastModified != null && StringUtils.equals(lastModified, lastModifiedResponse)) {
|
||||
throw new NotModifiedException("lastModifiedHeader is the same");
|
||||
}
|
||||
|
||||
@@ -141,16 +137,17 @@ public class HttpGetter {
|
||||
|
||||
HttpEntity entity = response.getEntity();
|
||||
byte[] content = null;
|
||||
String contentType = null;
|
||||
if (entity != null) {
|
||||
content = EntityUtils.toByteArray(entity);
|
||||
if (entity.getContentType() != null) {
|
||||
contentType = entity.getContentType().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
Header contentType = entity.getContentType();
|
||||
result = new HttpResult(content, contentType == null ? null
|
||||
: contentType.getValue(), lastModifiedHeader == null ? null
|
||||
: lastModifiedHeader.getValue(), eTagHeader == null ? null
|
||||
: eTagHeader.getValue(), duration);
|
||||
result = new HttpResult(content, contentType, lastModifiedHeader == null ? null : lastModifiedHeader.getValue(),
|
||||
eTagHeader == null ? null : eTagHeader.getValue(), duration);
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
@@ -165,8 +162,7 @@ public class HttpGetter {
|
||||
private String eTag;
|
||||
private long duration;
|
||||
|
||||
public HttpResult(byte[] content, String contentType,
|
||||
String lastModifiedSince, String eTag, long duration) {
|
||||
public HttpResult(byte[] content, String contentType, String lastModifiedSince, String eTag, long duration) {
|
||||
this.content = content;
|
||||
this.contentType = contentType;
|
||||
this.lastModifiedSince = lastModifiedSince;
|
||||
@@ -209,8 +205,7 @@ public class HttpGetter {
|
||||
HttpProtocolParams.setContentCharset(params, UTF8);
|
||||
HttpConnectionParams.setConnectionTimeout(params, timeout);
|
||||
HttpConnectionParams.setSoTimeout(params, timeout);
|
||||
client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0,
|
||||
false));
|
||||
client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
|
||||
return new DecompressingHttpClient(client);
|
||||
}
|
||||
|
||||
@@ -225,13 +220,11 @@ public class HttpGetter {
|
||||
|
||||
private static class DefaultTrustManager implements X509TrustManager {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
|
||||
throws CertificateException {
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
|
||||
throws CertificateException {
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,21 +233,18 @@ public class HttpGetter {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultHostnameVerifier implements
|
||||
X509HostnameVerifier {
|
||||
private static class DefaultHostnameVerifier implements X509HostnameVerifier {
|
||||
|
||||
@Override
|
||||
public void verify(String string, SSLSocket ssls) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String string, X509Certificate xc)
|
||||
throws SSLException {
|
||||
public void verify(String string, X509Certificate xc) throws SSLException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String string, String[] strings, String[] strings1)
|
||||
throws SSLException {
|
||||
public void verify(String string, String[] strings, String[] strings1) throws SSLException {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,13 +55,11 @@ public class MetricsBean {
|
||||
|
||||
}
|
||||
|
||||
public void entryUpdated(int statusesCount) {
|
||||
public void entryInserted() {
|
||||
|
||||
thisHour.entriesInserted++;
|
||||
thisMinute.entriesInserted++;
|
||||
|
||||
thisHour.statusesInserted += statusesCount;
|
||||
thisMinute.statusesInserted += statusesCount;
|
||||
}
|
||||
|
||||
public void entryCacheHit() {
|
||||
@@ -107,7 +105,6 @@ public class MetricsBean {
|
||||
private int feedsRefreshed;
|
||||
private int feedsUpdated;
|
||||
private int entriesInserted;
|
||||
private int statusesInserted;
|
||||
private int threadWaited;
|
||||
private int pushNotificationsReceived;
|
||||
private int pushFeedsQueued;
|
||||
@@ -138,14 +135,6 @@ public class MetricsBean {
|
||||
this.entriesInserted = entriesInserted;
|
||||
}
|
||||
|
||||
public int getStatusesInserted() {
|
||||
return statusesInserted;
|
||||
}
|
||||
|
||||
public void setStatusesInserted(int statusesInserted) {
|
||||
this.statusesInserted = statusesInserted;
|
||||
}
|
||||
|
||||
public int getThreadWaited() {
|
||||
return threadWaited;
|
||||
}
|
||||
|
||||
39
src/main/java/com/commafeed/backend/ScheduledTasks.java
Normal file
39
src/main/java/com/commafeed/backend/ScheduledTasks.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.commafeed.backend;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.ejb.Schedule;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
|
||||
/**
|
||||
* Contains all scheduled tasks
|
||||
*
|
||||
*/
|
||||
@Stateless
|
||||
public class ScheduledTasks {
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@Inject
|
||||
DatabaseCleaner cleaner;
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager em;
|
||||
|
||||
/**
|
||||
* clean old read statuses, runs every day at midnight
|
||||
*/
|
||||
@Schedule(hour = "0", persistent = false)
|
||||
private void cleanupOldStatuses() {
|
||||
Date threshold = applicationSettingsService.getUnreadThreshold();
|
||||
if (threshold != null) {
|
||||
cleaner.cleanStatusesOlderThan(threshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,24 +13,28 @@ import javax.ejb.Singleton;
|
||||
import javax.ejb.Startup;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.commafeed.backend.dao.UserDAO;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.commafeed.backend.dao.ApplicationSettingsDAO;
|
||||
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.backend.services.UserService;
|
||||
import com.google.api.client.util.Maps;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Starting point of the application
|
||||
*
|
||||
*/
|
||||
@Startup
|
||||
@Singleton
|
||||
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
|
||||
@Slf4j
|
||||
public class StartupBean {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(StartupBean.class);
|
||||
public static final String USERNAME_ADMIN = "admin";
|
||||
public static final String USERNAME_DEMO = "demo";
|
||||
|
||||
@@ -38,7 +42,7 @@ public class StartupBean {
|
||||
DatabaseUpdater databaseUpdater;
|
||||
|
||||
@Inject
|
||||
UserDAO userDAO;
|
||||
ApplicationSettingsDAO applicationSettingsDAO;
|
||||
|
||||
@Inject
|
||||
UserService userService;
|
||||
@@ -56,14 +60,19 @@ public class StartupBean {
|
||||
private void init() {
|
||||
|
||||
startupTime = System.currentTimeMillis();
|
||||
|
||||
// update database schema
|
||||
databaseUpdater.update();
|
||||
|
||||
if (userDAO.getCount() == 0) {
|
||||
if (applicationSettingsDAO.getCount() == 0) {
|
||||
// import initial data
|
||||
initialData();
|
||||
}
|
||||
applicationSettingsService.applyLogLevel();
|
||||
|
||||
initSupportedLanguages();
|
||||
|
||||
// start fetching feeds
|
||||
taskGiver.start();
|
||||
}
|
||||
|
||||
@@ -79,11 +88,13 @@ public class StartupBean {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
for (Object key : props.keySet()) {
|
||||
supportedLanguages.put(key.toString(),
|
||||
props.getProperty(key.toString()));
|
||||
supportedLanguages.put(key.toString(), props.getProperty(key.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create default users
|
||||
*/
|
||||
private void initialData() {
|
||||
log.info("Populating database with default values");
|
||||
|
||||
@@ -92,11 +103,8 @@ public class StartupBean {
|
||||
applicationSettingsService.save(settings);
|
||||
|
||||
try {
|
||||
userService.register(USERNAME_ADMIN, "admin",
|
||||
"admin@commafeed.com",
|
||||
Arrays.asList(Role.ADMIN, Role.USER), true);
|
||||
userService.register(USERNAME_DEMO, "demo", "demo@commafeed.com",
|
||||
Arrays.asList(Role.USER), true);
|
||||
userService.register(USERNAME_ADMIN, "admin", "admin@commafeed.com", Arrays.asList(Role.ADMIN, Role.USER), true);
|
||||
userService.register(USERNAME_DEMO, "demo", "demo@commafeed.com", Arrays.asList(Role.USER), true);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
package com.commafeed.backend.cache;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
|
||||
public abstract class CacheService {
|
||||
|
||||
// feed entries for faster refresh
|
||||
public abstract List<String> getLastEntries(Feed feed);
|
||||
|
||||
public abstract void setLastEntries(Feed feed, List<String> entries);
|
||||
|
||||
public String buildUniqueEntryKey(Feed feed, FeedEntry entry) {
|
||||
return DigestUtils.sha1Hex(entry.getGuid() +
|
||||
entry.getUrl());
|
||||
return DigestUtils.sha1Hex(entry.getGuid() + entry.getUrl());
|
||||
}
|
||||
|
||||
public abstract Category getRootCategory(User user);
|
||||
// user categories
|
||||
public abstract Category getUserRootCategory(User user);
|
||||
|
||||
public abstract void setRootCategory(User user, Category category);
|
||||
|
||||
public abstract Map<Long, Long> getUnreadCounts(User user);
|
||||
public abstract void setUserRootCategory(User user, Category category);
|
||||
|
||||
public abstract void setUnreadCounts(User user, Map<Long, Long> map);
|
||||
public abstract void invalidateUserRootCategory(User... users);
|
||||
|
||||
public abstract void invalidateUserData(User... users);
|
||||
// unread count
|
||||
public abstract UnreadCount getUnreadCount(FeedSubscription sub);
|
||||
|
||||
public abstract void setUnreadCount(FeedSubscription sub, UnreadCount count);
|
||||
|
||||
public abstract void invalidateUnreadCount(FeedSubscription... subs);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ package com.commafeed.backend.cache;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.inject.Alternative;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
|
||||
@Alternative
|
||||
@ApplicationScoped
|
||||
@@ -25,27 +26,32 @@ public class NoopCacheService extends CacheService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getRootCategory(User user) {
|
||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootCategory(User user, Category category) {
|
||||
public void setUnreadCount(FeedSubscription sub, UnreadCount count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, Long> getUnreadCounts(User user) {
|
||||
public void invalidateUnreadCount(FeedSubscription... subs) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getUserRootCategory(User user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnreadCounts(User user, Map<Long, Long> map) {
|
||||
public void setUserRootCategory(User user, Category category) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateUserData(User... users) {
|
||||
public void invalidateUserRootCategory(User... users) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,36 @@
|
||||
package com.commafeed.backend.cache;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.inject.Alternative;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.Pipeline;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.Models;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import com.google.api.client.util.Lists;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Alternative
|
||||
@ApplicationScoped
|
||||
@Slf4j
|
||||
public class RedisCacheService extends CacheService {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(RedisCacheService.class);
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public List<String> getLastEntries(Feed feed) {
|
||||
@@ -70,11 +67,11 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getRootCategory(User user) {
|
||||
public Category getUserRootCategory(User user) {
|
||||
Category cat = null;
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
String key = buildRedisRootCategoryKey(user);
|
||||
String key = buildRedisUserRootCategoryKey(user);
|
||||
String json = jedis.get(key);
|
||||
if (json != null) {
|
||||
cat = mapper.readValue(json, Category.class);
|
||||
@@ -88,10 +85,10 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootCategory(User user, Category category) {
|
||||
public void setUserRootCategory(User user, Category category) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
String key = buildRedisRootCategoryKey(user);
|
||||
String key = buildRedisUserRootCategoryKey(user);
|
||||
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
pipe.del(key);
|
||||
@@ -106,37 +103,35 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, Long> getUnreadCounts(User user) {
|
||||
Map<Long, Long> map = null;
|
||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
||||
UnreadCount count = null;
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
String key = buildRedisUnreadCountKey(user);
|
||||
String key = buildRedisUnreadCountKey(sub);
|
||||
String json = jedis.get(key);
|
||||
if (json != null) {
|
||||
MapType type = mapper.getTypeFactory().constructMapType(
|
||||
Map.class, Long.class, Long.class);
|
||||
map = mapper.readValue(json, type);
|
||||
count = mapper.readValue(json, UnreadCount.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
return map;
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnreadCounts(User user, Map<Long, Long> map) {
|
||||
public void setUnreadCount(FeedSubscription sub, UnreadCount count) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
String key = buildRedisUnreadCountKey(user);
|
||||
String key = buildRedisUnreadCountKey(sub);
|
||||
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
pipe.del(key);
|
||||
pipe.set(key, mapper.writeValueAsString(map));
|
||||
pipe.set(key, mapper.writeValueAsString(count));
|
||||
pipe.expire(key, (int) TimeUnit.MINUTES.toSeconds(30));
|
||||
pipe.sync();
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
@@ -144,15 +139,13 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateUserData(User... users) {
|
||||
public void invalidateUserRootCategory(User... users) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
if (users != null) {
|
||||
for (User user : users) {
|
||||
String key = buildRedisRootCategoryKey(user);
|
||||
pipe.del(key);
|
||||
key = buildRedisUnreadCountKey(user);
|
||||
String key = buildRedisUserRootCategoryKey(user);
|
||||
pipe.del(key);
|
||||
}
|
||||
}
|
||||
@@ -162,16 +155,33 @@ public class RedisCacheService extends CacheService {
|
||||
}
|
||||
}
|
||||
|
||||
private String buildRedisRootCategoryKey(User user) {
|
||||
return "root_cat:" + Models.getId(user);
|
||||
}
|
||||
|
||||
private String buildRedisUnreadCountKey(User user) {
|
||||
return "unread_count:" + Models.getId(user);
|
||||
@Override
|
||||
public void invalidateUnreadCount(FeedSubscription... subs) {
|
||||
Jedis jedis = pool.getResource();
|
||||
try {
|
||||
Pipeline pipe = jedis.pipelined();
|
||||
if (subs != null) {
|
||||
for (FeedSubscription sub : subs) {
|
||||
String key = buildRedisUnreadCountKey(sub);
|
||||
pipe.del(key);
|
||||
}
|
||||
}
|
||||
pipe.sync();
|
||||
} finally {
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildRedisEntryKey(Feed feed) {
|
||||
return "feed:" + feed.getId();
|
||||
return "f:" + Models.getId(feed);
|
||||
}
|
||||
|
||||
private String buildRedisUserRootCategoryKey(User user) {
|
||||
return "c:" + Models.getId(user);
|
||||
}
|
||||
|
||||
private String buildRedisUnreadCountKey(FeedSubscription sub) {
|
||||
return "u:" + Models.getId(sub);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
|
||||
CriteriaQuery<FeedCategory> query = builder.createQuery(getType());
|
||||
Root<FeedCategory> root = query.from(getType());
|
||||
Join<FeedCategory, User> userJoin = (Join<FeedCategory, User>) root
|
||||
.fetch(FeedCategory_.user);
|
||||
Join<FeedCategory, User> userJoin = (Join<FeedCategory, User>) root.fetch(FeedCategory_.user);
|
||||
|
||||
query.where(builder.equal(userJoin.get(User_.id), user.getId()));
|
||||
|
||||
@@ -38,14 +37,12 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
CriteriaQuery<FeedCategory> query = builder.createQuery(getType());
|
||||
Root<FeedCategory> root = query.from(getType());
|
||||
|
||||
Predicate p1 = builder.equal(
|
||||
root.get(FeedCategory_.user).get(User_.id), user.getId());
|
||||
Predicate p1 = builder.equal(root.get(FeedCategory_.user).get(User_.id), user.getId());
|
||||
Predicate p2 = builder.equal(root.get(FeedCategory_.id), id);
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
return Iterables.getFirst(cache(em.createQuery(query)).getResultList(),
|
||||
null);
|
||||
return Iterables.getFirst(cache(em.createQuery(query)).getResultList(), null);
|
||||
}
|
||||
|
||||
public FeedCategory findByName(User user, String name, FeedCategory parent) {
|
||||
@@ -60,8 +57,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
if (parent == null) {
|
||||
predicates.add(builder.isNull(root.get(FeedCategory_.parent)));
|
||||
} else {
|
||||
predicates
|
||||
.add(builder.equal(root.get(FeedCategory_.parent), parent));
|
||||
predicates.add(builder.equal(root.get(FeedCategory_.parent), parent));
|
||||
}
|
||||
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
@@ -85,8 +81,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
if (parent == null) {
|
||||
predicates.add(builder.isNull(root.get(FeedCategory_.parent)));
|
||||
} else {
|
||||
predicates
|
||||
.add(builder.equal(root.get(FeedCategory_.parent), parent));
|
||||
predicates.add(builder.equal(root.get(FeedCategory_.parent), parent));
|
||||
}
|
||||
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
@@ -94,8 +89,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
return em.createQuery(query).getResultList();
|
||||
}
|
||||
|
||||
public List<FeedCategory> findAllChildrenCategories(User user,
|
||||
FeedCategory parent) {
|
||||
public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) {
|
||||
List<FeedCategory> list = Lists.newArrayList();
|
||||
List<FeedCategory> all = findAll(user);
|
||||
for (FeedCategory cat : all) {
|
||||
|
||||
@@ -4,19 +4,17 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -26,59 +24,55 @@ import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.FeedSubscription_;
|
||||
import com.commafeed.backend.model.Feed_;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.User_;
|
||||
import com.commafeed.frontend.model.FeedCount;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Stateless
|
||||
public class FeedDAO extends GenericDAO<Feed> {
|
||||
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class FeedCount {
|
||||
public String value;
|
||||
public List<Feed> feeds;
|
||||
private List<Predicate> getUpdatablePredicates(CriteriaQuery<?> query, Root<Feed> root, Date lastLoginThreshold) {
|
||||
|
||||
List<Predicate> preds = Lists.newArrayList();
|
||||
Predicate isNull = builder.isNull(root.get(Feed_.disabledUntil));
|
||||
Predicate lessThan = builder.lessThan(root.get(Feed_.disabledUntil), new Date());
|
||||
preds.add(builder.or(isNull, lessThan));
|
||||
|
||||
if (lastLoginThreshold != null) {
|
||||
Subquery<Long> subquery = query.subquery(Long.class);
|
||||
Root<FeedSubscription> subroot = subquery.from(FeedSubscription.class);
|
||||
subquery.select(builder.count(subroot.get(FeedSubscription_.id)));
|
||||
|
||||
Join<FeedSubscription, User> userJoin = subroot.join(FeedSubscription_.user);
|
||||
Predicate p1 = builder.equal(subroot.get(FeedSubscription_.feed), root);
|
||||
Predicate p2 = builder.greaterThanOrEqualTo(userJoin.get(User_.lastLogin), lastLoginThreshold);
|
||||
subquery.where(p1, p2);
|
||||
|
||||
preds.add(builder.exists(subquery));
|
||||
}
|
||||
|
||||
return preds;
|
||||
}
|
||||
|
||||
private List<Predicate> getUpdatablePredicates(Root<Feed> root,
|
||||
Date threshold) {
|
||||
|
||||
Predicate hasSubscriptions = builder.isNotEmpty(root
|
||||
.get(Feed_.subscriptions));
|
||||
|
||||
Predicate neverUpdated = builder.isNull(root.get(Feed_.lastUpdated));
|
||||
Predicate updatedBeforeThreshold = builder.lessThan(
|
||||
root.get(Feed_.lastUpdated), threshold);
|
||||
|
||||
Predicate disabledDateIsNull = builder.isNull(root
|
||||
.get(Feed_.disabledUntil));
|
||||
Predicate disabledDateIsInPast = builder.lessThan(
|
||||
root.get(Feed_.disabledUntil), new Date());
|
||||
|
||||
return Lists.newArrayList(hasSubscriptions,
|
||||
builder.or(neverUpdated, updatedBeforeThreshold),
|
||||
builder.or(disabledDateIsNull, disabledDateIsInPast));
|
||||
}
|
||||
|
||||
public Long getUpdatableCount(Date threshold) {
|
||||
public Long getUpdatableCount(Date lastLoginThreshold) {
|
||||
CriteriaQuery<Long> query = builder.createQuery(Long.class);
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
query.select(builder.count(root));
|
||||
query.where(getUpdatablePredicates(root, threshold).toArray(
|
||||
new Predicate[0]));
|
||||
query.where(getUpdatablePredicates(query, root, lastLoginThreshold).toArray(new Predicate[0]));
|
||||
|
||||
TypedQuery<Long> q = em.createQuery(query);
|
||||
return q.getSingleResult();
|
||||
}
|
||||
|
||||
public List<Feed> findNextUpdatable(int count, Date threshold) {
|
||||
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
|
||||
CriteriaQuery<Feed> query = builder.createQuery(getType());
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
query.where(getUpdatablePredicates(root, threshold).toArray(
|
||||
new Predicate[0]));
|
||||
|
||||
query.orderBy(builder.asc(root.get(Feed_.lastUpdated)));
|
||||
query.where(getUpdatablePredicates(query, root, lastLoginThreshold).toArray(new Predicate[0]));
|
||||
query.orderBy(builder.asc(root.get(Feed_.disabledUntil)));
|
||||
|
||||
TypedQuery<Feed> q = em.createQuery(query);
|
||||
q.setMaxResults(count);
|
||||
@@ -87,18 +81,11 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
}
|
||||
|
||||
public Feed findByUrl(String url) {
|
||||
List<Feed> feeds = findByField(Feed_.urlHash, DigestUtils.sha1Hex(url));
|
||||
Feed feed = Iterables.getFirst(feeds, null);
|
||||
if (feed != null && StringUtils.equals(url, feed.getUrl())) {
|
||||
return feed;
|
||||
}
|
||||
|
||||
String normalized = FeedUtils.normalizeURL(url);
|
||||
feeds = findByField(Feed_.normalizedUrlHash,
|
||||
DigestUtils.sha1Hex(normalized));
|
||||
feed = Iterables.getFirst(feeds, null);
|
||||
if (feed != null
|
||||
&& StringUtils.equals(normalized, feed.getNormalizedUrl())) {
|
||||
List<Feed> feeds = findByField(Feed_.normalizedUrlHash, DigestUtils.sha1Hex(normalized));
|
||||
Feed feed = Iterables.getFirst(feeds, null);
|
||||
if (feed != null && StringUtils.equals(normalized, feed.getNormalizedUrl())) {
|
||||
return feed;
|
||||
}
|
||||
|
||||
@@ -109,19 +96,11 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
return findByField(Feed_.pushTopicHash, DigestUtils.sha1Hex(topic));
|
||||
}
|
||||
|
||||
public void deleteRelationships(Feed feed) {
|
||||
Query relationshipDeleteQuery = em
|
||||
.createNamedQuery("Feed.deleteEntryRelationships");
|
||||
relationshipDeleteQuery.setParameter("feedId", feed.getId());
|
||||
relationshipDeleteQuery.executeUpdate();
|
||||
}
|
||||
|
||||
public int deleteWithoutSubscriptions(int max) {
|
||||
CriteriaQuery<Feed> query = builder.createQuery(getType());
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
SetJoin<Feed, FeedSubscription> join = root.join(Feed_.subscriptions,
|
||||
JoinType.LEFT);
|
||||
SetJoin<Feed, FeedSubscription> join = root.join(Feed_.subscriptions, JoinType.LEFT);
|
||||
query.where(builder.isNull(join.get(FeedSubscription_.id)));
|
||||
TypedQuery<Feed> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
@@ -129,17 +108,13 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
List<Feed> list = q.getResultList();
|
||||
int deleted = list.size();
|
||||
|
||||
for (Feed feed : list) {
|
||||
deleteRelationships(feed);
|
||||
delete(feed);
|
||||
}
|
||||
delete(list);
|
||||
return deleted;
|
||||
|
||||
}
|
||||
|
||||
public static enum DuplicateMode {
|
||||
NORMALIZED_URL(Feed_.normalizedUrlHash), LAST_CONTENT(
|
||||
Feed_.lastContentHash), PUSH_TOPIC(Feed_.pushTopicHash);
|
||||
NORMALIZED_URL(Feed_.normalizedUrlHash), LAST_CONTENT(Feed_.lastContentHash), PUSH_TOPIC(Feed_.pushTopicHash);
|
||||
private SingularAttribute<Feed, String> path;
|
||||
|
||||
private DuplicateMode(SingularAttribute<Feed, String> path) {
|
||||
@@ -151,8 +126,7 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
}
|
||||
}
|
||||
|
||||
public List<FeedCount> findDuplicates(DuplicateMode mode, int offset,
|
||||
int limit, long minCount) {
|
||||
public List<FeedCount> findDuplicates(DuplicateMode mode, int offset, int limit, long minCount) {
|
||||
CriteriaQuery<String> query = builder.createQuery(String.class);
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
@@ -170,14 +144,12 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
|
||||
List<FeedCount> result = Lists.newArrayList();
|
||||
for (String pathValue : pathValues) {
|
||||
FeedCount fc = new FeedCount();
|
||||
fc.value = pathValue;
|
||||
fc.feeds = Lists.newArrayList();
|
||||
FeedCount fc = new FeedCount(pathValue);
|
||||
for (Feed feed : findByField(mode.getPath(), pathValue)) {
|
||||
Feed f = new Feed();
|
||||
f.setId(feed.getId());
|
||||
f.setUrl(feed.getUrl());
|
||||
fc.feeds.add(f);
|
||||
fc.getFeeds().add(f);
|
||||
}
|
||||
result.add(fc);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedEntryContent_;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
|
||||
|
||||
public Long findExisting(String contentHash, String titleHash) {
|
||||
|
||||
CriteriaQuery<Long> query = builder.createQuery(Long.class);
|
||||
Root<FeedEntryContent> root = query.from(getType());
|
||||
query.select(root.get(FeedEntryContent_.id));
|
||||
|
||||
Predicate p1 = builder.equal(root.get(FeedEntryContent_.contentHash), contentHash);
|
||||
Predicate p2 = builder.equal(root.get(FeedEntryContent_.titleHash), titleHash);
|
||||
|
||||
query.where(p1, p2);
|
||||
TypedQuery<Long> q = em.createQuery(query);
|
||||
limit(q, 0, 1);
|
||||
return Iterables.getFirst(q.getResultList(), null);
|
||||
|
||||
}
|
||||
|
||||
public int deleteWithoutEntries(int max) {
|
||||
CriteriaQuery<FeedEntryContent> query = builder.createQuery(getType());
|
||||
Root<FeedEntryContent> root = query.from(getType());
|
||||
|
||||
Join<FeedEntryContent, FeedEntry> join = root.join(FeedEntryContent_.entries, JoinType.LEFT);
|
||||
query.where(builder.isNull(join.get(FeedEntry_.id)));
|
||||
TypedQuery<FeedEntryContent> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
|
||||
List<FeedEntryContent> list = q.getResultList();
|
||||
int deleted = list.size();
|
||||
return deleted;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,77 +4,36 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.FeedFeedEntry;
|
||||
import com.commafeed.backend.model.FeedFeedEntry_;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.backend.model.Feed_;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
@Stateless
|
||||
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
public Long findExisting(String guid, Long feedId) {
|
||||
|
||||
protected static final Logger log = LoggerFactory
|
||||
.getLogger(FeedEntryDAO.class);
|
||||
|
||||
public static class EntryWithFeed {
|
||||
public FeedEntry entry;
|
||||
public FeedFeedEntry ffe;
|
||||
|
||||
public EntryWithFeed(FeedEntry entry, FeedFeedEntry ffe) {
|
||||
this.entry = entry;
|
||||
this.ffe = ffe;
|
||||
}
|
||||
}
|
||||
|
||||
public EntryWithFeed findExisting(String guid, String url, Long feedId) {
|
||||
|
||||
TypedQuery<EntryWithFeed> q = em.createNamedQuery(
|
||||
"EntryStatus.existing", EntryWithFeed.class);
|
||||
q.setParameter("guidHash", DigestUtils.sha1Hex(guid));
|
||||
q.setParameter("url", url);
|
||||
q.setParameter("feedId", feedId);
|
||||
|
||||
EntryWithFeed result = null;
|
||||
List<EntryWithFeed> list = q.getResultList();
|
||||
for (EntryWithFeed ewf : list) {
|
||||
if (ewf.entry != null && ewf.ffe != null) {
|
||||
result = ewf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = Iterables.getFirst(list, null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<FeedEntry> findByFeed(Feed feed, int offset, int limit) {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
CriteriaQuery<Long> query = builder.createQuery(Long.class);
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
SetJoin<FeedEntry, FeedFeedEntry> feedsJoin = root.join(FeedEntry_.feedRelationships);
|
||||
query.select(root.get(FeedEntry_.id));
|
||||
|
||||
query.where(builder.equal(feedsJoin.get(FeedFeedEntry_.feed), feed));
|
||||
query.orderBy(builder.desc(feedsJoin.get(FeedFeedEntry_.entryUpdated)));
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
limit(q, offset, limit);
|
||||
setTimeout(q, applicationSettingsService.get().getQueryTimeout());
|
||||
return q.getResultList();
|
||||
Predicate p1 = builder.equal(root.get(FeedEntry_.guidHash), DigestUtils.sha1Hex(guid));
|
||||
Predicate p2 = builder.equal(root.get(FeedEntry_.feed).get(Feed_.id), feedId);
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
TypedQuery<Long> q = em.createQuery(query);
|
||||
limit(q, 0, 1);
|
||||
List<Long> list = q.getResultList();
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
|
||||
public int delete(Date olderThan, int max) {
|
||||
@@ -90,20 +49,4 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
delete(list);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public int deleteWithoutFeeds(int max) {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
|
||||
SetJoin<FeedEntry, FeedFeedEntry> join = root.join(FeedEntry_.feedRelationships,
|
||||
JoinType.LEFT);
|
||||
query.where(builder.isNull(join.get(FeedFeedEntry_.feed)));
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
|
||||
List<FeedEntry> list = q.getResultList();
|
||||
int deleted = list.size();
|
||||
delete(list);
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,53 +10,42 @@ import javax.inject.Inject;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.criterion.Disjunction;
|
||||
import org.hibernate.criterion.MatchMode;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.ProjectionList;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.sql.JoinType;
|
||||
import org.hibernate.transform.Transformers;
|
||||
|
||||
import com.commafeed.backend.FixedSizeSortedSet;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedEntryContent_;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedEntryStatus_;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.FeedFeedEntry;
|
||||
import com.commafeed.backend.model.FeedFeedEntry_;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.Models;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserSettings.ReadingOrder;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@Stateless
|
||||
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
|
||||
protected static Logger log = LoggerFactory
|
||||
.getLogger(FeedEntryStatusDAO.class);
|
||||
|
||||
private static final Comparator<FeedEntry> ENTRY_COMPARATOR_DESC = new Comparator<FeedEntry>() {
|
||||
@Override
|
||||
public int compare(FeedEntry o1, FeedEntry o2) {
|
||||
return ObjectUtils.compare(o2.getUpdated(), o1.getUpdated());
|
||||
};
|
||||
};
|
||||
|
||||
private static final Comparator<FeedEntry> ENTRY_COMPARATOR_ASC = new Comparator<FeedEntry>() {
|
||||
@Override
|
||||
public int compare(FeedEntry o1, FeedEntry o2) {
|
||||
return ObjectUtils.compare(o1.getUpdated(), o2.getUpdated());
|
||||
};
|
||||
};
|
||||
private static final String ALIAS_STATUS = "status";
|
||||
private static final String ALIAS_ENTRY = "entry";
|
||||
|
||||
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_DESC = new Comparator<FeedEntryStatus>() {
|
||||
@Override
|
||||
@@ -81,22 +70,30 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
Root<FeedEntryStatus> root = query.from(getType());
|
||||
|
||||
Predicate p1 = builder.equal(root.get(FeedEntryStatus_.entry), entry);
|
||||
Predicate p2 = builder.equal(root.get(FeedEntryStatus_.subscription),
|
||||
sub);
|
||||
Predicate p2 = builder.equal(root.get(FeedEntryStatus_.subscription), sub);
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
List<FeedEntryStatus> statuses = em.createQuery(query).getResultList();
|
||||
FeedEntryStatus status = Iterables.getFirst(statuses, null);
|
||||
|
||||
return handleStatus(status, sub, entry);
|
||||
}
|
||||
|
||||
private FeedEntryStatus handleStatus(FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
|
||||
if (status == null) {
|
||||
Date unreadThreshold = applicationSettingsService.getUnreadThreshold();
|
||||
boolean read = unreadThreshold == null ? false : entry.getUpdated().before(unreadThreshold);
|
||||
status = new FeedEntryStatus(sub.getUser(), sub, entry);
|
||||
status.setRead(true);
|
||||
status.setRead(read);
|
||||
status.setMarkable(!read);
|
||||
} else {
|
||||
status.setMarkable(true);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public List<FeedEntryStatus> findStarred(User user, Date newerThan,
|
||||
int offset, int limit, ReadingOrder order, boolean includeContent) {
|
||||
public List<FeedEntryStatus> findStarred(User user, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) {
|
||||
|
||||
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
|
||||
Root<FeedEntryStatus> root = query.from(getType());
|
||||
@@ -108,8 +105,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
|
||||
if (newerThan != null) {
|
||||
predicates.add(builder.greaterThanOrEqualTo(
|
||||
root.get(FeedEntryStatus_.entryInserted), newerThan));
|
||||
predicates.add(builder.greaterThanOrEqualTo(root.get(FeedEntryStatus_.entryInserted), newerThan));
|
||||
}
|
||||
|
||||
orderStatusesBy(query, root, order);
|
||||
@@ -117,200 +113,156 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
TypedQuery<FeedEntryStatus> q = em.createQuery(query);
|
||||
limit(q, offset, limit);
|
||||
setTimeout(q);
|
||||
return lazyLoadContent(includeContent, q.getResultList());
|
||||
List<FeedEntryStatus> statuses = q.getResultList();
|
||||
for (FeedEntryStatus status : statuses) {
|
||||
status = handleStatus(status, status.getSubscription(), status.getEntry());
|
||||
}
|
||||
return lazyLoadContent(includeContent, statuses);
|
||||
}
|
||||
|
||||
public List<FeedEntryStatus> findBySubscriptions(
|
||||
List<FeedSubscription> subscriptions, String keywords,
|
||||
Date newerThan, int offset, int limit, ReadingOrder order,
|
||||
boolean includeContent) {
|
||||
private Criteria buildSearchCriteria(FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset, int limit,
|
||||
ReadingOrder order, Date last) {
|
||||
Criteria criteria = getSession().createCriteria(FeedEntry.class, ALIAS_ENTRY);
|
||||
|
||||
int capacity = offset + limit;
|
||||
Comparator<FeedEntry> comparator = order == ReadingOrder.desc ? ENTRY_COMPARATOR_DESC
|
||||
: ENTRY_COMPARATOR_ASC;
|
||||
FixedSizeSortedSet<FeedEntry> set = new FixedSizeSortedSet<FeedEntry>(
|
||||
capacity < 0 ? Integer.MAX_VALUE : capacity, comparator);
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
CriteriaQuery<FeedEntry> query = builder
|
||||
.createQuery(FeedEntry.class);
|
||||
Root<FeedEntry> root = query.from(FeedEntry.class);
|
||||
Join<FeedEntry, FeedFeedEntry> ffeJoin = root
|
||||
.join(FeedEntry_.feedRelationships);
|
||||
criteria.add(Restrictions.eq(FeedEntry_.feed.getName(), sub.getFeed()));
|
||||
|
||||
List<Predicate> predicates = Lists.newArrayList();
|
||||
predicates.add(builder.equal(ffeJoin.get(FeedFeedEntry_.feed),
|
||||
sub.getFeed()));
|
||||
if (keywords != null) {
|
||||
Criteria contentJoin = criteria.createCriteria(FeedEntry_.content.getName(), "content", JoinType.INNER_JOIN);
|
||||
|
||||
if (newerThan != null) {
|
||||
predicates.add(builder.greaterThanOrEqualTo(
|
||||
root.get(FeedEntry_.inserted), newerThan));
|
||||
for (String keyword : StringUtils.split(keywords)) {
|
||||
Disjunction or = Restrictions.disjunction();
|
||||
or.add(Restrictions.ilike(FeedEntryContent_.content.getName(), keyword, MatchMode.ANYWHERE));
|
||||
or.add(Restrictions.ilike(FeedEntryContent_.title.getName(), keyword, MatchMode.ANYWHERE));
|
||||
contentJoin.add(or);
|
||||
}
|
||||
|
||||
if (keywords != null) {
|
||||
Join<FeedEntry, FeedEntryContent> contentJoin = root
|
||||
.join(FeedEntry_.content);
|
||||
|
||||
String joinedKeywords = StringUtils.join(keywords.toLowerCase()
|
||||
.split(" "), "%");
|
||||
joinedKeywords = "%" + joinedKeywords + "%";
|
||||
|
||||
Predicate content = builder.like(builder.lower(contentJoin
|
||||
.get(FeedEntryContent_.content)), joinedKeywords);
|
||||
Predicate title = builder
|
||||
.like(builder.lower(contentJoin
|
||||
.get(FeedEntryContent_.title)), joinedKeywords);
|
||||
predicates.add(builder.or(content, title));
|
||||
}
|
||||
|
||||
if (order != null && !set.isEmpty() && set.isFull()) {
|
||||
Predicate filter = null;
|
||||
FeedEntry last = set.last();
|
||||
if (order == ReadingOrder.desc) {
|
||||
filter = builder.greaterThan(
|
||||
ffeJoin.get(FeedFeedEntry_.entryUpdated),
|
||||
last.getUpdated());
|
||||
} else {
|
||||
filter = builder.lessThan(
|
||||
ffeJoin.get(FeedFeedEntry_.entryUpdated),
|
||||
last.getUpdated());
|
||||
}
|
||||
predicates.add(filter);
|
||||
}
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
orderEntriesBy(query, ffeJoin, order);
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
limit(q, 0, capacity);
|
||||
setTimeout(q);
|
||||
|
||||
List<FeedEntry> list = q.getResultList();
|
||||
for (FeedEntry entry : list) {
|
||||
entry.setSubscription(sub);
|
||||
}
|
||||
set.addAll(list);
|
||||
}
|
||||
Criteria statusJoin = criteria.createCriteria(FeedEntry_.statuses.getName(), ALIAS_STATUS, JoinType.LEFT_OUTER_JOIN,
|
||||
Restrictions.eq(FeedEntryStatus_.subscription.getName(), sub));
|
||||
|
||||
List<FeedEntry> entries = set.asList();
|
||||
int size = entries.size();
|
||||
if (size < offset) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
if (unreadOnly) {
|
||||
|
||||
entries = entries.subList(Math.max(offset, 0), size);
|
||||
Disjunction or = Restrictions.disjunction();
|
||||
or.add(Restrictions.isNull(FeedEntryStatus_.read.getName()));
|
||||
or.add(Restrictions.eq(FeedEntryStatus_.read.getName(), false));
|
||||
statusJoin.add(or);
|
||||
|
||||
List<FeedEntryStatus> results = Lists.newArrayList();
|
||||
for (FeedEntry entry : entries) {
|
||||
FeedSubscription subscription = entry.getSubscription();
|
||||
results.add(getStatus(subscription, entry));
|
||||
}
|
||||
|
||||
return lazyLoadContent(includeContent, results);
|
||||
}
|
||||
|
||||
public List<FeedEntryStatus> findUnreadBySubscriptions(
|
||||
List<FeedSubscription> subscriptions, Date newerThan, int offset,
|
||||
int limit, ReadingOrder order, boolean includeContent) {
|
||||
|
||||
int capacity = offset + limit;
|
||||
Comparator<FeedEntryStatus> comparator = order == ReadingOrder.desc ? STATUS_COMPARATOR_DESC
|
||||
: STATUS_COMPARATOR_ASC;
|
||||
FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(
|
||||
capacity < 0 ? Integer.MAX_VALUE : capacity, comparator);
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
CriteriaQuery<FeedEntryStatus> query = builder
|
||||
.createQuery(getType());
|
||||
Root<FeedEntryStatus> root = query.from(getType());
|
||||
|
||||
List<Predicate> predicates = Lists.newArrayList();
|
||||
|
||||
predicates.add(builder.equal(
|
||||
root.get(FeedEntryStatus_.subscription), sub));
|
||||
predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read)));
|
||||
|
||||
if (newerThan != null) {
|
||||
predicates.add(builder.greaterThanOrEqualTo(
|
||||
root.get(FeedEntryStatus_.entryInserted), newerThan));
|
||||
Date unreadThreshold = applicationSettingsService.getUnreadThreshold();
|
||||
if (unreadThreshold != null) {
|
||||
criteria.add(Restrictions.ge(FeedEntry_.updated.getName(), unreadThreshold));
|
||||
}
|
||||
|
||||
if (order != null && !set.isEmpty() && set.isFull()) {
|
||||
Predicate filter = null;
|
||||
FeedEntryStatus last = set.last();
|
||||
if (order == ReadingOrder.desc) {
|
||||
filter = builder.greaterThan(
|
||||
root.get(FeedEntryStatus_.entryUpdated),
|
||||
last.getEntryUpdated());
|
||||
} else {
|
||||
filter = builder.lessThan(
|
||||
root.get(FeedEntryStatus_.entryUpdated),
|
||||
last.getEntryUpdated());
|
||||
}
|
||||
predicates.add(filter);
|
||||
}
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
orderStatusesBy(query, root, order);
|
||||
|
||||
TypedQuery<FeedEntryStatus> q = em.createQuery(query);
|
||||
limit(q, -1, limit);
|
||||
setTimeout(q);
|
||||
|
||||
List<FeedEntryStatus> list = q.getResultList();
|
||||
set.addAll(list);
|
||||
}
|
||||
|
||||
List<FeedEntryStatus> entries = set.asList();
|
||||
int size = entries.size();
|
||||
if (size < offset) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
entries = entries.subList(Math.max(offset, 0), size);
|
||||
return lazyLoadContent(includeContent, entries);
|
||||
}
|
||||
|
||||
public List<FeedEntryStatus> findAllUnread(User user, Date newerThan,
|
||||
int offset, int limit, ReadingOrder order, boolean includeContent) {
|
||||
|
||||
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
|
||||
Root<FeedEntryStatus> root = query.from(getType());
|
||||
|
||||
List<Predicate> predicates = Lists.newArrayList();
|
||||
|
||||
predicates.add(builder.equal(root.get(FeedEntryStatus_.user), user));
|
||||
predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read)));
|
||||
|
||||
if (newerThan != null) {
|
||||
predicates.add(builder.greaterThanOrEqualTo(
|
||||
root.get(FeedEntryStatus_.entryInserted), newerThan));
|
||||
criteria.add(Restrictions.ge(FeedEntry_.inserted.getName(), newerThan));
|
||||
}
|
||||
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
orderStatusesBy(query, root, order);
|
||||
|
||||
TypedQuery<FeedEntryStatus> q = em.createQuery(query);
|
||||
limit(q, offset, limit);
|
||||
setTimeout(q);
|
||||
|
||||
return lazyLoadContent(includeContent, q.getResultList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map between subscriptionId and unread count
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Map<Long, Long> getUnreadCount(User user) {
|
||||
Map<Long, Long> map = Maps.newHashMap();
|
||||
Query query = em.createNamedQuery("EntryStatus.unreadCounts");
|
||||
query.setParameter("user", user);
|
||||
setTimeout(query);
|
||||
List resultList = query.getResultList();
|
||||
for (Object o : resultList) {
|
||||
Object[] array = (Object[]) o;
|
||||
map.put((Long) array[0], (Long) array[1]);
|
||||
if (last != null) {
|
||||
if (order == ReadingOrder.desc) {
|
||||
criteria.add(Restrictions.gt(FeedEntry_.updated.getName(), last));
|
||||
} else {
|
||||
criteria.add(Restrictions.lt(FeedEntry_.updated.getName(), last));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
|
||||
if (order != null) {
|
||||
Order o = null;
|
||||
if (order == ReadingOrder.asc) {
|
||||
o = Order.asc(FeedEntry_.updated.getName());
|
||||
} else {
|
||||
o = Order.desc(FeedEntry_.updated.getName());
|
||||
}
|
||||
criteria.addOrder(o);
|
||||
}
|
||||
if (offset > -1) {
|
||||
criteria.setFirstResult(offset);
|
||||
}
|
||||
if (limit > -1) {
|
||||
criteria.setMaxResults(limit);
|
||||
}
|
||||
int timeout = applicationSettingsService.get().getQueryTimeout();
|
||||
if (timeout > 0) {
|
||||
// hibernate timeout is in seconds, jpa timeout is in millis
|
||||
criteria.setTimeout(timeout / 1000);
|
||||
}
|
||||
return criteria;
|
||||
}
|
||||
|
||||
private List<FeedEntryStatus> lazyLoadContent(boolean includeContent,
|
||||
List<FeedEntryStatus> results) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<FeedEntryStatus> findBySubscriptions(List<FeedSubscription> subs, boolean unreadOnly, String keywords, Date newerThan,
|
||||
int offset, int limit, ReadingOrder order, boolean includeContent, boolean onlyIds) {
|
||||
int capacity = offset + limit;
|
||||
Comparator<FeedEntryStatus> comparator = order == ReadingOrder.desc ? STATUS_COMPARATOR_DESC : STATUS_COMPARATOR_ASC;
|
||||
FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(capacity, comparator);
|
||||
for (FeedSubscription sub : subs) {
|
||||
Date last = (order != null && set.isFull()) ? set.last().getEntryUpdated() : null;
|
||||
Criteria criteria = buildSearchCriteria(sub, unreadOnly, keywords, newerThan, -1, capacity, order, last);
|
||||
ProjectionList projection = Projections.projectionList();
|
||||
projection.add(Projections.property("id"), "id");
|
||||
projection.add(Projections.property("updated"), "updated");
|
||||
projection.add(Projections.property(ALIAS_STATUS + ".id"), "status_id");
|
||||
criteria.setProjection(projection);
|
||||
criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
|
||||
List<Map<String, Object>> list = criteria.list();
|
||||
for (Map<String, Object> map : list) {
|
||||
Long id = (Long) map.get("id");
|
||||
Date updated = (Date) map.get("updated");
|
||||
Long statusId = (Long) map.get("status_id");
|
||||
|
||||
FeedEntry entry = new FeedEntry();
|
||||
entry.setId(id);
|
||||
entry.setUpdated(updated);
|
||||
|
||||
FeedEntryStatus status = new FeedEntryStatus();
|
||||
status.setId(statusId);
|
||||
status.setEntryUpdated(updated);
|
||||
status.setEntry(entry);
|
||||
status.setSubscription(sub);
|
||||
|
||||
set.add(status);
|
||||
}
|
||||
}
|
||||
|
||||
List<FeedEntryStatus> placeholders = set.asList();
|
||||
int size = placeholders.size();
|
||||
if (size < offset) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
placeholders = placeholders.subList(Math.max(offset, 0), size);
|
||||
|
||||
List<FeedEntryStatus> statuses = null;
|
||||
if (onlyIds) {
|
||||
statuses = placeholders;
|
||||
} else {
|
||||
statuses = Lists.newArrayList();
|
||||
for (FeedEntryStatus placeholder : placeholders) {
|
||||
Long statusId = placeholder.getId();
|
||||
FeedEntry entry = em.find(FeedEntry.class, placeholder.getEntry().getId());
|
||||
statuses.add(handleStatus(statusId == null ? null : findById(statusId), placeholder.getSubscription(), entry));
|
||||
}
|
||||
statuses = lazyLoadContent(includeContent, statuses);
|
||||
}
|
||||
return statuses;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public UnreadCount getUnreadCount(FeedSubscription subscription) {
|
||||
UnreadCount uc = null;
|
||||
Criteria criteria = buildSearchCriteria(subscription, true, null, null, -1, -1, null, null);
|
||||
ProjectionList projection = Projections.projectionList();
|
||||
projection.add(Projections.rowCount(), "count");
|
||||
projection.add(Projections.max(FeedEntry_.updated.getName()), "updated");
|
||||
criteria.setProjection(projection);
|
||||
criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
|
||||
List<Map<String, Object>> list = criteria.list();
|
||||
for (Map<String, Object> row : list) {
|
||||
Long count = (Long) row.get("count");
|
||||
Date updated = (Date) row.get("updated");
|
||||
uc = new UnreadCount(subscription.getId(), count, updated);
|
||||
}
|
||||
return uc;
|
||||
}
|
||||
|
||||
private List<FeedEntryStatus> lazyLoadContent(boolean includeContent, List<FeedEntryStatus> results) {
|
||||
if (includeContent) {
|
||||
for (FeedEntryStatus status : results) {
|
||||
Models.initialize(status.getSubscription().getFeed());
|
||||
@@ -320,18 +272,11 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
return results;
|
||||
}
|
||||
|
||||
private void orderEntriesBy(CriteriaQuery<?> query,
|
||||
Path<FeedFeedEntry> ffeJoin, ReadingOrder order) {
|
||||
orderBy(query, ffeJoin.get(FeedFeedEntry_.entryUpdated), order);
|
||||
}
|
||||
|
||||
private void orderStatusesBy(CriteriaQuery<?> query,
|
||||
Path<FeedEntryStatus> statusJoin, ReadingOrder order) {
|
||||
private void orderStatusesBy(CriteriaQuery<?> query, Path<FeedEntryStatus> statusJoin, ReadingOrder order) {
|
||||
orderBy(query, statusJoin.get(FeedEntryStatus_.entryUpdated), order);
|
||||
}
|
||||
|
||||
private void orderBy(CriteriaQuery<?> query, Path<Date> date,
|
||||
ReadingOrder order) {
|
||||
private void orderBy(CriteriaQuery<?> query, Path<Date> date, ReadingOrder order) {
|
||||
if (order != null) {
|
||||
if (order == ReadingOrder.asc) {
|
||||
query.orderBy(builder.asc(date));
|
||||
@@ -345,43 +290,10 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
setTimeout(query, applicationSettingsService.get().getQueryTimeout());
|
||||
}
|
||||
|
||||
public void markAllEntries(User user, Date olderThan) {
|
||||
List<FeedEntryStatus> statuses = findAllUnread(user, null, -1, -1,
|
||||
null, false);
|
||||
markList(statuses, olderThan);
|
||||
}
|
||||
|
||||
public void markSubscriptionEntries(List<FeedSubscription> subscriptions,
|
||||
Date olderThan) {
|
||||
List<FeedEntryStatus> statuses = findUnreadBySubscriptions(
|
||||
subscriptions, null, -1, -1, null, false);
|
||||
markList(statuses, olderThan);
|
||||
}
|
||||
|
||||
public void markStarredEntries(User user, Date olderThan) {
|
||||
List<FeedEntryStatus> statuses = findStarred(user, null, -1, -1, null,
|
||||
false);
|
||||
markList(statuses, olderThan);
|
||||
}
|
||||
|
||||
private void markList(List<FeedEntryStatus> statuses, Date olderThan) {
|
||||
List<FeedEntryStatus> list = Lists.newArrayList();
|
||||
for (FeedEntryStatus status : statuses) {
|
||||
if (!status.isRead()) {
|
||||
Date inserted = status.getEntry().getInserted();
|
||||
if (olderThan == null || inserted == null
|
||||
|| olderThan.after(inserted)) {
|
||||
if (status.isStarred()) {
|
||||
status.setRead(true);
|
||||
list.add(status);
|
||||
} else {
|
||||
delete(status);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
saveOrUpdate(list);
|
||||
public int deleteOldStatuses(Date olderThan) {
|
||||
Query query = em.createNamedQuery("Statuses.deleteOld");
|
||||
query.setParameter("date", olderThan);
|
||||
return query.executeUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
|
||||
Root<FeedSubscription> root = query.from(getType());
|
||||
|
||||
Predicate p1 = builder.equal(
|
||||
root.get(FeedSubscription_.user).get(User_.id), user.getId());
|
||||
Predicate p1 = builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId());
|
||||
Predicate p2 = builder.equal(root.get(FeedSubscription_.id), id);
|
||||
|
||||
root.fetch(FeedSubscription_.feed, JoinType.LEFT);
|
||||
@@ -37,8 +36,7 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
FeedSubscription sub = Iterables.getFirst(cache(em.createQuery(query))
|
||||
.getResultList(), null);
|
||||
FeedSubscription sub = Iterables.getFirst(cache(em.createQuery(query)).getResultList(), null);
|
||||
initRelations(sub);
|
||||
return sub;
|
||||
}
|
||||
@@ -47,11 +45,8 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
|
||||
Root<FeedSubscription> root = query.from(getType());
|
||||
|
||||
query.where(builder.equal(root.get(FeedSubscription_.feed)
|
||||
.get(Feed_.id), feed.getId()));
|
||||
List<FeedSubscription> list = cache(em.createQuery(query))
|
||||
.getResultList();
|
||||
initRelations(list);
|
||||
query.where(builder.equal(root.get(FeedSubscription_.feed), feed));
|
||||
List<FeedSubscription> list = cache(em.createQuery(query)).getResultList();
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -60,18 +55,15 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
|
||||
Root<FeedSubscription> root = query.from(getType());
|
||||
|
||||
Predicate p1 = builder.equal(
|
||||
root.get(FeedSubscription_.user).get(User_.id), user.getId());
|
||||
Predicate p2 = builder.equal(
|
||||
root.get(FeedSubscription_.feed).get(Feed_.id), feed.getId());
|
||||
Predicate p1 = builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId());
|
||||
Predicate p2 = builder.equal(root.get(FeedSubscription_.feed).get(Feed_.id), feed.getId());
|
||||
|
||||
root.fetch(FeedSubscription_.feed, JoinType.LEFT);
|
||||
root.fetch(FeedSubscription_.category, JoinType.LEFT);
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
FeedSubscription sub = Iterables.getFirst(cache(em.createQuery(query))
|
||||
.getResultList(), null);
|
||||
FeedSubscription sub = Iterables.getFirst(cache(em.createQuery(query)).getResultList(), null);
|
||||
initRelations(sub);
|
||||
return sub;
|
||||
}
|
||||
@@ -84,57 +76,46 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||
root.fetch(FeedSubscription_.feed, JoinType.LEFT);
|
||||
root.fetch(FeedSubscription_.category, JoinType.LEFT);
|
||||
|
||||
query.where(builder.equal(root.get(FeedSubscription_.user)
|
||||
.get(User_.id), user.getId()));
|
||||
query.where(builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId()));
|
||||
|
||||
List<FeedSubscription> list = cache(em.createQuery(query))
|
||||
.getResultList();
|
||||
List<FeedSubscription> list = cache(em.createQuery(query)).getResultList();
|
||||
initRelations(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<FeedSubscription> findByCategory(User user,
|
||||
FeedCategory category) {
|
||||
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
|
||||
|
||||
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
|
||||
Root<FeedSubscription> root = query.from(getType());
|
||||
|
||||
Predicate p1 = builder.equal(
|
||||
root.get(FeedSubscription_.user).get(User_.id), user.getId());
|
||||
Predicate p1 = builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId());
|
||||
Predicate p2 = null;
|
||||
if (category == null) {
|
||||
p2 = builder.isNull(
|
||||
root.get(FeedSubscription_.category));
|
||||
p2 = builder.isNull(root.get(FeedSubscription_.category));
|
||||
} else {
|
||||
p2 = builder.equal(
|
||||
root.get(FeedSubscription_.category).get(FeedCategory_.id),
|
||||
category.getId());
|
||||
p2 = builder.equal(root.get(FeedSubscription_.category).get(FeedCategory_.id), category.getId());
|
||||
|
||||
}
|
||||
|
||||
query.where(p1, p2);
|
||||
|
||||
List<FeedSubscription> list = cache(em.createQuery(query))
|
||||
.getResultList();
|
||||
List<FeedSubscription> list = cache(em.createQuery(query)).getResultList();
|
||||
initRelations(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<FeedSubscription> findByCategories(User user,
|
||||
List<FeedCategory> categories) {
|
||||
|
||||
List<Long> categoryIds = Lists.transform(categories,
|
||||
new Function<FeedCategory, Long>() {
|
||||
@Override
|
||||
public Long apply(FeedCategory input) {
|
||||
return input.getId();
|
||||
}
|
||||
});
|
||||
public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) {
|
||||
|
||||
List<Long> categoryIds = Lists.transform(categories, new Function<FeedCategory, Long>() {
|
||||
@Override
|
||||
public Long apply(FeedCategory input) {
|
||||
return input.getId();
|
||||
}
|
||||
});
|
||||
|
||||
List<FeedSubscription> subscriptions = Lists.newArrayList();
|
||||
for (FeedSubscription sub : findAll(user)) {
|
||||
if (sub.getCategory() != null
|
||||
&& categoryIds.contains(sub.getCategory().getId())) {
|
||||
if (sub.getCategory() != null && categoryIds.contains(sub.getCategory().getId())) {
|
||||
subscriptions.add(sub);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,13 @@ public abstract class GenericDAO<T extends AbstractModel> {
|
||||
builder = em.getCriteriaBuilder();
|
||||
}
|
||||
|
||||
public void saveOrUpdate(Collection<? extends AbstractModel> models) {
|
||||
public Session getSession() {
|
||||
Session session = em.unwrap(Session.class);
|
||||
return session;
|
||||
}
|
||||
|
||||
public void saveOrUpdate(Collection<? extends AbstractModel> models) {
|
||||
Session session = getSession();
|
||||
int i = 1;
|
||||
for (AbstractModel model : models) {
|
||||
session.saveOrUpdate(model);
|
||||
@@ -91,8 +96,7 @@ public abstract class GenericDAO<T extends AbstractModel> {
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
public List<T> findAll(int startIndex, int count, String orderBy,
|
||||
boolean asc) {
|
||||
public List<T> findAll(int startIndex, int count, String orderBy, boolean asc) {
|
||||
|
||||
CriteriaQuery<T> query = builder.createQuery(getType());
|
||||
Root<T> root = query.from(getType());
|
||||
@@ -121,8 +125,7 @@ public abstract class GenericDAO<T extends AbstractModel> {
|
||||
return findByField(field, value, false);
|
||||
}
|
||||
|
||||
protected <V> List<T> findByField(Attribute<T, V> field, V value,
|
||||
boolean cache) {
|
||||
protected <V> List<T> findByField(Attribute<T, V> field, V value, boolean cache) {
|
||||
CriteriaQuery<T> query = builder.createQuery(getType());
|
||||
Root<T> root = query.from(getType());
|
||||
|
||||
@@ -152,7 +155,7 @@ public abstract class GenericDAO<T extends AbstractModel> {
|
||||
query.unwrap(Query.class).setCacheable(true);
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
protected void setTimeout(javax.persistence.Query query, int queryTimeout) {
|
||||
if (queryTimeout > 0) {
|
||||
query.setHint("javax.persistence.query.timeout", queryTimeout);
|
||||
|
||||
@@ -18,11 +18,10 @@ public class UserDAO extends GenericDAO<User> {
|
||||
|
||||
CriteriaQuery<User> query = builder.createQuery(getType());
|
||||
Root<User> root = query.from(getType());
|
||||
query.where(builder.equal(builder.lower(root.get(User_.name)),
|
||||
name.toLowerCase()));
|
||||
query.where(builder.equal(builder.lower(root.get(User_.name)), name.toLowerCase()));
|
||||
TypedQuery<User> q = em.createQuery(query);
|
||||
cache(q);
|
||||
|
||||
|
||||
User user = null;
|
||||
try {
|
||||
user = q.getSingleResult();
|
||||
@@ -38,7 +37,7 @@ public class UserDAO extends GenericDAO<User> {
|
||||
query.where(builder.equal(root.get(User_.apiKey), key));
|
||||
TypedQuery<User> q = em.createQuery(query);
|
||||
cache(q);
|
||||
|
||||
|
||||
User user = null;
|
||||
try {
|
||||
user = q.getSingleResult();
|
||||
|
||||
@@ -33,8 +33,7 @@ public class UserRoleDAO extends GenericDAO<UserRole> {
|
||||
CriteriaQuery<UserRole> query = builder.createQuery(getType());
|
||||
Root<UserRole> root = query.from(getType());
|
||||
|
||||
query.where(builder.equal(root.get(UserRole_.user).get(User_.id),
|
||||
user.getId()));
|
||||
query.where(builder.equal(root.get(UserRole_.user).get(User_.id), user.getId()));
|
||||
return cache(em.createQuery(query)).getResultList();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ public class UserSettingsDAO extends GenericDAO<UserSettings> {
|
||||
CriteriaQuery<UserSettings> query = builder.createQuery(getType());
|
||||
Root<UserSettings> root = query.from(getType());
|
||||
|
||||
query.where(builder.equal(root.get(UserSettings_.user).get(User_.id),
|
||||
user.getId()));
|
||||
query.where(builder.equal(root.get(UserSettings_.user).get(User_.id), user.getId()));
|
||||
|
||||
UserSettings settings = null;
|
||||
try {
|
||||
|
||||
@@ -5,12 +5,12 @@ import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
@@ -19,20 +19,16 @@ import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
* Inspired/Ported from https://github.com/potatolondon/getfavicon
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class FaviconFetcher {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(FeedFetcher.class);
|
||||
|
||||
private static long MIN_ICON_LENGTH = 100;
|
||||
private static long MAX_ICON_LENGTH = 20000;
|
||||
private static long MAX_ICON_LENGTH = 100000;
|
||||
private static int TIMEOUT = 4000;
|
||||
|
||||
protected static List<String> ICON_MIMETYPES = Arrays.asList(
|
||||
"image/x-icon", "image/vnd.microsoft.icon", "image/ico",
|
||||
"image/icon", "text/ico", "application/ico", "image/x-ms-bmp",
|
||||
"image/x-bmp", "image/gif", "image/png", "image/jpeg");
|
||||
private static List<String> ICON_MIMETYPE_BLACKLIST = Arrays.asList(
|
||||
"application/xml", "text/html");
|
||||
protected static List<String> ICON_MIMETYPES = Arrays.asList("image/x-icon", "image/vnd.microsoft.icon", "image/ico", "image/icon",
|
||||
"text/ico", "application/ico", "image/x-ms-bmp", "image/x-bmp", "image/gif", "image/png", "image/jpeg");
|
||||
private static List<String> ICON_MIMETYPE_BLACKLIST = Arrays.asList("application/xml", "text/html");
|
||||
|
||||
@Inject
|
||||
HttpGetter getter;
|
||||
@@ -84,7 +80,7 @@ public class FaviconFetcher {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
boolean isValidIconResponse(byte[] content, String contentType) {
|
||||
private boolean isValidIconResponse(byte[] content, String contentType) {
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -101,14 +97,12 @@ public class FaviconFetcher {
|
||||
}
|
||||
|
||||
if (length < MIN_ICON_LENGTH) {
|
||||
log.debug("Length {} below MIN_ICON_LENGTH {}", length,
|
||||
MIN_ICON_LENGTH);
|
||||
log.debug("Length {} below MIN_ICON_LENGTH {}", length, MIN_ICON_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length > MAX_ICON_LENGTH) {
|
||||
log.debug("Length {} greater than MAX_ICON_LENGTH {}", length,
|
||||
MAX_ICON_LENGTH);
|
||||
log.debug("Length {} greater than MAX_ICON_LENGTH {}", length, MAX_ICON_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,8 +120,7 @@ public class FaviconFetcher {
|
||||
return null;
|
||||
}
|
||||
|
||||
Elements icons = doc
|
||||
.select("link[rel~=(?i)^(shortcut|icon|shortcut icon)$]");
|
||||
Elements icons = doc.select("link[rel~=(?i)^(shortcut|icon|shortcut icon)$]");
|
||||
|
||||
if (icons.isEmpty()) {
|
||||
log.debug("No icon found in page {}", url);
|
||||
|
||||
@@ -5,14 +5,14 @@ import java.util.Date;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.binary.StringUtils;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
@@ -20,52 +20,54 @@ import com.commafeed.backend.HttpGetter.NotModifiedException;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
|
||||
@Slf4j
|
||||
public class FeedFetcher {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(FeedFetcher.class);
|
||||
|
||||
@Inject
|
||||
FeedParser parser;
|
||||
|
||||
@Inject
|
||||
HttpGetter getter;
|
||||
|
||||
public FetchedFeed fetch(String feedUrl, boolean extractFeedUrlFromHtml,
|
||||
String lastModified, String eTag, Date lastPublishedDate,
|
||||
String lastContentHash) throws FeedException,
|
||||
ClientProtocolException, IOException, NotModifiedException {
|
||||
public FetchedFeed fetch(String feedUrl, boolean extractFeedUrlFromHtml, String lastModified, String eTag, Date lastPublishedDate,
|
||||
String lastContentHash) throws FeedException, ClientProtocolException, IOException, NotModifiedException {
|
||||
log.debug("Fetching feed {}", feedUrl);
|
||||
FetchedFeed fetchedFeed = null;
|
||||
|
||||
int timeout = 20000;
|
||||
|
||||
HttpResult result = getter.getBinary(feedUrl, lastModified, eTag, timeout);
|
||||
if (extractFeedUrlFromHtml) {
|
||||
String extractedUrl = extractFeedUrl(
|
||||
StringUtils.newStringUtf8(result.getContent()), feedUrl);
|
||||
if (org.apache.commons.lang.StringUtils.isNotBlank(extractedUrl)) {
|
||||
result = getter.getBinary(extractedUrl, lastModified, eTag, timeout);
|
||||
feedUrl = extractedUrl;
|
||||
byte[] content = result.getContent();
|
||||
|
||||
try {
|
||||
fetchedFeed = parser.parse(feedUrl, content);
|
||||
} catch (FeedException e) {
|
||||
if (extractFeedUrlFromHtml) {
|
||||
String extractedUrl = extractFeedUrl(StringUtils.newStringUtf8(result.getContent()), feedUrl);
|
||||
if (org.apache.commons.lang.StringUtils.isNotBlank(extractedUrl)) {
|
||||
feedUrl = extractedUrl;
|
||||
|
||||
result = getter.getBinary(extractedUrl, lastModified, eTag, timeout);
|
||||
content = result.getContent();
|
||||
fetchedFeed = parser.parse(feedUrl, content);
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
byte[] content = result.getContent();
|
||||
|
||||
if (content == null) {
|
||||
throw new IOException("Feed content is empty.");
|
||||
}
|
||||
|
||||
String hash = DigestUtils.sha1Hex(content);
|
||||
if (lastContentHash != null && hash != null
|
||||
&& lastContentHash.equals(hash)) {
|
||||
if (lastContentHash != null && hash != null && lastContentHash.equals(hash)) {
|
||||
log.debug("content hash not modified: {}", feedUrl);
|
||||
throw new NotModifiedException("content hash not modified");
|
||||
}
|
||||
|
||||
fetchedFeed = parser.parse(feedUrl, content);
|
||||
|
||||
if (lastPublishedDate != null
|
||||
&& fetchedFeed.getFeed().getLastPublishedDate() != null
|
||||
&& lastPublishedDate.getTime() == fetchedFeed.getFeed()
|
||||
.getLastPublishedDate().getTime()) {
|
||||
if (lastPublishedDate != null && fetchedFeed.getFeed().getLastPublishedDate() != null
|
||||
&& lastPublishedDate.getTime() == fetchedFeed.getFeed().getLastPublishedDate().getTime()) {
|
||||
log.debug("publishedDate not modified: {}", feedUrl);
|
||||
throw new NotModifiedException("publishedDate not modified");
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@ import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.Namespace;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
@@ -29,17 +28,14 @@ import com.sun.syndication.feed.synd.SyndLinkImpl;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
import com.sun.syndication.io.SyndFeedInput;
|
||||
|
||||
@Slf4j
|
||||
public class FeedParser {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(FeedParser.class);
|
||||
|
||||
private static final String ATOM_10_URI = "http://www.w3.org/2005/Atom";
|
||||
private static final Namespace ATOM_10_NS = Namespace
|
||||
.getNamespace(ATOM_10_URI);
|
||||
private static final Namespace ATOM_10_NS = Namespace.getNamespace(ATOM_10_URI);
|
||||
|
||||
private static final Date START = new Date(86400000);
|
||||
private static final Date END = new Date(
|
||||
1000l * Integer.MAX_VALUE - 86400000);
|
||||
private static final Date END = new Date(1000l * Integer.MAX_VALUE - 86400000);
|
||||
|
||||
private static final Function<SyndContent, String> CONTENT_TO_STRING = new Function<SyndContent, String>() {
|
||||
public String apply(SyndContent content) {
|
||||
@@ -52,15 +48,12 @@ public class FeedParser {
|
||||
FetchedFeed fetchedFeed = new FetchedFeed();
|
||||
Feed feed = fetchedFeed.getFeed();
|
||||
List<FeedEntry> entries = fetchedFeed.getEntries();
|
||||
feed.setLastUpdated(new Date());
|
||||
|
||||
try {
|
||||
String encoding = FeedUtils.guessEncoding(xml);
|
||||
String xmlString = FeedUtils.trimInvalidXmlCharacters(new String(
|
||||
xml, encoding));
|
||||
String xmlString = FeedUtils.trimInvalidXmlCharacters(new String(xml, encoding));
|
||||
if (xmlString == null) {
|
||||
throw new FeedException("Input string is null for url "
|
||||
+ feedUrl);
|
||||
throw new FeedException("Input string is null for url " + feedUrl);
|
||||
}
|
||||
InputSource source = new InputSource(new StringReader(xmlString));
|
||||
SyndFeed rss = new SyndFeedInput().build(source);
|
||||
@@ -89,21 +82,16 @@ public class FeedParser {
|
||||
continue;
|
||||
}
|
||||
entry.setGuid(FeedUtils.truncate(guid, 2048));
|
||||
entry.setGuidHash(DigestUtils.sha1Hex(guid));
|
||||
entry.setUrl(FeedUtils.truncate(
|
||||
FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink()),
|
||||
2048));
|
||||
entry.setUrl(FeedUtils.truncate(FeedUtils.toAbsoluteUrl(item.getLink(), feed.getLink()), 2048));
|
||||
entry.setUpdated(validateDate(getEntryUpdateDate(item), true));
|
||||
entry.setAuthor(item.getAuthor());
|
||||
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setContent(getContent(item));
|
||||
content.setTitle(getTitle(item));
|
||||
SyndEnclosure enclosure = (SyndEnclosure) Iterables.getFirst(
|
||||
item.getEnclosures(), null);
|
||||
content.setAuthor(StringUtils.trimToNull(item.getAuthor()));
|
||||
SyndEnclosure enclosure = (SyndEnclosure) Iterables.getFirst(item.getEnclosures(), null);
|
||||
if (enclosure != null) {
|
||||
content.setEnclosureUrl(FeedUtils.truncate(
|
||||
enclosure.getUrl(), 2048));
|
||||
content.setEnclosureUrl(FeedUtils.truncate(enclosure.getUrl(), 2048));
|
||||
content.setEnclosureType(enclosure.getType());
|
||||
}
|
||||
entry.setContent(content);
|
||||
@@ -113,21 +101,17 @@ public class FeedParser {
|
||||
Date lastEntryDate = null;
|
||||
Date publishedDate = validateDate(rss.getPublishedDate(), false);
|
||||
if (!entries.isEmpty()) {
|
||||
List<Long> sortedTimestamps = FeedUtils
|
||||
.getSortedTimestamps(entries);
|
||||
List<Long> sortedTimestamps = FeedUtils.getSortedTimestamps(entries);
|
||||
Long timestamp = sortedTimestamps.get(0);
|
||||
lastEntryDate = new Date(timestamp);
|
||||
publishedDate = getFeedPublishedDate(publishedDate, entries);
|
||||
publishedDate = (publishedDate == null || publishedDate.before(lastEntryDate)) ? lastEntryDate : publishedDate;
|
||||
}
|
||||
feed.setLastPublishedDate(validateDate(publishedDate, true));
|
||||
feed.setAverageEntryInterval(FeedUtils
|
||||
.averageTimeBetweenEntries(entries));
|
||||
feed.setLastPublishedDate(publishedDate);
|
||||
feed.setAverageEntryInterval(FeedUtils.averageTimeBetweenEntries(entries));
|
||||
feed.setLastEntryDate(lastEntryDate);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FeedException(String.format(
|
||||
"Could not parse feed from %s : %s", feedUrl,
|
||||
e.getMessage()), e);
|
||||
throw new FeedException(String.format("Could not parse feed from %s : %s", feedUrl, e.getMessage()), e);
|
||||
}
|
||||
return fetchedFeed;
|
||||
}
|
||||
@@ -146,8 +130,7 @@ public class FeedParser {
|
||||
for (Object object : elements) {
|
||||
if (object instanceof Element) {
|
||||
Element element = (Element) object;
|
||||
if ("link".equals(element.getName())
|
||||
&& ATOM_10_NS.equals(element.getNamespace())) {
|
||||
if ("link".equals(element.getName()) && ATOM_10_NS.equals(element.getNamespace())) {
|
||||
SyndLink link = new SyndLinkImpl();
|
||||
link.setRel(element.getAttributeValue("rel"));
|
||||
link.setHref(element.getAttributeValue("href"));
|
||||
@@ -158,17 +141,6 @@ public class FeedParser {
|
||||
}
|
||||
}
|
||||
|
||||
private Date getFeedPublishedDate(Date publishedDate,
|
||||
List<FeedEntry> entries) {
|
||||
|
||||
for (FeedEntry entry : entries) {
|
||||
if (publishedDate == null || entry.getUpdated().getTime() > publishedDate.getTime()) {
|
||||
publishedDate = entry.getUpdated();
|
||||
}
|
||||
}
|
||||
return publishedDate;
|
||||
}
|
||||
|
||||
private Date getEntryUpdateDate(SyndEntry item) {
|
||||
Date date = item.getUpdatedDate();
|
||||
if (date == null) {
|
||||
@@ -199,14 +171,11 @@ public class FeedParser {
|
||||
private String getContent(SyndEntry item) {
|
||||
String content = null;
|
||||
if (item.getContents().isEmpty()) {
|
||||
content = item.getDescription() == null ? null : item
|
||||
.getDescription().getValue();
|
||||
content = item.getDescription() == null ? null : item.getDescription().getValue();
|
||||
} else {
|
||||
content = StringUtils.join(Collections2.transform(
|
||||
item.getContents(), CONTENT_TO_STRING),
|
||||
SystemUtils.LINE_SEPARATOR);
|
||||
content = StringUtils.join(Collections2.transform(item.getContents(), CONTENT_TO_STRING), SystemUtils.LINE_SEPARATOR);
|
||||
}
|
||||
return content;
|
||||
return StringUtils.trimToNull(content);
|
||||
}
|
||||
|
||||
private String getTitle(SyndEntry item) {
|
||||
@@ -219,15 +188,14 @@ public class FeedParser {
|
||||
title = "(no title)";
|
||||
}
|
||||
}
|
||||
return title;
|
||||
return StringUtils.trimToNull(title);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String findHub(SyndFeed feed) {
|
||||
for (SyndLink l : (List<SyndLink>) feed.getLinks()) {
|
||||
if ("hub".equalsIgnoreCase(l.getRel())) {
|
||||
log.debug("found hub {} for feed {}", l.getHref(),
|
||||
feed.getLink());
|
||||
log.debug("found hub {} for feed {}", l.getHref(), feed.getLink());
|
||||
return l.getHref();
|
||||
}
|
||||
}
|
||||
@@ -238,8 +206,7 @@ public class FeedParser {
|
||||
private String findSelf(SyndFeed feed) {
|
||||
for (SyndLink l : (List<SyndLink>) feed.getLinks()) {
|
||||
if ("self".equalsIgnoreCase(l.getRel())) {
|
||||
log.debug("found self {} for feed {}", l.getHref(),
|
||||
feed.getLink());
|
||||
log.debug("found self {} for feed {}", l.getHref(), feed.getLink());
|
||||
return l.getHref();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.commafeed.backend.feeds;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
|
||||
public class FeedRefreshContext {
|
||||
private Feed feed;
|
||||
private List<FeedEntry> entries;
|
||||
private boolean isUrgent;
|
||||
|
||||
public FeedRefreshContext(Feed feed, boolean isUrgent) {
|
||||
this.feed = feed;
|
||||
this.isUrgent = isUrgent;
|
||||
}
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public void setFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public boolean isUrgent() {
|
||||
return isUrgent;
|
||||
}
|
||||
|
||||
public void setUrgent(boolean isUrgent) {
|
||||
this.isUrgent = isUrgent;
|
||||
}
|
||||
|
||||
public List<FeedEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void setEntries(List<FeedEntry> entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,42 +2,39 @@ package com.commafeed.backend.feeds;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Wraps a {@link ThreadPoolExecutor} instance. Blocks when queue is full instead of rejecting the task. Allow priority queueing by using
|
||||
* {@link Task} instead of {@link Runnable}
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class FeedRefreshExecutor {
|
||||
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(FeedRefreshExecutor.class);
|
||||
|
||||
private String poolName;
|
||||
private ThreadPoolExecutor pool;
|
||||
private LinkedBlockingDeque<Runnable> queue;
|
||||
|
||||
public FeedRefreshExecutor(final String poolName, int threads,
|
||||
int queueCapacity) {
|
||||
public FeedRefreshExecutor(final String poolName, int threads, int queueCapacity) {
|
||||
log.info("Creating pool {} with {} threads", poolName, threads);
|
||||
this.poolName = poolName;
|
||||
pool = new ThreadPoolExecutor(threads, threads, 0,
|
||||
TimeUnit.MILLISECONDS,
|
||||
queue = new LinkedBlockingDeque<Runnable>(queueCapacity) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
pool = new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS, queue = new LinkedBlockingDeque<Runnable>(queueCapacity) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public boolean offer(Runnable r) {
|
||||
Task task = (Task) r;
|
||||
if (task.isUrgent()) {
|
||||
return offerFirst(r);
|
||||
} else {
|
||||
return offerLast(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public boolean offer(Runnable r) {
|
||||
Task task = (Task) r;
|
||||
if (task.isUrgent()) {
|
||||
return offerFirst(r);
|
||||
} else {
|
||||
return offerLast(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
pool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
@@ -50,12 +47,10 @@ public class FeedRefreshExecutor {
|
||||
queue.put(r);
|
||||
}
|
||||
} catch (InterruptedException e1) {
|
||||
log.error(poolName
|
||||
+ " interrupted while waiting for queue.", e1);
|
||||
log.error(poolName + " interrupted while waiting for queue.", e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
pool.setThreadFactory(new NamedThreadFactory(poolName));
|
||||
}
|
||||
|
||||
public void execute(Task task) {
|
||||
@@ -80,34 +75,8 @@ public class FeedRefreshExecutor {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
log.error(
|
||||
"{} interrupted while waiting for threads to finish.",
|
||||
poolName);
|
||||
log.error("{} interrupted while waiting for threads to finish.", poolName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NamedThreadFactory implements ThreadFactory {
|
||||
private final ThreadGroup group;
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
private final String namePrefix;
|
||||
|
||||
private NamedThreadFactory(String poolName) {
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
group = (s != null) ? s.getThreadGroup() :
|
||||
Thread.currentThread().getThreadGroup();
|
||||
namePrefix = poolName + "-thread-";
|
||||
}
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(group, r,
|
||||
namePrefix + threadNumber.getAndIncrement(),
|
||||
0);
|
||||
if (t.isDaemon())
|
||||
t.setDaemon(false);
|
||||
if (t.getPriority() != Thread.NORM_PRIORITY)
|
||||
t.setPriority(Thread.NORM_PRIORITY);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,24 +12,28 @@ import javax.annotation.PreDestroy;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.MetricsBean;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.google.api.client.util.Maps;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Queues;
|
||||
|
||||
/**
|
||||
* Infinite loop fetching feeds from the database and queuing them to the {@link FeedRefreshWorker} pool. Also handles feed database updates
|
||||
* at the end of the cycle through {@link #giveBack(Feed)}.
|
||||
*
|
||||
*/
|
||||
@ApplicationScoped
|
||||
@Slf4j
|
||||
public class FeedRefreshTaskGiver {
|
||||
|
||||
protected static final Logger log = LoggerFactory.getLogger(FeedRefreshTaskGiver.class);
|
||||
|
||||
@Inject
|
||||
FeedDAO feedDAO;
|
||||
|
||||
@@ -44,16 +48,15 @@ public class FeedRefreshTaskGiver {
|
||||
|
||||
private int backgroundThreads;
|
||||
|
||||
private Queue<Feed> addQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<Feed> takeQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<FeedRefreshContext> addQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<FeedRefreshContext> takeQueue = Queues.newConcurrentLinkedQueue();
|
||||
private Queue<Feed> giveBackQueue = Queues.newConcurrentLinkedQueue();
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
backgroundThreads = applicationSettingsService.get()
|
||||
.getBackgroundThreads();
|
||||
backgroundThreads = applicationSettingsService.get().getBackgroundThreads();
|
||||
executor = Executors.newFixedThreadPool(1);
|
||||
}
|
||||
|
||||
@@ -83,12 +86,13 @@ public class FeedRefreshTaskGiver {
|
||||
public void run() {
|
||||
while (!executor.isShutdown()) {
|
||||
try {
|
||||
Feed feed = take();
|
||||
if (feed != null) {
|
||||
FeedRefreshContext context = take();
|
||||
if (context != null) {
|
||||
metricsBean.feedRefreshed();
|
||||
worker.updateFeed(feed);
|
||||
worker.updateFeed(context);
|
||||
} else {
|
||||
log.debug("nothing to do, sleeping for 15s");
|
||||
metricsBean.threadWaited();
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -103,72 +107,99 @@ public class FeedRefreshTaskGiver {
|
||||
});
|
||||
}
|
||||
|
||||
private Feed take() {
|
||||
Feed feed = takeQueue.poll();
|
||||
/**
|
||||
* take a feed from the refresh queue
|
||||
*/
|
||||
private FeedRefreshContext take() {
|
||||
FeedRefreshContext context = takeQueue.poll();
|
||||
|
||||
if (feed == null) {
|
||||
if (context == null) {
|
||||
refill();
|
||||
feed = takeQueue.poll();
|
||||
context = takeQueue.poll();
|
||||
}
|
||||
return feed;
|
||||
return context;
|
||||
}
|
||||
|
||||
public Long getUpdatableCount() {
|
||||
return feedDAO.getUpdatableCount(getThreshold());
|
||||
return feedDAO.getUpdatableCount(getLastLoginThreshold());
|
||||
}
|
||||
|
||||
private Date getThreshold() {
|
||||
boolean heavyLoad = applicationSettingsService.get().isHeavyLoad();
|
||||
Date threshold = DateUtils.addMinutes(new Date(), heavyLoad ? -15 : -5);
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void add(Feed feed) {
|
||||
Date threshold = getThreshold();
|
||||
if (feed.getLastUpdated() == null
|
||||
|| feed.getLastUpdated().before(threshold)) {
|
||||
addQueue.add(feed);
|
||||
/**
|
||||
* add a feed to the refresh queue
|
||||
*/
|
||||
public void add(Feed feed, boolean urgent) {
|
||||
int refreshInterval = applicationSettingsService.get().getRefreshIntervalMinutes();
|
||||
if (feed.getLastUpdated() == null || feed.getLastUpdated().before(DateUtils.addMinutes(new Date(), -1 * refreshInterval))) {
|
||||
addQueue.add(new FeedRefreshContext(feed, urgent));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* refills the refresh queue and empties the giveBack queue while at it
|
||||
*/
|
||||
private void refill() {
|
||||
Date now = new Date();
|
||||
|
||||
int count = Math.min(100, 3 * backgroundThreads);
|
||||
List<Feed> feeds = null;
|
||||
if (applicationSettingsService.get().isCrawlingPaused()) {
|
||||
feeds = Lists.newArrayList();
|
||||
} else {
|
||||
feeds = feedDAO.findNextUpdatable(count, getThreshold());
|
||||
|
||||
// first, get feeds that are up to refresh from the database
|
||||
List<FeedRefreshContext> contexts = Lists.newArrayList();
|
||||
if (!applicationSettingsService.get().isCrawlingPaused()) {
|
||||
List<Feed> feeds = feedDAO.findNextUpdatable(count, getLastLoginThreshold());
|
||||
for (Feed feed : feeds) {
|
||||
contexts.add(new FeedRefreshContext(feed, false));
|
||||
}
|
||||
}
|
||||
|
||||
// then, add to those the feeds we got from the add() method. We add them at the beginning of the list as they probably have a
|
||||
// higher priority
|
||||
int size = addQueue.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
feeds.add(0, addQueue.poll());
|
||||
contexts.add(0, addQueue.poll());
|
||||
}
|
||||
|
||||
Map<Long, Feed> map = Maps.newLinkedHashMap();
|
||||
for (Feed f : feeds) {
|
||||
f.setLastUpdated(now);
|
||||
map.put(f.getId(), f);
|
||||
// set the disabledDate to now as we use the disabledDate in feedDAO to decide what to refresh next. We also use a map to remove
|
||||
// duplicates.
|
||||
Map<Long, FeedRefreshContext> map = Maps.newLinkedHashMap();
|
||||
for (FeedRefreshContext context : contexts) {
|
||||
Feed feed = context.getFeed();
|
||||
feed.setDisabledUntil(new Date());
|
||||
map.put(feed.getId(), context);
|
||||
}
|
||||
|
||||
// refill the queue
|
||||
takeQueue.addAll(map.values());
|
||||
|
||||
// add feeds from the giveBack queue to the map, overriding duplicates
|
||||
size = giveBackQueue.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Feed f = giveBackQueue.poll();
|
||||
f.setLastUpdated(now);
|
||||
map.put(f.getId(), f);
|
||||
Feed feed = giveBackQueue.poll();
|
||||
map.put(feed.getId(), new FeedRefreshContext(feed, false));
|
||||
}
|
||||
|
||||
feedDAO.saveOrUpdate(map.values());
|
||||
// update all feeds in the database
|
||||
List<Feed> feeds = Lists.newArrayList();
|
||||
for (FeedRefreshContext context : map.values()) {
|
||||
feeds.add(context.getFeed());
|
||||
}
|
||||
feedDAO.saveOrUpdate(feeds);
|
||||
}
|
||||
|
||||
/**
|
||||
* give a feed back, updating it to the database during the next refill()
|
||||
*/
|
||||
public void giveBack(Feed feed) {
|
||||
String normalized = FeedUtils.normalizeURL(feed.getUrl());
|
||||
feed.setNormalizedUrl(normalized);
|
||||
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized));
|
||||
feed.setLastUpdated(new Date());
|
||||
giveBackQueue.add(feed);
|
||||
}
|
||||
|
||||
private Date getLastLoginThreshold() {
|
||||
if (applicationSettingsService.get().isHeavyLoad()) {
|
||||
return DateUtils.addDays(new Date(), -30);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.commafeed.backend.feeds;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -11,10 +12,12 @@ import javax.annotation.PreDestroy;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.MetricsBean;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
@@ -25,19 +28,19 @@ import com.commafeed.backend.feeds.FeedRefreshExecutor.Task;
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.pubsubhubbub.SubscriptionHandler;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.backend.services.FeedUpdateService;
|
||||
import com.google.api.client.util.Lists;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.Striped;
|
||||
|
||||
@ApplicationScoped
|
||||
@Slf4j
|
||||
public class FeedRefreshUpdater {
|
||||
|
||||
protected static Logger log = LoggerFactory
|
||||
.getLogger(FeedRefreshUpdater.class);
|
||||
|
||||
@Inject
|
||||
FeedUpdateService feedUpdateService;
|
||||
|
||||
@@ -72,7 +75,7 @@ public class FeedRefreshUpdater {
|
||||
public void init() {
|
||||
ApplicationSettings settings = applicationSettingsService.get();
|
||||
int threads = Math.max(settings.getDatabaseUpdateThreads(), 1);
|
||||
pool = new FeedRefreshExecutor("feed-refresh-updater", threads, 500 * threads);
|
||||
pool = new FeedRefreshExecutor("feed-refresh-updater", threads, Math.min(50 * threads, 1000));
|
||||
locks = Striped.lazyWeakLock(threads * 100000);
|
||||
}
|
||||
|
||||
@@ -81,25 +84,26 @@ public class FeedRefreshUpdater {
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
public void updateFeed(Feed feed, Collection<FeedEntry> entries) {
|
||||
pool.execute(new EntryTask(feed, entries));
|
||||
public void updateFeed(FeedRefreshContext context) {
|
||||
pool.execute(new EntryTask(context));
|
||||
}
|
||||
|
||||
private class EntryTask implements Task {
|
||||
|
||||
private Feed feed;
|
||||
private Collection<FeedEntry> entries;
|
||||
private FeedRefreshContext context;
|
||||
|
||||
public EntryTask(Feed feed, Collection<FeedEntry> entries) {
|
||||
this.feed = feed;
|
||||
this.entries = entries;
|
||||
public EntryTask(FeedRefreshContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean ok = true;
|
||||
if (entries.isEmpty() == false) {
|
||||
|
||||
Feed feed = context.getFeed();
|
||||
List<FeedEntry> entries = context.getEntries();
|
||||
if (entries.isEmpty()) {
|
||||
feed.setMessage("Feed has no entries");
|
||||
} else {
|
||||
List<String> lastEntries = cache.getLastEntries(feed);
|
||||
List<String> currentEntries = Lists.newArrayList();
|
||||
|
||||
@@ -109,25 +113,39 @@ public class FeedRefreshUpdater {
|
||||
if (!lastEntries.contains(cacheKey)) {
|
||||
log.debug("cache miss for {}", entry.getUrl());
|
||||
if (subscriptions == null) {
|
||||
subscriptions = feedSubscriptionDAO
|
||||
.findByFeed(feed);
|
||||
subscriptions = feedSubscriptionDAO.findByFeed(feed);
|
||||
}
|
||||
ok &= updateEntry(feed, entry, subscriptions);
|
||||
ok &= addEntry(feed, entry, subscriptions);
|
||||
metricsBean.entryCacheMiss();
|
||||
} else {
|
||||
log.debug("cache hit for {}", entry.getUrl());
|
||||
metricsBean.entryCacheHit();
|
||||
}
|
||||
|
||||
currentEntries.add(cacheKey);
|
||||
}
|
||||
cache.setLastEntries(feed, currentEntries);
|
||||
|
||||
if (subscriptions == null) {
|
||||
feed.setMessage("No new entries found");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(subscriptions)) {
|
||||
List<User> users = Lists.newArrayList();
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
users.add(sub.getUser());
|
||||
}
|
||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
|
||||
cache.invalidateUserRootCategory(users.toArray(new User[0]));
|
||||
}
|
||||
}
|
||||
|
||||
if (applicationSettingsService.get().isPubsubhubbub()) {
|
||||
handlePubSub(feed);
|
||||
}
|
||||
if (!ok) {
|
||||
feed.setDisabledUntil(null);
|
||||
// requeue asap
|
||||
feed.setDisabledUntil(new Date(0));
|
||||
}
|
||||
metricsBean.feedUpdated();
|
||||
taskGiver.giveBack(feed);
|
||||
@@ -135,31 +153,47 @@ public class FeedRefreshUpdater {
|
||||
|
||||
@Override
|
||||
public boolean isUrgent() {
|
||||
return feed.isUrgent();
|
||||
return context.isUrgent();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean updateEntry(final Feed feed, final FeedEntry entry,
|
||||
final List<FeedSubscription> subscriptions) {
|
||||
private boolean addEntry(final Feed feed, final FeedEntry entry, final List<FeedSubscription> subscriptions) {
|
||||
boolean success = false;
|
||||
|
||||
String key = StringUtils.trimToEmpty(entry.getGuid() + entry.getUrl());
|
||||
Lock lock = locks.get(key);
|
||||
boolean locked = false;
|
||||
// lock on feed, make sure we are not updating the same feed twice at
|
||||
// the same time
|
||||
String key1 = StringUtils.trimToEmpty("" + feed.getId());
|
||||
|
||||
// lock on content, make sure we are not updating the same entry
|
||||
// twice at the same time
|
||||
FeedEntryContent content = entry.getContent();
|
||||
String key2 = DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.getContent() + content.getTitle()));
|
||||
|
||||
Iterator<Lock> iterator = locks.bulkGet(Arrays.asList(key1, key2)).iterator();
|
||||
Lock lock1 = iterator.next();
|
||||
Lock lock2 = iterator.next();
|
||||
boolean locked1 = false;
|
||||
boolean locked2 = false;
|
||||
try {
|
||||
locked = lock.tryLock(1, TimeUnit.MINUTES);
|
||||
if (locked) {
|
||||
feedUpdateService.updateEntry(feed, entry, subscriptions);
|
||||
locked1 = lock1.tryLock(1, TimeUnit.MINUTES);
|
||||
locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
|
||||
if (locked1 && locked2) {
|
||||
boolean inserted = feedUpdateService.addEntry(feed, entry);
|
||||
if (inserted) {
|
||||
metricsBean.entryInserted();
|
||||
}
|
||||
success = true;
|
||||
} else {
|
||||
log.error("lock timeout for " + feed.getUrl() + " - " + key);
|
||||
log.error("lock timeout for " + feed.getUrl() + " - " + key1);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.error("interrupted while waiting for lock for " + feed.getUrl()
|
||||
+ " : " + e.getMessage(), e);
|
||||
log.error("interrupted while waiting for lock for " + feed.getUrl() + " : " + e.getMessage(), e);
|
||||
} finally {
|
||||
if (locked) {
|
||||
lock.unlock();
|
||||
if (locked1) {
|
||||
lock1.unlock();
|
||||
}
|
||||
if (locked2) {
|
||||
lock2.unlock();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
|
||||
@@ -8,26 +8,26 @@ import javax.annotation.PreDestroy;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
|
||||
import com.commafeed.backend.HttpGetter.NotModifiedException;
|
||||
import com.commafeed.backend.MetricsBean;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.feeds.FeedRefreshExecutor.Task;
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
|
||||
/**
|
||||
* Calls {@link FeedFetcher} and handles its outcome
|
||||
*
|
||||
*/
|
||||
@ApplicationScoped
|
||||
@Slf4j
|
||||
public class FeedRefreshWorker {
|
||||
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(FeedRefreshWorker.class);
|
||||
|
||||
@Inject
|
||||
FeedRefreshUpdater feedRefreshUpdater;
|
||||
|
||||
@@ -40,20 +40,13 @@ public class FeedRefreshWorker {
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@Inject
|
||||
MetricsBean metricsBean;
|
||||
|
||||
@Inject
|
||||
FeedEntryDAO feedEntryDAO;
|
||||
|
||||
private FeedRefreshExecutor pool;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
ApplicationSettings settings = applicationSettingsService.get();
|
||||
int threads = settings.getBackgroundThreads();
|
||||
pool = new FeedRefreshExecutor("feed-refresh-worker", threads,
|
||||
20 * threads);
|
||||
pool = new FeedRefreshExecutor("feed-refresh-worker", threads, Math.min(20 * threads, 1000));
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
@@ -61,8 +54,8 @@ public class FeedRefreshWorker {
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
public void updateFeed(Feed feed) {
|
||||
pool.execute(new FeedTask(feed));
|
||||
public void updateFeed(FeedRefreshContext context) {
|
||||
pool.execute(new FeedTask(context));
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
@@ -75,50 +68,45 @@ public class FeedRefreshWorker {
|
||||
|
||||
private class FeedTask implements Task {
|
||||
|
||||
private Feed feed;
|
||||
private FeedRefreshContext context;
|
||||
|
||||
public FeedTask(Feed feed) {
|
||||
this.feed = feed;
|
||||
public FeedTask(FeedRefreshContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
update(feed);
|
||||
update(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUrgent() {
|
||||
return feed.isUrgent();
|
||||
return context.isUrgent();
|
||||
}
|
||||
}
|
||||
|
||||
private void update(Feed feed) {
|
||||
Date now = new Date();
|
||||
private void update(FeedRefreshContext context) {
|
||||
Feed feed = context.getFeed();
|
||||
int refreshInterval = applicationSettingsService.get().getRefreshIntervalMinutes();
|
||||
Date disabledUntil = DateUtils.addMinutes(new Date(), refreshInterval);
|
||||
try {
|
||||
FetchedFeed fetchedFeed = fetcher.fetch(feed.getUrl(), false,
|
||||
feed.getLastModifiedHeader(), feed.getEtagHeader(),
|
||||
FetchedFeed fetchedFeed = fetcher.fetch(feed.getUrl(), false, feed.getLastModifiedHeader(), feed.getEtagHeader(),
|
||||
feed.getLastPublishedDate(), feed.getLastContentHash());
|
||||
// stops here if NotModifiedException or any other exception is
|
||||
// thrown
|
||||
List<FeedEntry> entries = fetchedFeed.getEntries();
|
||||
|
||||
Date disabledUntil = null;
|
||||
if (applicationSettingsService.get().isHeavyLoad()) {
|
||||
disabledUntil = FeedUtils.buildDisabledUntil(fetchedFeed
|
||||
.getFeed().getLastEntryDate(), fetchedFeed.getFeed()
|
||||
.getAverageEntryInterval());
|
||||
disabledUntil = FeedUtils.buildDisabledUntil(fetchedFeed.getFeed().getLastEntryDate(), fetchedFeed.getFeed()
|
||||
.getAverageEntryInterval(), disabledUntil);
|
||||
}
|
||||
|
||||
feed.setLastUpdateSuccess(now);
|
||||
feed.setLink(fetchedFeed.getFeed().getLink());
|
||||
feed.setLastModifiedHeader(fetchedFeed.getFeed()
|
||||
.getLastModifiedHeader());
|
||||
feed.setLastModifiedHeader(fetchedFeed.getFeed().getLastModifiedHeader());
|
||||
feed.setEtagHeader(fetchedFeed.getFeed().getEtagHeader());
|
||||
feed.setLastContentHash(fetchedFeed.getFeed().getLastContentHash());
|
||||
feed.setLastPublishedDate(fetchedFeed.getFeed()
|
||||
.getLastPublishedDate());
|
||||
feed.setAverageEntryInterval(fetchedFeed.getFeed()
|
||||
.getAverageEntryInterval());
|
||||
feed.setLastPublishedDate(fetchedFeed.getFeed().getLastPublishedDate());
|
||||
feed.setAverageEntryInterval(fetchedFeed.getFeed().getAverageEntryInterval());
|
||||
feed.setLastEntryDate(fetchedFeed.getFeed().getLastEntryDate());
|
||||
|
||||
feed.setErrorCount(0);
|
||||
@@ -126,36 +114,27 @@ public class FeedRefreshWorker {
|
||||
feed.setDisabledUntil(disabledUntil);
|
||||
|
||||
handlePubSub(feed, fetchedFeed.getFeed());
|
||||
feedRefreshUpdater.updateFeed(feed, entries);
|
||||
context.setEntries(entries);
|
||||
feedRefreshUpdater.updateFeed(context);
|
||||
|
||||
} catch (NotModifiedException e) {
|
||||
log.debug("Feed not modified : {} - {}", feed.getUrl(),
|
||||
e.getMessage());
|
||||
log.debug("Feed not modified : {} - {}", feed.getUrl(), e.getMessage());
|
||||
|
||||
Date disabledUntil = null;
|
||||
if (applicationSettingsService.get().isHeavyLoad()) {
|
||||
disabledUntil = FeedUtils
|
||||
.buildDisabledUntil(feed.getLastEntryDate(),
|
||||
feed.getAverageEntryInterval());
|
||||
disabledUntil = FeedUtils.buildDisabledUntil(feed.getLastEntryDate(), feed.getAverageEntryInterval(), disabledUntil);
|
||||
}
|
||||
feed.setErrorCount(0);
|
||||
feed.setMessage(null);
|
||||
feed.setMessage(e.getMessage());
|
||||
feed.setDisabledUntil(disabledUntil);
|
||||
|
||||
taskGiver.giveBack(feed);
|
||||
} catch (Exception e) {
|
||||
String message = "Unable to refresh feed " + feed.getUrl() + " : "
|
||||
+ e.getMessage();
|
||||
if (e instanceof FeedException) {
|
||||
log.debug(e.getClass().getName() + " " + message, e);
|
||||
} else {
|
||||
log.debug(e.getClass().getName() + " " + message, e);
|
||||
}
|
||||
String message = "Unable to refresh feed " + feed.getUrl() + " : " + e.getMessage();
|
||||
log.debug(e.getClass().getName() + " " + message, e);
|
||||
|
||||
feed.setErrorCount(feed.getErrorCount() + 1);
|
||||
feed.setMessage(message);
|
||||
feed.setDisabledUntil(FeedUtils.buildDisabledUntil(feed
|
||||
.getErrorCount()));
|
||||
feed.setDisabledUntil(FeedUtils.buildDisabledUntil(feed.getErrorCount()));
|
||||
|
||||
taskGiver.giveBack(feed);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.commafeed.backend.feeds;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
@@ -21,30 +24,31 @@ import org.jsoup.safety.Cleaner;
|
||||
import org.jsoup.safety.Whitelist;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.mozilla.universalchardet.UniversalDetector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.css.sac.InputSource;
|
||||
import org.w3c.dom.css.CSSStyleDeclaration;
|
||||
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.google.api.client.util.Base64;
|
||||
import com.google.api.client.util.Lists;
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gwt.i18n.client.HasDirection.Direction;
|
||||
import com.google.gwt.i18n.shared.BidiUtils;
|
||||
import com.steadystate.css.parser.CSSOMParser;
|
||||
|
||||
import edu.uci.ics.crawler4j.url.URLCanonicalizer;
|
||||
|
||||
/**
|
||||
* Utility methods related to feed handling
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class FeedUtils {
|
||||
|
||||
protected static Logger log = LoggerFactory.getLogger(FeedUtils.class);
|
||||
|
||||
private static final String ESCAPED_QUESTION_MARK = Pattern.quote("?");
|
||||
private static final List<String> ALLOWED_IFRAME_CSS_RULES = Arrays.asList(
|
||||
"height", "width", "border");
|
||||
private static final char[] DISALLOWED_IFRAME_CSS_RULE_CHARACTERS = new char[] {
|
||||
'(', ')' };
|
||||
|
||||
private static final List<String> ALLOWED_IFRAME_CSS_RULES = Arrays.asList("height", "width", "border");
|
||||
private static final List<String> ALLOWED_IMG_CSS_RULES = Arrays.asList("display", "width", "height");
|
||||
private static final char[] FORBIDDEN_CSS_RULE_CHARACTERS = new char[] { '(', ')' };
|
||||
|
||||
public static String truncate(String string, int length) {
|
||||
if (string != null) {
|
||||
@@ -54,8 +58,8 @@ public class FeedUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect feed encoding by using the declared encoding in the xml processing
|
||||
* instruction and by detecting the characters used in the feed
|
||||
* Detect feed encoding by using the declared encoding in the xml processing instruction and by detecting the characters used in the
|
||||
* feed
|
||||
*
|
||||
*/
|
||||
public static String guessEncoding(byte[] bytes) {
|
||||
@@ -64,6 +68,8 @@ public class FeedUtils {
|
||||
if (StringUtils.endsWith(extracted, "1") == false) {
|
||||
return extracted;
|
||||
}
|
||||
} else if (StringUtils.startsWithIgnoreCase(extracted, "windows-")) {
|
||||
return extracted;
|
||||
}
|
||||
return detectEncoding(bytes);
|
||||
}
|
||||
@@ -87,8 +93,7 @@ public class FeedUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the url. The resulting url is not meant to be fetched but
|
||||
* rather used as a mean to identify a feed and avoid duplicates
|
||||
* Normalize the url. The resulting url is not meant to be fetched but rather used as a mean to identify a feed and avoid duplicates
|
||||
*/
|
||||
public static String normalizeURL(String url) {
|
||||
if (url == null) {
|
||||
@@ -113,13 +118,11 @@ public class FeedUtils {
|
||||
normalized = normalized.replace("//www.", "//");
|
||||
|
||||
// feedproxy redirects to feedburner
|
||||
normalized = normalized.replace("feedproxy.google.com",
|
||||
"feeds.feedburner.com");
|
||||
normalized = normalized.replace("feedproxy.google.com", "feeds.feedburner.com");
|
||||
|
||||
// feedburner feeds have a special treatment
|
||||
if (normalized.split(ESCAPED_QUESTION_MARK)[0].contains("feedburner.com")) {
|
||||
normalized = normalized.replace("feeds2.feedburner.com",
|
||||
"feeds.feedburner.com");
|
||||
normalized = normalized.replace("feeds2.feedburner.com", "feeds.feedburner.com");
|
||||
normalized = normalized.split(ESCAPED_QUESTION_MARK)[0];
|
||||
normalized = StringUtils.removeEnd(normalized, "/");
|
||||
}
|
||||
@@ -146,17 +149,13 @@ public class FeedUtils {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public static String handleContent(String content, String baseUri,
|
||||
boolean keepTextOnly) {
|
||||
public static String handleContent(String content, String baseUri, boolean keepTextOnly) {
|
||||
if (StringUtils.isNotBlank(content)) {
|
||||
baseUri = StringUtils.trimToEmpty(baseUri);
|
||||
Whitelist whitelist = new Whitelist();
|
||||
whitelist.addTags("a", "b", "blockquote", "br", "caption", "cite",
|
||||
"code", "col", "colgroup", "dd", "div", "dl", "dt", "em",
|
||||
"h1", "h2", "h3", "h4", "h5", "h6", "i", "iframe", "img",
|
||||
"li", "ol", "p", "pre", "q", "small", "strike", "strong",
|
||||
"sub", "sup", "table", "tbody", "td", "tfoot", "th",
|
||||
"thead", "tr", "u", "ul");
|
||||
whitelist.addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt", "em",
|
||||
"h1", "h2", "h3", "h4", "h5", "h6", "i", "iframe", "img", "li", "ol", "p", "pre", "q", "small", "strike", "strong",
|
||||
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
|
||||
|
||||
whitelist.addAttributes("div", "dir");
|
||||
whitelist.addAttributes("pre", "dir");
|
||||
@@ -167,22 +166,16 @@ public class FeedUtils {
|
||||
whitelist.addAttributes("blockquote", "cite");
|
||||
whitelist.addAttributes("col", "span", "width");
|
||||
whitelist.addAttributes("colgroup", "span", "width");
|
||||
whitelist.addAttributes("iframe", "src", "height", "width",
|
||||
"allowfullscreen", "frameborder", "style");
|
||||
whitelist.addAttributes("img", "align", "alt", "height", "src",
|
||||
"title", "width");
|
||||
whitelist.addAttributes("iframe", "src", "height", "width", "allowfullscreen", "frameborder", "style");
|
||||
whitelist.addAttributes("img", "align", "alt", "height", "src", "title", "width", "style");
|
||||
whitelist.addAttributes("ol", "start", "type");
|
||||
whitelist.addAttributes("q", "cite");
|
||||
whitelist.addAttributes("table", "border", "bordercolor",
|
||||
"summary", "width");
|
||||
whitelist.addAttributes("td", "border", "bordercolor", "abbr",
|
||||
"axis", "colspan", "rowspan", "width");
|
||||
whitelist.addAttributes("th", "border", "bordercolor", "abbr",
|
||||
"axis", "colspan", "rowspan", "scope", "width");
|
||||
whitelist.addAttributes("table", "border", "bordercolor", "summary", "width");
|
||||
whitelist.addAttributes("td", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "width");
|
||||
whitelist.addAttributes("th", "border", "bordercolor", "abbr", "axis", "colspan", "rowspan", "scope", "width");
|
||||
whitelist.addAttributes("ul", "type");
|
||||
|
||||
whitelist.addProtocols("a", "href", "ftp", "http", "https",
|
||||
"mailto");
|
||||
whitelist.addProtocols("a", "href", "ftp", "http", "https", "mailto");
|
||||
whitelist.addProtocols("blockquote", "cite", "http", "https");
|
||||
whitelist.addProtocols("img", "src", "http", "https");
|
||||
whitelist.addProtocols("q", "cite", "http", "https");
|
||||
@@ -199,8 +192,13 @@ public class FeedUtils {
|
||||
e.attr("style", escaped);
|
||||
}
|
||||
|
||||
clean.outputSettings(new OutputSettings().escapeMode(
|
||||
EscapeMode.base).prettyPrint(false));
|
||||
for (Element e : clean.select("img[style]")) {
|
||||
String style = e.attr("style");
|
||||
String escaped = escapeImgCss(style);
|
||||
e.attr("style", escaped);
|
||||
}
|
||||
|
||||
clean.outputSettings(new OutputSettings().escapeMode(EscapeMode.base).prettyPrint(false));
|
||||
Element body = clean.body();
|
||||
if (keepTextOnly) {
|
||||
content = body.text();
|
||||
@@ -212,12 +210,11 @@ public class FeedUtils {
|
||||
}
|
||||
|
||||
public static String escapeIFrameCss(String orig) {
|
||||
List<String> rules = Lists.newArrayList();
|
||||
String rule = "";
|
||||
CSSOMParser parser = new CSSOMParser();
|
||||
try {
|
||||
CSSStyleDeclaration decl = parser
|
||||
.parseStyleDeclaration(new InputSource(new StringReader(
|
||||
orig)));
|
||||
List<String> rules = Lists.newArrayList();
|
||||
CSSStyleDeclaration decl = parser.parseStyleDeclaration(new InputSource(new StringReader(orig)));
|
||||
|
||||
for (int i = 0; i < decl.getLength(); i++) {
|
||||
String property = decl.item(i);
|
||||
@@ -226,17 +223,40 @@ public class FeedUtils {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ALLOWED_IFRAME_CSS_RULES.contains(property)
|
||||
&& StringUtils.containsNone(value,
|
||||
DISALLOWED_IFRAME_CSS_RULE_CHARACTERS)) {
|
||||
rules.add(property + ":" + decl.getPropertyValue(property)
|
||||
+ ";");
|
||||
if (ALLOWED_IFRAME_CSS_RULES.contains(property) && StringUtils.containsNone(value, FORBIDDEN_CSS_RULE_CHARACTERS)) {
|
||||
rules.add(property + ":" + decl.getPropertyValue(property) + ";");
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
rule = StringUtils.join(rules, "");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return StringUtils.join(rules, "");
|
||||
return rule;
|
||||
}
|
||||
|
||||
public static String escapeImgCss(String orig) {
|
||||
String rule = "";
|
||||
CSSOMParser parser = new CSSOMParser();
|
||||
try {
|
||||
List<String> rules = Lists.newArrayList();
|
||||
CSSStyleDeclaration decl = parser.parseStyleDeclaration(new InputSource(new StringReader(orig)));
|
||||
|
||||
for (int i = 0; i < decl.getLength(); i++) {
|
||||
String property = decl.item(i);
|
||||
String value = decl.getPropertyValue(property);
|
||||
if (StringUtils.isBlank(property) || StringUtils.isBlank(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ALLOWED_IMG_CSS_RULES.contains(property) && StringUtils.containsNone(value, FORBIDDEN_CSS_RULE_CHARACTERS)) {
|
||||
rules.add(property + ":" + decl.getPropertyValue(property) + ";");
|
||||
}
|
||||
}
|
||||
rule = StringUtils.join(rules, "");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
public static boolean isRTL(FeedEntry entry) {
|
||||
@@ -278,8 +298,7 @@ public class FeedUtils {
|
||||
}
|
||||
|
||||
if (c >= 32 || c == 9 || c == 10 || c == 13) {
|
||||
if (!Character.isHighSurrogate(c)
|
||||
&& !Character.isLowSurrogate(c)) {
|
||||
if (!Character.isHighSurrogate(c) && !Character.isLowSurrogate(c)) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
@@ -300,14 +319,13 @@ public class FeedUtils {
|
||||
disabledHours = Math.min(24 * 7, disabledHours);
|
||||
return DateUtils.addHours(now, disabledHours);
|
||||
}
|
||||
return null;
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the feed was refreshed successfully
|
||||
*/
|
||||
public static Date buildDisabledUntil(Date publishedDate,
|
||||
Long averageEntryInterval) {
|
||||
public static Date buildDisabledUntil(Date publishedDate, Long averageEntryInterval, Date defaultRefreshInterval) {
|
||||
Date now = new Date();
|
||||
|
||||
if (publishedDate == null) {
|
||||
@@ -323,10 +341,16 @@ public class FeedUtils {
|
||||
// older than a week, recheck in 6 hours
|
||||
return DateUtils.addHours(now, 6);
|
||||
} else if (averageEntryInterval != null) {
|
||||
// use average time between entries to decide when to refresh next
|
||||
// use average time between entries to decide when to refresh next, divided by factor
|
||||
int factor = 2;
|
||||
return new Date(Math.min(DateUtils.addHours(now, 6).getTime(),
|
||||
now.getTime() + averageEntryInterval / factor));
|
||||
|
||||
// not more than 6 hours
|
||||
long date = Math.min(DateUtils.addHours(now, 6).getTime(), now.getTime() + averageEntryInterval / factor);
|
||||
|
||||
// not less than default refresh interval
|
||||
date = Math.max(defaultRefreshInterval.getTime(), date);
|
||||
|
||||
return new Date(date);
|
||||
} else {
|
||||
// unknown case, recheck in 24 hours
|
||||
return DateUtils.addHours(now, 24);
|
||||
@@ -378,14 +402,11 @@ public class FeedUtils {
|
||||
return baseUrl + url;
|
||||
}
|
||||
|
||||
public static String getFaviconUrl(FeedSubscription subscription,
|
||||
String publicUrl) {
|
||||
return removeTrailingSlash(publicUrl) + "/rest/feed/favicon/"
|
||||
+ subscription.getId();
|
||||
public static String getFaviconUrl(FeedSubscription subscription, String publicUrl) {
|
||||
return removeTrailingSlash(publicUrl) + "/rest/feed/favicon/" + subscription.getId();
|
||||
}
|
||||
|
||||
public static String proxyImages(String content, String publicUrl,
|
||||
boolean proxyImages) {
|
||||
public static String proxyImages(String content, String publicUrl, boolean proxyImages) {
|
||||
if (!proxyImages) {
|
||||
return content;
|
||||
}
|
||||
@@ -398,8 +419,7 @@ public class FeedUtils {
|
||||
for (Element element : elements) {
|
||||
String href = element.attr("src");
|
||||
if (href != null) {
|
||||
String proxy = removeTrailingSlash(publicUrl)
|
||||
+ "/rest/server/proxy?u=" + imageProxyEncoder(href);
|
||||
String proxy = removeTrailingSlash(publicUrl) + "/rest/server/proxy?u=" + imageProxyEncoder(href);
|
||||
element.attr("src", proxy);
|
||||
}
|
||||
}
|
||||
@@ -433,4 +453,27 @@ public class FeedUtils {
|
||||
return rot13(new String(Base64.decodeBase64(code)));
|
||||
}
|
||||
|
||||
public static void removeUnwantedFromSearch(List<Entry> entries, String keywords) {
|
||||
if (StringUtils.isBlank(keywords)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<Entry> it = entries.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry entry = it.next();
|
||||
boolean keep = true;
|
||||
for (String keyword : keywords.split(" ")) {
|
||||
String title = Jsoup.parse(entry.getTitle()).text();
|
||||
String content = Jsoup.parse(entry.getContent()).text();
|
||||
if (!StringUtils.containsIgnoreCase(content, keyword) && !StringUtils.containsIgnoreCase(title, keyword)) {
|
||||
keep = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!keep) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.List;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.google.api.client.util.Lists;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class FetchedFeed {
|
||||
|
||||
|
||||
@@ -28,13 +28,11 @@ public class OPMLExporter {
|
||||
public Opml export(User user) {
|
||||
Opml opml = new Opml();
|
||||
opml.setFeedType("opml_1.1");
|
||||
opml.setTitle(String.format("%s subscriptions in CommaFeed",
|
||||
user.getName()));
|
||||
opml.setTitle(String.format("%s subscriptions in CommaFeed", user.getName()));
|
||||
opml.setCreated(new Date());
|
||||
|
||||
List<FeedCategory> categories = feedCategoryDAO.findAll(user);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO
|
||||
.findAll(user);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user);
|
||||
|
||||
for (FeedCategory cat : categories) {
|
||||
opml.getOutlines().add(buildCategoryOutline(cat, subscriptions));
|
||||
@@ -50,20 +48,17 @@ public class OPMLExporter {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Outline buildCategoryOutline(FeedCategory cat,
|
||||
List<FeedSubscription> subscriptions) {
|
||||
private Outline buildCategoryOutline(FeedCategory cat, List<FeedSubscription> subscriptions) {
|
||||
Outline outline = new Outline();
|
||||
outline.setText(cat.getName());
|
||||
outline.setTitle(cat.getName());
|
||||
|
||||
for (FeedCategory child : cat.getChildren()) {
|
||||
outline.getChildren().add(
|
||||
buildCategoryOutline(child, subscriptions));
|
||||
outline.getChildren().add(buildCategoryOutline(child, subscriptions));
|
||||
}
|
||||
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
if (sub.getCategory() != null
|
||||
&& sub.getCategory().getId().equals(cat.getId())) {
|
||||
if (sub.getCategory() != null && sub.getCategory().getId().equals(cat.getId())) {
|
||||
outline.getChildren().add(buildSubscriptionOutline(sub));
|
||||
}
|
||||
}
|
||||
@@ -76,11 +71,9 @@ public class OPMLExporter {
|
||||
outline.setText(sub.getTitle());
|
||||
outline.setTitle(sub.getTitle());
|
||||
outline.setType("rss");
|
||||
outline.getAttributes().add(
|
||||
new Attribute("xmlUrl", sub.getFeed().getUrl()));
|
||||
outline.getAttributes().add(new Attribute("xmlUrl", sub.getFeed().getUrl()));
|
||||
if (sub.getFeed().getLink() != null) {
|
||||
outline.getAttributes().add(
|
||||
new Attribute("htmlUrl", sub.getFeed().getLink()));
|
||||
outline.getAttributes().add(new Attribute("htmlUrl", sub.getFeed().getLink()));
|
||||
}
|
||||
return outline;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
@@ -25,10 +25,9 @@ import com.sun.syndication.io.WireFeedInput;
|
||||
|
||||
@Stateless
|
||||
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
|
||||
@Slf4j
|
||||
public class OPMLImporter {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(OPMLImporter.class);
|
||||
|
||||
@Inject
|
||||
FeedSubscriptionService feedSubscriptionService;
|
||||
|
||||
@@ -63,13 +62,12 @@ public class OPMLImporter {
|
||||
if (name == null) {
|
||||
name = FeedUtils.truncate(outline.getTitle(), 128);
|
||||
}
|
||||
FeedCategory category = feedCategoryDAO.findByName(user, name,
|
||||
parent);
|
||||
FeedCategory category = feedCategoryDAO.findByName(user, name, parent);
|
||||
if (category == null) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
name = "Unnamed category";
|
||||
}
|
||||
|
||||
|
||||
category = new FeedCategory();
|
||||
category.setName(name);
|
||||
category.setParent(parent);
|
||||
@@ -91,15 +89,13 @@ public class OPMLImporter {
|
||||
}
|
||||
// make sure we continue with the import process even a feed failed
|
||||
try {
|
||||
feedSubscriptionService.subscribe(user, outline.getXmlUrl(),
|
||||
name, parent);
|
||||
feedSubscriptionService.subscribe(user, outline.getXmlUrl(), name, parent);
|
||||
} catch (FeedSubscriptionException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("error while importing {}: {}", outline.getXmlUrl(),
|
||||
e.getMessage());
|
||||
log.error("error while importing {}: {}", outline.getXmlUrl(), e.getMessage());
|
||||
}
|
||||
}
|
||||
cache.invalidateUserData(user);
|
||||
cache.invalidateUserRootCategory(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,26 @@ import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.TableGenerator;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Abstract model for all entities, defining id and table generator
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class AbstractModel implements Serializable {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.TABLE, generator = "gen")
|
||||
@TableGenerator(name = "gen", table = "hibernate_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_next_hi_value", allocationSize = 1000)
|
||||
@TableGenerator(
|
||||
name = "gen",
|
||||
table = "hibernate_sequences",
|
||||
pkColumnName = "sequence_name",
|
||||
valueColumnName = "sequence_next_hi_value",
|
||||
allocationSize = 1000)
|
||||
private Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@ package com.commafeed.backend.model;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.apache.log4j.Level;
|
||||
|
||||
@Entity
|
||||
@Table(name = "APPLICATIONSETTINGS")
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Getter
|
||||
@Setter
|
||||
public class ApplicationSettings extends AbstractModel {
|
||||
|
||||
private String publicUrl;
|
||||
@@ -35,169 +35,9 @@ public class ApplicationSettings extends AbstractModel {
|
||||
private boolean imageProxyEnabled;
|
||||
private int queryTimeout;
|
||||
private boolean crawlingPaused;
|
||||
|
||||
private int keepStatusDays = 0;
|
||||
private int refreshIntervalMinutes = 5;
|
||||
@Column(length = 255)
|
||||
private String announcement;
|
||||
|
||||
public String getPublicUrl() {
|
||||
return publicUrl;
|
||||
}
|
||||
|
||||
public void setPublicUrl(String publicUrl) {
|
||||
this.publicUrl = publicUrl;
|
||||
}
|
||||
|
||||
public boolean isAllowRegistrations() {
|
||||
return allowRegistrations;
|
||||
}
|
||||
|
||||
public void setAllowRegistrations(boolean allowRegistrations) {
|
||||
this.allowRegistrations = allowRegistrations;
|
||||
}
|
||||
|
||||
public String getGoogleClientId() {
|
||||
return googleClientId;
|
||||
}
|
||||
|
||||
public void setGoogleClientId(String googleClientId) {
|
||||
this.googleClientId = googleClientId;
|
||||
}
|
||||
|
||||
public String getGoogleClientSecret() {
|
||||
return googleClientSecret;
|
||||
}
|
||||
|
||||
public void setGoogleClientSecret(String googleClientSecret) {
|
||||
this.googleClientSecret = googleClientSecret;
|
||||
}
|
||||
|
||||
public int getBackgroundThreads() {
|
||||
return backgroundThreads;
|
||||
}
|
||||
|
||||
public void setBackgroundThreads(int backgroundThreads) {
|
||||
this.backgroundThreads = backgroundThreads;
|
||||
}
|
||||
|
||||
public String getSmtpHost() {
|
||||
return smtpHost;
|
||||
}
|
||||
|
||||
public void setSmtpHost(String smtpHost) {
|
||||
this.smtpHost = smtpHost;
|
||||
}
|
||||
|
||||
public int getSmtpPort() {
|
||||
return smtpPort;
|
||||
}
|
||||
|
||||
public void setSmtpPort(int smtpPort) {
|
||||
this.smtpPort = smtpPort;
|
||||
}
|
||||
|
||||
public boolean isSmtpTls() {
|
||||
return smtpTls;
|
||||
}
|
||||
|
||||
public void setSmtpTls(boolean smtpTls) {
|
||||
this.smtpTls = smtpTls;
|
||||
}
|
||||
|
||||
public String getSmtpUserName() {
|
||||
return smtpUserName;
|
||||
}
|
||||
|
||||
public void setSmtpUserName(String smtpUserName) {
|
||||
this.smtpUserName = smtpUserName;
|
||||
}
|
||||
|
||||
public String getSmtpPassword() {
|
||||
return smtpPassword;
|
||||
}
|
||||
|
||||
public void setSmtpPassword(String smtpPassword) {
|
||||
this.smtpPassword = smtpPassword;
|
||||
}
|
||||
|
||||
public String getGoogleAnalyticsTrackingCode() {
|
||||
return googleAnalyticsTrackingCode;
|
||||
}
|
||||
|
||||
public void setGoogleAnalyticsTrackingCode(
|
||||
String googleAnalyticsTrackingCode) {
|
||||
this.googleAnalyticsTrackingCode = googleAnalyticsTrackingCode;
|
||||
}
|
||||
|
||||
public String getAnnouncement() {
|
||||
return announcement;
|
||||
}
|
||||
|
||||
public void setAnnouncement(String announcement) {
|
||||
this.announcement = announcement;
|
||||
}
|
||||
|
||||
public boolean isFeedbackButton() {
|
||||
return feedbackButton;
|
||||
}
|
||||
|
||||
public void setFeedbackButton(boolean feedbackButton) {
|
||||
this.feedbackButton = feedbackButton;
|
||||
}
|
||||
|
||||
public boolean isPubsubhubbub() {
|
||||
return pubsubhubbub;
|
||||
}
|
||||
|
||||
public void setPubsubhubbub(boolean pubsubhubbub) {
|
||||
this.pubsubhubbub = pubsubhubbub;
|
||||
}
|
||||
|
||||
public boolean isHeavyLoad() {
|
||||
return heavyLoad;
|
||||
}
|
||||
|
||||
public void setHeavyLoad(boolean heavyLoad) {
|
||||
this.heavyLoad = heavyLoad;
|
||||
}
|
||||
|
||||
public int getDatabaseUpdateThreads() {
|
||||
return databaseUpdateThreads;
|
||||
}
|
||||
|
||||
public void setDatabaseUpdateThreads(int databaseUpdateThreads) {
|
||||
this.databaseUpdateThreads = databaseUpdateThreads;
|
||||
}
|
||||
|
||||
public String getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public void setLogLevel(String logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public boolean isImageProxyEnabled() {
|
||||
return imageProxyEnabled;
|
||||
}
|
||||
|
||||
public void setImageProxyEnabled(boolean imageProxyEnabled) {
|
||||
this.imageProxyEnabled = imageProxyEnabled;
|
||||
}
|
||||
|
||||
public int getQueryTimeout() {
|
||||
return queryTimeout;
|
||||
}
|
||||
|
||||
public void setQueryTimeout(int queryTimeout) {
|
||||
this.queryTimeout = queryTimeout;
|
||||
}
|
||||
|
||||
public boolean isCrawlingPaused() {
|
||||
return crawlingPaused;
|
||||
}
|
||||
|
||||
public void setCrawlingPaused(boolean crawlingPaused) {
|
||||
this.crawlingPaused = crawlingPaused;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@ import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@@ -20,6 +23,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class Feed extends AbstractModel {
|
||||
|
||||
/**
|
||||
@@ -28,9 +33,6 @@ public class Feed extends AbstractModel {
|
||||
@Column(length = 2048, nullable = false)
|
||||
private String url;
|
||||
|
||||
@Column(length = 40, nullable = false)
|
||||
private String urlHash;
|
||||
|
||||
@Column(length = 2048, nullable = false)
|
||||
private String normalizedUrl;
|
||||
|
||||
@@ -61,12 +63,6 @@ public class Feed extends AbstractModel {
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastEntryDate;
|
||||
|
||||
/**
|
||||
* Last time we successfully refreshed the feed
|
||||
*/
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastUpdateSuccess;
|
||||
|
||||
/**
|
||||
* error message while retrieving the feed
|
||||
*/
|
||||
@@ -107,8 +103,8 @@ public class Feed extends AbstractModel {
|
||||
@Column(length = 40)
|
||||
private String lastContentHash;
|
||||
|
||||
@OneToMany(mappedBy = "feed")
|
||||
private Set<FeedFeedEntry> entryRelationships;
|
||||
@OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedEntry> entries;
|
||||
|
||||
@OneToMany(mappedBy = "feed")
|
||||
private Set<FeedSubscription> subscriptions;
|
||||
@@ -134,13 +130,6 @@ public class Feed extends AbstractModel {
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date pushLastPing;
|
||||
|
||||
/**
|
||||
* Denotes a feed that needs to be refreshed before others. Currently used
|
||||
* when a feed is queued manually for refresh. Not persisted.
|
||||
*/
|
||||
@Transient
|
||||
private boolean urgent;
|
||||
|
||||
public Feed() {
|
||||
|
||||
}
|
||||
@@ -148,189 +137,4 @@ public class Feed extends AbstractModel {
|
||||
public Feed(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Date getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
public void setLastUpdated(Date lastUpdated) {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Set<FeedSubscription> getSubscriptions() {
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
public void setSubscriptions(Set<FeedSubscription> subscriptions) {
|
||||
this.subscriptions = subscriptions;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
public void setLink(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public int getErrorCount() {
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
public void setErrorCount(int errorCount) {
|
||||
this.errorCount = errorCount;
|
||||
}
|
||||
|
||||
public Date getDisabledUntil() {
|
||||
return disabledUntil;
|
||||
}
|
||||
|
||||
public void setDisabledUntil(Date disabledUntil) {
|
||||
this.disabledUntil = disabledUntil;
|
||||
}
|
||||
|
||||
public String getUrlHash() {
|
||||
return urlHash;
|
||||
}
|
||||
|
||||
public void setUrlHash(String urlHash) {
|
||||
this.urlHash = urlHash;
|
||||
}
|
||||
|
||||
public String getLastModifiedHeader() {
|
||||
return lastModifiedHeader;
|
||||
}
|
||||
|
||||
public void setLastModifiedHeader(String lastModifiedHeader) {
|
||||
this.lastModifiedHeader = lastModifiedHeader;
|
||||
}
|
||||
|
||||
public String getEtagHeader() {
|
||||
return etagHeader;
|
||||
}
|
||||
|
||||
public void setEtagHeader(String etagHeader) {
|
||||
this.etagHeader = etagHeader;
|
||||
}
|
||||
|
||||
public Date getLastUpdateSuccess() {
|
||||
return lastUpdateSuccess;
|
||||
}
|
||||
|
||||
public void setLastUpdateSuccess(Date lastUpdateSuccess) {
|
||||
this.lastUpdateSuccess = lastUpdateSuccess;
|
||||
}
|
||||
|
||||
public String getPushHub() {
|
||||
return pushHub;
|
||||
}
|
||||
|
||||
public void setPushHub(String pushHub) {
|
||||
this.pushHub = pushHub;
|
||||
}
|
||||
|
||||
public String getPushTopic() {
|
||||
return pushTopic;
|
||||
}
|
||||
|
||||
public void setPushTopic(String pushTopic) {
|
||||
this.pushTopic = pushTopic;
|
||||
}
|
||||
|
||||
public Date getPushLastPing() {
|
||||
return pushLastPing;
|
||||
}
|
||||
|
||||
public void setPushLastPing(Date pushLastPing) {
|
||||
this.pushLastPing = pushLastPing;
|
||||
}
|
||||
|
||||
public Date getLastPublishedDate() {
|
||||
return lastPublishedDate;
|
||||
}
|
||||
|
||||
public void setLastPublishedDate(Date lastPublishedDate) {
|
||||
this.lastPublishedDate = lastPublishedDate;
|
||||
}
|
||||
|
||||
public String getLastContentHash() {
|
||||
return lastContentHash;
|
||||
}
|
||||
|
||||
public void setLastContentHash(String lastContentHash) {
|
||||
this.lastContentHash = lastContentHash;
|
||||
}
|
||||
|
||||
public Long getAverageEntryInterval() {
|
||||
return averageEntryInterval;
|
||||
}
|
||||
|
||||
public void setAverageEntryInterval(Long averageEntryInterval) {
|
||||
this.averageEntryInterval = averageEntryInterval;
|
||||
}
|
||||
|
||||
public Date getLastEntryDate() {
|
||||
return lastEntryDate;
|
||||
}
|
||||
|
||||
public void setLastEntryDate(Date lastEntryDate) {
|
||||
this.lastEntryDate = lastEntryDate;
|
||||
}
|
||||
|
||||
public String getPushTopicHash() {
|
||||
return pushTopicHash;
|
||||
}
|
||||
|
||||
public void setPushTopicHash(String pushTopicHash) {
|
||||
this.pushTopicHash = pushTopicHash;
|
||||
}
|
||||
|
||||
public boolean isUrgent() {
|
||||
return urgent;
|
||||
}
|
||||
|
||||
public void setUrgent(boolean urgent) {
|
||||
this.urgent = urgent;
|
||||
}
|
||||
|
||||
public String getNormalizedUrl() {
|
||||
return normalizedUrl;
|
||||
}
|
||||
|
||||
public void setNormalizedUrl(String normalizedUrl) {
|
||||
this.normalizedUrl = normalizedUrl;
|
||||
}
|
||||
|
||||
public String getNormalizedUrlHash() {
|
||||
return normalizedUrlHash;
|
||||
}
|
||||
|
||||
public void setNormalizedUrlHash(String normalizedUrlHash) {
|
||||
this.normalizedUrlHash = normalizedUrlHash;
|
||||
}
|
||||
|
||||
public Set<FeedFeedEntry> getEntryRelationships() {
|
||||
return entryRelationships;
|
||||
}
|
||||
|
||||
public void setEntryRelationships(Set<FeedFeedEntry> entryRelationships) {
|
||||
this.entryRelationships = entryRelationships;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,16 +11,19 @@ import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDCATEGORIES")
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedCategory extends AbstractModel {
|
||||
|
||||
@Column(length = 128, nullable = false)
|
||||
@@ -45,63 +48,4 @@ public class FeedCategory extends AbstractModel {
|
||||
|
||||
private Integer position;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public FeedCategory getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(FeedCategory parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Set<FeedSubscription> getSubscriptions() {
|
||||
if (subscriptions == null) {
|
||||
return Sets.newHashSet();
|
||||
}
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
public void setSubscriptions(Set<FeedSubscription> subscriptions) {
|
||||
this.subscriptions = subscriptions;
|
||||
}
|
||||
|
||||
public Set<FeedCategory> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set<FeedCategory> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public boolean isCollapsed() {
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
public void setCollapsed(boolean collapsed) {
|
||||
this.collapsed = collapsed;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,12 +9,15 @@ import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@@ -24,6 +27,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedEntry extends AbstractModel {
|
||||
|
||||
@Column(length = 2048, nullable = false)
|
||||
@@ -32,19 +37,16 @@ public class FeedEntry extends AbstractModel {
|
||||
@Column(length = 40, nullable = false)
|
||||
private String guidHash;
|
||||
|
||||
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedFeedEntry> feedRelationships;
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private Feed feed;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
|
||||
@OneToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(nullable = false, updatable = false)
|
||||
private FeedEntryContent content;
|
||||
|
||||
@Column(length = 2048)
|
||||
private String url;
|
||||
|
||||
@Column(name = "author", length = 128)
|
||||
private String author;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date inserted;
|
||||
|
||||
@@ -54,90 +56,4 @@ public class FeedEntry extends AbstractModel {
|
||||
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedEntryStatus> statuses;
|
||||
|
||||
/**
|
||||
* useful placeholder for the subscription, not persisted
|
||||
*/
|
||||
@Transient
|
||||
private FeedSubscription subscription;
|
||||
|
||||
public String getGuid() {
|
||||
return guid;
|
||||
}
|
||||
|
||||
public void setGuid(String guid) {
|
||||
this.guid = guid;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Date getUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
public void setUpdated(Date updated) {
|
||||
this.updated = updated;
|
||||
}
|
||||
|
||||
public Set<FeedEntryStatus> getStatuses() {
|
||||
return statuses;
|
||||
}
|
||||
|
||||
public void setStatuses(Set<FeedEntryStatus> statuses) {
|
||||
this.statuses = statuses;
|
||||
}
|
||||
|
||||
public Date getInserted() {
|
||||
return inserted;
|
||||
}
|
||||
|
||||
public void setInserted(Date inserted) {
|
||||
this.inserted = inserted;
|
||||
}
|
||||
|
||||
public FeedEntryContent getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(FeedEntryContent content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getGuidHash() {
|
||||
return guidHash;
|
||||
}
|
||||
|
||||
public void setGuidHash(String guidHash) {
|
||||
this.guidHash = guidHash;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public Set<FeedFeedEntry> getFeedRelationships() {
|
||||
return feedRelationships;
|
||||
}
|
||||
|
||||
public void setFeedRelationships(Set<FeedFeedEntry> feedRelationships) {
|
||||
this.feedRelationships = feedRelationships;
|
||||
}
|
||||
|
||||
public FeedSubscription getSubscription() {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
public void setSubscription(FeedSubscription subscription) {
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@@ -14,51 +20,33 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedEntryContent extends AbstractModel {
|
||||
|
||||
@Column(length = 2048)
|
||||
private String title;
|
||||
|
||||
@Column(length = 40)
|
||||
private String titleHash;
|
||||
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
private String content;
|
||||
|
||||
@Column(length = 40)
|
||||
private String contentHash;
|
||||
|
||||
@Column(name = "author", length = 128)
|
||||
private String author;
|
||||
|
||||
@Column(length = 2048)
|
||||
private String enclosureUrl;
|
||||
|
||||
@Column(length = 255)
|
||||
private String enclosureType;
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getEnclosureUrl() {
|
||||
return enclosureUrl;
|
||||
}
|
||||
|
||||
public void setEnclosureUrl(String enclosureUrl) {
|
||||
this.enclosureUrl = enclosureUrl;
|
||||
}
|
||||
|
||||
public String getEnclosureType() {
|
||||
return enclosureType;
|
||||
}
|
||||
|
||||
public void setEnclosureType(String enclosureType) {
|
||||
this.enclosureType = enclosureType;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
@OneToMany(mappedBy = "content")
|
||||
private Set<FeedEntry> entries;
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@@ -20,6 +24,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedEntryStatus extends AbstractModel {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@@ -34,6 +40,9 @@ public class FeedEntryStatus extends AbstractModel {
|
||||
private boolean read;
|
||||
private boolean starred;
|
||||
|
||||
@Transient
|
||||
private boolean markable;
|
||||
|
||||
/**
|
||||
* Denormalization starts here
|
||||
*/
|
||||
@@ -60,60 +69,4 @@ public class FeedEntryStatus extends AbstractModel {
|
||||
setEntryUpdated(entry.getUpdated());
|
||||
}
|
||||
|
||||
public FeedSubscription getSubscription() {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
public void setSubscription(FeedSubscription subscription) {
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
public FeedEntry getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void setEntry(FeedEntry entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public void setStarred(boolean starred) {
|
||||
this.starred = starred;
|
||||
}
|
||||
|
||||
public Date getEntryInserted() {
|
||||
return entryInserted;
|
||||
}
|
||||
|
||||
public void setEntryInserted(Date entryInserted) {
|
||||
this.entryInserted = entryInserted;
|
||||
}
|
||||
|
||||
public Date getEntryUpdated() {
|
||||
return entryUpdated;
|
||||
}
|
||||
|
||||
public void setEntryUpdated(Date entryUpdated) {
|
||||
this.entryUpdated = entryUpdated;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEED_FEEDENTRIES")
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
public class FeedFeedEntry implements Serializable {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "FEED_ID")
|
||||
private Feed feed;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "FEEDENTRY_ID")
|
||||
private FeedEntry entry;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date entryUpdated;
|
||||
|
||||
public FeedFeedEntry() {
|
||||
|
||||
}
|
||||
|
||||
public FeedFeedEntry(Feed feed, FeedEntry entry) {
|
||||
this.feed = feed;
|
||||
this.entry = entry;
|
||||
this.entryUpdated = entry.getUpdated();
|
||||
}
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public void setFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public FeedEntry getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void setEntry(FeedEntry entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public Date getEntryUpdated() {
|
||||
return entryUpdated;
|
||||
}
|
||||
|
||||
public void setEntryUpdated(Date entryUpdated) {
|
||||
this.entryUpdated = entryUpdated;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,9 @@ import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@@ -20,6 +23,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class FeedSubscription extends AbstractModel {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@@ -44,52 +49,4 @@ public class FeedSubscription extends AbstractModel {
|
||||
|
||||
private Integer position;
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public void setFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public FeedCategory getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(FeedCategory category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public Set<FeedEntryStatus> getStatuses() {
|
||||
return statuses;
|
||||
}
|
||||
|
||||
public void setStatuses(Set<FeedEntryStatus> statuses) {
|
||||
this.statuses = statuses;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@@ -23,6 +26,8 @@ import com.google.common.collect.Sets;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class User extends AbstractModel {
|
||||
|
||||
@Column(length = 32, nullable = false, unique = true)
|
||||
@@ -55,107 +60,10 @@ public class User extends AbstractModel {
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date recoverPasswordTokenDate;
|
||||
|
||||
@OneToMany(mappedBy = "user", cascade = { CascadeType.PERSIST,
|
||||
CascadeType.REMOVE })
|
||||
@OneToMany(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
|
||||
private Set<UserRole> roles = Sets.newHashSet();
|
||||
|
||||
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
|
||||
private Set<FeedSubscription> subscriptions;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(byte[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public byte[] getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(byte[] salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public Set<UserRole> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<UserRole> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public boolean isDisabled() {
|
||||
return disabled;
|
||||
}
|
||||
|
||||
public void setDisabled(boolean disabled) {
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Date getLastLogin() {
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
public void setLastLogin(Date lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getRecoverPasswordToken() {
|
||||
return recoverPasswordToken;
|
||||
}
|
||||
|
||||
public void setRecoverPasswordToken(String recoverPasswordToken) {
|
||||
this.recoverPasswordToken = recoverPasswordToken;
|
||||
}
|
||||
|
||||
public Date getRecoverPasswordTokenDate() {
|
||||
return recoverPasswordTokenDate;
|
||||
}
|
||||
|
||||
public void setRecoverPasswordTokenDate(Date recoverPasswordTokenDate) {
|
||||
this.recoverPasswordTokenDate = recoverPasswordTokenDate;
|
||||
}
|
||||
|
||||
public Set<FeedSubscription> getSubscriptions() {
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
public void setSubscriptions(Set<FeedSubscription> subscriptions) {
|
||||
this.subscriptions = subscriptions;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@@ -18,6 +21,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserRole extends AbstractModel {
|
||||
|
||||
public static enum Role {
|
||||
@@ -41,20 +46,4 @@ public class UserRole extends AbstractModel {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(Role role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ import javax.persistence.Lob;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@@ -19,6 +22,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserSettings extends AbstractModel {
|
||||
|
||||
public enum ReadingMode {
|
||||
@@ -63,84 +68,4 @@ public class UserSettings extends AbstractModel {
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
private String customCss;
|
||||
|
||||
public ReadingMode getReadingMode() {
|
||||
return readingMode;
|
||||
}
|
||||
|
||||
public void setReadingMode(ReadingMode readingMode) {
|
||||
this.readingMode = readingMode;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getCustomCss() {
|
||||
return customCss;
|
||||
}
|
||||
|
||||
public void setCustomCss(String customCss) {
|
||||
this.customCss = customCss;
|
||||
}
|
||||
|
||||
public ReadingOrder getReadingOrder() {
|
||||
return readingOrder;
|
||||
}
|
||||
|
||||
public void setReadingOrder(ReadingOrder readingOrder) {
|
||||
this.readingOrder = readingOrder;
|
||||
}
|
||||
|
||||
public boolean isShowRead() {
|
||||
return showRead;
|
||||
}
|
||||
|
||||
public void setShowRead(boolean showRead) {
|
||||
this.showRead = showRead;
|
||||
}
|
||||
|
||||
public boolean isSocialButtons() {
|
||||
return socialButtons;
|
||||
}
|
||||
|
||||
public void setSocialButtons(boolean socialButtons) {
|
||||
this.socialButtons = socialButtons;
|
||||
}
|
||||
|
||||
public ViewMode getViewMode() {
|
||||
return viewMode;
|
||||
}
|
||||
|
||||
public void setViewMode(ViewMode viewMode) {
|
||||
this.viewMode = viewMode;
|
||||
}
|
||||
|
||||
public boolean isScrollMarks() {
|
||||
return scrollMarks;
|
||||
}
|
||||
|
||||
public void setScrollMarks(boolean scrollMarks) {
|
||||
this.scrollMarks = scrollMarks;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getTheme() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
public void setTheme(String theme) {
|
||||
this.theme = theme;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpResponse;
|
||||
@@ -14,21 +16,22 @@ import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.frontend.rest.resources.PubSubHubbubCallbackREST;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Sends push subscription requests. Callback is handled by {@link PubSubHubbubCallbackREST}
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class SubscriptionHandler {
|
||||
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(SubscriptionHandler.class);
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@@ -47,16 +50,13 @@ public class SubscriptionHandler {
|
||||
|
||||
String hub = feed.getPushHub();
|
||||
String topic = feed.getPushTopic();
|
||||
String publicUrl = FeedUtils
|
||||
.removeTrailingSlash(applicationSettingsService.get()
|
||||
.getPublicUrl());
|
||||
String publicUrl = FeedUtils.removeTrailingSlash(applicationSettingsService.get().getPublicUrl());
|
||||
|
||||
log.debug("sending new pubsub subscription to {} for {}", hub, topic);
|
||||
|
||||
HttpPost post = new HttpPost(hub);
|
||||
List<NameValuePair> nvp = Lists.newArrayList();
|
||||
nvp.add(new BasicNameValuePair("hub.callback", publicUrl
|
||||
+ "/rest/push/callback"));
|
||||
nvp.add(new BasicNameValuePair("hub.callback", publicUrl + "/rest/push/callback"));
|
||||
nvp.add(new BasicNameValuePair("hub.topic", topic));
|
||||
nvp.add(new BasicNameValuePair("hub.mode", "subscribe"));
|
||||
nvp.add(new BasicNameValuePair("hub.verify", "async"));
|
||||
@@ -65,8 +65,7 @@ public class SubscriptionHandler {
|
||||
nvp.add(new BasicNameValuePair("hub.lease_seconds", ""));
|
||||
|
||||
post.setHeader(HttpHeaders.USER_AGENT, "CommaFeed");
|
||||
post.setHeader(HttpHeaders.CONTENT_TYPE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED);
|
||||
post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
HttpClient client = HttpGetter.newClient(20000);
|
||||
try {
|
||||
@@ -77,23 +76,19 @@ public class SubscriptionHandler {
|
||||
if (code != 204 && code != 202 && code != 200) {
|
||||
String message = EntityUtils.toString(response.getEntity());
|
||||
String pushpressError = " is value is not allowed. You may only subscribe to";
|
||||
if (code == 400
|
||||
&& StringUtils.contains(message, pushpressError)) {
|
||||
if (code == 400 && StringUtils.contains(message, pushpressError)) {
|
||||
String[] tokens = message.split(" ");
|
||||
feed.setPushTopic(tokens[tokens.length - 1]);
|
||||
taskGiver.giveBack(feed);
|
||||
log.debug("handled pushpress subfeed {} : {}", topic,
|
||||
feed.getPushTopic());
|
||||
log.debug("handled pushpress subfeed {} : {}", topic, feed.getPushTopic());
|
||||
} else {
|
||||
throw new Exception("Unexpected response code: " + code
|
||||
+ " " + response.getStatusLine().getReasonPhrase()
|
||||
+ " - " + message);
|
||||
throw new Exception("Unexpected response code: " + code + " " + response.getStatusLine().getReasonPhrase() + " - "
|
||||
+ message);
|
||||
}
|
||||
}
|
||||
log.debug("subscribed to {} for {}", hub, topic);
|
||||
} catch (Exception e) {
|
||||
log.error("Could not subscribe to {} for {} : " + e.getMessage(),
|
||||
hub, topic);
|
||||
log.error("Could not subscribe to {} for {} : " + e.getMessage(), hub, topic);
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@ import org.jdom.Element;
|
||||
|
||||
import com.sun.syndication.feed.opml.Opml;
|
||||
|
||||
public class OPML11Generator extends
|
||||
com.sun.syndication.io.impl.OPML10Generator {
|
||||
/**
|
||||
* Add missing title to the generated OPML
|
||||
*
|
||||
*/
|
||||
public class OPML11Generator extends com.sun.syndication.io.impl.OPML10Generator {
|
||||
|
||||
public OPML11Generator() {
|
||||
super("opml_1.1");
|
||||
|
||||
@@ -5,21 +5,22 @@ import org.jdom.Element;
|
||||
|
||||
import com.sun.syndication.io.impl.OPML10Parser;
|
||||
|
||||
/**
|
||||
* Support for OPML 1.1 parsing
|
||||
*
|
||||
*/
|
||||
public class OPML11Parser extends OPML10Parser {
|
||||
|
||||
public OPML11Parser() {
|
||||
super("opml_1.1");
|
||||
}
|
||||
|
||||
public OPML11Parser() {
|
||||
super("opml_1.1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMyType(Document document) {
|
||||
Element e = document.getRootElement();
|
||||
|
||||
if (e.getName().equals("opml")
|
||||
&& (e.getChild("head") == null || e.getChild("head").getChild(
|
||||
"docs") == null)
|
||||
&& (e.getAttributeValue("version") == null || e
|
||||
.getAttributeValue("version").equals("1.1"))) {
|
||||
if (e.getName().equals("opml") && (e.getChild("head") == null || e.getChild("head").getChild("docs") == null)
|
||||
&& (e.getAttributeValue("version") == null || e.getAttributeValue("version").equals("1.1"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import com.sun.syndication.feed.synd.SyndContentImpl;
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
import com.sun.syndication.feed.synd.impl.ConverterForRSS090;
|
||||
|
||||
/**
|
||||
* Support description tag for RSS09
|
||||
*
|
||||
*/
|
||||
public class RSS090DescriptionConverter extends ConverterForRSS090 {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,6 +6,10 @@ import com.sun.syndication.feed.rss.Description;
|
||||
import com.sun.syndication.feed.rss.Item;
|
||||
import com.sun.syndication.io.impl.RSS090Parser;
|
||||
|
||||
/**
|
||||
* Support description tag for RSS09
|
||||
*
|
||||
*/
|
||||
public class RSS090DescriptionParser extends RSS090Parser {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,8 +26,7 @@ public class RSSRDF10Parser extends RSS10Parser {
|
||||
|
||||
Element rssRoot = document.getRootElement();
|
||||
Namespace defaultNS = rssRoot.getNamespace();
|
||||
List additionalNSs = Lists.newArrayList(rssRoot
|
||||
.getAdditionalNamespaces());
|
||||
List additionalNSs = Lists.newArrayList(rssRoot.getAdditionalNamespaces());
|
||||
List<Element> children = rssRoot.getChildren();
|
||||
if (CollectionUtils.isNotEmpty(children)) {
|
||||
Element child = children.get(0);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.ejb.Singleton;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
@@ -29,12 +31,16 @@ public class ApplicationSettingsService {
|
||||
|
||||
public ApplicationSettings get() {
|
||||
if (settings == null) {
|
||||
settings = Iterables.getFirst(applicationSettingsDAO.findAll(),
|
||||
null);
|
||||
settings = Iterables.getFirst(applicationSettingsDAO.findAll(), null);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
public Date getUnreadThreshold() {
|
||||
int keepStatusDays = get().getKeepStatusDays();
|
||||
return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void applyLogLevel() {
|
||||
String logLevel = get().getLogLevel();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
|
||||
public class FeedEntryContentService {
|
||||
|
||||
@Inject
|
||||
FeedEntryContentDAO feedEntryContentDAO;
|
||||
|
||||
/**
|
||||
* this is NOT thread-safe
|
||||
*/
|
||||
public FeedEntryContent findOrCreate(FeedEntryContent content, String baseUrl) {
|
||||
|
||||
String contentHash = DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.getContent()));
|
||||
String titleHash = DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.getTitle()));
|
||||
Long existingId = feedEntryContentDAO.findExisting(contentHash, titleHash);
|
||||
|
||||
FeedEntryContent result = null;
|
||||
if (existingId == null) {
|
||||
content.setContentHash(contentHash);
|
||||
content.setTitleHash(titleHash);
|
||||
|
||||
content.setAuthor(FeedUtils.truncate(FeedUtils.handleContent(content.getAuthor(), baseUrl, true), 128));
|
||||
content.setTitle(FeedUtils.truncate(FeedUtils.handleContent(content.getTitle(), baseUrl, true), 2048));
|
||||
content.setContent(FeedUtils.handleContent(content.getContent(), baseUrl, false));
|
||||
result = content;
|
||||
feedEntryContentDAO.saveOrUpdate(result);
|
||||
} else {
|
||||
result = new FeedEntryContent();
|
||||
result.setId(existingId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
@@ -10,6 +14,7 @@ import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Stateless
|
||||
public class FeedEntryService {
|
||||
@@ -19,50 +24,37 @@ public class FeedEntryService {
|
||||
|
||||
@Inject
|
||||
FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
|
||||
@Inject
|
||||
|
||||
@Inject
|
||||
FeedEntryDAO feedEntryDAO;
|
||||
|
||||
public void markEntry(User user, Long entryId, Long subscriptionId,
|
||||
boolean read) {
|
||||
FeedSubscription sub = feedSubscriptionDAO.findById(user,
|
||||
subscriptionId);
|
||||
if (sub == null) {
|
||||
return;
|
||||
}
|
||||
@Inject
|
||||
CacheService cache;
|
||||
|
||||
public void markEntry(User user, Long entryId, boolean read) {
|
||||
|
||||
FeedEntry entry = feedEntryDAO.findById(entryId);
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry);
|
||||
|
||||
if (read) {
|
||||
if (status.getId() != null) {
|
||||
if (status.isStarred()) {
|
||||
status.setRead(true);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
} else {
|
||||
feedEntryStatusDAO.delete(status);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status.getId() == null) {
|
||||
status = new FeedEntryStatus(user, sub, entry);
|
||||
status.setSubscription(sub);
|
||||
}
|
||||
status.setRead(false);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
FeedSubscription sub = feedSubscriptionDAO.findByFeed(user, entry.getFeed());
|
||||
if (sub == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry);
|
||||
if (status.isMarkable()) {
|
||||
status.setRead(read);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
cache.invalidateUnreadCount(sub);
|
||||
cache.invalidateUserRootCategory(user);
|
||||
}
|
||||
}
|
||||
|
||||
public void starEntry(User user, Long entryId, Long subscriptionId,
|
||||
boolean starred) {
|
||||
public void starEntry(User user, Long entryId, Long subscriptionId, boolean starred) {
|
||||
|
||||
FeedSubscription sub = feedSubscriptionDAO.findById(user,
|
||||
subscriptionId);
|
||||
FeedSubscription sub = feedSubscriptionDAO.findById(user, subscriptionId);
|
||||
if (sub == null) {
|
||||
return;
|
||||
}
|
||||
@@ -73,24 +65,34 @@ public class FeedEntryService {
|
||||
}
|
||||
|
||||
FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry);
|
||||
status.setStarred(starred);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
}
|
||||
|
||||
if (!starred) {
|
||||
if (status.getId() != null) {
|
||||
if (!status.isRead()) {
|
||||
status.setStarred(false);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
} else {
|
||||
feedEntryStatusDAO.delete(status);
|
||||
public void markSubscriptionEntries(User user, List<FeedSubscription> subscriptions, Date olderThan) {
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO
|
||||
.findBySubscriptions(subscriptions, true, null, null, -1, -1, null, false, false);
|
||||
markList(statuses, olderThan);
|
||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
|
||||
cache.invalidateUserRootCategory(user);
|
||||
}
|
||||
|
||||
public void markStarredEntries(User user, Date olderThan) {
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findStarred(user, null, -1, -1, null, false);
|
||||
markList(statuses, olderThan);
|
||||
}
|
||||
|
||||
private void markList(List<FeedEntryStatus> statuses, Date olderThan) {
|
||||
List<FeedEntryStatus> list = Lists.newArrayList();
|
||||
for (FeedEntryStatus status : statuses) {
|
||||
if (!status.isRead()) {
|
||||
Date inserted = status.getEntry().getInserted();
|
||||
if (olderThan == null || inserted == null || olderThan.after(inserted)) {
|
||||
status.setRead(true);
|
||||
list.add(status);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status.getId() == null) {
|
||||
status = new FeedEntryStatus(user, sub, entry);
|
||||
status.setSubscription(sub);
|
||||
status.setRead(true);
|
||||
}
|
||||
status.setStarred(true);
|
||||
feedEntryStatusDAO.saveOrUpdate(status);
|
||||
}
|
||||
feedEntryStatusDAO.saveOrUpdate(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.Singleton;
|
||||
@@ -28,9 +30,9 @@ public class FeedService {
|
||||
String normalized = FeedUtils.normalizeURL(url);
|
||||
feed = new Feed();
|
||||
feed.setUrl(url);
|
||||
feed.setUrlHash(DigestUtils.sha1Hex(url));
|
||||
feed.setNormalizedUrl(normalized);
|
||||
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized));
|
||||
feed.setDisabledUntil(new Date(0));
|
||||
feedDAO.saveOrUpdate(feed);
|
||||
}
|
||||
return feed;
|
||||
|
||||
@@ -6,9 +6,9 @@ import java.util.Map;
|
||||
import javax.ejb.ApplicationException;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
@@ -18,18 +18,15 @@ import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedCategory;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.Models;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.api.client.util.Lists;
|
||||
import com.commafeed.frontend.model.UnreadCount;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@Slf4j
|
||||
public class FeedSubscriptionService {
|
||||
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(FeedSubscriptionService.class);
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@ApplicationException
|
||||
public static class FeedSubscriptionException extends RuntimeException {
|
||||
@@ -59,63 +56,62 @@ public class FeedSubscriptionService {
|
||||
@Inject
|
||||
CacheService cache;
|
||||
|
||||
public Feed subscribe(User user, String url, String title,
|
||||
FeedCategory category) {
|
||||
public Feed subscribe(User user, String url, String title, FeedCategory category) {
|
||||
|
||||
final String pubUrl = applicationSettingsService.get().getPublicUrl();
|
||||
if (StringUtils.isBlank(pubUrl)) {
|
||||
throw new FeedSubscriptionException(
|
||||
"Public URL of this CommaFeed instance is not set");
|
||||
throw new FeedSubscriptionException("Public URL of this CommaFeed instance is not set");
|
||||
}
|
||||
if (url.startsWith(pubUrl)) {
|
||||
throw new FeedSubscriptionException(
|
||||
"Could not subscribe to a feed from this CommaFeed instance");
|
||||
throw new FeedSubscriptionException("Could not subscribe to a feed from this CommaFeed instance");
|
||||
}
|
||||
|
||||
Feed feed = feedService.findOrCreate(url);
|
||||
|
||||
FeedSubscription sub = feedSubscriptionDAO.findByFeed(user, feed);
|
||||
boolean newSubscription = false;
|
||||
if (sub == null) {
|
||||
sub = new FeedSubscription();
|
||||
sub.setFeed(feed);
|
||||
sub.setUser(user);
|
||||
newSubscription = true;
|
||||
}
|
||||
sub.setCategory(category);
|
||||
sub.setPosition(0);
|
||||
sub.setTitle(FeedUtils.truncate(title, 128));
|
||||
feedSubscriptionDAO.saveOrUpdate(sub);
|
||||
|
||||
if (newSubscription) {
|
||||
try {
|
||||
List<FeedEntryStatus> statuses = Lists.newArrayList();
|
||||
List<FeedEntry> allEntries = feedEntryDAO.findByFeed(feed, 0,
|
||||
10);
|
||||
for (FeedEntry entry : allEntries) {
|
||||
FeedEntryStatus status = new FeedEntryStatus(user, sub, entry);
|
||||
status.setRead(false);
|
||||
status.setSubscription(sub);
|
||||
statuses.add(status);
|
||||
}
|
||||
feedEntryStatusDAO.saveOrUpdate(statuses);
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"could not fetch initial statuses when importing {} : {}",
|
||||
feed.getUrl(), e.getMessage());
|
||||
}
|
||||
}
|
||||
taskGiver.add(feed);
|
||||
taskGiver.add(feed, false);
|
||||
cache.invalidateUserRootCategory(user);
|
||||
return feed;
|
||||
}
|
||||
|
||||
public Map<Long, Long> getUnreadCount(User user) {
|
||||
Map<Long, Long> map = cache.getUnreadCounts(user);
|
||||
if (map == null) {
|
||||
log.debug("unread count cache miss for {}", Models.getId(user));
|
||||
map = feedEntryStatusDAO.getUnreadCount(user);
|
||||
cache.setUnreadCounts(user, map);
|
||||
public boolean unsubscribe(User user, Long subId) {
|
||||
FeedSubscription sub = feedSubscriptionDAO.findById(user, subId);
|
||||
if (sub != null) {
|
||||
feedSubscriptionDAO.delete(sub);
|
||||
cache.invalidateUserRootCategory(user);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
||||
UnreadCount count = cache.getUnreadCount(sub);
|
||||
if (count == null) {
|
||||
log.debug("unread count cache miss for {}", Models.getId(sub));
|
||||
count = feedEntryStatusDAO.getUnreadCount(sub);
|
||||
cache.setUnreadCount(sub, count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public Map<Long, UnreadCount> getUnreadCount(User user) {
|
||||
Map<Long, UnreadCount> map = Maps.newHashMap();
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
for (FeedSubscription sub : subs) {
|
||||
map.put(sub.getId(), getUnreadCount(sub));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,91 +1,43 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import com.commafeed.backend.MetricsBean;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO.EntryWithFeed;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedFeedEntry;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Stateless
|
||||
public class FeedUpdateService {
|
||||
|
||||
@PersistenceContext
|
||||
protected EntityManager em;
|
||||
|
||||
@Inject
|
||||
FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryDAO feedEntryDAO;
|
||||
|
||||
@Inject
|
||||
FeedEntryStatusDAO feedEntryStatusDAO;
|
||||
FeedEntryContentService feedEntryContentService;
|
||||
|
||||
@Inject
|
||||
MetricsBean metricsBean;
|
||||
/**
|
||||
* this is NOT thread-safe
|
||||
*/
|
||||
public boolean addEntry(Feed feed, FeedEntry entry) {
|
||||
|
||||
@Inject
|
||||
CacheService cache;
|
||||
|
||||
public void updateEntry(Feed feed, FeedEntry entry,
|
||||
List<FeedSubscription> subscriptions) {
|
||||
|
||||
EntryWithFeed existing = feedEntryDAO.findExisting(entry.getGuid(),
|
||||
entry.getUrl(), feed.getId());
|
||||
|
||||
FeedEntry update = null;
|
||||
FeedFeedEntry ffe = null;
|
||||
if (existing == null) {
|
||||
entry.setAuthor(FeedUtils.truncate(FeedUtils.handleContent(
|
||||
entry.getAuthor(), feed.getLink(), true), 128));
|
||||
FeedEntryContent content = entry.getContent();
|
||||
content.setTitle(FeedUtils.truncate(FeedUtils.handleContent(
|
||||
content.getTitle(), feed.getLink(), true), 2048));
|
||||
content.setContent(FeedUtils.handleContent(content.getContent(),
|
||||
feed.getLink(), false));
|
||||
|
||||
entry.setInserted(new Date());
|
||||
ffe = new FeedFeedEntry(feed, entry);
|
||||
|
||||
update = entry;
|
||||
} else if (existing.ffe == null) {
|
||||
ffe = new FeedFeedEntry(feed, existing.entry);
|
||||
update = existing.entry;
|
||||
Long existing = feedEntryDAO.findExisting(entry.getGuid(), feed.getId());
|
||||
if (existing != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (update != null) {
|
||||
List<FeedEntryStatus> statusUpdateList = Lists.newArrayList();
|
||||
List<User> users = Lists.newArrayList();
|
||||
for (FeedSubscription sub : subscriptions) {
|
||||
User user = sub.getUser();
|
||||
FeedEntryStatus status = new FeedEntryStatus(user, sub, update);
|
||||
status.setSubscription(sub);
|
||||
statusUpdateList.add(status);
|
||||
users.add(user);
|
||||
}
|
||||
cache.invalidateUserData(users.toArray(new User[0]));
|
||||
feedEntryDAO.saveOrUpdate(update);
|
||||
feedEntryStatusDAO.saveOrUpdate(statusUpdateList);
|
||||
em.persist(ffe);
|
||||
metricsBean.entryUpdated(statusUpdateList.size());
|
||||
}
|
||||
FeedEntryContent content = feedEntryContentService.findOrCreate(entry.getContent(), feed.getLink());
|
||||
entry.setGuidHash(DigestUtils.sha1Hex(entry.getGuid()));
|
||||
entry.setContent(content);
|
||||
entry.setInserted(new Date());
|
||||
entry.setFeed(feed);
|
||||
|
||||
feedEntryDAO.saveOrUpdate(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,22 +12,20 @@ import javax.mail.Transport;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.User;
|
||||
|
||||
/**
|
||||
* Mailing service
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MailService implements Serializable {
|
||||
|
||||
protected static Logger log = LoggerFactory.getLogger(MailService.class);
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public void sendMail(User user, String subject, String content)
|
||||
throws Exception {
|
||||
public void sendMail(User user, String subject, String content) throws Exception {
|
||||
|
||||
ApplicationSettings settings = applicationSettingsService.get();
|
||||
|
||||
@@ -50,8 +48,7 @@ public class MailService implements Serializable {
|
||||
|
||||
Message message = new MimeMessage(session);
|
||||
message.setFrom(new InternetAddress(username, "CommaFeed"));
|
||||
message.setRecipients(Message.RecipientType.TO,
|
||||
InternetAddress.parse(dest));
|
||||
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(dest));
|
||||
message.setSubject("CommaFeed - " + subject);
|
||||
message.setContent(content, "text/html; charset=utf-8");
|
||||
|
||||
|
||||
@@ -10,25 +10,19 @@ import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.commafeed.backend.dao.UserDAO;
|
||||
|
||||
// http://www.javacodegeeks.com/2012/05/secure-password-storage-donts-dos-and.html
|
||||
// taken from http://www.javacodegeeks.com/2012/05/secure-password-storage-donts-dos-and.html
|
||||
@SuppressWarnings("serial")
|
||||
@Slf4j
|
||||
public class PasswordEncryptionService implements Serializable {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UserDAO.class);
|
||||
|
||||
public boolean authenticate(String attemptedPassword,
|
||||
byte[] encryptedPassword, byte[] salt) {
|
||||
public boolean authenticate(String attemptedPassword, byte[] encryptedPassword, byte[] salt) {
|
||||
// Encrypt the clear-text password using the same salt that was used to
|
||||
// encrypt the original password
|
||||
byte[] encryptedAttemptedPassword = null;
|
||||
try {
|
||||
encryptedAttemptedPassword = getEncryptedPassword(
|
||||
attemptedPassword, salt);
|
||||
encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);
|
||||
} catch (Exception e) {
|
||||
// should never happen
|
||||
log.error(e.getMessage(), e);
|
||||
@@ -53,8 +47,7 @@ public class PasswordEncryptionService implements Serializable {
|
||||
// http://blog.crackpassword.com/2010/09/smartphone-forensics-cracking-blackberry-backup-passwords/
|
||||
int iterations = 20000;
|
||||
|
||||
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations,
|
||||
derivedKeyLength);
|
||||
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, derivedKeyLength);
|
||||
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
|
||||
@@ -48,15 +48,13 @@ public class UserService {
|
||||
|
||||
User user = userDAO.findByName(name);
|
||||
if (user != null && !user.isDisabled()) {
|
||||
boolean authenticated = encryptionService.authenticate(password,
|
||||
user.getPassword(), user.getSalt());
|
||||
boolean authenticated = encryptionService.authenticate(password, user.getPassword(), user.getSalt());
|
||||
if (authenticated) {
|
||||
Date lastLogin = user.getLastLogin();
|
||||
Date now = new Date();
|
||||
// only update lastLogin field every hour in order to not
|
||||
// invalidate the cache everytime someone logs in
|
||||
if (lastLogin == null
|
||||
|| lastLogin.before(DateUtils.addHours(now, -1))) {
|
||||
if (lastLogin == null || lastLogin.before(DateUtils.addHours(now, -1))) {
|
||||
user.setLastLogin(now);
|
||||
userDAO.saveOrUpdate(user);
|
||||
}
|
||||
@@ -66,39 +64,30 @@ public class UserService {
|
||||
return null;
|
||||
}
|
||||
|
||||
public User register(String name, String password, String email,
|
||||
Collection<Role> roles) {
|
||||
public User register(String name, String password, String email, Collection<Role> roles) {
|
||||
return register(name, password, email, roles, false);
|
||||
}
|
||||
|
||||
public User register(String name, String password, String email,
|
||||
Collection<Role> roles, boolean forceRegistration) {
|
||||
public User register(String name, String password, String email, Collection<Role> roles, boolean forceRegistration) {
|
||||
|
||||
Preconditions.checkNotNull(name);
|
||||
Preconditions.checkArgument(StringUtils.length(name) <= 32,
|
||||
"Name too long (32 characters maximum)");
|
||||
Preconditions.checkArgument(StringUtils.length(name) <= 32, "Name too long (32 characters maximum)");
|
||||
Preconditions.checkNotNull(password);
|
||||
|
||||
if (!forceRegistration) {
|
||||
Preconditions.checkState(applicationSettingsService.get()
|
||||
.isAllowRegistrations(),
|
||||
Preconditions.checkState(applicationSettingsService.get().isAllowRegistrations(),
|
||||
"Registrations are closed on this CommaFeed instance");
|
||||
|
||||
Preconditions.checkNotNull(email);
|
||||
Preconditions.checkArgument(StringUtils.length(name) >= 3,
|
||||
"Name too short (3 characters minimum)");
|
||||
Preconditions.checkArgument(
|
||||
forceRegistration || StringUtils.length(password) >= 6,
|
||||
"Password too short (6 characters maximum)");
|
||||
Preconditions.checkArgument(StringUtils.contains(email, "@"),
|
||||
"Invalid email address");
|
||||
Preconditions.checkArgument(StringUtils.length(name) >= 3, "Name too short (3 characters minimum)");
|
||||
Preconditions
|
||||
.checkArgument(forceRegistration || StringUtils.length(password) >= 6, "Password too short (6 characters maximum)");
|
||||
Preconditions.checkArgument(StringUtils.contains(email, "@"), "Invalid email address");
|
||||
}
|
||||
|
||||
Preconditions.checkArgument(userDAO.findByName(name) == null,
|
||||
"Name already taken");
|
||||
Preconditions.checkArgument(userDAO.findByName(name) == null, "Name already taken");
|
||||
if (StringUtils.isNotBlank(email)) {
|
||||
Preconditions.checkArgument(userDAO.findByEmail(email) == null,
|
||||
"Email already taken");
|
||||
Preconditions.checkArgument(userDAO.findByEmail(email) == null, "Email already taken");
|
||||
}
|
||||
|
||||
User user = new User();
|
||||
@@ -122,8 +111,7 @@ public class UserService {
|
||||
}
|
||||
|
||||
public String generateApiKey(User user) {
|
||||
byte[] key = encryptionService.getEncryptedPassword(UUID.randomUUID()
|
||||
.toString(), user.getSalt());
|
||||
byte[] key = encryptionService.getEncryptedPassword(UUID.randomUUID().toString(), user.getSalt());
|
||||
return DigestUtils.sha1Hex(key);
|
||||
}
|
||||
}
|
||||
|
||||
101
src/main/java/com/commafeed/frontend/APIGenerator.java
Normal file
101
src/main/java/com/commafeed/frontend/APIGenerator.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package com.commafeed.frontend;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.wicket.util.io.IOUtils;
|
||||
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.commafeed.frontend.model.request.MarkRequest;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.wordnik.swagger.annotations.Api;
|
||||
import com.wordnik.swagger.core.Documentation;
|
||||
import com.wordnik.swagger.core.DocumentationEndPoint;
|
||||
import com.wordnik.swagger.core.SwaggerSpec;
|
||||
import com.wordnik.swagger.core.util.TypeUtil;
|
||||
import com.wordnik.swagger.jaxrs.HelpApi;
|
||||
import com.wordnik.swagger.jaxrs.JaxrsApiReader;
|
||||
|
||||
@SupportedAnnotationTypes("com.wordnik.swagger.annotations.Api")
|
||||
@SupportedOptions("outputDirectory")
|
||||
public class APIGenerator extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
try {
|
||||
return processInternal(annotations, roundEnv);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processInternal(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception {
|
||||
JaxrsApiReader.setFormatString("");
|
||||
TypeUtil.addAllowablePackage(Entries.class.getPackage().getName());
|
||||
TypeUtil.addAllowablePackage(MarkRequest.class.getPackage().getName());
|
||||
|
||||
String apiVersion = "1.0";
|
||||
String swaggerVersion = SwaggerSpec.version();
|
||||
String basePath = "../rest";
|
||||
|
||||
Documentation doc = new Documentation();
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(Api.class)) {
|
||||
TypeElement type = (TypeElement) element;
|
||||
String fqn = type.getQualifiedName().toString();
|
||||
Class<?> resource = Class.forName(fqn);
|
||||
|
||||
Api api = resource.getAnnotation(Api.class);
|
||||
String apiPath = api.value();
|
||||
|
||||
Documentation apiDoc = JaxrsApiReader.read(resource, apiVersion, swaggerVersion, basePath, apiPath);
|
||||
apiDoc = new HelpApi(null).filterDocs(apiDoc, null, null, null, null);
|
||||
|
||||
apiDoc.setSwaggerVersion(swaggerVersion);
|
||||
apiDoc.setApiVersion(apiVersion);
|
||||
write(apiDoc.getResourcePath(), apiDoc, element);
|
||||
|
||||
doc.addApi(new DocumentationEndPoint(api.value(), api.description()));
|
||||
|
||||
}
|
||||
doc.setSwaggerVersion(swaggerVersion);
|
||||
doc.setApiVersion(apiVersion);
|
||||
|
||||
write(doc.getResourcePath(), doc, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void write(String resourcePath, Object doc, Element element) throws Exception {
|
||||
String fileName = StringUtils.defaultString(resourcePath, "resources");
|
||||
fileName = StringUtils.removeStart(fileName, "/");
|
||||
|
||||
FileObject resource = null;
|
||||
try {
|
||||
resource = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", fileName, element);
|
||||
} catch (Exception e) {
|
||||
// already processed
|
||||
}
|
||||
if (resource != null) {
|
||||
OutputStream os = resource.openOutputStream();
|
||||
try {
|
||||
IOUtils.write(new ObjectMapper().writeValueAsString(doc), os, "UTF-8");
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.commafeed.frontend;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.wicket.Application;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.IRequestCycleProvider;
|
||||
import org.apache.wicket.Page;
|
||||
import org.apache.wicket.RuntimeConfigurationType;
|
||||
import org.apache.wicket.Session;
|
||||
@@ -20,6 +23,7 @@ import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSessio
|
||||
import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication;
|
||||
import org.apache.wicket.authroles.authorization.strategies.role.Roles;
|
||||
import org.apache.wicket.cdi.CdiConfiguration;
|
||||
import org.apache.wicket.cdi.CdiContainer;
|
||||
import org.apache.wicket.cdi.ConversationPropagation;
|
||||
import org.apache.wicket.core.request.handler.PageProvider;
|
||||
import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
|
||||
@@ -31,13 +35,16 @@ import org.apache.wicket.markup.html.WebPage;
|
||||
import org.apache.wicket.request.IRequestHandler;
|
||||
import org.apache.wicket.request.Request;
|
||||
import org.apache.wicket.request.Response;
|
||||
import org.apache.wicket.request.Url;
|
||||
import org.apache.wicket.request.UrlRenderer;
|
||||
import org.apache.wicket.request.component.IRequestableComponent;
|
||||
import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
import org.apache.wicket.request.cycle.RequestCycleContext;
|
||||
import org.apache.wicket.util.cookies.CookieUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.services.ApplicationPropertiesService;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.frontend.pages.DemoLoginPage;
|
||||
import com.commafeed.frontend.pages.HomePage;
|
||||
import com.commafeed.frontend.pages.LogoutPage;
|
||||
@@ -45,43 +52,38 @@ import com.commafeed.frontend.pages.NextUnreadRedirectPage;
|
||||
import com.commafeed.frontend.pages.PasswordRecoveryCallbackPage;
|
||||
import com.commafeed.frontend.pages.PasswordRecoveryPage;
|
||||
import com.commafeed.frontend.pages.WelcomePage;
|
||||
import com.commafeed.frontend.utils.WicketUtils;
|
||||
import com.commafeed.frontend.utils.exception.DisplayExceptionPage;
|
||||
|
||||
@Slf4j
|
||||
public class CommaFeedApplication extends AuthenticatedWebApplication {
|
||||
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(CommaFeedApplication.class);
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public CommaFeedApplication() {
|
||||
super();
|
||||
String prod = ResourceBundle.getBundle("application").getString(
|
||||
"production");
|
||||
setConfigurationType(Boolean.valueOf(prod) ? RuntimeConfigurationType.DEPLOYMENT
|
||||
: RuntimeConfigurationType.DEVELOPMENT);
|
||||
boolean prod = ApplicationPropertiesService.get().isProduction();
|
||||
setConfigurationType(prod ? RuntimeConfigurationType.DEPLOYMENT : RuntimeConfigurationType.DEVELOPMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
setupInjection();
|
||||
setupSecurity();
|
||||
|
||||
mountPage("welcome", WelcomePage.class);
|
||||
mountPage("demo", DemoLoginPage.class);
|
||||
|
||||
|
||||
mountPage("recover", PasswordRecoveryPage.class);
|
||||
mountPage("recover2", PasswordRecoveryCallbackPage.class);
|
||||
|
||||
|
||||
mountPage("logout", LogoutPage.class);
|
||||
mountPage("error", DisplayExceptionPage.class);
|
||||
|
||||
// mountPage("google/import/redirect", GoogleImportRedirectPage.class);
|
||||
// mountPage(GoogleImportCallbackPage.PAGE_PATH,
|
||||
// GoogleImportCallbackPage.class);
|
||||
|
||||
mountPage("next", NextUnreadRedirectPage.class);
|
||||
|
||||
setupInjection();
|
||||
setupSecurity();
|
||||
|
||||
getMarkupSettings().setStripWicketTags(true);
|
||||
getMarkupSettings().setCompressWhitespace(true);
|
||||
getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
|
||||
@@ -89,8 +91,7 @@ public class CommaFeedApplication extends AuthenticatedWebApplication {
|
||||
setHeaderResponseDecorator(new IHeaderResponseDecorator() {
|
||||
@Override
|
||||
public IHeaderResponse decorate(IHeaderResponse response) {
|
||||
return new JavaScriptFilteredIntoFooterHeaderResponse(response,
|
||||
"footer-container");
|
||||
return new JavaScriptFilteredIntoFooterHeaderResponse(response, "footer-container");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -98,63 +99,77 @@ public class CommaFeedApplication extends AuthenticatedWebApplication {
|
||||
@Override
|
||||
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
|
||||
AjaxRequestTarget target = cycle.find(AjaxRequestTarget.class);
|
||||
// redirect to the error page if ajax request, render error on
|
||||
// current page otherwise
|
||||
RedirectPolicy policy = target == null ? RedirectPolicy.NEVER_REDIRECT
|
||||
: RedirectPolicy.AUTO_REDIRECT;
|
||||
return new RenderPageRequestHandler(new PageProvider(
|
||||
new DisplayExceptionPage(ex)), policy);
|
||||
// redirect to the error page if ajax request, render error on current page otherwise
|
||||
RedirectPolicy policy = target == null ? RedirectPolicy.NEVER_REDIRECT : RedirectPolicy.AUTO_REDIRECT;
|
||||
return new RenderPageRequestHandler(new PageProvider(new DisplayExceptionPage(ex)), policy);
|
||||
}
|
||||
});
|
||||
|
||||
setRequestCycleProvider(new IRequestCycleProvider() {
|
||||
@Override
|
||||
public RequestCycle get(RequestCycleContext context) {
|
||||
return new RequestCycle(context) {
|
||||
@Override
|
||||
protected UrlRenderer newUrlRenderer() {
|
||||
return new UrlRenderer(getRequest()) {
|
||||
@Override
|
||||
public String renderUrl(Url url) {
|
||||
// override wicket's relative-to-absolute url conversion with what we know is the correct protocol
|
||||
String publicUrl = applicationSettingsService.get().getPublicUrl();
|
||||
if (StringUtils.isNotBlank(publicUrl)) {
|
||||
Url parsed = Url.parse(publicUrl);
|
||||
url.setProtocol(parsed.getProtocol());
|
||||
url.setPort(parsed.getPort());
|
||||
}
|
||||
return super.renderUrl(url);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupSecurity() {
|
||||
getSecuritySettings().setAuthenticationStrategy(
|
||||
new DefaultAuthenticationStrategy("LoggedIn") {
|
||||
getSecuritySettings().setAuthenticationStrategy(new DefaultAuthenticationStrategy("LoggedIn") {
|
||||
|
||||
private CookieUtils cookieUtils = null;
|
||||
private CookieUtils cookieUtils = null;
|
||||
|
||||
@Override
|
||||
protected CookieUtils getCookieUtils() {
|
||||
@Override
|
||||
protected CookieUtils getCookieUtils() {
|
||||
|
||||
if (cookieUtils == null) {
|
||||
cookieUtils = new CookieUtils() {
|
||||
@Override
|
||||
protected void initializeCookie(Cookie cookie) {
|
||||
super.initializeCookie(cookie);
|
||||
cookie.setHttpOnly(true);
|
||||
}
|
||||
};
|
||||
if (cookieUtils == null) {
|
||||
cookieUtils = new CookieUtils() {
|
||||
@Override
|
||||
protected void initializeCookie(Cookie cookie) {
|
||||
super.initializeCookie(cookie);
|
||||
cookie.setHttpOnly(true);
|
||||
}
|
||||
return cookieUtils;
|
||||
}
|
||||
});
|
||||
getSecuritySettings().setAuthorizationStrategy(
|
||||
new IAuthorizationStrategy() {
|
||||
};
|
||||
}
|
||||
return cookieUtils;
|
||||
}
|
||||
});
|
||||
getSecuritySettings().setAuthorizationStrategy(new IAuthorizationStrategy() {
|
||||
|
||||
@Override
|
||||
public <T extends IRequestableComponent> boolean isInstantiationAuthorized(
|
||||
Class<T> componentClass) {
|
||||
boolean authorized = true;
|
||||
@Override
|
||||
public <T extends IRequestableComponent> boolean isInstantiationAuthorized(Class<T> componentClass) {
|
||||
boolean authorized = true;
|
||||
|
||||
boolean restricted = componentClass
|
||||
.isAnnotationPresent(SecurityCheck.class);
|
||||
if (restricted) {
|
||||
SecurityCheck annotation = componentClass
|
||||
.getAnnotation(SecurityCheck.class);
|
||||
Roles roles = CommaFeedSession.get().getRoles();
|
||||
authorized = roles.hasAnyRole(new Roles(annotation
|
||||
.value().name()));
|
||||
}
|
||||
return authorized;
|
||||
}
|
||||
boolean restricted = componentClass.isAnnotationPresent(SecurityCheck.class);
|
||||
if (restricted) {
|
||||
SecurityCheck annotation = componentClass.getAnnotation(SecurityCheck.class);
|
||||
Roles roles = CommaFeedSession.get().getRoles();
|
||||
authorized = roles.hasAnyRole(new Roles(annotation.value().name()));
|
||||
}
|
||||
return authorized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionAuthorized(Component component,
|
||||
Action action) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public boolean isActionAuthorized(Component component, Action action) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,15 +179,21 @@ public class CommaFeedApplication extends AuthenticatedWebApplication {
|
||||
|
||||
protected void setupInjection() {
|
||||
try {
|
||||
BeanManager beanManager = (BeanManager) new InitialContext()
|
||||
.lookup("java:comp/BeanManager");
|
||||
new CdiConfiguration(beanManager).setPropagation(
|
||||
ConversationPropagation.NONE).configure(this);
|
||||
BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
|
||||
CdiContainer container = new CdiConfiguration(beanManager).setPropagation(ConversationPropagation.NONE).configure(this);
|
||||
container.getNonContextualManager().inject(this);
|
||||
} catch (NamingException e) {
|
||||
log.warn("Could not locate bean manager. CDI is disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartResponseAtSignInPage() {
|
||||
// preserve https if it was set
|
||||
RequestCycle.get().getUrlRenderer().setBaseUrl(Url.parse(WicketUtils.getClientFullUrl()));
|
||||
super.restartResponseAtSignInPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session newSession(Request request, Response response) {
|
||||
return new CommaFeedSession(request);
|
||||
|
||||
@@ -2,32 +2,27 @@ package com.commafeed.frontend;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import lombok.Getter;
|
||||
|
||||
import org.apache.wicket.Session;
|
||||
import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
|
||||
import org.apache.wicket.authroles.authorization.strategies.role.Roles;
|
||||
import org.apache.wicket.request.Request;
|
||||
|
||||
import com.commafeed.backend.dao.UserRoleDAO;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
import com.commafeed.backend.services.UserService;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public class CommaFeedSession extends AuthenticatedWebSession {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Inject
|
||||
UserService userService;
|
||||
|
||||
@Inject
|
||||
UserRoleDAO userRoleDAO;
|
||||
|
||||
private User user;
|
||||
private Roles roles = new Roles();
|
||||
|
||||
@Getter(lazy = true)
|
||||
private final CommaFeedSessionServices services = newServices();
|
||||
|
||||
public CommaFeedSession(Request request) {
|
||||
super(request);
|
||||
}
|
||||
@@ -47,7 +42,7 @@ public class CommaFeedSession extends AuthenticatedWebSession {
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String userName, String password) {
|
||||
User user = userService.login(userName, password);
|
||||
User user = getServices().getUserService().login(userName, password);
|
||||
setUser(user);
|
||||
return user != null;
|
||||
}
|
||||
@@ -59,7 +54,7 @@ public class CommaFeedSession extends AuthenticatedWebSession {
|
||||
} else {
|
||||
|
||||
Set<String> roleSet = Sets.newHashSet();
|
||||
for (Role role : userRoleDAO.findRoles(user)) {
|
||||
for (Role role : getServices().getUserRoleDAO().findRoles(user)) {
|
||||
roleSet.add(role.name());
|
||||
}
|
||||
this.user = user;
|
||||
@@ -67,4 +62,8 @@ public class CommaFeedSession extends AuthenticatedWebSession {
|
||||
}
|
||||
}
|
||||
|
||||
private CommaFeedSessionServices newServices() {
|
||||
return new CommaFeedSessionServices();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.commafeed.frontend;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.wicket.Component;
|
||||
|
||||
import com.commafeed.backend.dao.UserRoleDAO;
|
||||
import com.commafeed.backend.services.UserService;
|
||||
|
||||
// extend Component in order to benefit from injection
|
||||
public class CommaFeedSessionServices extends Component {
|
||||
|
||||
@Inject
|
||||
UserService userService;
|
||||
|
||||
@Inject
|
||||
UserRoleDAO userRoleDAO;
|
||||
|
||||
public CommaFeedSessionServices() {
|
||||
super("services");
|
||||
}
|
||||
|
||||
public UserService getUserService() {
|
||||
return userService;
|
||||
}
|
||||
|
||||
public UserRoleDAO getUserRoleDAO() {
|
||||
return userRoleDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRender() {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
53
src/main/java/com/commafeed/frontend/InterceptingFilter.java
Normal file
53
src/main/java/com/commafeed/frontend/InterceptingFilter.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.commafeed.frontend;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@WebFilter(urlPatterns = "/*")
|
||||
public class InterceptingFilter implements Filter {
|
||||
|
||||
private static final String HEADER_CORS = "Access-Control-Allow-Origin";
|
||||
private static final String HEADER_CORS_VALUE = "*";
|
||||
private static final String HEADER_CORS_METHODS = "Access-Control-Allow-Methods";
|
||||
private static final String HEADER_CORS_METHODS_VALUE = "POST, GET, OPTIONS";
|
||||
private static final String HEADER_CORS_MAXAGE = "Access-Control-Max-Age";
|
||||
private static final String HEADER_CORS_MAXAGE_VALUE = "2592000";
|
||||
private static final String HEADER_CORS_ALLOW_HEADERS = "Access-Control-Allow-Headers";
|
||||
private static final String HEADER_CORS_ALLOW_HEADERS_VALUE = "Authorization";
|
||||
private static final String HEADER_CORS_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
|
||||
private static final String HEADER_CORS_ALLOW_CREDENTIALS_VALUE = "true";
|
||||
|
||||
private static final String HEADER_X_UA_COMPATIBLE = "X-UA-Compatible";
|
||||
private static final String HEADER_X_UA_COMPATIBLE_VALUE = "IE=Edge,chrome=1";
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
resp.addHeader(HEADER_CORS, HEADER_CORS_VALUE);
|
||||
resp.addHeader(HEADER_CORS_METHODS, HEADER_CORS_METHODS_VALUE);
|
||||
resp.addHeader(HEADER_CORS_MAXAGE, HEADER_CORS_MAXAGE_VALUE);
|
||||
resp.addHeader(HEADER_CORS_ALLOW_HEADERS, HEADER_CORS_ALLOW_HEADERS_VALUE);
|
||||
resp.addHeader(HEADER_CORS_ALLOW_CREDENTIALS, HEADER_CORS_ALLOW_CREDENTIALS_VALUE);
|
||||
|
||||
resp.addHeader(HEADER_X_UA_COMPATIBLE, HEADER_X_UA_COMPATIBLE_VALUE);
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,18 +3,15 @@ package com.commafeed.frontend.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Entry details")
|
||||
@Data
|
||||
public class Category implements Serializable {
|
||||
|
||||
@ApiProperty("category id")
|
||||
@@ -37,61 +34,4 @@ public class Category implements Serializable {
|
||||
|
||||
@ApiProperty("position of the category in the list")
|
||||
private Integer position;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<Category> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<Category> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public List<Subscription> getFeeds() {
|
||||
return feeds;
|
||||
}
|
||||
|
||||
public void setFeeds(List<Subscription> feeds) {
|
||||
this.feeds = feeds;
|
||||
}
|
||||
|
||||
public boolean isExpanded() {
|
||||
return expanded;
|
||||
}
|
||||
|
||||
public void setExpanded(boolean expanded) {
|
||||
this.expanded = expanded;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,18 +3,15 @@ package com.commafeed.frontend.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("List of entries with some metadata")
|
||||
@Data
|
||||
public class Entries implements Serializable {
|
||||
|
||||
@ApiProperty("name of the feed or the category requested")
|
||||
@@ -35,63 +32,13 @@ public class Entries implements Serializable {
|
||||
@ApiProperty("if the query has more elements")
|
||||
private boolean hasMore;
|
||||
|
||||
@ApiProperty("the requested offset")
|
||||
private int offset;
|
||||
|
||||
@ApiProperty("the requested limit")
|
||||
private int limit;
|
||||
|
||||
@ApiProperty("list of entries")
|
||||
private List<Entry> entries = Lists.newArrayList();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void setEntries(List<Entry> entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getErrorCount() {
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
public void setErrorCount(int errorCount) {
|
||||
this.errorCount = errorCount;
|
||||
}
|
||||
|
||||
public String getFeedLink() {
|
||||
return feedLink;
|
||||
}
|
||||
|
||||
public void setFeedLink(String feedLink) {
|
||||
this.feedLink = feedLink;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public boolean isHasMore() {
|
||||
return hasMore;
|
||||
}
|
||||
|
||||
public void setHasMore(boolean hasMore) {
|
||||
this.hasMore = hasMore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.sun.syndication.feed.synd.SyndContentImpl;
|
||||
@@ -19,40 +18,40 @@ import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Entry details")
|
||||
@Data
|
||||
public class Entry implements Serializable {
|
||||
|
||||
public static Entry build(FeedEntryStatus status, String publicUrl,
|
||||
boolean proxyImages) {
|
||||
public static Entry build(FeedEntryStatus status, String publicUrl, boolean proxyImages) {
|
||||
Entry entry = new Entry();
|
||||
|
||||
FeedEntry feedEntry = status.getEntry();
|
||||
FeedSubscription sub = status.getSubscription();
|
||||
|
||||
entry.setRead(status.isRead());
|
||||
entry.setStarred(status.isStarred());
|
||||
FeedEntryContent content = feedEntry.getContent();
|
||||
|
||||
entry.setId(String.valueOf(feedEntry.getId()));
|
||||
entry.setGuid(feedEntry.getGuid());
|
||||
entry.setTitle(feedEntry.getContent().getTitle());
|
||||
entry.setContent(FeedUtils.proxyImages(feedEntry.getContent()
|
||||
.getContent(), publicUrl, proxyImages));
|
||||
entry.setRtl(FeedUtils.isRTL(feedEntry));
|
||||
entry.setAuthor(feedEntry.getAuthor());
|
||||
entry.setEnclosureUrl(feedEntry.getContent().getEnclosureUrl());
|
||||
entry.setEnclosureType(feedEntry.getContent().getEnclosureType());
|
||||
entry.setRead(status.isRead());
|
||||
entry.setStarred(status.isStarred());
|
||||
entry.setMarkable(status.isMarkable());
|
||||
entry.setDate(feedEntry.getUpdated());
|
||||
entry.setInsertedDate(feedEntry.getInserted());
|
||||
entry.setUrl(feedEntry.getUrl());
|
||||
|
||||
entry.setFeedName(sub.getTitle());
|
||||
entry.setFeedId(String.valueOf(sub.getId()));
|
||||
entry.setFeedUrl(sub.getFeed().getUrl());
|
||||
entry.setFeedLink(sub.getFeed().getLink());
|
||||
entry.setIconUrl(FeedUtils.getFaviconUrl(sub, publicUrl));
|
||||
|
||||
if (content != null) {
|
||||
entry.setRtl(FeedUtils.isRTL(feedEntry));
|
||||
entry.setTitle(content.getTitle());
|
||||
entry.setContent(FeedUtils.proxyImages(content.getContent(), publicUrl, proxyImages));
|
||||
entry.setAuthor(content.getAuthor());
|
||||
entry.setEnclosureUrl(content.getEnclosureUrl());
|
||||
entry.setEnclosureType(content.getEnclosureType());
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -124,148 +123,6 @@ public class Entry implements Serializable {
|
||||
@ApiProperty("starred status")
|
||||
private boolean starred;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getFeedId() {
|
||||
return feedId;
|
||||
}
|
||||
|
||||
public void setFeedId(String feedId) {
|
||||
this.feedId = feedId;
|
||||
}
|
||||
|
||||
public String getFeedName() {
|
||||
return feedName;
|
||||
}
|
||||
|
||||
public void setFeedName(String feedName) {
|
||||
this.feedName = feedName;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public void setStarred(boolean starred) {
|
||||
this.starred = starred;
|
||||
}
|
||||
|
||||
public String getFeedUrl() {
|
||||
return feedUrl;
|
||||
}
|
||||
|
||||
public void setFeedUrl(String feedUrl) {
|
||||
this.feedUrl = feedUrl;
|
||||
}
|
||||
|
||||
public String getEnclosureUrl() {
|
||||
return enclosureUrl;
|
||||
}
|
||||
|
||||
public void setEnclosureUrl(String enclosureUrl) {
|
||||
this.enclosureUrl = enclosureUrl;
|
||||
}
|
||||
|
||||
public String getEnclosureType() {
|
||||
return enclosureType;
|
||||
}
|
||||
|
||||
public void setEnclosureType(String enclosureType) {
|
||||
this.enclosureType = enclosureType;
|
||||
}
|
||||
|
||||
public String getGuid() {
|
||||
return guid;
|
||||
}
|
||||
|
||||
public void setGuid(String guid) {
|
||||
this.guid = guid;
|
||||
}
|
||||
|
||||
public String getFeedLink() {
|
||||
return feedLink;
|
||||
}
|
||||
|
||||
public void setFeedLink(String feedLink) {
|
||||
this.feedLink = feedLink;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public String getIconUrl() {
|
||||
return iconUrl;
|
||||
}
|
||||
|
||||
public void setIconUrl(String iconUrl) {
|
||||
this.iconUrl = iconUrl;
|
||||
}
|
||||
|
||||
public boolean isRtl() {
|
||||
return rtl;
|
||||
}
|
||||
|
||||
public void setRtl(boolean rtl) {
|
||||
this.rtl = rtl;
|
||||
}
|
||||
|
||||
public Date getInsertedDate() {
|
||||
return insertedDate;
|
||||
}
|
||||
|
||||
public void setInsertedDate(Date insertedDate) {
|
||||
this.insertedDate = insertedDate;
|
||||
}
|
||||
|
||||
@ApiProperty("wether the entry is still markable (old entry statuses are discarded)")
|
||||
private boolean markable;
|
||||
}
|
||||
|
||||
25
src/main/java/com/commafeed/frontend/model/FeedCount.java
Normal file
25
src/main/java/com/commafeed/frontend/model/FeedCount.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.commafeed.frontend.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
|
||||
@ApiClass("Feed count")
|
||||
@Data
|
||||
public class FeedCount implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String value;
|
||||
private List<Feed> feeds = Lists.newArrayList();;
|
||||
|
||||
public FeedCount(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,35 +2,16 @@ package com.commafeed.frontend.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Feed details")
|
||||
@Data
|
||||
public class FeedInfo implements Serializable {
|
||||
|
||||
private String url;
|
||||
private String title;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,14 @@ package com.commafeed.frontend.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.google.api.client.util.Maps;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Server infos")
|
||||
@Data
|
||||
public class ServerInfo implements Serializable {
|
||||
|
||||
private String announcement;
|
||||
@@ -21,36 +18,4 @@ public class ServerInfo implements Serializable {
|
||||
private String gitCommit;
|
||||
private Map<String, String> supportedLanguages = Maps.newHashMap();
|
||||
|
||||
public String getAnnouncement() {
|
||||
return announcement;
|
||||
}
|
||||
|
||||
public void setAnnouncement(String announcement) {
|
||||
this.announcement = announcement;
|
||||
}
|
||||
|
||||
public Map<String, String> getSupportedLanguages() {
|
||||
return supportedLanguages;
|
||||
}
|
||||
|
||||
public void setSupportedLanguages(Map<String, String> supportedLanguages) {
|
||||
this.supportedLanguages = supportedLanguages;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getGitCommit() {
|
||||
return gitCommit;
|
||||
}
|
||||
|
||||
public void setGitCommit(String gitCommit) {
|
||||
this.gitCommit = gitCommit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("User settings")
|
||||
@Data
|
||||
public class Settings implements Serializable {
|
||||
|
||||
@ApiProperty(value = "user's preferred language, english if none")
|
||||
@@ -42,76 +39,4 @@ public class Settings implements Serializable {
|
||||
@ApiProperty(value = "user's custom css for the website")
|
||||
private String customCss;
|
||||
|
||||
public String getReadingMode() {
|
||||
return readingMode;
|
||||
}
|
||||
|
||||
public void setReadingMode(String readingMode) {
|
||||
this.readingMode = readingMode;
|
||||
}
|
||||
|
||||
public String getCustomCss() {
|
||||
return customCss;
|
||||
}
|
||||
|
||||
public void setCustomCss(String customCss) {
|
||||
this.customCss = customCss;
|
||||
}
|
||||
|
||||
public String getReadingOrder() {
|
||||
return readingOrder;
|
||||
}
|
||||
|
||||
public void setReadingOrder(String readingOrder) {
|
||||
this.readingOrder = readingOrder;
|
||||
}
|
||||
|
||||
public boolean isShowRead() {
|
||||
return showRead;
|
||||
}
|
||||
|
||||
public void setShowRead(boolean showRead) {
|
||||
this.showRead = showRead;
|
||||
}
|
||||
|
||||
public boolean isSocialButtons() {
|
||||
return socialButtons;
|
||||
}
|
||||
|
||||
public void setSocialButtons(boolean socialButtons) {
|
||||
this.socialButtons = socialButtons;
|
||||
}
|
||||
|
||||
public String getViewMode() {
|
||||
return viewMode;
|
||||
}
|
||||
|
||||
public void setViewMode(String viewMode) {
|
||||
this.viewMode = viewMode;
|
||||
}
|
||||
|
||||
public boolean isScrollMarks() {
|
||||
return scrollMarks;
|
||||
}
|
||||
|
||||
public void setScrollMarks(boolean scrollMarks) {
|
||||
this.scrollMarks = scrollMarks;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getTheme() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
public void setTheme(String theme) {
|
||||
this.theme = theme;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ package com.commafeed.frontend.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
@@ -15,13 +13,11 @@ import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("User information")
|
||||
@Data
|
||||
public class Subscription implements Serializable {
|
||||
|
||||
public static Subscription build(FeedSubscription subscription,
|
||||
String publicUrl, long unreadCount) {
|
||||
public static Subscription build(FeedSubscription subscription, String publicUrl, UnreadCount unreadCount) {
|
||||
Date now = new Date();
|
||||
FeedCategory category = subscription.getCategory();
|
||||
Feed feed = subscription.getFeed();
|
||||
@@ -35,12 +31,10 @@ public class Subscription implements Serializable {
|
||||
sub.setFeedLink(feed.getLink());
|
||||
sub.setIconUrl(FeedUtils.getFaviconUrl(subscription, publicUrl));
|
||||
sub.setLastRefresh(feed.getLastUpdated());
|
||||
sub.setNextRefresh((feed.getDisabledUntil() != null && feed
|
||||
.getDisabledUntil().before(now)) ? null : feed
|
||||
.getDisabledUntil());
|
||||
sub.setUnread(unreadCount);
|
||||
sub.setCategoryId(category == null ? null : String.valueOf(category
|
||||
.getId()));
|
||||
sub.setNextRefresh((feed.getDisabledUntil() != null && feed.getDisabledUntil().before(now)) ? null : feed.getDisabledUntil());
|
||||
sub.setUnread(unreadCount.getUnreadCount());
|
||||
sub.setNewestItemTime(unreadCount.getNewestItemTime());
|
||||
sub.setCategoryId(category == null ? null : String.valueOf(category.getId()));
|
||||
return sub;
|
||||
}
|
||||
|
||||
@@ -80,100 +74,7 @@ public class Subscription implements Serializable {
|
||||
@ApiProperty("position of the subscription's in the list")
|
||||
private Integer position;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getUnread() {
|
||||
return unread;
|
||||
}
|
||||
|
||||
public void setUnread(long unread) {
|
||||
this.unread = unread;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getFeedUrl() {
|
||||
return feedUrl;
|
||||
}
|
||||
|
||||
public void setFeedUrl(String feedUrl) {
|
||||
this.feedUrl = feedUrl;
|
||||
}
|
||||
|
||||
public int getErrorCount() {
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
public void setErrorCount(int errorCount) {
|
||||
this.errorCount = errorCount;
|
||||
}
|
||||
|
||||
public String getFeedLink() {
|
||||
return feedLink;
|
||||
}
|
||||
|
||||
public void setFeedLink(String feedLink) {
|
||||
this.feedLink = feedLink;
|
||||
}
|
||||
|
||||
public Date getLastRefresh() {
|
||||
return lastRefresh;
|
||||
}
|
||||
|
||||
public void setLastRefresh(Date lastRefresh) {
|
||||
this.lastRefresh = lastRefresh;
|
||||
}
|
||||
|
||||
public String getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(String categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public Date getNextRefresh() {
|
||||
return nextRefresh;
|
||||
}
|
||||
|
||||
public void setNextRefresh(Date nextRefresh) {
|
||||
this.nextRefresh = nextRefresh;
|
||||
}
|
||||
|
||||
public String getIconUrl() {
|
||||
return iconUrl;
|
||||
}
|
||||
|
||||
public void setIconUrl(String iconUrl) {
|
||||
this.iconUrl = iconUrl;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
@ApiProperty("date of the newest item")
|
||||
private Date newestItemTime;
|
||||
|
||||
}
|
||||
@@ -1,45 +1,29 @@
|
||||
package com.commafeed.frontend.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Unread count")
|
||||
@Data
|
||||
public class UnreadCount implements Serializable {
|
||||
|
||||
private long feedId;
|
||||
private long unreadCount;
|
||||
private Date newestItemTime;
|
||||
|
||||
public UnreadCount() {
|
||||
|
||||
}
|
||||
|
||||
public UnreadCount(long feedId, long unreadCount) {
|
||||
public UnreadCount(long feedId, long unreadCount, Date newestItemTime) {
|
||||
this.feedId = feedId;
|
||||
this.unreadCount = unreadCount;
|
||||
}
|
||||
|
||||
public long getFeedId() {
|
||||
return feedId;
|
||||
}
|
||||
|
||||
public void setFeedId(long feedId) {
|
||||
this.feedId = feedId;
|
||||
}
|
||||
|
||||
public long getUnreadCount() {
|
||||
return unreadCount;
|
||||
}
|
||||
|
||||
public void setUnreadCount(long unreadCount) {
|
||||
this.unreadCount = unreadCount;
|
||||
this.newestItemTime = newestItemTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,14 @@ package com.commafeed.frontend.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("User information")
|
||||
@Data
|
||||
public class UserModel implements Serializable {
|
||||
|
||||
@ApiProperty(value = "user id", required = true)
|
||||
@@ -43,76 +40,4 @@ public class UserModel implements Serializable {
|
||||
@ApiProperty(value = "user is admin")
|
||||
private boolean admin;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return admin;
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getLastLogin() {
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
public void setLastLogin(Date lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Add Category Request")
|
||||
@Data
|
||||
public class AddCategoryRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "name", required = true)
|
||||
@@ -21,20 +18,4 @@ public class AddCategoryRequest implements Serializable {
|
||||
@ApiProperty(value = "parent category id, if any")
|
||||
private String parentId;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Category modification request")
|
||||
@Data
|
||||
public class CategoryModificationRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "id", required = true)
|
||||
@@ -27,36 +24,4 @@ public class CategoryModificationRequest implements Serializable {
|
||||
@ApiProperty(value = "new display position, null if not changed")
|
||||
private Integer position;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Mark Request")
|
||||
@Data
|
||||
public class CollapseRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "category id", required = true)
|
||||
@@ -21,20 +18,4 @@ public class CollapseRequest implements Serializable {
|
||||
@ApiProperty(value = "collapse", required = true)
|
||||
private boolean collapse;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isCollapse() {
|
||||
return collapse;
|
||||
}
|
||||
|
||||
public void setCollapse(boolean collapse) {
|
||||
this.collapse = collapse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,30 +2,17 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Feed information request")
|
||||
@Data
|
||||
public class FeedInfoRequest implements Serializable {
|
||||
|
||||
|
||||
@ApiProperty(value = "feed url", required = true)
|
||||
private String url;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,14 @@ package com.commafeed.frontend.model.request;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Feed merge Request")
|
||||
@Data
|
||||
public class FeedMergeRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "merge into this feed", required = true)
|
||||
@@ -22,20 +19,4 @@ public class FeedMergeRequest implements Serializable {
|
||||
@ApiProperty(value = "id of the feeds to merge", required = true)
|
||||
private List<Long> feedIds;
|
||||
|
||||
public Long getIntoFeedId() {
|
||||
return intoFeedId;
|
||||
}
|
||||
|
||||
public void setIntoFeedId(Long intoFeedId) {
|
||||
this.intoFeedId = intoFeedId;
|
||||
}
|
||||
|
||||
public List<Long> getFeedIds() {
|
||||
return feedIds;
|
||||
}
|
||||
|
||||
public void setFeedIds(List<Long> feedIds) {
|
||||
this.feedIds = feedIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Feed modification request")
|
||||
@Data
|
||||
public class FeedModificationRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "id", required = true)
|
||||
@@ -27,36 +24,4 @@ public class FeedModificationRequest implements Serializable {
|
||||
@ApiProperty(value = "new display position, null if not changed")
|
||||
private Integer position;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(String categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,28 +2,17 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass
|
||||
@Data
|
||||
public class IDRequest implements Serializable {
|
||||
|
||||
@ApiProperty
|
||||
private Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,62 +1,30 @@
|
||||
package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Mark Request")
|
||||
@Data
|
||||
public class MarkRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "entry id, category id, 'all' or 'starred'", required = true)
|
||||
private String id;
|
||||
|
||||
@ApiProperty(value = "feed id, only required when marking an entry")
|
||||
private Long feedId;
|
||||
|
||||
@ApiProperty(value = "mark as read or unread")
|
||||
private boolean read;
|
||||
|
||||
@ApiProperty(value = "only entries older than this, pass the timestamp you got from the entry list to prevent marking an entry that was not retrieved", required = false)
|
||||
@ApiProperty(
|
||||
value = "only entries older than this, pass the timestamp you got from the entry list to prevent marking an entry that was not retrieved",
|
||||
required = false)
|
||||
private Long olderThan;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
public Long getOlderThan() {
|
||||
return olderThan;
|
||||
}
|
||||
|
||||
public void setOlderThan(Long olderThan) {
|
||||
this.olderThan = olderThan;
|
||||
}
|
||||
|
||||
public Long getFeedId() {
|
||||
return feedId;
|
||||
}
|
||||
|
||||
public void setFeedId(Long feedId) {
|
||||
this.feedId = feedId;
|
||||
}
|
||||
@ApiProperty(value = "if marking a category or 'all', exclude those subscriptions from the marking", required = false)
|
||||
private List<Long> excludedSubscriptions;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,28 +3,17 @@ package com.commafeed.frontend.model.request;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Multiple Mark Request")
|
||||
@Data
|
||||
public class MultipleMarkRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "list of mark requests", required = true)
|
||||
private List<MarkRequest> requests;
|
||||
|
||||
public List<MarkRequest> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
public void setRequests(List<MarkRequest> requests) {
|
||||
this.requests = requests;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.commafeed.frontend.model.request;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@SuppressWarnings("serial")
|
||||
@ApiClass("Profile modification request")
|
||||
public class ProfileModificationRequest {
|
||||
@Data
|
||||
public class ProfileModificationRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "changes email of the user, if specified")
|
||||
private String email;
|
||||
@@ -21,28 +21,4 @@ public class ProfileModificationRequest {
|
||||
@ApiProperty(value = "generate a new api key")
|
||||
private boolean newApiKey;
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public boolean isNewApiKey() {
|
||||
return newApiKey;
|
||||
}
|
||||
|
||||
public void setNewApiKey(boolean newApiKey) {
|
||||
this.newApiKey = newApiKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,15 +2,12 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Data
|
||||
public class RegistrationRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "username, between 3 and 32 characters", required = true)
|
||||
@@ -22,27 +19,4 @@ public class RegistrationRequest implements Serializable {
|
||||
@ApiProperty(value = "email address for password recovery", required = true)
|
||||
private String email;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Star Request")
|
||||
@Data
|
||||
public class StarRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "id", required = true)
|
||||
@@ -24,28 +21,4 @@ public class StarRequest implements Serializable {
|
||||
@ApiProperty(value = "starred or not")
|
||||
private boolean starred;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public void setStarred(boolean starred) {
|
||||
this.starred = starred;
|
||||
}
|
||||
|
||||
public Long getFeedId() {
|
||||
return feedId;
|
||||
}
|
||||
|
||||
public void setFeedId(Long feedId) {
|
||||
this.feedId = feedId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@ package com.commafeed.frontend.model.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import lombok.Data;
|
||||
|
||||
import com.wordnik.swagger.annotations.ApiClass;
|
||||
import com.wordnik.swagger.annotations.ApiProperty;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ApiClass("Subscription request")
|
||||
@Data
|
||||
public class SubscribeRequest implements Serializable {
|
||||
|
||||
@ApiProperty(value = "url of the feed", required = true)
|
||||
@@ -24,28 +21,4 @@ public class SubscribeRequest implements Serializable {
|
||||
@ApiProperty(value = "id of the user category to place the feed in")
|
||||
private String categoryId;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(String categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.backend.services.MailService;
|
||||
import com.commafeed.frontend.CommaFeedSession;
|
||||
import com.commafeed.frontend.utils.WicketUtils;
|
||||
import com.google.api.client.util.Maps;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class BasePage extends WebPage {
|
||||
@@ -79,15 +79,12 @@ public abstract class BasePage extends WebPage {
|
||||
if (user != null) {
|
||||
UserSettings settings = userSettingsDAO.findByUser(user);
|
||||
if (settings != null) {
|
||||
lang = settings.getLanguage() == null ? "en" : settings
|
||||
.getLanguage();
|
||||
theme = settings.getTheme() == null ? "default" : settings
|
||||
.getTheme();
|
||||
lang = settings.getLanguage() == null ? "en" : settings.getLanguage();
|
||||
theme = settings.getTheme() == null ? "default" : settings.getTheme();
|
||||
}
|
||||
}
|
||||
|
||||
add(new TransparentWebMarkupContainer("html").setMarkupId(
|
||||
"theme-" + theme).add(new AttributeModifier("lang", lang)));
|
||||
add(new TransparentWebMarkupContainer("html").setMarkupId("theme-" + theme).add(new AttributeModifier("lang", lang)));
|
||||
|
||||
settings = applicationSettingsService.get();
|
||||
add(new HeaderResponseContainer("footer-container", "footer-container"));
|
||||
@@ -107,8 +104,7 @@ public abstract class BasePage extends WebPage {
|
||||
if (getApplication().getConfigurationType() == RuntimeConfigurationType.DEPLOYMENT) {
|
||||
long startupTime = startupBean.getStartupTime();
|
||||
String suffix = "?" + startupTime;
|
||||
response.render(JavaScriptHeaderItem.forUrl("static/all.js"
|
||||
+ suffix));
|
||||
response.render(JavaScriptHeaderItem.forUrl("static/all.js" + suffix));
|
||||
response.render(CssHeaderItem.forUrl("static/all.css" + suffix));
|
||||
} else {
|
||||
response.render(JavaScriptHeaderItem.forUrl("wro/lib.js"));
|
||||
|
||||
@@ -16,8 +16,7 @@ public class DemoLoginPage extends WebPage {
|
||||
UserService userService;
|
||||
|
||||
public DemoLoginPage() {
|
||||
CommaFeedSession.get().authenticate(StartupBean.USERNAME_DEMO,
|
||||
StartupBean.USERNAME_DEMO);
|
||||
CommaFeedSession.get().authenticate(StartupBean.USERNAME_DEMO, StartupBean.USERNAME_DEMO);
|
||||
setResponsePage(getApplication().getHomePage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
package com.commafeed.frontend.pages;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.wicket.RestartResponseException;
|
||||
import org.apache.wicket.markup.html.WebPage;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
import com.commafeed.backend.StartupBean;
|
||||
import com.commafeed.backend.dao.UserDAO;
|
||||
import com.commafeed.backend.feeds.FeedUtils;
|
||||
import com.commafeed.backend.feeds.OPMLImporter;
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.frontend.CommaFeedSession;
|
||||
import com.commafeed.frontend.utils.WicketUtils;
|
||||
import com.commafeed.frontend.utils.exception.DisplayException;
|
||||
import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl;
|
||||
import com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest;
|
||||
import com.google.api.client.auth.oauth2.BearerToken;
|
||||
import com.google.api.client.auth.oauth2.TokenResponse;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class GoogleImportCallbackPage extends WebPage {
|
||||
|
||||
private static final String TOKEN_URL = "https://accounts.google.com/o/oauth2/token";
|
||||
private static final String EXPORT_URL = "https://www.google.com/reader/subscriptions/export";
|
||||
|
||||
public static final String PAGE_PATH = "google/import/callback";
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@Inject
|
||||
OPMLImporter importer;
|
||||
|
||||
@Inject
|
||||
UserDAO userDAO;
|
||||
|
||||
public static String getCallbackUrl(String publicUrl) {
|
||||
return FeedUtils.removeTrailingSlash(publicUrl) + "/" + PAGE_PATH;
|
||||
}
|
||||
|
||||
public GoogleImportCallbackPage(PageParameters params) {
|
||||
|
||||
HttpServletRequest request = WicketUtils.getHttpServletRequest();
|
||||
StringBuffer urlBuffer = request.getRequestURL();
|
||||
if (request.getQueryString() != null) {
|
||||
urlBuffer.append('?').append(request.getQueryString());
|
||||
}
|
||||
AuthorizationCodeResponseUrl responseUrl = new AuthorizationCodeResponseUrl(
|
||||
urlBuffer.toString());
|
||||
String code = responseUrl.getCode();
|
||||
|
||||
if (responseUrl.getError() != null) {
|
||||
// user declined
|
||||
throw new RestartResponseException(getApplication().getHomePage());
|
||||
} else if (code == null) {
|
||||
throw new DisplayException("Missing authorization code");
|
||||
} else {
|
||||
ApplicationSettings settings = applicationSettingsService.get();
|
||||
String redirectUri = getCallbackUrl(settings.getPublicUrl());
|
||||
String clientId = settings.getGoogleClientId();
|
||||
String clientSecret = settings.getGoogleClientSecret();
|
||||
|
||||
HttpTransport httpTransport = new NetHttpTransport();
|
||||
JacksonFactory jsonFactory = new JacksonFactory();
|
||||
|
||||
AuthorizationCodeTokenRequest tokenRequest = new AuthorizationCodeTokenRequest(
|
||||
httpTransport, jsonFactory, new GenericUrl(TOKEN_URL), code);
|
||||
tokenRequest.setRedirectUri(redirectUri);
|
||||
tokenRequest.put("client_id", clientId);
|
||||
tokenRequest.put("client_secret", clientSecret);
|
||||
tokenRequest.setGrantType("authorization_code");
|
||||
|
||||
try {
|
||||
// potential fix for invalid_grant error, happens if local
|
||||
// system time is ahead of google servers time
|
||||
Thread.sleep(1000);
|
||||
TokenResponse tokenResponse = tokenRequest.execute();
|
||||
String accessToken = tokenResponse.getAccessToken();
|
||||
|
||||
HttpRequest httpRequest = httpTransport.createRequestFactory()
|
||||
.buildGetRequest(new GenericUrl(EXPORT_URL));
|
||||
BearerToken.authorizationHeaderAccessMethod().intercept(
|
||||
httpRequest, accessToken);
|
||||
String opml = httpRequest.execute().parseAsString();
|
||||
User user = CommaFeedSession.get().getUser();
|
||||
if (user != null) {
|
||||
if (StartupBean.USERNAME_DEMO.equals(user.getName())) {
|
||||
throw new DisplayException(
|
||||
"Import is disabled for the demo account");
|
||||
}
|
||||
importer.importOpml(CommaFeedSession.get().getUser(), opml);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new DisplayException(e);
|
||||
}
|
||||
}
|
||||
setResponsePage(getApplication().getHomePage());
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.commafeed.frontend.pages;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.wicket.markup.html.WebPage;
|
||||
import org.apache.wicket.request.flow.RedirectToUrlException;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class GoogleImportRedirectPage extends WebPage {
|
||||
|
||||
private static Logger log = Logger
|
||||
.getLogger(GoogleImportRedirectPage.class);
|
||||
|
||||
private static final String SCOPE = "https://www.google.com/reader/subscriptions/export email profile";
|
||||
private static final String AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public GoogleImportRedirectPage() {
|
||||
|
||||
ApplicationSettings settings = applicationSettingsService.get();
|
||||
|
||||
String clientId = settings.getGoogleClientId();
|
||||
|
||||
String redirectUri = GoogleImportCallbackPage.getCallbackUrl(settings.getPublicUrl());
|
||||
try {
|
||||
URIBuilder builder = new URIBuilder(AUTH_URL);
|
||||
|
||||
builder.addParameter("redirect_uri", redirectUri);
|
||||
builder.addParameter("response_type", "code");
|
||||
builder.addParameter("scope", SCOPE);
|
||||
builder.addParameter("approval_prompt", "force");
|
||||
builder.addParameter("client_id", clientId);
|
||||
builder.addParameter("access_type", "offline");
|
||||
|
||||
throw new RedirectToUrlException(builder.build().toString());
|
||||
} catch (URISyntaxException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,12 @@ public class HomePage extends BasePage {
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
|
||||
response.render(CssHeaderItem.forReference(
|
||||
new UserCustomCssReference() {
|
||||
@Override
|
||||
protected String getCss() {
|
||||
UserSettings settings = userSettingsDAO
|
||||
.findByUser(CommaFeedSession.get().getUser());
|
||||
return settings == null ? null : settings
|
||||
.getCustomCss();
|
||||
}
|
||||
}, new PageParameters().add("_t", System.currentTimeMillis()),
|
||||
null));
|
||||
response.render(CssHeaderItem.forReference(new UserCustomCssReference() {
|
||||
@Override
|
||||
protected String getCss() {
|
||||
UserSettings settings = userSettingsDAO.findByUser(CommaFeedSession.get().getUser());
|
||||
return settings == null ? null : settings.getCustomCss();
|
||||
}
|
||||
}, new PageParameters().add("_t", System.currentTimeMillis()), null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,20 +52,15 @@ public class NextUnreadRedirectPage extends WebPage {
|
||||
}
|
||||
|
||||
List<FeedEntryStatus> statuses = null;
|
||||
if (StringUtils.isBlank(categoryId)
|
||||
|| CategoryREST.ALL.equals(categoryId)) {
|
||||
statuses = feedEntryStatusDAO.findAllUnread(user, null, 0, 1,
|
||||
order, true);
|
||||
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
statuses = feedEntryStatusDAO.findBySubscriptions(subs, true, null, null, 0, 1, order, true, false);
|
||||
} else {
|
||||
FeedCategory category = feedCategoryDAO.findById(user,
|
||||
Long.valueOf(categoryId));
|
||||
FeedCategory category = feedCategoryDAO.findById(user, Long.valueOf(categoryId));
|
||||
if (category != null) {
|
||||
List<FeedCategory> children = feedCategoryDAO
|
||||
.findAllChildrenCategories(user, category);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO
|
||||
.findByCategories(user, children);
|
||||
statuses = feedEntryStatusDAO.findUnreadBySubscriptions(
|
||||
subscriptions, null, 0, 1, order, true);
|
||||
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user, category);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user, children);
|
||||
statuses = feedEntryStatusDAO.findBySubscriptions(subscriptions, true, null, null, 0, 1, order, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,10 @@ public class PasswordRecoveryCallbackPage extends BasePage {
|
||||
if (user == null) {
|
||||
throw new DisplayException("email not found");
|
||||
}
|
||||
if (user.getRecoverPasswordToken() == null
|
||||
|| !user.getRecoverPasswordToken().equals(token)) {
|
||||
if (user.getRecoverPasswordToken() == null || !user.getRecoverPasswordToken().equals(token)) {
|
||||
throw new DisplayException("invalid token");
|
||||
}
|
||||
if (user.getRecoverPasswordTokenDate().before(
|
||||
DateUtils.addDays(new Date(), -2))) {
|
||||
if (user.getRecoverPasswordTokenDate().before(DateUtils.addDays(new Date(), -2))) {
|
||||
throw new DisplayException("token expired");
|
||||
}
|
||||
|
||||
@@ -57,8 +55,7 @@ public class PasswordRecoveryCallbackPage extends BasePage {
|
||||
protected void onSubmit() {
|
||||
String passwd = password.getObject();
|
||||
if (StringUtils.equals(passwd, confirm.getObject())) {
|
||||
byte[] password = encryptionService.getEncryptedPassword(
|
||||
passwd, user.getSalt());
|
||||
byte[] password = encryptionService.getEncryptedPassword(passwd, user.getSalt());
|
||||
user.setPassword(password);
|
||||
user.setApiKey(userService.generateApiKey(user));
|
||||
user.setRecoverPasswordToken(null);
|
||||
@@ -71,10 +68,8 @@ public class PasswordRecoveryCallbackPage extends BasePage {
|
||||
}
|
||||
};
|
||||
add(form);
|
||||
form.add(new PasswordTextField("password", password).setResetPassword(
|
||||
true).add(StringValidator.minimumLength(6)));
|
||||
form.add(new PasswordTextField("confirm", confirm).setResetPassword(
|
||||
true).add(StringValidator.minimumLength(6)));
|
||||
form.add(new PasswordTextField("password", password).setResetPassword(true).add(StringValidator.minimumLength(6)));
|
||||
form.add(new PasswordTextField("confirm", confirm).setResetPassword(true).add(StringValidator.minimumLength(6)));
|
||||
|
||||
form.add(new BookmarkablePageLink<Void>("cancel", HomePage.class));
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user