ACS-9835-Improve code quality in alfresco-transform-core (#1116)

This commit is contained in:
Arindam Roy
2025-07-18 15:46:21 +05:30
committed by GitHub
parent ac477aea65
commit f075c0da9a
239 changed files with 8956 additions and 9395 deletions

View File

@@ -38,29 +38,23 @@ jobs:
pre_commit: pre_commit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.26.0
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: changed-files
uses: Alfresco/alfresco-build-tools/.github/actions/github-list-changes@v8.14.1
with:
write-list-to-env: true
- uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.14.1
veracode_sca: veracode_sca:
name: "Veracode - Source Clear Scan (SCA)" name: "Veracode - Source Clear Scan (SCA)"
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- pre_commit
if: > if: >
github.ref_name == 'master' || github.ref_name == 'master' ||
github.event_name == 'pull_request' github.event_name == 'pull_request'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.26.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.26.0
- name: "Clean-up SNAPSHOT artifacts" - name: "Clean-up SNAPSHOT artifacts"
run: find "${HOME}/.m2/repository/" -type d -name "*-SNAPSHOT*" | xargs -r -l rm -rf run: find "${HOME}/.m2/repository/" -type d -name "*-SNAPSHOT*" | xargs -r -l rm -rf
- uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/veracode@v8.26.0
continue-on-error: true continue-on-error: true
with: with:
srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }} srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }}
@@ -68,14 +62,16 @@ jobs:
veracode_sast: veracode_sast:
name: "Pipeline SAST Scan" name: "Pipeline SAST Scan"
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- pre_commit
if: > if: >
(github.ref_name == 'master' || startsWith(github.ref_name, 'SP/') || startsWith(github.ref_name, 'HF/') || github.event_name == 'pull_request') && (github.ref_name == 'master' || startsWith(github.ref_name, 'SP/') || startsWith(github.ref_name, 'HF/') || github.event_name == 'pull_request') &&
github.actor != 'dependabot[bot]' && github.actor != 'dependabot[bot]' &&
!contains(github.event.head_commit.message, '[skip tests]') !contains(github.event.head_commit.message, '[skip tests]')
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.26.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.26.0
- name: "Login to Docker Hub" - name: "Login to Docker Hub"
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
@@ -87,7 +83,7 @@ jobs:
registry: quay.io registry: quay.io
username: ${{ secrets.QUAY_USERNAME }} username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }} password: ${{ secrets.QUAY_PASSWORD }}
- uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v8.26.0
with: with:
token: ${{ secrets.BOT_GITHUB_TOKEN }} token: ${{ secrets.BOT_GITHUB_TOKEN }}
repository: "Alfresco/veracode-baseline-archive" repository: "Alfresco/veracode-baseline-archive"
@@ -141,8 +137,8 @@ jobs:
!contains(github.event.head_commit.message, '[skip tests]') !contains(github.event.head_commit.message, '[skip tests]')
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.26.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.26.0
- uses: Alfresco/ya-pmd-scan@v4.1.0 - uses: Alfresco/ya-pmd-scan@v4.1.0
with: with:
classpath-build-command: "mvn -ntp package -DskipTests" classpath-build-command: "mvn -ntp package -DskipTests"
@@ -150,13 +146,15 @@ jobs:
build_and_test: build_and_test:
name: "Core & Base Snapshot deployment" name: "Core & Base Snapshot deployment"
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- pre_commit
if: > if: >
github.ref_name == 'master' && github.ref_name == 'master' &&
github.event_name != 'pull_request' github.event_name != 'pull_request'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.26.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.26.0
- name: "Login to Docker Hub" - name: "Login to Docker Hub"
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
@@ -186,6 +184,8 @@ jobs:
all_tests_matrix: all_tests_matrix:
name: ${{ matrix.testName }} name: ${{ matrix.testName }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- pre_commit
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -210,8 +210,8 @@ jobs:
testProfile: aio-test testProfile: aio-test
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.26.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.26.0
- name: "Login to Docker Hub" - name: "Login to Docker Hub"
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
@@ -251,8 +251,8 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
persist-credentials: false persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.26.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.26.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
with: with:
@@ -274,7 +274,7 @@ jobs:
sudo service docker restart sudo service docker restart
- name: "Clean-up SNAPSHOT artifacts" - name: "Clean-up SNAPSHOT artifacts"
run: find "${HOME}/.m2/repository/" -type d -name "*-SNAPSHOT*" | xargs -r -l rm -rf run: find "${HOME}/.m2/repository/" -type d -name "*-SNAPSHOT*" | xargs -r -l rm -rf
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.14.1 - uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v8.26.0
with: with:
username: ${{ env.GIT_USERNAME }} username: ${{ env.GIT_USERNAME }}
email: ${{ env.GIT_EMAIL }} email: ${{ env.GIT_EMAIL }}

View File

@@ -0,0 +1,32 @@
name: Pre-Commit formatter
on:
push:
branches:
- precommit/**
jobs:
format-code:
name: "Reformat code"
runs-on: ubuntu-latest
if: contains(github.event.head_commit.message, '[reformat code]')
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: "3.9"
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
continue-on-error: true
with:
extra_args: --all-files
- name: Update secrets baseline
run: pip install detect-secrets && detect-secrets scan --baseline .secrets.baseline
- uses: Alfresco/alfresco-build-tools/.github/actions/git-commit-changes@v8.26.0
with:
username: ${{ secrets.BOT_GITHUB_USERNAME }}
add-options: -u
commit-message: "Apply Pre-Commit code formatting"
skip-if-no-changes: true
- name: Push changes
run: git push

View File

@@ -8,7 +8,7 @@ repos:
hooks: hooks:
- id: check-format-and-headers - id: check-format-and-headers
name: Check format and headers and fix if necessary name: Check format and headers and fix if necessary
entry: ./scripts/check-format-and-headers.sh entry: ./scripts/hooks/check-format-and-headers.sh
language: script language: script
files: ".*.java" files: ".*.java"
pass_filenames: false pass_filenames: false

View File

@@ -133,8 +133,7 @@
"filename": "deprecated/alfresco-transformer-base/src/main/resources/application.yaml", "filename": "deprecated/alfresco-transformer-base/src/main/resources/application.yaml",
"hashed_secret": "8c1ab56bc026c89468942df91166c35a277455a7", "hashed_secret": "8c1ab56bc026c89468942df91166c35a277455a7",
"is_verified": false, "is_verified": false,
"line_number": 10, "line_number": 10
"is_secret": false
} }
], ],
"engines/base/src/main/resources/application.yaml": [ "engines/base/src/main/resources/application.yaml": [
@@ -143,8 +142,7 @@
"filename": "engines/base/src/main/resources/application.yaml", "filename": "engines/base/src/main/resources/application.yaml",
"hashed_secret": "8c1ab56bc026c89468942df91166c35a277455a7", "hashed_secret": "8c1ab56bc026c89468942df91166c35a277455a7",
"is_verified": false, "is_verified": false,
"line_number": 10, "line_number": 10
"is_secret": false
} }
], ],
"engines/libreoffice/src/test/resources/quick.vdx": [ "engines/libreoffice/src/test/resources/quick.vdx": [
@@ -153,10 +151,9 @@
"filename": "engines/libreoffice/src/test/resources/quick.vdx", "filename": "engines/libreoffice/src/test/resources/quick.vdx",
"hashed_secret": "37c332070388a4797ccf1fa64f7029a193ce0371", "hashed_secret": "37c332070388a4797ccf1fa64f7029a193ce0371",
"is_verified": false, "is_verified": false,
"line_number": 2, "line_number": 2
"is_secret": false
} }
] ]
}, },
"generated_at": "2024-09-26T14:01:33Z" "generated_at": "2025-07-18T08:03:08Z"
} }

51
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,51 @@
### Contributing
Thanks for your interest in contributing to this project!
The following is a set of guidelines for contributing to this library. Most of them will make the life of the reviewer easier and therefore decrease the time required for the patch be included in the next version.
The project uses [pre-commit](https://pre-commit.com/) to format code (with [Spotless](https://github.com/diffplug/spotless)), validate license headers and check for secrets (with [detect-secrets](https://github.com/Yelp/detect-secrets)). To install the pre-commit hooks then first install pre-commit and then run:
```shell
pre-commit install
```
When you make a commit then these hooks will run and check the modified files. If it makes changes then you can review them and then `git commit` again to accept the changes.
#### Code Quality
This project uses `spotless` that enforces `alfresco-formatter.xml` to ensure code quality.
The code style definition file is taken always form the `master` branch of `alfresco-community-repo`.
All downstream projects use this code style definition file as well.
To check code-style violations you can use:
```bash
mvn spotless:check
```
To reformat files you can use:
```bash
mvn spotless:apply
```
#### Secret Detection
We are using [detect-secrets](https://github.com/Yelp/detect-secrets) to try to avoid accidentally publishing secret keys.
If you have pre-commit installed then this should run automatically when making a commit. Usually there should be no issues,
but if it finds a potential issue (e.g. a high entropy string) then you will see the following:
```shell
Detect secrets...........................................................Failed
- hook id: detect-secrets
- exit code: 1
ERROR: Potential secrets about to be committed to git repo!
Secret Type: Secret Keyword
Location: test.txt:1
```
If this is a false positive and you actually want to commit the string then run these two commands:
```shell
detect-secrets scan --baseline .secrets.baseline
detect-secrets audit .secrets.baseline
```
This will update the baseline file to include your new code and then allow you to review the detected secret and mark it as a false positive.
Once you are finished then you can add `.secrets.baseline` to the staged changes and you should be able to create a commit.

View File

@@ -1,401 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="23">
<profile kind="CodeFormatterProfile" name="Spotless" version="23">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" 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.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" 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_new_line_after_annotation_on_package" value="insert"/>
<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_space_before_closing_paren_in_while" value="do not insert"/>
<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.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not 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_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<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.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<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.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<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_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not 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_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<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.keep_annotation_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="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_space_after_assignment_operator" value="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_opening_paren_in_for" 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_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<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.alignment_for_union_type_in_multicatch" value="16"/>
<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.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<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_before_comma_in_array_initializer" value="do not 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.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<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_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="0"/>
<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_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<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_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" 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.keep_if_then_body_block_on_one_line" value="one_line_if_empty"/>
<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_before_closing_brace_in_array_initializer" value="do not insert"/>
<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.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="next_line"/>
<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.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<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.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" 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_comma_in_allocation_expression" value="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_brace_in_array_initializer" 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_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<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_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" 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.comment.format_html" value="true"/>
<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.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<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_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
<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.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" 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_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="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.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<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_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" 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.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="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.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="next_line"/>
<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_between_brackets_in_array_type_reference" value="do not 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.alignment_for_string_concatenation" value="16"/>
<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_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<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_before_prefix_operator" 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_before_shift_operator" value="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.insert_space_after_comma_in_type_parameters" value="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_after_shift_operator" 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_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<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.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<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_before_opening_paren_in_synchronized" value="insert"/>
<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.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<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.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" 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.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<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.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" 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.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<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.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<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_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<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_after_opening_paren_in_catch" value="do not insert"/>
<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_before_opening_brace_in_anonymous_type_declaration" 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_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<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_question_in_wildcard" value="do not 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_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="999"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_if_empty"/>
<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.keep_loop_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_if_empty"/>
<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.alignment_for_additive_operator" value="16"/>
<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_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_if_empty"/>
<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_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" 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_type_parameters" 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_paren_in_method_invocation" value="do not 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.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<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_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="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.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<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_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="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_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
<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.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<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.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="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.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<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_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<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.brace_position_for_switch" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not 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.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<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.keep_record_constructor_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
<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.blank_lines_before_new_chunk" value="1"/>
<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_after_opening_paren_in_constructor_declaration" value="do not 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_before_closing_angle_bracket_in_type_arguments" 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.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<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_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<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.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<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_space_after_comma_in_annotation" value="insert"/>
<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.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<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_comma_in_permitted_types" 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.keep_enum_declaration_on_one_line" value="one_line_if_empty"/>
<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.brace_position_for_enum_constant" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<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_before_comma_in_for_increments" value="do not insert"/>
<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.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not 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.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="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.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="999"/>
<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.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

View File

@@ -26,17 +26,50 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import org.alfresco.transform.common.TransformerDebug; import static java.util.stream.Collectors.joining;
import org.alfresco.transform.client.model.InternalContext;
import org.alfresco.transform.client.model.TransformReply; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.client.model.TransformRequest; import static org.springframework.http.HttpStatus.CREATED;
import org.alfresco.transform.messages.TransformRequestValidator; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.alfresco.transform.config.TransformConfig; import static org.springframework.http.HttpStatus.OK;
import org.alfresco.transform.registry.TransformServiceRegistry; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import org.alfresco.transform.exceptions.TransformException; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient; import static org.springframework.util.StringUtils.getFilenameExtension;
import org.alfresco.transformer.logging.LogEntry;
import org.alfresco.transformer.model.FileRefResponse; import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION;
import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION_DEFAULT;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG;
import static org.alfresco.transform.config.CoreVersionDecorator.setOrClearCoreVersion;
import static org.alfresco.transformer.fs.FileManager.TempFileProvider.createTempFile;
import static org.alfresco.transformer.fs.FileManager.buildFile;
import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.fs.FileManager.deleteFile;
import static org.alfresco.transformer.fs.FileManager.getFilenameFromContentDisposition;
import static org.alfresco.transformer.fs.FileManager.save;
import static org.alfresco.transformer.util.RequestParamMap.FILE;
import static org.alfresco.transformer.util.RequestParamMap.SOURCE_ENCODING;
import static org.alfresco.transformer.util.RequestParamMap.SOURCE_EXTENSION;
import static org.alfresco.transformer.util.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transformer.util.RequestParamMap.TARGET_EXTENSION;
import static org.alfresco.transformer.util.RequestParamMap.TARGET_MIMETYPE;
import static org.alfresco.transformer.util.RequestParamMap.TEST_DELAY;
import static org.alfresco.transformer.util.RequestParamMap.TRANSFORM_NAME_PROPERTY;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import jakarta.servlet.http.HttpServletRequest;
import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.FileUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -56,85 +89,57 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest; import org.alfresco.transform.client.model.InternalContext;
import java.io.File; import org.alfresco.transform.client.model.TransformReply;
import java.io.IOException; import org.alfresco.transform.client.model.TransformRequest;
import java.net.URL; import org.alfresco.transform.common.TransformerDebug;
import java.util.Arrays; import org.alfresco.transform.config.TransformConfig;
import java.util.HashMap; import org.alfresco.transform.exceptions.TransformException;
import java.util.List; import org.alfresco.transform.messages.TransformRequestValidator;
import java.util.Map; import org.alfresco.transform.registry.TransformServiceRegistry;
import java.util.concurrent.atomic.AtomicInteger; import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient;
import org.alfresco.transformer.logging.LogEntry;
import static java.util.stream.Collectors.joining; import org.alfresco.transformer.model.FileRefResponse;
import static org.alfresco.transform.config.CoreVersionDecorator.setOrClearCoreVersion;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION;
import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION_DEFAULT;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG;
import static org.alfresco.transformer.fs.FileManager.TempFileProvider.createTempFile;
import static org.alfresco.transformer.fs.FileManager.buildFile;
import static org.alfresco.transformer.fs.FileManager.createAttachment;
import static org.alfresco.transformer.fs.FileManager.createSourceFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFile;
import static org.alfresco.transformer.fs.FileManager.createTargetFileName;
import static org.alfresco.transformer.fs.FileManager.deleteFile;
import static org.alfresco.transformer.fs.FileManager.getFilenameFromContentDisposition;
import static org.alfresco.transformer.fs.FileManager.save;
import static org.alfresco.transformer.util.RequestParamMap.FILE;
import static org.alfresco.transformer.util.RequestParamMap.SOURCE_ENCODING;
import static org.alfresco.transformer.util.RequestParamMap.SOURCE_EXTENSION;
import static org.alfresco.transformer.util.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transformer.util.RequestParamMap.TARGET_EXTENSION;
import static org.alfresco.transformer.util.RequestParamMap.TARGET_MIMETYPE;
import static org.alfresco.transformer.util.RequestParamMap.TEST_DELAY;
import static org.alfresco.transformer.util.RequestParamMap.TRANSFORM_NAME_PROPERTY;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.springframework.util.StringUtils.getFilenameExtension;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* <p>Abstract Controller, provides structure and helper methods to sub-class transformer controllers. Sub classes * <p>
* should implement {@link #transformImpl(String, String, String, Map, File, File)} and unimplemented methods from * Abstract Controller, provides structure and helper methods to sub-class transformer controllers. Sub classes should implement {@link #transformImpl(String, String, String, Map, File, File)} and unimplemented methods from {@link TransformController}.
* {@link TransformController}.</p> * </p>
* *
* <p>Status Codes:</p> * <p>
* <ul> * Status Codes:
* <li>200 Success</li> * </p>
* <li>400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)</li> * <ul>
* <li>400 Bad Request: Request parameter <name> is of the wrong type</li> * <li>200 Success</li>
* <li>400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)</li> * <li>400 Bad Request: Request parameter <name> is missing (missing mandatory parameter)</li>
* <li>400 Bad Request: The source filename was not supplied</li> * <li>400 Bad Request: Request parameter <name> is of the wrong type</li>
* <li>500 Internal Server Error: (no message with low level IO problems)</li> * <li>400 Bad Request: Transformer exit code was not 0 (possible problem with the source file)</li>
* <li>500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)</li> * <li>400 Bad Request: The source filename was not supplied</li>
* <li>500 Internal Server Error: Transformer version check exit code was not 0</li> * <li>500 Internal Server Error: (no message with low level IO problems)</li>
* <li>500 Internal Server Error: Transformer version check failed to create any output</li> * <li>500 Internal Server Error: The target filename was not supplied (should not happen as targetExtension is checked)</li>
* <li>500 Internal Server Error: Could not read the target file</li> * <li>500 Internal Server Error: Transformer version check exit code was not 0</li>
* <li>500 Internal Server Error: The target filename was malformed (should not happen because of other checks)</li> * <li>500 Internal Server Error: Transformer version check failed to create any output</li>
* <li>500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)</li> * <li>500 Internal Server Error: Could not read the target file</li>
* <li>500 Internal Server Error: Filename encoding error</li> * <li>500 Internal Server Error: The target filename was malformed (should not happen because of other checks)</li>
* <li>507 Insufficient Storage: Failed to store the source file</li> * <li>500 Internal Server Error: Transformer failed to create an output file (the exit code was 0, so there should be some content)</li>
* <li>500 Internal Server Error: Filename encoding error</li>
* <li>507 Insufficient Storage: Failed to store the source file</li>
* *
* <li>408 Request Timeout -- TODO implement general timeout mechanism rather than depend on transformer timeout * <li>408 Request Timeout -- TODO implement general timeout mechanism rather than depend on transformer timeout (might be possible for external processes)</li>
* (might be possible for external processes)</li> * <li>415 Unsupported Media Type -- TODO possibly implement a check on supported source and target mimetypes (probably not)</li>
* <li>415 Unsupported Media Type -- TODO possibly implement a check on supported source and target mimetypes (probably not)</li> * <li>429 Too Many Requests: Returned by liveness probe</li>
* <li>429 Too Many Requests: Returned by liveness probe</li> * </ul>
* </ul> * <p>
* <p>Provides methods to help super classes perform /transform requests. Also responses to /version, /ready and /live * Provides methods to help super classes perform /transform requests. Also responses to /version, /ready and /live requests.
* requests.</p> * </p>
*/ */
@Deprecated @Deprecated
public abstract class AbstractTransformerController implements TransformController public abstract class AbstractTransformerController implements TransformController
{ {
private static final Logger logger = LoggerFactory.getLogger( private static final Logger logger = LoggerFactory.getLogger(
AbstractTransformerController.class); AbstractTransformerController.class);
// Request parameters that are not part of transform options // Request parameters that are not part of transform options
public static final List<String> NON_TRANSFORM_OPTION_REQUEST_PARAMETERS = Arrays.asList(SOURCE_EXTENSION, public static final List<String> NON_TRANSFORM_OPTION_REQUEST_PARAMETERS = Arrays.asList(SOURCE_EXTENSION,
@@ -166,16 +171,16 @@ public abstract class AbstractTransformerController implements TransformControll
@PostMapping(value = ENDPOINT_TRANSFORM, consumes = MULTIPART_FORM_DATA_VALUE) @PostMapping(value = ENDPOINT_TRANSFORM, consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Resource> transform(HttpServletRequest request, public ResponseEntity<Resource> transform(HttpServletRequest request,
@RequestParam(value = FILE, required = false) MultipartFile sourceMultipartFile, @RequestParam(value = FILE, required = false) MultipartFile sourceMultipartFile,
@RequestParam(TARGET_EXTENSION) String targetExtension, @RequestParam(TARGET_EXTENSION) String targetExtension,
@RequestParam(value = SOURCE_MIMETYPE, required = false) String sourceMimetype, @RequestParam(value = SOURCE_MIMETYPE, required = false) String sourceMimetype,
@RequestParam(value = TARGET_MIMETYPE, required = false) String targetMimetype, @RequestParam(value = TARGET_MIMETYPE, required = false) String targetMimetype,
@RequestParam Map<String, String> requestParameters, @RequestParam Map<String, String> requestParameters,
@RequestParam(value = TEST_DELAY, required = false) Long testDelay, @RequestParam(value = TEST_DELAY, required = false) Long testDelay,
// The TRANSFORM_NAME_PROPERTY param allows ACS legacy transformers to specify which transform to use, // The TRANSFORM_NAME_PROPERTY param allows ACS legacy transformers to specify which transform to use,
// It can be removed once legacy transformers are removed from ACS. // It can be removed once legacy transformers are removed from ACS.
@RequestParam(value = TRANSFORM_NAME_PROPERTY, required = false) String requestTransformName) @RequestParam(value = TRANSFORM_NAME_PROPERTY, required = false) String requestTransformName)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
@@ -189,7 +194,7 @@ public abstract class AbstractTransformerController implements TransformControll
String sourceFilename; String sourceFilename;
if (directUrl.isBlank()) if (directUrl.isBlank())
{ {
if (sourceMultipartFile == null) if (sourceMultipartFile == null)
{ {
throw new TransformException(BAD_REQUEST, "Required request part 'file' is not present"); throw new TransformException(BAD_REQUEST, "Required request part 'file' is not present");
} }
@@ -208,7 +213,7 @@ public abstract class AbstractTransformerController implements TransformControll
Map<String, String> transformOptions = getTransformOptions(requestParameters); Map<String, String> transformOptions = getTransformOptions(requestParameters);
String transformName = getTransformerName(sourceMimetype, targetMimetype, requestTransformName, sourceFile, transformOptions); String transformName = getTransformerName(sourceMimetype, targetMimetype, requestTransformName, sourceFile, transformOptions);
String reference = "e"+httpRequestCount.getAndIncrement(); String reference = "e" + httpRequestCount.getAndIncrement();
transformerDebug.pushTransform(reference, sourceMimetype, targetMimetype, sourceFile, transformName); transformerDebug.pushTransform(reference, sourceMimetype, targetMimetype, sourceFile, transformName);
transformerDebug.logOptions(reference, requestParameters); transformerDebug.logOptions(reference, requestParameters);
try try
@@ -260,17 +265,18 @@ public abstract class AbstractTransformerController implements TransformControll
/** /**
* '/transform' endpoint which consumes and produces 'application/json' * '/transform' endpoint which consumes and produces 'application/json'
* *
* This is the way to tell Spring to redirect the request to this endpoint * This is the way to tell Spring to redirect the request to this endpoint instead of the one which produces 'html'
* instead of the one which produces 'html'
* *
* @param request The transformation request * @param request
* @param timeout Transformation timeout * The transformation request
* @param timeout
* Transformation timeout
* @return A transformation reply * @return A transformation reply
*/ */
@PostMapping(value = ENDPOINT_TRANSFORM, produces = APPLICATION_JSON_VALUE) @PostMapping(value = ENDPOINT_TRANSFORM, produces = APPLICATION_JSON_VALUE)
@ResponseBody @ResponseBody
public ResponseEntity<TransformReply> transform(@RequestBody TransformRequest request, public ResponseEntity<TransformReply> transform(@RequestBody TransformRequest request,
@RequestParam(value = "timeout", required = false) Long timeout) @RequestParam(value = "timeout", required = false) Long timeout)
{ {
logger.trace("Received {}, timeout {} ms", request, timeout); logger.trace("Received {}, timeout {} ms", request, timeout);
@@ -288,10 +294,10 @@ public abstract class AbstractTransformerController implements TransformControll
{ {
reply.setStatus(BAD_REQUEST.value()); reply.setStatus(BAD_REQUEST.value());
reply.setErrorDetails(errors reply.setErrorDetails(errors
.getAllErrors() .getAllErrors()
.stream() .stream()
.map(Object::toString) .map(Object::toString)
.collect(joining(", "))); .collect(joining(", ")));
transformerDebug.logFailure(reply); transformerDebug.logFailure(reply);
logger.trace("Invalid request, sending {}", reply); logger.trace("Invalid request, sending {}", reply);
@@ -343,7 +349,7 @@ public abstract class AbstractTransformerController implements TransformControll
// Create local temp target file in order to run the transformation // Create local temp target file in order to run the transformation
final String targetFilename = createTargetFileName(sourceFile.getName(), final String targetFilename = createTargetFileName(sourceFile.getName(),
request.getTargetExtension()); request.getTargetExtension());
final File targetFile = buildFile(targetFilename); final File targetFile = buildFile(targetFilename);
// Run the transformation // Run the transformation
@@ -417,7 +423,7 @@ public abstract class AbstractTransformerController implements TransformControll
catch (Exception e) catch (Exception e)
{ {
logger.error("Failed to delete local temp target file '{}'. Error will be ignored ", logger.error("Failed to delete local temp target file '{}'. Error will be ignored ",
targetFile, e); targetFile, e);
} }
try try
{ {
@@ -461,14 +467,16 @@ public abstract class AbstractTransformerController implements TransformControll
/** /**
* Loads the file with the specified sourceReference from Alfresco Shared File Store * Loads the file with the specified sourceReference from Alfresco Shared File Store
* *
* @param sourceReference reference to the file in Alfresco Shared File Store * @param sourceReference
* @param sourceExtension default extension if the file in Alfresco Shared File Store has none * reference to the file in Alfresco Shared File Store
* @param sourceExtension
* default extension if the file in Alfresco Shared File Store has none
* @return the file containing the source content for the transformation * @return the file containing the source content for the transformation
*/ */
private File loadSourceFile(final String sourceReference, final String sourceExtension) private File loadSourceFile(final String sourceReference, final String sourceExtension)
{ {
ResponseEntity<Resource> responseEntity = alfrescoSharedFileStoreClient ResponseEntity<Resource> responseEntity = alfrescoSharedFileStoreClient
.retrieveFile(sourceReference); .retrieveFile(sourceReference);
getProbeTestTransform().incrementTransformerCount(); getProbeTestTransform().incrementTransformerCount();
HttpHeaders headers = responseEntity.getHeaders(); HttpHeaders headers = responseEntity.getHeaders();
@@ -482,14 +490,14 @@ public abstract class AbstractTransformerController implements TransformControll
if (body == null) if (body == null)
{ {
String message = "Source file with reference: " + sourceReference + " is null or empty. " String message = "Source file with reference: " + sourceReference + " is null or empty. "
+ "Transformation will fail and stop now as there is no content to be transformed."; + "Transformation will fail and stop now as there is no content to be transformed.";
logger.warn(message); logger.warn(message);
throw new TransformException(BAD_REQUEST, message); throw new TransformException(BAD_REQUEST, message);
} }
final File file = createTempFile("source_", "." + extension); final File file = createTempFile("source_", "." + extension);
logger.debug("Read source content {} length={} contentType={}", logger.debug("Read source content {} length={} contentType={}",
sourceReference, size, contentType); sourceReference, size, contentType);
save(body, file); save(body, file);
LogEntry.setSource(filename, size); LogEntry.setSource(filename, size);
@@ -500,23 +508,23 @@ public abstract class AbstractTransformerController implements TransformControll
{ {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append(prefix).append(" - ") sb.append(prefix).append(" - ")
.append(e.getClass().getSimpleName()).append(": ") .append(e.getClass().getSimpleName()).append(": ")
.append(e.getMessage()); .append(e.getMessage());
while (e.getCause() != null) while (e.getCause() != null)
{ {
e = e.getCause(); e = e.getCause();
sb.append(", cause ") sb.append(", cause ")
.append(e.getClass().getSimpleName()).append(": ") .append(e.getClass().getSimpleName()).append(": ")
.append(e.getMessage()); .append(e.getMessage());
} }
return sb.toString(); return sb.toString();
} }
private String getTransformerName(String sourceMimetype, String targetMimetype, private String getTransformerName(String sourceMimetype, String targetMimetype,
String requestTransformName, File sourceFile, String requestTransformName, File sourceFile,
Map<String, String> transformOptions) Map<String, String> transformOptions)
{ {
// Check if transformName was provided in the request (this can happen for ACS legacy transformers) // Check if transformName was provided in the request (this can happen for ACS legacy transformers)
String transformName = requestTransformName; String transformName = requestTransformName;
@@ -532,7 +540,7 @@ public abstract class AbstractTransformerController implements TransformControll
} }
protected String getTransformerName(final File sourceFile, final String sourceMimetype, protected String getTransformerName(final File sourceFile, final String sourceMimetype,
final String targetMimetype, final Map<String, String> transformOptions) final String targetMimetype, final Map<String, String> transformOptions)
{ {
// The transformOptions always contains sourceEncoding when sent to a T-Engine, even though it should not be // The transformOptions always contains sourceEncoding when sent to a T-Engine, even though it should not be
// used to select a transformer. Similar to source and target mimetypes and extensions, but these are not // used to select a transformer. Similar to source and target mimetypes and extensions, but these are not
@@ -563,7 +571,7 @@ public abstract class AbstractTransformerController implements TransformControll
if (namesAndValues.length % 2 != 0) if (namesAndValues.length % 2 != 0)
{ {
logger.error( logger.error(
"Incorrect number of parameters. Should have an even number as they are names and values."); "Incorrect number of parameters. Should have an even number as they are names and values.");
} }
Map<String, String> transformOptions = new HashMap<>(); Map<String, String> transformOptions = new HashMap<>();
@@ -571,7 +579,7 @@ public abstract class AbstractTransformerController implements TransformControll
{ {
String name = namesAndValues[i].toString(); String name = namesAndValues[i].toString();
Object value = namesAndValues[i + 1]; Object value = namesAndValues[i + 1];
if (value != null && (!(value instanceof String) || !((String)value).isBlank())) if (value != null && (!(value instanceof String) || !((String) value).isBlank()))
{ {
transformOptions.put(name, value.toString()); transformOptions.put(name, value.toString());
} }

View File

@@ -30,16 +30,10 @@ import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import java.util.Optional; import java.util.Optional;
import jakarta.jms.Destination; import jakarta.jms.Destination;
import jakarta.jms.JMSException; import jakarta.jms.JMSException;
import jakarta.jms.Message; import jakarta.jms.Message;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.messaging.TransformMessageConverter;
import org.alfresco.transformer.messaging.TransformReplySender;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -49,16 +43,18 @@ import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.messaging.TransformMessageConverter;
import org.alfresco.transformer.messaging.TransformReplySender;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Queue Transformer service. * Queue Transformer service. This service reads all the requests for the particular engine, forwards them to the worker component (at this time the injected controller - to be refactored) and sends back the reply to the {@link Message#getJMSReplyTo()} value. If this value is missing we've got to a dead end.
* This service reads all the requests for the particular engine, forwards them to the worker
* component (at this time the injected controller - to be refactored) and sends back the reply
* to the {@link Message#getJMSReplyTo()} value. If this value is missing we've got to a dead end.
* *
* @author Lucian Tuca * @author Lucian Tuca created on 18/12/2018
* created on 18/12/2018
*/ */
@Deprecated @Deprecated
@Component @Component
@@ -96,16 +92,16 @@ public class QueueTransformService
if (replyToDestinationQueue == null) if (replyToDestinationQueue == null)
{ {
logger.error( logger.error(
"Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ", "Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ",
correlationId); correlationId);
return; return;
} }
} }
catch (JMSException e) catch (JMSException e)
{ {
logger.error( logger.error(
"Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ", "Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ",
correlationId); correlationId);
return; return;
} }
@@ -120,7 +116,7 @@ public class QueueTransformService
{ {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
replyWithError(replyToDestinationQueue, HttpStatus.valueOf(e.getStatusCode()), replyWithError(replyToDestinationQueue, HttpStatus.valueOf(e.getStatusCode()),
e.getMessage(), correlationId); e.getMessage(), correlationId);
return; return;
} }
@@ -128,21 +124,21 @@ public class QueueTransformService
{ {
logger.error("T-Request from message with correlationID {} is null!", correlationId); logger.error("T-Request from message with correlationID {} is null!", correlationId);
replyWithInternalSvErr(replyToDestinationQueue, replyWithInternalSvErr(replyToDestinationQueue,
"JMS exception during T-Request deserialization: ", correlationId); "JMS exception during T-Request deserialization: ", correlationId);
return; return;
} }
TransformReply reply = transformController.transform(transformRequest.get(), null) TransformReply reply = transformController.transform(transformRequest.get(), null)
.getBody(); .getBody();
transformReplySender.send(replyToDestinationQueue, reply); transformReplySender.send(replyToDestinationQueue, reply);
} }
/** /**
* Tries to convert the JMS {@link Message} to a {@link TransformRequest} * Tries to convert the JMS {@link Message} to a {@link TransformRequest} If any error occurs, a {@link TransformException} is thrown
* If any error occurs, a {@link TransformException} is thrown
* *
* @param msg Message to be deserialized * @param msg
* Message to be deserialized
* @return The converted {@link TransformRequest} instance * @return The converted {@link TransformRequest} instance
*/ */
private Optional<TransformRequest> convert(final Message msg, String correlationId) private Optional<TransformRequest> convert(final Message msg, String correlationId)
@@ -154,42 +150,39 @@ public class QueueTransformService
} }
catch (MessageConversionException e) catch (MessageConversionException e)
{ {
String message = String message = "MessageConversionException during T-Request deserialization of message with correlationID "
"MessageConversionException during T-Request deserialization of message with correlationID " + correlationId + ": ";
+ correlationId + ": ";
throw new TransformException(BAD_REQUEST, message + e.getMessage()); throw new TransformException(BAD_REQUEST, message + e.getMessage());
} }
catch (JMSException e) catch (JMSException e)
{ {
String message = String message = "JMSException during T-Request deserialization of message with correlationID "
"JMSException during T-Request deserialization of message with correlationID " + correlationId + ": ";
+ correlationId + ": ";
throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage()); throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage());
} }
catch (Exception e) catch (Exception e)
{ {
String message = String message = "Exception during T-Request deserialization of message with correlationID "
"Exception during T-Request deserialization of message with correlationID " + correlationId + ": ";
+ correlationId + ": ";
throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage()); throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage());
} }
} }
private void replyWithInternalSvErr(final Destination destination, final String msg, private void replyWithInternalSvErr(final Destination destination, final String msg,
final String correlationId) final String correlationId)
{ {
replyWithError(destination, INTERNAL_SERVER_ERROR, msg, correlationId); replyWithError(destination, INTERNAL_SERVER_ERROR, msg, correlationId);
} }
private void replyWithError(final Destination destination, final HttpStatus status, private void replyWithError(final Destination destination, final HttpStatus status,
final String msg, final String msg,
final String correlationId) final String correlationId)
{ {
final TransformReply reply = TransformReply final TransformReply reply = TransformReply
.builder() .builder()
.withStatus(status.value()) .withStatus(status.value())
.withErrorDetails(msg) .withErrorDetails(msg)
.build(); .build();
transformReplySender.send(destination, reply, correlationId); transformReplySender.send(destination, reply, correlationId);
} }

View File

@@ -27,21 +27,16 @@
package org.alfresco.transformer; package org.alfresco.transformer;
import static java.text.MessageFormat.format; import static java.text.MessageFormat.format;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.logging.LogEntry;
import org.alfresco.transformer.probes.ProbeTestTransform;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException; import org.springframework.beans.TypeMismatchException;
@@ -53,13 +48,17 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.logging.LogEntry;
import org.alfresco.transformer.probes.ProbeTestTransform;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* TransformController interface. * TransformController interface. <br/>
* <br/> * It contains much of the common boilerplate code that each of its concrete implementations need as default methods.
* It contains much of the common boilerplate code that each of
* its concrete implementations need as default methods.
*/ */
@Deprecated @Deprecated
public interface TransformController public interface TransformController
@@ -69,19 +68,24 @@ public interface TransformController
/** /**
* Should be overridden in subclasses to initiate the transformation. * Should be overridden in subclasses to initiate the transformation.
* *
* @param transformName the name of the transformer in the engine_config.json file * @param transformName
* @param sourceMimetype mimetype of the source * the name of the transformer in the engine_config.json file
* @param targetMimetype mimetype of the target * @param sourceMimetype
* @param transformOptions transform options from the client * mimetype of the source
* @param sourceFile the source file * @param targetMimetype
* @param targetFile the target file * mimetype of the target
* @param transformOptions
* transform options from the client
* @param sourceFile
* the source file
* @param targetFile
* the target file
*/ */
void transformImpl(String transformName, String sourceMimetype, String targetMimetype, void transformImpl(String transformName, String sourceMimetype, String targetMimetype,
Map<String, String> transformOptions, File sourceFile, File targetFile); Map<String, String> transformOptions, File sourceFile, File targetFile);
/** /**
* @deprecated use {@link #transformImpl(String, String, String, Map, File, File)} and timeout should be part of * @deprecated use {@link #transformImpl(String, String, String, Map, File, File)} and timeout should be part of the transformOptions created from the TransformRequest.
* the transformOptions created from the TransformRequest.
*/ */
@Deprecated @Deprecated
ResponseEntity<TransformReply> transform(TransformRequest transformRequest, Long timeout); ResponseEntity<TransformReply> transform(TransformRequest transformRequest, Long timeout);
@@ -91,10 +95,9 @@ public interface TransformController
*/ */
@Deprecated @Deprecated
default void processTransform(final File sourceFile, final File targetFile, default void processTransform(final File sourceFile, final File targetFile,
final String sourceMimetype, final String targetMimetype, final String sourceMimetype, final String targetMimetype,
final Map<String, String> transformOptions, final Long timeout) final Map<String, String> transformOptions, final Long timeout)
{ {}
}
/** /**
* @return a friendly name for the T-Engine. * @return a friendly name for the T-Engine.
@@ -131,8 +134,7 @@ public interface TransformController
} }
/** /**
* @return the name of a template to display when there is an error when using the test UI for the T-Engine. * @return the name of a template to display when there is an error when using the test UI for the T-Engine. Defaults to {@code "error"}.
* Defaults to {@code "error"}.
* @See #transformForm * @See #transformForm
*/ */
@GetMapping("/error") @GetMapping("/error")
@@ -142,8 +144,7 @@ public interface TransformController
} }
/** /**
* @return the name of a template to display log messages when using the test UI for the T-Engine. * @return the name of a template to display log messages when using the test UI for the T-Engine. Defaults to {@code "log"}.
* Defaults to {@code "log"}.
* @See #transformForm * @See #transformForm
*/ */
@GetMapping("/log") @GetMapping("/log")
@@ -178,13 +179,13 @@ public interface TransformController
return probe(request, true); return probe(request, true);
} }
//region [Exception Handlers] // region [Exception Handlers]
@ExceptionHandler(TypeMismatchException.class) @ExceptionHandler(TypeMismatchException.class)
default void handleParamsTypeMismatch(HttpServletResponse response, default void handleParamsTypeMismatch(HttpServletResponse response,
MissingServletRequestParameterException e) throws IOException MissingServletRequestParameterException e) throws IOException
{ {
final String message = format("Request parameter ''{0}'' is of the wrong type", e final String message = format("Request parameter ''{0}'' is of the wrong type", e
.getParameterName()); .getParameterName());
final int statusCode = BAD_REQUEST.value(); final int statusCode = BAD_REQUEST.value();
logger.error(message, e); logger.error(message, e);
@@ -196,7 +197,7 @@ public interface TransformController
@ExceptionHandler(MissingServletRequestParameterException.class) @ExceptionHandler(MissingServletRequestParameterException.class)
default void handleMissingParams(HttpServletResponse response, default void handleMissingParams(HttpServletResponse response,
MissingServletRequestParameterException e) throws IOException MissingServletRequestParameterException e) throws IOException
{ {
final String message = format("Request parameter ''{0}'' is missing", e.getParameterName()); final String message = format("Request parameter ''{0}'' is missing", e.getParameterName());
final int statusCode = BAD_REQUEST.value(); final int statusCode = BAD_REQUEST.value();
@@ -210,7 +211,7 @@ public interface TransformController
@ExceptionHandler(TransformException.class) @ExceptionHandler(TransformException.class)
default void transformExceptionWithMessage(HttpServletResponse response, default void transformExceptionWithMessage(HttpServletResponse response,
TransformException e) throws IOException TransformException e) throws IOException
{ {
final String message = e.getMessage(); final String message = e.getMessage();
final int statusCode = e.getStatus().value(); final int statusCode = e.getStatus().value();
@@ -223,5 +224,5 @@ public interface TransformController
response.sendError(statusCode, getTransformerName() + " - " + message); response.sendError(statusCode, getTransformerName() + " - " + message);
} }
//endregion // endregion
} }

View File

@@ -33,22 +33,22 @@ import static org.alfresco.transformer.fs.FileManager.deleteFile;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.transformer.logging.LogEntry;
import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.alfresco.transformer.logging.LogEntry;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* TransformInterceptor * TransformInterceptor <br/>
* <br/> * Handles ThreadLocal Log entries for each request.
* Handles ThreadLocal Log entries for each request.
*/ */
@Deprecated @Deprecated
public class TransformInterceptor implements AsyncHandlerInterceptor public class TransformInterceptor implements AsyncHandlerInterceptor
{ {
@Override @Override
public boolean preHandle(HttpServletRequest request, public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) HttpServletResponse response, Object handler)
{ {
LogEntry.start(); LogEntry.start();
return true; return true;
@@ -56,7 +56,7 @@ public class TransformInterceptor implements AsyncHandlerInterceptor
@Override @Override
public void afterCompletion(HttpServletRequest request, public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) HttpServletResponse response, Object handler, Exception ex)
{ {
// TargetFile cannot be deleted until completion, otherwise 0 bytes are sent. // TargetFile cannot be deleted until completion, otherwise 0 bytes are sent.
deleteFile(request, SOURCE_FILE); deleteFile(request, SOURCE_FILE);

View File

@@ -26,33 +26,34 @@
package org.alfresco.transformer; package org.alfresco.transformer;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnSingleStepTransformers;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnSingleStepTransformers;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import org.alfresco.transform.config.TransformConfig; import com.fasterxml.jackson.databind.ObjectMapper;
import org.alfresco.transform.registry.AbstractTransformRegistry;
import org.alfresco.transform.registry.CombinedTransformConfig;
import org.alfresco.transform.registry.TransformCache;
import org.alfresco.transform.exceptions.TransformException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.alfresco.transform.config.TransformConfig;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transform.registry.AbstractTransformRegistry;
import org.alfresco.transform.registry.CombinedTransformConfig;
import org.alfresco.transform.registry.TransformCache;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Used by clients to work out if a transformation is supported based on the engine_config.json. * Used by clients to work out if a transformation is supported based on the engine_config.json.
*/ */
@Deprecated @Deprecated
public class TransformRegistryImpl extends AbstractTransformRegistry public class TransformRegistryImpl extends AbstractTransformRegistry

View File

@@ -31,8 +31,6 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import java.io.File; import java.io.File;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.model.FileRefResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
@@ -45,10 +43,13 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.model.FileRefResponse;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Simple Rest client that call Alfresco Shared File Store * Simple Rest client that call Alfresco Shared File Store
*/ */
@Deprecated @Deprecated
public class AlfrescoSharedFileStoreClient public class AlfrescoSharedFileStoreClient
@@ -62,7 +63,8 @@ public class AlfrescoSharedFileStoreClient
/** /**
* Retrieves a file from Shared File Store using given file reference * Retrieves a file from Shared File Store using given file reference
* *
* @param fileRef File reference * @param fileRef
* File reference
* @return ResponseEntity<Resource> * @return ResponseEntity<Resource>
*/ */
public ResponseEntity<Resource> retrieveFile(String fileRef) public ResponseEntity<Resource> retrieveFile(String fileRef)
@@ -70,7 +72,7 @@ public class AlfrescoSharedFileStoreClient
try try
{ {
return restTemplate.getForEntity(fileStoreUrl + "/" + fileRef, return restTemplate.getForEntity(fileStoreUrl + "/" + fileRef,
org.springframework.core.io.Resource.class); org.springframework.core.io.Resource.class);
} }
catch (HttpClientErrorException e) catch (HttpClientErrorException e)
{ {
@@ -81,7 +83,8 @@ public class AlfrescoSharedFileStoreClient
/** /**
* Stores given file in Shared File Store * Stores given file in Shared File Store
* *
* @param file File to be stored * @param file
* File to be stored
* @return A FileRefResponse containing detail about file's reference * @return A FileRefResponse containing detail about file's reference
*/ */
public FileRefResponse saveFile(File file) public FileRefResponse saveFile(File file)
@@ -94,9 +97,9 @@ public class AlfrescoSharedFileStoreClient
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map,
headers); headers);
ResponseEntity<FileRefResponse> responseEntity = restTemplate ResponseEntity<FileRefResponse> responseEntity = restTemplate
.exchange(fileStoreUrl, POST, requestEntity, FileRefResponse.class); .exchange(fileStoreUrl, POST, requestEntity, FileRefResponse.class);
return responseEntity.getBody(); return responseEntity.getBody();
} }
catch (HttpClientErrorException e) catch (HttpClientErrorException e)

View File

@@ -26,6 +26,15 @@
*/ */
package org.alfresco.transformer.config; package org.alfresco.transformer.config;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -46,18 +55,10 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
@Deprecated @Deprecated
@Configuration @Configuration
public class MTLSConfig { public class MTLSConfig
{
@Value("${client.ssl.key-store:#{null}}") @Value("${client.ssl.key-store:#{null}}")
private Resource keyStoreResource; private Resource keyStoreResource;
@@ -83,23 +84,26 @@ public class MTLSConfig {
@Bean @Bean
public RestTemplate restTemplate(SSLContextBuilder apacheSSLContextBuilder) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException public RestTemplate restTemplate(SSLContextBuilder apacheSSLContextBuilder) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException
{ {
if(isTlsOrMtlsConfigured()) if (isTlsOrMtlsConfigured())
{ {
return createRestTemplateWithSslContext(apacheSSLContextBuilder); return createRestTemplateWithSslContext(apacheSSLContextBuilder);
} else { }
else
{
return new RestTemplate(); return new RestTemplate();
} }
} }
@Bean @Bean
public SSLContextBuilder apacheSSLContextBuilder() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException { public SSLContextBuilder apacheSSLContextBuilder() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException
{
SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
if(isKeystoreConfigured()) if (isKeystoreConfigured())
{ {
KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword);
sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword);
} }
if(isTruststoreConfigured()) if (isTruststoreConfigured())
{ {
sslContextBuilder sslContextBuilder
.setKeyStoreType(trustStoreType) .setKeyStoreType(trustStoreType)
@@ -124,20 +128,20 @@ public class MTLSConfig {
return keyStoreResource != null; return keyStoreResource != null;
} }
private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException { private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException
final SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = {
SSLConnectionSocketFactoryBuilder.create() final SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(sslContextBuilder.build()) .setSslContext(sslContextBuilder.build())
.setTlsVersions(TLS.V_1_2, TLS.V_1_3); .setTlsVersions(TLS.V_1_2, TLS.V_1_3);
if (hostNameVerificationDisabled) { if (hostNameVerificationDisabled)
{
sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE); sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
} }
final SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryBuilder.build(); final SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryBuilder.build();
final Registry<ConnectionSocketFactory> sslSocketFactoryRegistry = final Registry<ConnectionSocketFactory> sslSocketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslConnectionSocketFactory)
.register("https", sslConnectionSocketFactory) .build();
.build();
final PoolingHttpClientConnectionManager sslConnectionManager = new PoolingHttpClientConnectionManager(sslSocketFactoryRegistry); final PoolingHttpClientConnectionManager sslConnectionManager = new PoolingHttpClientConnectionManager(sslSocketFactoryRegistry);

View File

@@ -26,18 +26,19 @@
*/ */
package org.alfresco.transformer.config; package org.alfresco.transformer.config;
import org.alfresco.transform.messages.TransformRequestValidator; import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import org.alfresco.transform.registry.TransformServiceRegistry;
import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transformer.TransformInterceptor;
import org.alfresco.transformer.TransformRegistryImpl;
import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM; import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transform.messages.TransformRequestValidator;
import org.alfresco.transform.registry.TransformServiceRegistry;
import org.alfresco.transformer.TransformInterceptor;
import org.alfresco.transformer.TransformRegistryImpl;
import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
@@ -50,8 +51,8 @@ public class WebApplicationConfig implements WebMvcConfigurer
public void addInterceptors(InterceptorRegistry registry) public void addInterceptors(InterceptorRegistry registry)
{ {
registry registry
.addInterceptor(transformInterceptor()) .addInterceptor(transformInterceptor())
.addPathPatterns(ENDPOINT_TRANSFORM, "/live", "/ready"); .addPathPatterns(ENDPOINT_TRANSFORM, "/live", "/ready");
} }
@Bean @Bean

View File

@@ -26,10 +26,11 @@
*/ */
package org.alfresco.transformer.executors; package org.alfresco.transformer.executors;
import static org.alfresco.transformer.executors.RuntimeExec.ExecutionResult;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.alfresco.transformer.executors.RuntimeExec.ExecutionResult;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;
@@ -81,7 +82,7 @@ public abstract class AbstractCommandExecutor implements CommandExecutor
if (version.isEmpty()) if (version.isEmpty())
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
"Transformer version check failed to create any output"); "Transformer version check failed to create any output");
} }
return version; return version;
} }

View File

@@ -26,16 +26,16 @@
*/ */
package org.alfresco.transformer.executors; package org.alfresco.transformer.executors;
import org.alfresco.transformer.logging.LogEntry;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.transformer.logging.LogEntry;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Basic interface for executing transformations via Shell commands * Basic interface for executing transformations via Shell commands
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -47,7 +47,7 @@ public interface CommandExecutor extends Transformer
String version(); String version();
default void run(String options, File sourceFile, File targetFile, default void run(String options, File sourceFile, File targetFile,
Long timeout) Long timeout)
{ {
LogEntry.setOptions(options); LogEntry.setOptions(options);
@@ -60,7 +60,7 @@ public interface CommandExecutor extends Transformer
} }
default void run(String options, File sourceFile, String pageRange, File targetFile, default void run(String options, File sourceFile, String pageRange, File targetFile,
Long timeout) Long timeout)
{ {
LogEntry.setOptions(pageRange + (pageRange.isEmpty() ? "" : " ") + options); LogEntry.setOptions(pageRange + (pageRange.isEmpty() ? "" : " ") + options);

View File

@@ -37,28 +37,21 @@ import java.util.StringTokenizer;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* DUPLICATED FROM *alfresco-core*. * DUPLICATED FROM *alfresco-core*.
* *
* This class is used to tokenize strings used as parameters for {@link RuntimeExec} objects. * This class is used to tokenize strings used as parameters for {@link RuntimeExec} objects. Examples of such strings are as follows (ImageMagick-like parameters):
* Examples of such strings are as follows (ImageMagick-like parameters): * <ul>
* <ul> * <li><tt>-font Helvetica -pointsize 50</tt></li>
* <li><tt>-font Helvetica -pointsize 50</tt></li> * <li><tt>-font Helvetica -pointsize 50 -draw "circle 100,100 150,150"</tt></li>
* <li><tt>-font Helvetica -pointsize 50 -draw "circle 100,100 150,150"</tt></li> * <li><tt>-font Helvetica -pointsize 50 -draw "gravity south fill black text 0,12 'CopyRight'"</tt></li>
* <li><tt>-font Helvetica -pointsize 50 -draw "gravity south fill black text 0,12 'CopyRight'"</tt></li> * </ul>
* </ul> * The first is the simple case which would be parsed into Strings as follows: <tt>"-font", "Helvetica", "-pointsize", "50"</tt>
* The first is the simple case which would be parsed into Strings as follows: * <p/>
* <tt>"-font", "Helvetica", "-pointsize", "50"</tt> * The second is more complex in that it includes a quoted parameter, which would be parsed as a single String: <tt>"-font", "Helvetica", "-pointsize", "50", "circle 100,100 150,150"</tt> Note however that the quotation characters will be stripped from the token.
* <p/> * <p/>
* The second is more complex in that it includes a quoted parameter, which would be parsed as a single String: * The third shows an example with embedded quotation marks, which would parse to: <tt>"-font", "Helvetica", "-pointsize", "50", "gravity south fill black text 0,12 'CopyRight'"</tt> In this case, the embedded quotation marks (which must be different from those surrounding the parameter) are preserved in the extracted token.
* <tt>"-font", "Helvetica", "-pointsize", "50", "circle 100,100 150,150"</tt> * <p/>
* Note however that the quotation characters will be stripped from the token. * The class does not understand escaped quotes such as <tt>p1 p2 "a b c \"hello\" d" p4</tt>
* <p/>
* The third shows an example with embedded quotation marks, which would parse to:
* <tt>"-font", "Helvetica", "-pointsize", "50", "gravity south fill black text 0,12 'CopyRight'"</tt>
* In this case, the embedded quotation marks (which must be different from those surrounding the parameter)
* are preserved in the extracted token.
* <p/>
* The class does not understand escaped quotes such as <tt>p1 p2 "a b c \"hello\" d" p4</tt>
* *
* @author Neil Mc Erlean * @author Neil Mc Erlean
* @since 3.4.2 * @since 3.4.2
@@ -82,15 +75,12 @@ public class ExecParameterTokenizer
} }
/** /**
* This method returns the tokens in a parameter string. * This method returns the tokens in a parameter string. Any tokens not contained within single or double quotes will be tokenized in the normal way i.e. by using whitespace separators and the standard StringTokenizer algorithm. Any tokens which are contained within single or double quotes will be returned as single String instances and will have their quote marks removed.
* Any tokens not contained within single or double quotes will be tokenized in the normal
* way i.e. by using whitespace separators and the standard StringTokenizer algorithm.
* Any tokens which are contained within single or double quotes will be returned as single
* String instances and will have their quote marks removed.
* <p/> * <p/>
* See above for examples. * See above for examples.
* *
* @throws NullPointerException if the string to be tokenized was null. * @throws NullPointerException
* if the string to be tokenized was null.
*/ */
public List<String> getAllTokens() public List<String> getAllTokens()
{ {
@@ -108,7 +98,7 @@ public class ExecParameterTokenizer
{ {
// Contains no quotes. // Contains no quotes.
for (StringTokenizer standardTokenizer = new StringTokenizer( for (StringTokenizer standardTokenizer = new StringTokenizer(
str); standardTokenizer.hasMoreTokens(); ) str); standardTokenizer.hasMoreTokens();)
{ {
tokens.add(standardTokenizer.nextToken()); tokens.add(standardTokenizer.nextToken());
} }
@@ -119,7 +109,7 @@ public class ExecParameterTokenizer
// So we need to identify the quoted regions within the string. // So we need to identify the quoted regions within the string.
List<Pair<Integer, Integer>> quotedRegions = new ArrayList<>(); List<Pair<Integer, Integer>> quotedRegions = new ArrayList<>();
for (Pair<Integer, Integer> next = identifyNextQuotedRegion(str, 0); next != null; ) for (Pair<Integer, Integer> next = identifyNextQuotedRegion(str, 0); next != null;)
{ {
quotedRegions.add(next); quotedRegions.add(next);
next = identifyNextQuotedRegion(str, next.getSecond() + 1); next = identifyNextQuotedRegion(str, next.getSecond() + 1);
@@ -140,12 +130,10 @@ public class ExecParameterTokenizer
} }
/** /**
* The substrings will be a list of quoted and unquoted substrings. * The substrings will be a list of quoted and unquoted substrings. The unquoted ones need to be further tokenized in the normal way. The quoted ones must not be tokenized, but need their quotes stripped off.
* The unquoted ones need to be further tokenized in the normal way.
* The quoted ones must not be tokenized, but need their quotes stripped off.
*/ */
private List<Substring> getSubstrings(String str, private List<Substring> getSubstrings(String str,
List<Pair<Integer, Integer>> quotedRegionIndices) List<Pair<Integer, Integer>> quotedRegionIndices)
{ {
List<Substring> result = new ArrayList<>(); List<Substring> result = new ArrayList<>();
@@ -156,10 +144,10 @@ public class ExecParameterTokenizer
{ {
int startIndexOfNextQuotedRegion = nextQuotedRegionIndices.getFirst() - 1; int startIndexOfNextQuotedRegion = nextQuotedRegionIndices.getFirst() - 1;
result.add(new UnquotedSubstring( result.add(new UnquotedSubstring(
str.substring(cursorPosition, startIndexOfNextQuotedRegion))); str.substring(cursorPosition, startIndexOfNextQuotedRegion)));
} }
result.add(new QuotedSubstring(str.substring(nextQuotedRegionIndices.getFirst(), result.add(new QuotedSubstring(str.substring(nextQuotedRegionIndices.getFirst(),
nextQuotedRegionIndices.getSecond()))); nextQuotedRegionIndices.getSecond())));
cursorPosition = nextQuotedRegionIndices.getSecond(); cursorPosition = nextQuotedRegionIndices.getSecond();
} }
@@ -203,12 +191,12 @@ public class ExecParameterTokenizer
} }
private Pair<Integer, Integer> findIndexOfClosingQuote(String str, int indexOfStartingQuote, private Pair<Integer, Integer> findIndexOfClosingQuote(String str, int indexOfStartingQuote,
char quoteChar) char quoteChar)
{ {
// So we know which type of quote char we're dealing with. Either ' or ". // So we know which type of quote char we're dealing with. Either ' or ".
// Now we need to find the closing quote. // Now we need to find the closing quote.
int indexAfterClosingQuote = str.indexOf(quoteChar, int indexAfterClosingQuote = str.indexOf(quoteChar,
indexOfStartingQuote + 1) + 1; // + 1 to search after opening quote. + 1 to give result including closing quote. indexOfStartingQuote + 1) + 1; // + 1 to search after opening quote. + 1 to give result including closing quote.
if (indexAfterClosingQuote == 0) // -1 + 1 if (indexAfterClosingQuote == 0) // -1 + 1
{ {
@@ -300,8 +288,10 @@ public class ExecParameterTokenizer
/** /**
* Make a new one. * Make a new one.
* *
* @param first The first member. * @param first
* @param second The second member. * The first member.
* @param second
* The second member.
*/ */
public Pair(F first, S second) public Pair(F first, S second)
{ {
@@ -339,16 +329,20 @@ public class ExecParameterTokenizer
this.second = second; this.second = second;
} }
@Override public boolean equals(Object o) @Override
public boolean equals(Object o)
{ {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (o == null || getClass() != o.getClass())
return false;
Pair<?, ?> pair = (Pair<?, ?>) o; Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) && return Objects.equals(first, pair.first) &&
Objects.equals(second, pair.second); Objects.equals(second, pair.second);
} }
@Override public int hashCode() @Override
public int hashCode()
{ {
return Objects.hash(first, second); return Objects.hash(first, second);
} }
@@ -360,4 +354,3 @@ public class ExecParameterTokenizer
} }
} }
} }

View File

@@ -31,7 +31,7 @@ import java.io.File;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Basic interface for executing transformations inside Java/JVM. * Basic interface for executing transformations inside Java/JVM.
* *
* @author Cezar Leahu * @author Cezar Leahu
* @author adavis * @author adavis

View File

@@ -52,53 +52,52 @@ import org.slf4j.LoggerFactory;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* DUPLICATED FROM *alfresco-core*. * DUPLICATED FROM *alfresco-core*.
* *
* This acts as a session similar to the <code>java.lang.Process</code>, but * This acts as a session similar to the <code>java.lang.Process</code>, but logs the system standard and error streams.
* logs the system standard and error streams. * <p>
* <p> * The bean can be configured to execute a command directly, or be given a map of commands keyed by the <i>os.name</i> Java system property. In this map, the default key that is used when no match is found is the <b>{@link #KEY_OS_DEFAULT *}</b> key.
* The bean can be configured to execute a command directly, or be given a map * <p>
* of commands keyed by the <i>os.name</i> Java system property. In this map, * Use the {@link #setProcessDirectory(String) processDirectory} property to change the default location from which the command executes. The process's environment can be configured using the {@link #setProcessProperties(Map) processProperties} property.
* the default key that is used when no match is found is the * <p>
* <b>{@link #KEY_OS_DEFAULT *}</b> key. * Commands may use placeholders, e.g.
* <p> *
* Use the {@link #setProcessDirectory(String) processDirectory} property to change the default location * <pre>
* from which the command executes. The process's environment can be configured using the * <code>
* {@link #setProcessProperties(Map) processProperties} property.
* <p>
* Commands may use placeholders, e.g.
* <pre><code>
* find * find
* -name * -name
* ${filename} * ${filename}
* </code></pre> * </code>
* The <b>filename</b> property will be substituted for any supplied value prior to * </pre>
* each execution of the command. Currently, no checks are made to get or check the *
* properties contained within the command string. It is up to the client code to * The <b>filename</b> property will be substituted for any supplied value prior to each execution of the command. Currently, no checks are made to get or check the properties contained within the command string. It is up to the client code to dynamically extract the properties required if the required properties are not known up front.
* dynamically extract the properties required if the required properties are not * <p>
* known up front. * Sometimes, a variable may contain several arguments. . In this case, the arguments need to be tokenized using a standard <tt>StringTokenizer</tt>. To force tokenization of a value, use:
* <p> *
* Sometimes, a variable may contain several arguments. . In this case, the arguments * <pre>
* need to be tokenized using a standard <tt>StringTokenizer</tt>. To force tokenization * <code>
* of a value, use:
* <pre><code>
* SPLIT:${userArgs} * SPLIT:${userArgs}
* </code></pre> * </code>
* You should not use this just to split up arguments that are known to require tokenization * </pre>
* up front. The <b>SPLIT:</b> directive works for the entire argument and will not do anything *
* if it is not at the beginning of the argument. Do not use <b>SPLIT:</b> to break up arguments * You should not use this just to split up arguments that are known to require tokenization up front. The <b>SPLIT:</b> directive works for the entire argument and will not do anything if it is not at the beginning of the argument. Do not use <b>SPLIT:</b> to break up arguments that are fixed, so avoid doing this:
* that are fixed, so avoid doing this: *
* <pre><code> * <pre>
* <code>
* SPLIT:ls -lih * SPLIT:ls -lih
* </code></pre> * </code>
* Instead, break the command up explicitly: * </pre>
* <pre><code> *
* Instead, break the command up explicitly:
*
* <pre>
* <code>
* ls * ls
* -lih * -lih
* </code></pre> * </code>
* </pre>
* *
* Tokenization of quoted parameter values is handled by ExecParameterTokenizer, which * Tokenization of quoted parameter values is handled by ExecParameterTokenizer, which describes the support in more detail.
* describes the support in more detail.
* *
* @author Derek Hulley * @author Derek Hulley
*/ */
@@ -128,7 +127,7 @@ public class RuntimeExec
private final Timer timer = new Timer(true); private final Timer timer = new Timer(true);
/** /**
* Default constructor. Initialize this instance by setting individual properties. * Default constructor. Initialize this instance by setting individual properties.
*/ */
public RuntimeExec() public RuntimeExec()
{ {
@@ -156,15 +155,16 @@ public class RuntimeExec
sb.append("\n"); sb.append("\n");
} }
sb.append(" env props: ").append(Arrays.toString(processProperties)).append("\n") sb.append(" env props: ").append(Arrays.toString(processProperties)).append("\n")
.append(" dir: ").append(processDirectory).append("\n") .append(" dir: ").append(processDirectory).append("\n")
.append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n"); .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n");
return sb.toString(); return sb.toString();
} }
/** /**
* Set the command to execute regardless of operating system * Set the command to execute regardless of operating system
* *
* @param command an array of strings representing the command (first entry) and arguments * @param command
* an array of strings representing the command (first entry) and arguments
* @since 3.0 * @since 3.0
*/ */
public void setCommand(String[] command) public void setCommand(String[] command)
@@ -173,11 +173,12 @@ public class RuntimeExec
} }
/** /**
* Sets the assumed charset of OUT and ERR streams generated by the executed command. * Sets the assumed charset of OUT and ERR streams generated by the executed command. This defaults to the system default charset: {@link Charset#defaultCharset()}.
* This defaults to the system default charset: {@link Charset#defaultCharset()}.
* *
* @param charsetCode a supported character set code * @param charsetCode
* @throws UnsupportedCharsetException if the characterset code is not recognised by Java * a supported character set code
* @throws UnsupportedCharsetException
* if the characterset code is not recognised by Java
*/ */
public void setCharset(String charsetCode) public void setCharset(String charsetCode)
{ {
@@ -185,14 +186,10 @@ public class RuntimeExec
} }
/** /**
* Set whether to wait for completion of the command or not. If there is no wait for completion, * Set whether to wait for completion of the command or not. If there is no wait for completion, then the return value of <i>out</i> and <i>err</i> buffers cannot be relied upon as the command may still be in progress. Failure is therefore not possible unless the calling thread waits for execution.
* then the return value of <i>out</i> and <i>err</i> buffers cannot be relied upon as the
* command may still be in progress. Failure is therefore not possible unless the calling thread
* waits for execution.
* *
* @param waitForCompletion <tt>true</tt> (default) is to wait for the command to exit, * @param waitForCompletion
* or <tt>false</tt> to just return an exit code of 0 and whatever * <tt>true</tt> (default) is to wait for the command to exit, or <tt>false</tt> to just return an exit code of 0 and whatever output is available at that point.
* output is available at that point.
* @since 2.1 * @since 2.1
*/ */
public void setWaitForCompletion(boolean waitForCompletion) public void setWaitForCompletion(boolean waitForCompletion)
@@ -201,26 +198,17 @@ public class RuntimeExec
} }
/** /**
* Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system * Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used to get a command where there is not direct match to the operating system key.
* property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used
* to get a command where there is not direct match to the operating system key.
* <p> * <p>
* Each command is an array of strings, the first of which represents the command and all subsequent * Each command is an array of strings, the first of which represents the command and all subsequent entries in the array represent the arguments. All elements of the array will be checked for the presence of any substitution parameters (e.g. '{dir}'). The parameters can be set using the {@link #setDefaultProperties(Map) defaults} or by passing the substitution values into the {@link #execute(Map)} command.
* entries in the array represent the arguments. All elements of the array will be checked for
* the presence of any substitution parameters (e.g. '{dir}'). The parameters can be set using the
* {@link #setDefaultProperties(Map) defaults} or by passing the substitution values into the
* {@link #execute(Map)} command.
* <p> * <p>
* If parameters passed may be multiple arguments, or if the values provided in the map are themselves * If parameters passed may be multiple arguments, or if the values provided in the map are themselves collections of arguments (not recommended), then prefix the value with <b>SPLIT:</b> to ensure that the value is tokenized before being passed to the command. Any values that are not split, will be passed to the command as single arguments. For example:<br>
* collections of arguments (not recommended), then prefix the value with <b>SPLIT:</b> to ensure that
* the value is tokenized before being passed to the command. Any values that are not split, will be
* passed to the command as single arguments. For example:<br>
* '<b>SPLIT: dir . ..</b>' becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br> * '<b>SPLIT: dir . ..</b>' becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br>
* '<b>SPLIT: dir ${path}</b>' (if path is '<b>. ..</b>') becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br> * '<b>SPLIT: dir ${path}</b>' (if path is '<b>. ..</b>') becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br>
* The splitting occurs post-subtitution. Where the arguments are known, it is advisable to avoid * The splitting occurs post-subtitution. Where the arguments are known, it is advisable to avoid <b>SPLIT:</b>.
* <b>SPLIT:</b>.
* *
* @param commandsByOS a map of command string arrays, keyed by operating system names * @param commandsByOS
* a map of command string arrays, keyed by operating system names
* @see #setDefaultProperties(Map) * @see #setDefaultProperties(Map)
* @since 3.0 * @since 3.0
*/ */
@@ -235,7 +223,7 @@ public class RuntimeExec
// go through the commands keys, looking for one that matches by regular expression matching // go through the commands keys, looking for one that matches by regular expression matching
for (String osName : commandsByOS.keySet()) for (String osName : commandsByOS.keySet())
{ {
// Ignore * options. It is dealt with later. // Ignore * options. It is dealt with later.
if (osName.equals(KEY_OS_DEFAULT)) if (osName.equals(KEY_OS_DEFAULT))
{ {
continue; continue;
@@ -257,26 +245,25 @@ public class RuntimeExec
if (command == null) if (command == null)
{ {
throw new RuntimeException( throw new RuntimeException(
"No command found for OS " + serverOs + " or '" + KEY_OS_DEFAULT + "': \n" + "No command found for OS " + serverOs + " or '" + KEY_OS_DEFAULT + "': \n" +
" commands: " + commandsByOS); " commands: " + commandsByOS);
} }
this.command = command; this.command = command;
} }
/** /**
* Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system * Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used to get a command where there is not direct match to the operating system key.
* property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used
* to get a command where there is not direct match to the operating system key.
* *
* @param commandsByOS a map of command string keyed by operating system names * @param commandsByOS
* a map of command string keyed by operating system names
* @deprecated Use {@link #setCommandsAndArguments(Map)} * @deprecated Use {@link #setCommandsAndArguments(Map)}
*/ */
public void setCommandMap(Map<String, String> commandsByOS) public void setCommandMap(Map<String, String> commandsByOS)
{ {
// This is deprecated, so issue a warning // This is deprecated, so issue a warning
logger.warn( logger.warn(
"The bean RuntimeExec property 'commandMap' has been deprecated;" + "The bean RuntimeExec property 'commandMap' has been deprecated;" +
" use 'commandsAndArguments' instead. See https://issues.alfresco.com/jira/browse/ETHREEOH-579."); " use 'commandsAndArguments' instead. See https://issues.alfresco.com/jira/browse/ETHREEOH-579.");
Map<String, String[]> fixed = new LinkedHashMap<>(); Map<String, String[]> fixed = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : commandsByOS.entrySet()) for (Map.Entry<String, String> entry : commandsByOS.entrySet())
{ {
@@ -294,14 +281,12 @@ public class RuntimeExec
} }
/** /**
* Set the default command-line properties to use when executing the command. * Set the default command-line properties to use when executing the command. These are properties that substitute variables defined in the command string itself. Properties supplied during execution will overwrite the default properties.
* These are properties that substitute variables defined in the command string itself.
* Properties supplied during execution will overwrite the default properties.
* <p> * <p>
* <code>null</code> properties will be treated as an empty string for substitution * <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes.
* *
* @param defaultProperties property values * @param defaultProperties
* property values
*/ */
public void setDefaultProperties(Map<String, String> defaultProperties) public void setDefaultProperties(Map<String, String> defaultProperties)
{ {
@@ -309,14 +294,12 @@ public class RuntimeExec
} }
/** /**
* Set additional runtime properties (environment properties) that will used * Set additional runtime properties (environment properties) that will used by the executing process.
* by the executing process.
* <p> * <p>
* Any keys or properties that start and end with <b>${...}</b> will be removed on the assumption * Any keys or properties that start and end with <b>${...}</b> will be removed on the assumption that these are unset properties. <tt>null</tt> values are translated to empty strings. All keys and values are trimmed of leading and trailing whitespace.
* that these are unset properties. <tt>null</tt> values are translated to empty strings.
* All keys and values are trimmed of leading and trailing whitespace.
* *
* @param processProperties Runtime process properties * @param processProperties
* Runtime process properties
* @see Runtime#exec(String, String[], java.io.File) * @see Runtime#exec(String, String[], java.io.File)
*/ */
public void setProcessProperties(Map<String, String> processProperties) public void setProcessProperties(Map<String, String> processProperties)
@@ -373,13 +356,12 @@ public class RuntimeExec
} }
/** /**
* Adds a property to existed processProperties. * Adds a property to existed processProperties. Property should not be null or empty. If property with the same value already exists then no change is made. If property exists with a different value then old value is replaced with the new one.
* Property should not be null or empty.
* If property with the same value already exists then no change is made.
* If property exists with a different value then old value is replaced with the new one.
* *
* @param name - property name * @param name
* @param value - property value * - property name
* @param value
* - property value
*/ */
public void setProcessProperty(String name, String value) public void setProcessProperty(String name, String value)
{ {
@@ -429,10 +411,10 @@ public class RuntimeExec
/** /**
* Set the runtime location from which the command is executed. * Set the runtime location from which the command is executed.
* <p> * <p>
* If the value is an unsubsititued variable (<b>${...}</b>) then it is ignored. * If the value is an unsubsititued variable (<b>${...}</b>) then it is ignored. If the location is not visible at the time of setting, a warning is issued only.
* If the location is not visible at the time of setting, a warning is issued only.
* *
* @param processDirectory the runtime location from which to execute the command * @param processDirectory
* the runtime location from which to execute the command
*/ */
public void setProcessDirectory(String processDirectory) public void setProcessDirectory(String processDirectory)
{ {
@@ -446,17 +428,18 @@ public class RuntimeExec
if (!this.processDirectory.exists()) if (!this.processDirectory.exists())
{ {
logger.warn( logger.warn(
"The runtime process directory is not visible when setting property " + "The runtime process directory is not visible when setting property " +
"'processDirectory': \n{}", this); "'processDirectory': \n{}",
this);
} }
} }
} }
/** /**
* A comma or space separated list of values that, if returned by the executed command, * A comma or space separated list of values that, if returned by the executed command, indicate an error value. This defaults to <b>"1, 2"</b>.
* indicate an error value. This defaults to <b>"1, 2"</b>.
* *
* @param errCodesStr the error codes for the execution * @param errCodesStr
* the error codes for the execution
*/ */
public void setErrorCodes(String errCodesStr) public void setErrorCodes(String errCodesStr)
{ {
@@ -474,7 +457,7 @@ public class RuntimeExec
catch (NumberFormatException e) catch (NumberFormatException e)
{ {
throw new RuntimeException( throw new RuntimeException(
"Property 'errorCodes' must be comma-separated list of integers: " + errCodesStr); "Property 'errorCodes' must be comma-separated list of integers: " + errCodesStr);
} }
} }
} }
@@ -492,9 +475,8 @@ public class RuntimeExec
/** /**
* Executes the statement that this instance was constructed with. * Executes the statement that this instance was constructed with.
* *
* @param properties the properties that the command might be executed with. * @param properties
* <code>null</code> properties will be treated as an empty string for substitution * the properties that the command might be executed with. <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes.
* @return Returns the full execution results * @return Returns the full execution results
*/ */
public ExecutionResult execute(Map<String, String> properties) public ExecutionResult execute(Map<String, String> properties)
@@ -503,15 +485,12 @@ public class RuntimeExec
} }
/** /**
* Executes the statement that this instance was constructed with an optional * Executes the statement that this instance was constructed with an optional timeout after which the command is asked to
* timeout after which the command is asked to
* *
* @param properties the properties that the command might be executed with. * @param properties
* <code>null</code> properties will be treated as an empty string for substitution * the properties that the command might be executed with. <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes. * @param timeoutMs
* @param timeoutMs a timeout after which {@link Process#destroy()} is called. * a timeout after which {@link Process#destroy()} is called. ignored if less than or equal to zero. Note this method does not guarantee to terminate the process (it is not a kill -9).
* ignored if less than or equal to zero. Note this method does not guarantee
* to terminate the process (it is not a kill -9).
* @return Returns the full execution results * @return Returns the full execution results
*/ */
public ExecutionResult execute(Map<String, String> properties, final long timeoutMs) public ExecutionResult execute(Map<String, String> properties, final long timeoutMs)
@@ -533,13 +512,12 @@ public class RuntimeExec
// execute the command with full property replacement // execute the command with full property replacement
commandToExecute = getCommand(properties); commandToExecute = getCommand(properties);
final Process thisProcess = runtime.exec(commandToExecute, processProperties, final Process thisProcess = runtime.exec(commandToExecute, processProperties,
processDirectory); processDirectory);
process = thisProcess; process = thisProcess;
if (timeoutMs > 0) if (timeoutMs > 0)
{ {
final String[] command = commandToExecute; final String[] command = commandToExecute;
timer.schedule(new TimerTask() timer.schedule(new TimerTask() {
{
@Override @Override
public void run() public void run()
{ {
@@ -551,8 +529,8 @@ public class RuntimeExec
catch (IllegalThreadStateException stillRunning) catch (IllegalThreadStateException stillRunning)
{ {
logger.debug( logger.debug(
"Process has taken too long ({} seconds). Killing process {}", "Process has taken too long ({} seconds). Killing process {}",
timeoutMs / 1000, Arrays.deepToString(command)); timeoutMs / 1000, Arrays.deepToString(command));
} }
} }
}, timeoutMs); }, timeoutMs);
@@ -564,16 +542,16 @@ public class RuntimeExec
String execOut = ""; String execOut = "";
String execErr = e.getMessage(); String execErr = e.getMessage();
ExecutionResult result = new ExecutionResult(null, commandToExecute, errCodes, ExecutionResult result = new ExecutionResult(null, commandToExecute, errCodes,
defaultFailureExitValue, execOut, execErr); defaultFailureExitValue, execOut, execErr);
logFullEnvironmentDump(result); logFullEnvironmentDump(result);
return result; return result;
} }
// create the stream gobblers // create the stream gobblers
InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread( InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread(
process.getInputStream(), charset); process.getInputStream(), charset);
InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread( InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread(
process.getErrorStream(), charset); process.getErrorStream(), charset);
// start gobbling // start gobbling
stdOutGobbler.start(); stdOutGobbler.start();
@@ -608,7 +586,7 @@ public class RuntimeExec
// construct the return value // construct the return value
ExecutionResult result = new ExecutionResult(process, commandToExecute, errCodes, exitValue, ExecutionResult result = new ExecutionResult(process, commandToExecute, errCodes, exitValue,
execOut, execErr); execOut, execErr);
// done // done
logFullEnvironmentDump(result); logFullEnvironmentDump(result);
@@ -651,12 +629,11 @@ public class RuntimeExec
} }
logger.debug("Result: " + result.toString()); logger.debug("Result: " + result.toString());
// close output stream (connected to input stream of native subprocess) // close output stream (connected to input stream of native subprocess)
} }
/** /**
* @return Returns the command that will be executed if no additional properties * @return Returns the command that will be executed if no additional properties were to be supplied
* were to be supplied
*/ */
public String[] getCommand() public String[] getCommand()
{ {
@@ -666,12 +643,11 @@ public class RuntimeExec
/** /**
* Get the command that will be executed post substitution. * Get the command that will be executed post substitution.
* <p> * <p>
* <code>null</code> properties will be treated as an empty string for substitution * <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes.
* *
* @param properties the properties that the command might be executed with * @param properties
* @return Returns the command that will be executed should the additional properties * the properties that the command might be executed with
* be supplied * @return Returns the command that will be executed should the additional properties be supplied
*/ */
public String[] getCommand(Map<String, String> properties) public String[] getCommand(Map<String, String> properties)
{ {
@@ -720,7 +696,7 @@ public class RuntimeExec
// There may be quoted arguments here (see ALF-7482) // There may be quoted arguments here (see ALF-7482)
ExecParameterTokenizer quoteAwareTokenizer = new ExecParameterTokenizer( ExecParameterTokenizer quoteAwareTokenizer = new ExecParameterTokenizer(
unsplitAdjustedValue); unsplitAdjustedValue);
List<String> tokens = quoteAwareTokenizer.getAllTokens(); List<String> tokens = quoteAwareTokenizer.getAllTokens();
adjustedCommandElements.addAll(tokens); adjustedCommandElements.addAll(tokens);
} }
@@ -748,15 +724,16 @@ public class RuntimeExec
private final String stdErr; private final String stdErr;
/** /**
* @param process the process attached to Java - <tt>null</tt> is allowed * @param process
* the process attached to Java - <tt>null</tt> is allowed
*/ */
private ExecutionResult( private ExecutionResult(
final Process process, final Process process,
final String[] command, final String[] command,
final Set<Integer> errCodes, final Set<Integer> errCodes,
final int exitValue, final int exitValue,
final String stdOut, final String stdOut,
final String stdErr) final String stdErr)
{ {
this.process = process; this.process = process;
this.command = command; this.command = command;
@@ -774,20 +751,18 @@ public class RuntimeExec
StringBuilder sb = new StringBuilder(128); StringBuilder sb = new StringBuilder(128);
sb.append("Execution result: \n") sb.append("Execution result: \n")
.append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n") .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n")
.append(" command: "); .append(" command: ");
appendCommand(sb, command).append("\n") appendCommand(sb, command).append("\n")
.append(" succeeded: ").append(getSuccess()).append("\n") .append(" succeeded: ").append(getSuccess()).append("\n")
.append(" exit code: ").append(exitValue).append("\n") .append(" exit code: ").append(exitValue).append("\n")
.append(" out: ").append(out).append("\n") .append(" out: ").append(out).append("\n")
.append(" err: ").append(err); .append(" err: ").append(err);
return sb.toString(); return sb.toString();
} }
/** /**
* Appends the command in a form that make running from the command line simpler. * Appends the command in a form that make running from the command line simpler. It is not a real attempt at making a command given all the operating system and shell options, but makes copy, paste and edit a bit simpler.
* It is not a real attempt at making a command given all the operating system
* and shell options, but makes copy, paste and edit a bit simpler.
*/ */
private StringBuilder appendCommand(StringBuilder sb, String[] command) private StringBuilder appendCommand(StringBuilder sb, String[] command)
{ {
@@ -823,10 +798,7 @@ public class RuntimeExec
} }
/** /**
* A helper method to force a kill of the process that generated this result. This is * A helper method to force a kill of the process that generated this result. This is useful in cases where the process started is not expected to exit, or doesn't exit quickly. If the {@linkplain RuntimeExec#setWaitForCompletion(boolean) "wait for completion"} flag is <tt>false</tt> then the process may still be running when this result is returned.
* useful in cases where the process started is not expected to exit, or doesn't exit
* quickly. If the {@linkplain RuntimeExec#setWaitForCompletion(boolean) "wait for completion"}
* flag is <tt>false</tt> then the process may still be running when this result is returned.
* *
* @return <tt>true</tt> if the process was killed, otherwise <tt>false</tt> * @return <tt>true</tt> if the process was killed, otherwise <tt>false</tt>
*/ */
@@ -849,7 +821,8 @@ public class RuntimeExec
} }
/** /**
* @param exitValue the command exit value * @param exitValue
* the command exit value
* @return Returns true if the code is a listed failure code * @return Returns true if the code is a listed failure code
* @see #setErrorCodes(String) * @see #setErrorCodes(String)
*/ */
@@ -859,8 +832,7 @@ public class RuntimeExec
} }
/** /**
* @return Returns true if the command was deemed to be successful according to the * @return Returns true if the command was deemed to be successful according to the failure codes returned by the execution.
* failure codes returned by the execution.
*/ */
public boolean getSuccess() public boolean getSuccess()
{ {
@@ -884,8 +856,7 @@ public class RuntimeExec
} }
/** /**
* Gobbles an <code>InputStream</code> and writes it into a * Gobbles an <code>InputStream</code> and writes it into a <code>StringBuffer</code>
* <code>StringBuffer</code>
* <p> * <p>
* The reading of the input stream is buffered. * The reading of the input stream is buffered.
*/ */
@@ -893,12 +864,12 @@ public class RuntimeExec
{ {
private final InputStream is; private final InputStream is;
private final Charset charset; private final Charset charset;
private final StringBuffer buffer; // we require the synchronization private final StringBuffer buffer; // we require the synchronization
private boolean completed; private boolean completed;
/** /**
* @param is an input stream to read - it will be wrapped in a buffer * @param is
* for reading * an input stream to read - it will be wrapped in a buffer for reading
*/ */
public InputStreamReaderThread(InputStream is, Charset charset) public InputStreamReaderThread(InputStream is, Charset charset)
{ {
@@ -940,7 +911,7 @@ public class RuntimeExec
// The thread has finished consuming the stream // The thread has finished consuming the stream
completed = true; completed = true;
// Notify waiters // Notify waiters
this.notifyAll(); // Note: Method is synchronized this.notifyAll(); // Note: Method is synchronized
} }
} }
@@ -959,13 +930,13 @@ public class RuntimeExec
this.wait(1000L); // 200 ms this.wait(1000L); // 200 ms
} }
catch (InterruptedException ignore) catch (InterruptedException ignore)
{ {}
}
} }
} }
/** /**
* @param msg the message to add to the buffer * @param msg
* the message to add to the buffer
*/ */
public void addToBuffer(String msg) public void addToBuffer(String msg)
{ {
@@ -986,4 +957,3 @@ public class RuntimeExec
} }
} }
} }

View File

@@ -27,21 +27,22 @@ package org.alfresco.transformer.executors;
* #L% * #L%
*/ */
import org.alfresco.transform.exceptions.TransformException; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import java.io.File;
import java.util.Map;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_METADATA_EMBED; import static org.alfresco.transform.common.Mimetype.MIMETYPE_METADATA_EMBED;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_METADATA_EXTRACT; import static org.alfresco.transform.common.Mimetype.MIMETYPE_METADATA_EXTRACT;
import static org.alfresco.transformer.util.RequestParamMap.TRANSFORM_NAME_PARAMETER; import static org.alfresco.transformer.util.RequestParamMap.TRANSFORM_NAME_PARAMETER;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import java.io.File;
import java.util.Map;
import org.alfresco.transform.exceptions.TransformException;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Basic interface for executing transforms and metadata extract or embed actions. * Basic interface for executing transforms and metadata extract or embed actions.
* *
* @author adavis * @author adavis
*/ */
@@ -55,14 +56,15 @@ public interface Transformer
String getTransformerId(); String getTransformerId();
default void transform(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions, default void transform(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
File sourceFile, File targetFile) throws TransformException { File sourceFile, File targetFile) throws TransformException
{
final String transformName = transformOptions.remove(TRANSFORM_NAME_PARAMETER); final String transformName = transformOptions.remove(TRANSFORM_NAME_PARAMETER);
transformExtractOrEmbed(transformName, sourceMimetype, targetMimetype, transformOptions, sourceFile, targetFile); transformExtractOrEmbed(transformName, sourceMimetype, targetMimetype, transformOptions, sourceFile, targetFile);
} }
default void transformExtractOrEmbed(String transformName, String sourceMimetype, String targetMimetype, default void transformExtractOrEmbed(String transformName, String sourceMimetype, String targetMimetype,
Map<String, String> transformOptions, Map<String, String> transformOptions,
File sourceFile, File targetFile) throws TransformException File sourceFile, File targetFile) throws TransformException
{ {
try try
{ {
@@ -109,25 +111,20 @@ public interface Transformer
} }
default void transform(String transformName, String sourceMimetype, String targetMimetype, default void transform(String transformName, String sourceMimetype, String targetMimetype,
Map<String, String> transformOptions, Map<String, String> transformOptions,
File sourceFile, File targetFile) throws Exception File sourceFile, File targetFile) throws Exception
{ {}
}
default void extractMetadata(String transformName, String sourceMimetype, String targetMimetype, default void extractMetadata(String transformName, String sourceMimetype, String targetMimetype,
Map<String, String> transformOptions, Map<String, String> transformOptions,
File sourceFile, File targetFile) throws Exception File sourceFile, File targetFile) throws Exception
{ {}
}
/** /**
* @deprecated The content repository has no non test embed metadata implementations. * @deprecated The content repository has no non test embed metadata implementations. This code exists in case there are custom implementations, that need to be converted to T-Engines. It is simply a copy and paste from the content repository and has received limited testing.
* This code exists in case there are custom implementations, that need to be converted to T-Engines.
* It is simply a copy and paste from the content repository and has received limited testing.
*/ */
default void embedMetadata(String transformName, String sourceMimetype, String targetMimetype, default void embedMetadata(String transformName, String sourceMimetype, String targetMimetype,
Map<String, String> transformOptions, Map<String, String> transformOptions,
File sourceFile, File targetFile) throws Exception File sourceFile, File targetFile) throws Exception
{ {}
}
} }

View File

@@ -39,11 +39,8 @@ import java.net.MalformedURLException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.Arrays; import java.util.Arrays;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.logging.LogEntry;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@@ -52,6 +49,9 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils; import org.springframework.web.util.UriUtils;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.logging.LogEntry;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
*/ */
@@ -66,10 +66,11 @@ public class FileManager
* Returns a File to be used to store the result of a transformation. * Returns a File to be used to store the result of a transformation.
* *
* @param request * @param request
* @param filename The targetFilename supplied in the request. Only the filename if a path is used as part of the * @param filename
* temporary filename. * The targetFilename supplied in the request. Only the filename if a path is used as part of the temporary filename.
* @return a temporary File. * @return a temporary File.
* @throws TransformException if there was no target filename. * @throws TransformException
* if there was no target filename.
*/ */
public static File createTargetFile(HttpServletRequest request, String filename) public static File createTargetFile(HttpServletRequest request, String filename)
{ {
@@ -96,9 +97,11 @@ public class FileManager
/** /**
* Checks the filename is okay to uses in a temporary file name. * Checks the filename is okay to uses in a temporary file name.
* *
* @param filename or path to be checked. * @param filename
* or path to be checked.
* @return the filename part of the supplied filename if it was a path. * @return the filename part of the supplied filename if it was a path.
* @throws TransformException if there was no target filename. * @throws TransformException
* if there was no target filename.
*/ */
private static String checkFilename(boolean source, String filename) private static String checkFilename(boolean source, String filename)
{ {
@@ -117,7 +120,7 @@ public class FileManager
try try
{ {
Files.copy(multipartFile.getInputStream(), file.toPath(), Files.copy(multipartFile.getInputStream(), file.toPath(),
StandardCopyOption.REPLACE_EXISTING); StandardCopyOption.REPLACE_EXISTING);
} }
catch (IOException e) catch (IOException e)
{ {
@@ -149,13 +152,13 @@ public class FileManager
else else
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
"Could not read the target file: " + file.getPath()); "Could not read the target file: " + file.getPath());
} }
} }
catch (MalformedURLException e) catch (MalformedURLException e)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
"The target filename was malformed: " + file.getPath(), e); "The target filename was malformed: " + file.getPath(), e);
} }
} }
@@ -167,10 +170,10 @@ public class FileManager
{ {
String[] strings = contentDisposition.split("; *"); String[] strings = contentDisposition.split("; *");
filename = Arrays.stream(strings) filename = Arrays.stream(strings)
.filter(s -> s.startsWith(FILENAME)) .filter(s -> s.startsWith(FILENAME))
.findFirst() .findFirst()
.map(s -> s.substring(FILENAME.length())) .map(s -> s.substring(FILENAME.length()))
.orElse(""); .orElse("");
} }
return filename; return filename;
} }
@@ -178,8 +181,10 @@ public class FileManager
/** /**
* Returns the file name for the target file * Returns the file name for the target file
* *
* @param fileName Desired file name * @param fileName
* @param targetExtension File extension * Desired file name
* @param targetExtension
* File extension
* @return Target file name * @return Target file name
*/ */
public static String createTargetFileName(final String fileName, final String targetExtension) public static String createTargetFileName(final String fileName, final String targetExtension)
@@ -199,16 +204,18 @@ public class FileManager
} }
return sourceFilename.substring(0, sourceFilename.length() - ext.length() - 1) + return sourceFilename.substring(0, sourceFilename.length() - ext.length() - 1) +
'.' + targetExtension; '.' + targetExtension;
} }
/** /**
* Returns a File that holds the source content for a transformation. * Returns a File that holds the source content for a transformation.
* *
* @param request * @param request
* @param multipartFile from the request * @param multipartFile
* from the request
* @return a temporary File. * @return a temporary File.
* @throws TransformException if there was no source filename. * @throws TransformException
* if there was no source filename.
*/ */
public static File createSourceFile(HttpServletRequest request, MultipartFile multipartFile) public static File createSourceFile(HttpServletRequest request, MultipartFile multipartFile)
{ {
@@ -231,13 +238,12 @@ public class FileManager
} }
} }
public static ResponseEntity<Resource> createAttachment(String targetFilename, File public static ResponseEntity<Resource> createAttachment(String targetFilename, File targetFile)
targetFile)
{ {
Resource targetResource = load(targetFile); Resource targetResource = load(targetFile);
targetFilename = UriUtils.encodePath(getFilename(targetFilename), "UTF-8"); targetFilename = UriUtils.encodePath(getFilename(targetFilename), "UTF-8");
return ResponseEntity.ok().header(CONTENT_DISPOSITION, return ResponseEntity.ok().header(CONTENT_DISPOSITION,
"attachment; filename*=UTF-8''" + targetFilename).body(targetResource); "attachment; filename*=UTF-8''" + targetFilename).body(targetResource);
} }
/** /**
@@ -255,8 +261,9 @@ public class FileManager
catch (IOException e) catch (IOException e)
{ {
throw new RuntimeException( throw new RuntimeException(
"Failed to created temp file: \n prefix: " + prefix + "Failed to created temp file: \n prefix: " + prefix +
"\n suffix: " + suffix + "\n directory: " + directory, e); "\n suffix: " + suffix + "\n directory: " + directory,
e);
} }
} }

View File

@@ -27,6 +27,7 @@
package org.alfresco.transformer.logging; package org.alfresco.transformer.logging;
import static java.lang.Math.max; import static java.lang.Math.max;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -42,10 +43,7 @@ import org.slf4j.LoggerFactory;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Provides setter and getter methods to allow the current Thread to set various log properties and for these * Provides setter and getter methods to allow the current Thread to set various log properties and for these values to be retrieved. The {@link #complete()} method should be called at the end of a request to flush the current entry to an internal log Collection of the latest entries. The {@link #getLog()} method is used to obtain access to this collection.
* values to be retrieved. The {@link #complete()} method should be called at the end of a request to flush the
* current entry to an internal log Collection of the latest entries. The {@link #getLog()} method is used to obtain
* access to this collection.
*/ */
@Deprecated @Deprecated
public final class LogEntry public final class LogEntry
@@ -204,8 +202,9 @@ public final class LogEntry
if (logEntry.statusCode == OK.value()) if (logEntry.statusCode == OK.value())
{ {
logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start -
logEntry.durationStreamIn - max(logEntry.durationTransform, logEntry.durationStreamIn - max(logEntry.durationTransform,
0) - max(logEntry.durationDelay, 0); 0)
- max(logEntry.durationDelay, 0);
} }
currentLogEntry.remove(); currentLogEntry.remove();
@@ -233,18 +232,20 @@ public final class LogEntry
public String getDuration() public String getDuration()
{ {
long duration = durationStreamIn + max(durationTransform, 0) + max(durationDelay, 0) + max( long duration = durationStreamIn + max(durationTransform, 0) + max(durationDelay, 0) + max(
durationStreamOut, 0); durationStreamOut, 0);
return duration <= 5 return duration <= 5
? "" ? ""
: time(duration) + : time(duration) +
" (" + " (" +
(time(durationStreamIn) + ' ' + (time(durationStreamIn) + ' ' +
time(durationTransform) + ' ' + time(durationTransform) + ' ' +
(durationDelay > 0 (durationDelay > 0
? time(durationDelay) + ' ' + (durationStreamOut < 0 ? "-" : time( ? time(durationDelay) + ' ' + (durationStreamOut < 0 ? "-"
durationStreamOut)) : time(
: time(durationStreamOut))).trim() + durationStreamOut))
")"; : time(durationStreamOut))).trim()
+
")";
} }
public String getSource() public String getSource()
@@ -279,17 +280,19 @@ public final class LogEntry
private String time(long ms) private String time(long ms)
{ {
return ms == -1 ? "" : size(ms, "1ms", return ms == -1 ? ""
new String[]{"ms", "s", "min", "hr"}, : size(ms, "1ms",
new long[]{1000, 60 * 1000, 60 * 60 * 1000, Long.MAX_VALUE}); new String[]{"ms", "s", "min", "hr"},
new long[]{1000, 60 * 1000, 60 * 60 * 1000, Long.MAX_VALUE});
} }
private String size(long size) private String size(long size)
{ {
// TODO fix numeric overflow in TB expression // TODO fix numeric overflow in TB expression
return size == -1 ? "" : size(size, "1 byte", return size == -1 ? ""
new String[]{"bytes", " KB", " MB", " GB", " TB"}, : size(size, "1 byte",
new long[]{1024, 1024 * 1024, 1024 * 1024 * 1024, 1024 * 1024 * 1024 * 1024, Long.MAX_VALUE}); new String[]{"bytes", " KB", " MB", " GB", " TB"},
new long[]{1024, 1024 * 1024, 1024 * 1024 * 1024, 1024 * 1024 * 1024 * 1024, Long.MAX_VALUE});
} }
private String size(long size, String singleValue, String[] units, long[] dividers) private String size(long size, String singleValue, String[] units, long[] dividers)

View File

@@ -32,8 +32,7 @@ package org.alfresco.transformer.logging;
@Deprecated @Deprecated
public interface StandardMessages public interface StandardMessages
{ {
String LICENCE = String LICENCE = "If the Alfresco software was purchased under a paid Alfresco license, the terms of the paid license agreement \n" +
"If the Alfresco software was purchased under a paid Alfresco license, the terms of the paid license agreement \n" + "will prevail. Otherwise, the software is provided under terms of the GNU LGPL v3 license. \n" +
"will prevail. Otherwise, the software is provided under terms of the GNU LGPL v3 license. \n" + "See the license at http://www.gnu.org/licenses/lgpl-3.0.txt. or in /LICENSE.txt \n\n";
"See the license at http://www.gnu.org/licenses/lgpl-3.0.txt. or in /LICENSE.txt \n\n";
} }

View File

@@ -29,7 +29,6 @@ package org.alfresco.transformer.messaging;
import jakarta.jms.ConnectionFactory; import jakarta.jms.ConnectionFactory;
import jakarta.jms.Queue; import jakarta.jms.Queue;
import org.alfresco.transform.messages.TransformRequestValidator;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -45,14 +44,14 @@ import org.springframework.lang.NonNull;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.alfresco.transform.messages.TransformRequestValidator;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* JMS and messaging configuration for the T-Engines. Contains the basic config in order to have the * JMS and messaging configuration for the T-Engines. Contains the basic config in order to have the T-Engine able to read from queues and send a reply back.
* T-Engine able to read from queues and send a reply back.
* *
* @author Lucian Tuca * @author Lucian Tuca created on 18/12/2018
* created on 18/12/2018
*/ */
@Deprecated @Deprecated
@Configuration @Configuration
@@ -79,8 +78,8 @@ public class MessagingConfig implements JmsListenerConfigurer
@Bean @Bean
@ConditionalOnProperty(name = "activemq.url") @ConditionalOnProperty(name = "activemq.url")
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
final ConnectionFactory connectionFactory, final ConnectionFactory connectionFactory,
final TransformMessageConverter transformMessageConverter) final TransformMessageConverter transformMessageConverter)
{ {
final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory); factory.setConnectionFactory(connectionFactory);
@@ -102,10 +101,8 @@ public class MessagingConfig implements JmsListenerConfigurer
@Bean @Bean
@ConditionalOnProperty(name = "activemq.url") @ConditionalOnProperty(name = "activemq.url")
public Queue engineRequestQueue( public Queue engineRequestQueue(
@Value("${queue.engineRequestQueue}") String engineRequestQueueValue) @Value("${queue.engineRequestQueue}") String engineRequestQueueValue)
{ {
return new ActiveMQQueue(engineRequestQueueValue); return new ActiveMQQueue(engineRequestQueueValue);
} }
} }

View File

@@ -36,7 +36,7 @@ import org.springframework.context.annotation.Configuration;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Prints JMS status information at application startup. * Prints JMS status information at application startup.
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -58,7 +58,7 @@ public class MessagingInfo
// Note: as per application.yaml the broker url is appended with "?jms.watchTopicAdvisories=false". If this needs to be fully // Note: as per application.yaml the broker url is appended with "?jms.watchTopicAdvisories=false". If this needs to be fully
// overridden then it would require explicitly setting both "spring.activemq.broker-url" *and* "activemq.url" (latter to non-false value). // overridden then it would require explicitly setting both "spring.activemq.broker-url" *and* "activemq.url" (latter to non-false value).
if ((activemqUrl != null) && (! activemqUrl.equals("false"))) if ((activemqUrl != null) && (!activemqUrl.equals("false")))
{ {
logger.info("JMS client is ENABLED - ACTIVEMQ_URL ='{}'", activemqUrl); logger.info("JMS client is ENABLED - ACTIVEMQ_URL ='{}'", activemqUrl);
} }

View File

@@ -27,11 +27,13 @@
package org.alfresco.transformer.messaging; package org.alfresco.transformer.messaging;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.Session;
import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.converter.MessageConverter;
@@ -39,15 +41,13 @@ import org.springframework.jms.support.converter.MessageType;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.jms.JMSException; import org.alfresco.transform.client.model.TransformReply;
import jakarta.jms.Message; import org.alfresco.transform.client.model.TransformRequest;
import jakarta.jms.Session;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* TODO: Duplicated from the Router * TODO: Duplicated from the Router Custom wrapper over MappingJackson2MessageConverter for T-Request/T-Reply objects.
* Custom wrapper over MappingJackson2MessageConverter for T-Request/T-Reply objects.
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -56,16 +56,13 @@ import jakarta.jms.Session;
public class TransformMessageConverter implements MessageConverter public class TransformMessageConverter implements MessageConverter
{ {
private static final MappingJackson2MessageConverter converter; private static final MappingJackson2MessageConverter converter;
private static final JavaType TRANSFORM_REQUEST_TYPE = private static final JavaType TRANSFORM_REQUEST_TYPE = TypeFactory.defaultInstance().constructType(TransformRequest.class);
TypeFactory.defaultInstance().constructType(TransformRequest.class);
static static
{ {
converter = new MappingJackson2MessageConverter() converter = new MappingJackson2MessageConverter() {
{
@Override @Override
@NonNull @NonNull protected JavaType getJavaTypeForMessage(final Message message) throws JMSException
protected JavaType getJavaTypeForMessage(final Message message) throws JMSException
{ {
if (message.getStringProperty("_type") == null) if (message.getStringProperty("_type") == null)
{ {
@@ -77,23 +74,20 @@ public class TransformMessageConverter implements MessageConverter
converter.setTargetType(MessageType.BYTES); converter.setTargetType(MessageType.BYTES);
converter.setTypeIdPropertyName("_type"); converter.setTypeIdPropertyName("_type");
converter.setTypeIdMappings(ImmutableMap.of( converter.setTypeIdMappings(ImmutableMap.of(
TransformRequest.class.getName(), TransformRequest.class, TransformRequest.class.getName(), TransformRequest.class,
TransformReply.class.getName(), TransformReply.class) TransformReply.class.getName(), TransformReply.class));
);
} }
@Override @Override
@NonNull @NonNull public Message toMessage(
public Message toMessage( @NonNull final Object object,
@NonNull final Object object, @NonNull final Session session) throws JMSException, MessageConversionException
@NonNull final Session session) throws JMSException, MessageConversionException
{ {
return converter.toMessage(object, session); return converter.toMessage(object, session);
} }
@Override @Override
@NonNull @NonNull public Object fromMessage(@NonNull final Message message) throws JMSException
public Object fromMessage(@NonNull final Message message) throws JMSException
{ {
return converter.fromMessage(message); return converter.fromMessage(message);
} }

View File

@@ -28,20 +28,20 @@ package org.alfresco.transformer.messaging;
import jakarta.jms.Destination; import jakarta.jms.Destination;
import org.alfresco.transform.client.model.TransformReply;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.alfresco.transform.client.model.TransformReply;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* TODO: Duplicated from the Router * TODO: Duplicated from the Router TransformReplySender Bean
* TransformReplySender Bean * <p/>
* <p/> * JMS message sender/publisher
* JMS message sender/publisher
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -65,7 +65,7 @@ public class TransformReplySender
{ {
try try
{ {
//jmsTemplate.setSessionTransacted(true); // do we need this? // jmsTemplate.setSessionTransacted(true); // do we need this?
jmsTemplate.convertAndSend(destination, reply, m -> { jmsTemplate.convertAndSend(destination, reply, m -> {
m.setJMSCorrelationID(correlationId); m.setJMSCorrelationID(correlationId);
return m; return m;

View File

@@ -26,11 +26,6 @@
*/ */
package org.alfresco.transformer.metadataExtractors; package org.alfresco.transformer.metadataExtractors;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -48,46 +43,39 @@ import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.TreeMap; import java.util.TreeMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Helper methods for metadata extract and embed. * Helper methods for metadata extract and embed.
* <p> * <p>
* <i>Much of the code is based on AbstractMappingMetadataExtracter from the * <i>Much of the code is based on AbstractMappingMetadataExtracter from the content repository. The code has been simplified to only set up mapping one way.</i>
* content repository. The code has been simplified to only set up mapping one way.</i> * <p>
* <p> * If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-extract"} (specified in the {@code engine_config.json}), it is indicating that it can extract metadata from {@code <MIMETYPE>}.
* If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-extract"}
* (specified in the {@code engine_config.json}), it is indicating that it can extract metadata from {@code <MIMETYPE>}.
* *
* The transform results in a Map of extracted properties encoded as json being returned to the content repository. * The transform results in a Map of extracted properties encoded as json being returned to the content repository.
* <ul> * <ul>
* <li>The content repository will use a transform in preference to any metadata extractors it might have defined * <li>The content repository will use a transform in preference to any metadata extractors it might have defined locally for the same MIMETYPE.</li>
* locally for the same MIMETYPE.</li> * <li>The T-Engine's Controller class will call a method in a class that extends {@link AbstractMetadataExtractor} based on the source and target mediatypes in the normal way.</li>
* <li>The T-Engine's Controller class will call a method in a class that extends {@link AbstractMetadataExtractor} * <li>The method extracts ALL available metadata is extracted from the document and then calls {@link #mapMetadataAndWrite(File, Map, Map)}.</li>
* based on the source and target mediatypes in the normal way.</li> * <li>Selected values from the available metadata are mapped into content repository property names and values, depending on what is defined in a {@code "<classname>_metadata_extract.properties"} file.</li>
* <li>The method extracts ALL available metadata is extracted from the document and then calls * <li>The selected values are set back to the content repository as a JSON representation of a Map, where the values are applied to the source node.</li>
* {@link #mapMetadataAndWrite(File, Map, Map)}.</li> * </ul>
* <li>Selected values from the available metadata are mapped into content repository property names and values, * To support the same functionality as metadata extractors configured inside the content repository, extra key value pairs may be returned from {@link #extractMetadata}. These are:
* depending on what is defined in a {@code "<classname>_metadata_extract.properties"} file.</li> * <ul>
* <li>The selected values are set back to the content repository as a JSON representation of a Map, where the values * <li>{@code "sys:overwritePolicy"} which can specify the {@code org.alfresco.repo.content.metadata.MetadataExtracter.OverwritePolicy} name. Defaults to "PRAGMATIC".</li>
* are applied to the source node.</li> * <li>{@code "sys:enableStringTagging"} if {@code "true"} finds or creates tags for each string mapped to {@code cm:taggable}. Defaults to {@code "false"} to ignore mapping strings to tags.</li>
* </ul> * <li>{@code "sys:carryAspectProperties"}</li>
* To support the same functionality as metadata extractors configured inside the content repository, * <li>{@code "sys:stringTaggingSeparators"}</li>
* extra key value pairs may be returned from {@link #extractMetadata}. These are: * </ul>
* <ul>
* <li>{@code "sys:overwritePolicy"} which can specify the
* {@code org.alfresco.repo.content.metadata.MetadataExtracter.OverwritePolicy} name. Defaults to "PRAGMATIC".</li>
* <li>{@code "sys:enableStringTagging"} if {@code "true"} finds or creates tags for each string mapped to
* {@code cm:taggable}. Defaults to {@code "false"} to ignore mapping strings to tags.</li>
* <li>{@code "sys:carryAspectProperties"} </li>
* <li>{@code "sys:stringTaggingSeparators"} </li>
* </ul>
* *
* If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-embed"}, it is * If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-embed"}, it is indicating that it can embed metadata in {@code <MIMETYPE>}.
* indicating that it can embed metadata in {@code <MIMETYPE>}.
* *
* The transform results in a new version of supplied source file that contains the metadata supplied in the transform * The transform results in a new version of supplied source file that contains the metadata supplied in the transform options.
* options.
* *
* @author Jesper Steen Møller * @author Jesper Steen Møller
* @author Derek Hulley * @author Derek Hulley
@@ -136,10 +124,10 @@ public abstract class AbstractMetadataExtractor
} }
public abstract Map<String, Serializable> extractMetadata(String sourceMimetype, Map<String, String> transformOptions, public abstract Map<String, Serializable> extractMetadata(String sourceMimetype, Map<String, String> transformOptions,
File sourceFile) throws Exception; File sourceFile) throws Exception;
public void embedMetadata(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions, public void embedMetadata(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
File sourceFile, File targetFile) throws Exception File sourceFile, File targetFile) throws Exception
{ {
// Default nothing, as embedding is not supported in most cases // Default nothing, as embedding is not supported in most cases
} }
@@ -208,9 +196,8 @@ public abstract class AbstractMetadataExtractor
/** /**
* Based on AbstractMappingMetadataExtracter#getDefaultMapping. * Based on AbstractMappingMetadataExtracter#getDefaultMapping.
* *
* This method provides a <i>mapping</i> of where to store the values extracted from the documents. The list of * This method provides a <i>mapping</i> of where to store the values extracted from the documents. The list of properties need <b>not</b> include all metadata values extracted from the document. This mapping should be defined in a file based on the class name: {@code "<classname>_metadata_extract.properties"}
* properties need <b>not</b> include all metadata values extracted from the document. This mapping should be *
* defined in a file based on the class name: {@code "<classname>_metadata_extract.properties"}
* @return Returns a static mapping. It may not be null. * @return Returns a static mapping. It may not be null.
*/ */
private Map<String, Set<String>> buildExtractMapping() private Map<String, Set<String>> buildExtractMapping()
@@ -219,7 +206,7 @@ public abstract class AbstractMetadataExtractor
Properties properties = readProperties(filename); Properties properties = readProperties(filename);
if (properties == null) if (properties == null)
{ {
logger.error("Failed to read "+filename); logger.error("Failed to read " + filename);
} }
Map<String, String> namespacesByPrefix = getNamespaces(properties); Map<String, String> namespacesByPrefix = getNamespaces(properties);
@@ -260,13 +247,10 @@ public abstract class AbstractMetadataExtractor
/** /**
* Based on AbstractMappingMetadataExtracter#getDefaultEmbedMapping. * Based on AbstractMappingMetadataExtracter#getDefaultEmbedMapping.
* *
* This method provides a <i>mapping</i> of model properties that should be embedded in the content. The list of * This method provides a <i>mapping</i> of model properties that should be embedded in the content. The list of properties need <b>not</b> include all properties. This mapping should be defined in a file based on the class name: {@code "<classname>_metadata_embed.properties"}
* properties need <b>not</b> include all properties. This mapping should be defined in a file based on the class
* name: {@code "<classname>_metadata_embed.properties"}
* <p> * <p>
* If no {@code "<classname>_metadata_embed.properties"} file is found, a reverse of the * If no {@code "<classname>_metadata_embed.properties"} file is found, a reverse of the {@code "<classname>_metadata_extract.properties"} will be assumed. A last win approach will be used for handling duplicates.
* {@code "<classname>_metadata_extract.properties"} will be assumed. A last win approach will be used for handling *
* duplicates.
* @return Returns a static mapping. It may not be null. * @return Returns a static mapping. It may not be null.
*/ */
private Map<String, Set<String>> buildEmbedMapping() private Map<String, Set<String>> buildEmbedMapping()
@@ -306,7 +290,8 @@ public abstract class AbstractMetadataExtractor
modelProperty = getQNameString(namespacesByPrefix, entry, modelProperty, EMBED); modelProperty = getQNameString(namespacesByPrefix, entry, modelProperty, EMBED);
String[] metadataKeysArray = metadataKeysString.split(","); String[] metadataKeysArray = metadataKeysString.split(",");
Set<String> metadataKeys = new HashSet<String>(metadataKeysArray.length); Set<String> metadataKeys = new HashSet<String>(metadataKeysArray.length);
for (String metadataKey : metadataKeysArray) { for (String metadataKey : metadataKeysArray)
{
metadataKeys.add(metadataKey.trim()); metadataKeys.add(metadataKey.trim());
} }
// Create the entry // Create the entry
@@ -367,8 +352,7 @@ public abstract class AbstractMetadataExtractor
} }
} }
catch (IOException ignore) catch (IOException ignore)
{ {}
}
return properties; return properties;
} }
@@ -410,21 +394,21 @@ public abstract class AbstractMetadataExtractor
} }
/** /**
* Adds a value to the map, conserving null values. Values are converted to null if: * Adds a value to the map, conserving null values. Values are converted to null if:
* <ul> * <ul>
* <li>it is an empty string value after trimming</li> * <li>it is an empty string value after trimming</li>
* <li>it is an empty collection</li> * <li>it is an empty collection</li>
* <li>it is an empty array</li> * <li>it is an empty array</li>
* </ul> * </ul>
* String values are trimmed before being put into the map. * String values are trimmed before being put into the map. Otherwise, it is up to the extracter to ensure that the value is a <tt>Serializable</tt>. It is not appropriate to implicitly convert values in order to make them <tt>Serializable</tt> - the best conversion method will depend on the value's specific meaning.
* Otherwise, it is up to the extracter to ensure that the value is a <tt>Serializable</tt>.
* It is not appropriate to implicitly convert values in order to make them <tt>Serializable</tt>
* - the best conversion method will depend on the value's specific meaning.
* *
* @param key the destination key * @param key
* @param value the serializable value * the destination key
* @param destination the map to put values into * @param value
* @return Returns <tt>true</tt> if set, otherwise <tt>false</tt> * the serializable value
* @param destination
* the map to put values into
* @return Returns <tt>true</tt> if set, otherwise <tt>false</tt>
*/ */
// Copied from the content repository's AbstractMappingMetadataExtracter. // Copied from the content repository's AbstractMappingMetadataExtracter.
protected boolean putRawValue(String key, Serializable value, Map<String, Serializable> destination) protected boolean putRawValue(String key, Serializable value, Map<String, Serializable> destination)
@@ -471,11 +455,10 @@ public abstract class AbstractMetadataExtractor
} }
/** /**
* The {@code transformOptions} may contain a replacement set of mappings. These will be used in place of the * The {@code transformOptions} may contain a replacement set of mappings. These will be used in place of the default mappings from read from file if supplied.
* default mappings from read from file if supplied.
*/ */
public void extractMetadata(String sourceMimetype, Map<String, String> transformOptions, File sourceFile, public void extractMetadata(String sourceMimetype, Map<String, String> transformOptions, File sourceFile,
File targetFile) throws Exception File targetFile) throws Exception
{ {
Map<String, Set<String>> mapping = getExtractMappingFromOptions(transformOptions, defaultExtractMapping); Map<String, Set<String>> mapping = getExtractMappingFromOptions(transformOptions, defaultExtractMapping);
@@ -493,8 +476,7 @@ public abstract class AbstractMetadataExtractor
} }
} }
private Map<String, Set<String>> getExtractMappingFromOptions(Map<String, String> transformOptions, Map<String, private Map<String, Set<String>> getExtractMappingFromOptions(Map<String, String> transformOptions, Map<String, Set<String>> defaultExtractMapping)
Set<String>> defaultExtractMapping)
{ {
String extractMappingOption = transformOptions.get(EXTRACT_MAPPING); String extractMappingOption = transformOptions.get(EXTRACT_MAPPING);
if (extractMappingOption != null) if (extractMappingOption != null)
@@ -506,15 +488,14 @@ public abstract class AbstractMetadataExtractor
} }
catch (JsonProcessingException e) catch (JsonProcessingException e)
{ {
throw new IllegalArgumentException("Failed to read "+ EXTRACT_MAPPING +" from request", e); throw new IllegalArgumentException("Failed to read " + EXTRACT_MAPPING + " from request", e);
} }
} }
return defaultExtractMapping; return defaultExtractMapping;
} }
/** /**
* @deprecated use {@link #extractMetadata(String, Map, File, File)} rather than calling this method. * @deprecated use {@link #extractMetadata(String, Map, File, File)} rather than calling this method. By default call the overloaded method with the default {@code extractMapping}.
* By default call the overloaded method with the default {@code extractMapping}.
*/ */
@Deprecated @Deprecated
public void mapMetadataAndWrite(File targetFile, Map<String, Serializable> metadata) throws IOException public void mapMetadataAndWrite(File targetFile, Map<String, Serializable> metadata) throws IOException
@@ -523,12 +504,12 @@ public abstract class AbstractMetadataExtractor
} }
public void mapMetadataAndWrite(File targetFile, Map<String, Serializable> metadata, public void mapMetadataAndWrite(File targetFile, Map<String, Serializable> metadata,
Map<String, Set<String>> extractMapping) throws IOException Map<String, Set<String>> extractMapping) throws IOException
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Raw metadata:"); logger.debug("Raw metadata:");
metadata.forEach((k,v) -> logger.debug(" "+k+"="+v)); metadata.forEach((k, v) -> logger.debug(" " + k + "=" + v));
} }
metadata = mapRawToSystem(metadata, extractMapping); metadata = mapRawToSystem(metadata, extractMapping);
@@ -538,12 +519,14 @@ public abstract class AbstractMetadataExtractor
/** /**
* Based on AbstractMappingMetadataExtracter#mapRawToSystem. * Based on AbstractMappingMetadataExtracter#mapRawToSystem.
* *
* @param rawMetadata Metadata keyed by document properties * @param rawMetadata
* @param extractMapping Mapping between document ans system properties * Metadata keyed by document properties
* @return Returns the metadata keyed by the system properties * @param extractMapping
* Mapping between document ans system properties
* @return Returns the metadata keyed by the system properties
*/ */
private Map<String, Serializable> mapRawToSystem(Map<String, Serializable> rawMetadata, private Map<String, Serializable> mapRawToSystem(Map<String, Serializable> rawMetadata,
Map<String, Set<String>> extractMapping) Map<String, Set<String>> extractMapping)
{ {
boolean debugEnabled = logger.isDebugEnabled(); boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) if (debugEnabled)
@@ -571,12 +554,12 @@ public abstract class AbstractMetadataExtractor
continue; continue;
} }
Set<String> systemQNames = extractMapping.get(documentKey); Set<String> systemQNames = extractMapping.get(documentKey);
for (String systemQName : systemQNames) for (String systemQName : systemQNames)
{ {
if (debugEnabled) if (debugEnabled)
{ {
logger.debug(" "+systemQName+"="+documentValue+" ("+documentKey+")"); logger.debug(" " + systemQName + "=" + documentValue + " (" + documentKey + ")");
} }
systemProperties.put(systemQName, documentValue); systemProperties.put(systemQName, documentValue);
} }

View File

@@ -31,16 +31,17 @@ import java.util.Objects;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* TODO: Copied from org.alfresco.store.entity (alfresco-shared-file-store). To be discussed * TODO: Copied from org.alfresco.store.entity (alfresco-shared-file-store). To be discussed
* *
* POJO that represents content reference ({@link java.util.UUID}) * POJO that represents content reference ({@link java.util.UUID})
*/ */
@Deprecated @Deprecated
public class FileRefEntity public class FileRefEntity
{ {
private String fileRef; private String fileRef;
public FileRefEntity() {} public FileRefEntity()
{}
public FileRefEntity(String fileRef) public FileRefEntity(String fileRef)
{ {
@@ -60,8 +61,10 @@ public class FileRefEntity
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (o == null || getClass() != o.getClass())
return false;
FileRefEntity that = (FileRefEntity) o; FileRefEntity that = (FileRefEntity) o;
return Objects.equals(fileRef, that.fileRef); return Objects.equals(fileRef, that.fileRef);
} }

View File

@@ -29,16 +29,17 @@ package org.alfresco.transformer.model;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* TODO: Copied from org.alfresco.store.entity (alfresco-shared-file-store). To be discussed * TODO: Copied from org.alfresco.store.entity (alfresco-shared-file-store). To be discussed
* *
* POJO that describes the ContentRefEntry response, contains {@link FileRefEntity} according to API spec * POJO that describes the ContentRefEntry response, contains {@link FileRefEntity} according to API spec
*/ */
@Deprecated @Deprecated
public class FileRefResponse public class FileRefResponse
{ {
private FileRefEntity entry; private FileRefEntity entry;
public FileRefResponse() {} public FileRefResponse()
{}
public FileRefResponse(FileRefEntity entry) public FileRefResponse(FileRefEntity entry)
{ {

View File

@@ -26,14 +26,15 @@
*/ */
package org.alfresco.transformer.probes; package org.alfresco.transformer.probes;
import static org.alfresco.transformer.fs.FileManager.SOURCE_FILE;
import static org.alfresco.transformer.fs.FileManager.TARGET_FILE;
import static org.alfresco.transformer.fs.FileManager.TempFileProvider.createTempFile;
import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE; import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
import static org.alfresco.transformer.fs.FileManager.SOURCE_FILE;
import static org.alfresco.transformer.fs.FileManager.TARGET_FILE;
import static org.alfresco.transformer.fs.FileManager.TempFileProvider.createTempFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -41,38 +42,33 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transformer.AbstractTransformerController; import org.alfresco.transformer.AbstractTransformerController;
import org.alfresco.transformer.logging.LogEntry; import org.alfresco.transformer.logging.LogEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Provides the logic performing test transformations by the live and ready probes. * Provides the logic performing test transformations by the live and ready probes.
* *
* <p><b>K8s probes</b>: A readiness probe indicates if the pod should accept request. <b>It does not indicate that a pod is * <p>
* ready after startup</b>. The liveness probe indicates when to kill the pod. <b>Both probes are called throughout the * <b>K8s probes</b>: A readiness probe indicates if the pod should accept request. <b>It does not indicate that a pod is ready after startup</b>. The liveness probe indicates when to kill the pod. <b>Both probes are called throughout the lifetime of the pod</b> and a <b>liveness probes can take place before a readiness probe.</b> The k8s <b>initialDelaySeconds field is not fully honoured</b> as it is multiplied by a random number, so is actually a maximum initial delay in seconds, but could be 0.
* lifetime of the pod</b> and a <b>liveness probes can take place before a readiness probe.</b> The k8s * </p>
* <b>initialDelaySeconds field is not fully honoured</b> as it is multiplied by a random number, so is
* actually a maximum initial delay in seconds, but could be 0. </p>
* *
* <p>Live and readiness probes might do test transforms. The first 6 requests result in a transformation * <p>
* of a small test file. The average time is remembered, but excludes the first one which is normally longer. This is * Live and readiness probes might do test transforms. The first 6 requests result in a transformation of a small test file. The average time is remembered, but excludes the first one which is normally longer. This is used in future requests to discover if transformations are becoming slower. The request also returns a non 200 status code resulting in the k8s pod being terminated, after a predefined number of transformations have been performed or if any transformation takes a long time. These are controlled by environment variables:
* used in future requests to discover if transformations are becoming slower. The request also returns a non 200 status * </p>
* code resulting in the k8s pod being terminated, after a predefined number of transformations have been performed or * <ul>
* if any transformation takes a long time. These are controlled by environment variables:</p> * <li>livenessPercent - The percentage slower the small test transform must be to indicate there is a problem.</li>
* <ul> * <li>livenessTransformPeriodSeconds - As liveness probes should be frequent, not every request should result in a test transformation. This value defines the gap between transformations.</li>
* <li>livenessPercent - The percentage slower the small test transform must be to indicate there is a problem.</li> * <li>maxTransforms - the maximum number of transformation to be performed before a restart.</li>
* <li>livenessTransformPeriodSeconds - As liveness probes should be frequent, not every request should result in * <li>maxTransformSeconds - the maximum time for a transformation, including failed ones.</li>
* a test transformation. This value defines the gap between transformations.</li> * </ul>
* <li>maxTransforms - the maximum number of transformation to be performed before a restart.</li>
* <li>maxTransformSeconds - the maximum time for a transformation, including failed ones.</li>
* </ul>
*/ */
@Deprecated @Deprecated
public abstract class ProbeTestTransform public abstract class ProbeTestTransform
@@ -115,18 +111,23 @@ public abstract class ProbeTestTransform
/** /**
* See Probes.md for more info. * See Probes.md for more info.
* *
* @param expectedLength was the length of the target file during testing * @param expectedLength
* @param plusOrMinus simply allows for some variation in the transformed size caused by new versions of dates * was the length of the target file during testing
* @param livenessPercent indicates that for this type of transform a variation up to 2 and a half times is not * @param plusOrMinus
* unreasonable under load * simply allows for some variation in the transformed size caused by new versions of dates
* @param maxTransforms default values normally supplied by helm. Not identical so we can be sure which value is used. * @param livenessPercent
* @param maxTransformSeconds default values normally supplied by helm. Not identical so we can be sure which value is used. * indicates that for this type of transform a variation up to 2 and a half times is not unreasonable under load
* @param livenessTransformPeriodSeconds default values normally supplied by helm. Not identical so we can be sure which value is used. * @param maxTransforms
* default values normally supplied by helm. Not identical so we can be sure which value is used.
* @param maxTransformSeconds
* default values normally supplied by helm. Not identical so we can be sure which value is used.
* @param livenessTransformPeriodSeconds
* default values normally supplied by helm. Not identical so we can be sure which value is used.
*/ */
public ProbeTestTransform(AbstractTransformerController controller, public ProbeTestTransform(AbstractTransformerController controller,
String sourceFilename, String targetFilename, long expectedLength, long plusOrMinus, String sourceFilename, String targetFilename, long expectedLength, long plusOrMinus,
int livenessPercent, long maxTransforms, long maxTransformSeconds, int livenessPercent, long maxTransforms, long maxTransformSeconds,
long livenessTransformPeriodSeconds) long livenessTransformPeriodSeconds)
{ {
this.sourceFilename = sourceFilename; this.sourceFilename = sourceFilename;
this.targetFilename = targetFilename; this.targetFilename = targetFilename;
@@ -137,7 +138,7 @@ public abstract class ProbeTestTransform
maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms); maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms);
maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds) * 1000; maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds) * 1000;
livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds", livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds",
livenessTransformPeriodSeconds) * 1000; livenessTransformPeriodSeconds) * 1000;
livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false); livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false);
} }
@@ -148,8 +149,7 @@ public abstract class ProbeTestTransform
return Boolean.parseBoolean(System.getenv(name)); return Boolean.parseBoolean(System.getenv(name));
} }
catch (Exception ignore) catch (Exception ignore)
{ {}
}
return defaultValue; return defaultValue;
} }
@@ -164,8 +164,7 @@ public abstract class ProbeTestTransform
l = Long.parseLong(env); l = Long.parseLong(env);
} }
catch (NumberFormatException ignore) catch (NumberFormatException ignore)
{ {}
}
} }
if (l <= 0) if (l <= 0)
{ {
@@ -187,9 +186,9 @@ public abstract class ProbeTestTransform
} }
return (isLiveProbe && livenessTransformPeriod > 0 && return (isLiveProbe && livenessTransformPeriod > 0 &&
(transCount <= AVERAGE_OVER_TRANSFORMS || nextTransformTime < System.currentTimeMillis())) (transCount <= AVERAGE_OVER_TRANSFORMS || nextTransformTime < System.currentTimeMillis()))
|| !initialised.get() || !initialised.get()
? doTransform(request, isLiveProbe) ? doTransform(request, isLiveProbe)
: doNothing(isLiveProbe); : doNothing(isLiveProbe);
} }
private String doNothing(boolean isLiveProbe) private String doNothing(boolean isLiveProbe)
@@ -215,8 +214,7 @@ public abstract class ProbeTestTransform
do do
{ {
nextTransformTime += livenessTransformPeriod; nextTransformTime += livenessTransformPeriod;
} } while (nextTransformTime < start);
while (nextTransformTime < start);
} }
File sourceFile = getSourceFile(request, isLiveProbe); File sourceFile = getSourceFile(request, isLiveProbe);
@@ -233,9 +231,9 @@ public abstract class ProbeTestTransform
if (time > maxTime) if (time > maxTime)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
getMessagePrefix(isLiveProbe) + getMessagePrefix(isLiveProbe) +
message + " which is more than " + livenessPercent + message + " which is more than " + livenessPercent +
"% slower than the normal value of " + normalTime + "ms"); "% slower than the normal value of " + normalTime + "ms");
} }
// We don't care if the ready or live probe works out if we are 'ready' to take requests. // We don't care if the ready or live probe works out if we are 'ready' to take requests.
@@ -251,15 +249,15 @@ public abstract class ProbeTestTransform
if (die.get()) if (die.get())
{ {
throw new TransformException(TOO_MANY_REQUESTS, throw new TransformException(TOO_MANY_REQUESTS,
getMessagePrefix(isLiveProbe) + "Transformer requested to die. A transform took " + getMessagePrefix(isLiveProbe) + "Transformer requested to die. A transform took " +
"longer than " + (maxTransformTime / 1000) + " seconds"); "longer than " + (maxTransformTime / 1000) + " seconds");
} }
if (maxTransformCount > 0 && transformCount.get() > maxTransformCount) if (maxTransformCount > 0 && transformCount.get() > maxTransformCount)
{ {
throw new TransformException(TOO_MANY_REQUESTS, throw new TransformException(TOO_MANY_REQUESTS,
getMessagePrefix(isLiveProbe) + "Transformer requested to die. It has performed " + getMessagePrefix(isLiveProbe) + "Transformer requested to die. It has performed " +
"more than " + maxTransformCount + " transformations"); "more than " + maxTransformCount + " transformations");
} }
} }
@@ -311,11 +309,11 @@ public abstract class ProbeTestTransform
maxTime = (normalTime * (livenessPercent + 100)) / 100; maxTime = (normalTime * (livenessPercent + 100)) / 100;
if ((!isLiveProbe && !readySent.getAndSet( if ((!isLiveProbe && !readySent.getAndSet(
true)) || transCount > AVERAGE_OVER_TRANSFORMS) true)) || transCount > AVERAGE_OVER_TRANSFORMS)
{ {
nextTransformTime = System.currentTimeMillis() + livenessTransformPeriod; nextTransformTime = System.currentTimeMillis() + livenessTransformPeriod;
logger.trace("{} - {}ms+{}%={}ms", message, normalTime, livenessPercent, logger.trace("{} - {}ms+{}%={}ms", message, normalTime, livenessPercent,
maxTime); maxTime);
} }
} }
else if (!isLiveProbe && !readySent.getAndSet(true)) else if (!isLiveProbe && !readySent.getAndSet(true))
@@ -333,15 +331,15 @@ public abstract class ProbeTestTransform
if (!targetFile.exists() || !targetFile.isFile()) if (!targetFile.exists() || !targetFile.isFile())
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
probeMessage + "Target File \"" + targetFile.getAbsolutePath() + "\" did not exist"); probeMessage + "Target File \"" + targetFile.getAbsolutePath() + "\" did not exist");
} }
long length = targetFile.length(); long length = targetFile.length();
if (length < minExpectedLength || length > maxExpectedLength) if (length < minExpectedLength || length > maxExpectedLength)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
probeMessage + "Target File \"" + targetFile.getAbsolutePath() + probeMessage + "Target File \"" + targetFile.getAbsolutePath() +
"\" was the wrong size (" + length + "). Needed to be between " + "\" was the wrong size (" + length + "). Needed to be between " +
minExpectedLength + " and " + maxExpectedLength); minExpectedLength + " and " + maxExpectedLength);
} }
LogEntry.setTargetSize(length); LogEntry.setTargetSize(length);
LogEntry.setStatusCodeAndMessage(OK.value(), probeMessage + "Success - " + message); LogEntry.setStatusCodeAndMessage(OK.value(), probeMessage + "Success - " + message);

View File

@@ -29,9 +29,8 @@ package org.alfresco.transformer.util;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Partially duplicated from *alfresco-data-model*. * Partially duplicated from *alfresco-data-model*.
*/ */
@Deprecated @Deprecated
public interface MimetypeMap extends org.alfresco.transform.common.Mimetype public interface MimetypeMap extends org.alfresco.transform.common.Mimetype
{ {}
}

View File

@@ -1,48 +1,47 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transformer.util; package org.alfresco.transformer.util;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Extends the list of transform options with historic request parameters or 'extra' parameters used in testing * Extends the list of transform options with historic request parameters or 'extra' parameters used in testing or communication in the all-in-one transformer.
* or communication in the all-in-one transformer. */
*/ @Deprecated
@Deprecated public interface RequestParamMap extends org.alfresco.transform.client.util.RequestParamMap
public interface RequestParamMap extends org.alfresco.transform.client.util.RequestParamMap {
{ // This property can be sent by acs repository's legacy transformers to force a transform,
// This property can be sent by acs repository's legacy transformers to force a transform, // instead of letting this T-Engine determine it based on the request parameters.
// instead of letting this T-Engine determine it based on the request parameters. // This allows clients to specify transform names as they appear in the engine config files, for example:
// This allows clients to specify transform names as they appear in the engine config files, for example: // imagemagick, libreoffice, PdfBox, TikaAuto, ....
// imagemagick, libreoffice, PdfBox, TikaAuto, .... // See ATS-731.
// See ATS-731. @Deprecated
@Deprecated String TRANSFORM_NAME_PROPERTY = "transformName";
String TRANSFORM_NAME_PROPERTY = "transformName"; String TRANSFORM_NAME_PARAMETER = "alfresco.transform-name-parameter";
String TRANSFORM_NAME_PARAMETER = "alfresco.transform-name-parameter";
String TEST_DELAY = "testDelay";
String TEST_DELAY = "testDelay"; }
}

View File

@@ -35,7 +35,8 @@ public class Util
/** /**
* Safely converts a {@link String} to an {@link Integer} * Safely converts a {@link String} to an {@link Integer}
* *
* @param param String to be converted * @param param
* String to be converted
* @return Null if param is null or converted value as {@link Integer} * @return Null if param is null or converted value as {@link Integer}
*/ */
public static Integer stringToInteger(final String param) public static Integer stringToInteger(final String param)
@@ -46,7 +47,8 @@ public class Util
/** /**
* Safely converts a {@link String} to a {@link Boolean} * Safely converts a {@link String} to a {@link Boolean}
* *
* @param param String to be converted * @param param
* String to be converted
* @return Null if param is null or converted value as {@link Boolean} * @return Null if param is null or converted value as {@link Boolean}
*/ */
public static Boolean stringToBoolean(final String param) public static Boolean stringToBoolean(final String param)
@@ -57,7 +59,8 @@ public class Util
/** /**
* Safely converts a {@link String} to a {@link Long} * Safely converts a {@link String} to a {@link Long}
* *
* @param param String to be converted * @param param
* String to be converted
* @return Null if param is null or converted value as {@link Boolean} * @return Null if param is null or converted value as {@link Boolean}
*/ */
public static Long stringToLong(final String param) public static Long stringToLong(final String param)

View File

@@ -26,13 +26,14 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.springframework.test.util.AssertionErrors.assertTrue; import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -45,8 +46,7 @@ import org.springframework.util.LinkedMultiValueMap;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Super class for testing controllers with a server. Includes tests for the AbstractTransformerController itself. * Super class for testing controllers with a server. Includes tests for the AbstractTransformerController itself. Note: Currently uses json rather than HTML as json is returned by this spring boot test harness.
* Note: Currently uses json rather than HTML as json is returned by this spring boot test harness.
*/ */
@Deprecated @Deprecated
public abstract class AbstractHttpRequestTest public abstract class AbstractHttpRequestTest
@@ -74,7 +74,7 @@ public abstract class AbstractHttpRequestTest
public void logPageExists() public void logPageExists()
{ {
String result = restTemplate.getForObject("http://localhost:" + port + "/log", String result = restTemplate.getForObject("http://localhost:" + port + "/log",
String.class); String.class);
String title = getTransformerName() + ' ' + "Log"; String title = getTransformerName() + ' ' + "Log";
assertTrue("\"" + title + "\" should be part of the page title", result.contains(title)); assertTrue("\"" + title + "\" should be part of the page title", result.contains(title));
@@ -84,11 +84,11 @@ public abstract class AbstractHttpRequestTest
public void errorPageExists() public void errorPageExists()
{ {
String result = restTemplate.getForObject("http://localhost:" + port + "/error", String result = restTemplate.getForObject("http://localhost:" + port + "/error",
String.class); String.class);
String title = getTransformerName() + ' ' + "Error Page"; String title = getTransformerName() + ' ' + "Error Page";
assertTrue("\"" + title + "\" should be part of the page title", assertTrue("\"" + title + "\" should be part of the page title",
result.contains("Error Page")); result.contains("Error Page"));
} }
@Test @Test
@@ -111,18 +111,18 @@ public abstract class AbstractHttpRequestTest
private void assertMissingParameter(String name) private void assertMissingParameter(String name)
{ {
assertTransformError(true, assertTransformError(true,
getTransformerName() + " - Request parameter '" + name + "' is missing", null); getTransformerName() + " - Request parameter '" + name + "' is missing", null);
} }
protected void assertTransformError(boolean addFile, protected void assertTransformError(boolean addFile,
String errorMessage, String errorMessage,
LinkedMultiValueMap<String, Object> additionalParams) LinkedMultiValueMap<String, Object> additionalParams)
{ {
LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>(); LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>();
if (addFile) if (addFile)
{ {
parameters.add("file", parameters.add("file",
new org.springframework.core.io.ClassPathResource("quick." + getSourceExtension())); new org.springframework.core.io.ClassPathResource("quick." + getSourceExtension()));
} }
if (additionalParams != null) if (additionalParams != null)
{ {
@@ -132,7 +132,7 @@ public abstract class AbstractHttpRequestTest
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(parameters, HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(parameters,
headers); headers);
sendTranformationRequest(entity, errorMessage); sendTranformationRequest(entity, errorMessage);
} }
@@ -153,10 +153,10 @@ public abstract class AbstractHttpRequestTest
} }
protected void sendTranformationRequest( protected void sendTranformationRequest(
final HttpEntity<LinkedMultiValueMap<String, Object>> entity, final String errorMessage) final HttpEntity<LinkedMultiValueMap<String, Object>> entity, final String errorMessage)
{ {
final ResponseEntity<String> response = restTemplate.exchange(ENDPOINT_TRANSFORM, POST, entity, final ResponseEntity<String> response = restTemplate.exchange(ENDPOINT_TRANSFORM, POST, entity,
String.class, ""); String.class, "");
assertEquals(errorMessage, getErrorMessage(response.getBody())); assertEquals(errorMessage, getErrorMessage(response.getBody()));
} }

View File

@@ -27,12 +27,14 @@
package org.alfresco.transformer; package org.alfresco.transformer;
import static java.text.MessageFormat.format; import static java.text.MessageFormat.format;
import static org.alfresco.transformer.EngineClient.sendTRequest;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_METADATA_EXTRACT;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_METADATA_EXTRACT;
import static org.alfresco.transformer.EngineClient.sendTRequest;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -42,22 +44,21 @@ import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Super class of metadata integration tests. Sub classes should provide the following: * Super class of metadata integration tests. Sub classes should provide the following:
* <p> * <p>
* <ul> * <ul>
* <li>A method providing a * <li>A method providing a Stream of test files: {@code public static Stream<TestFileInfo> engineTransformations()};</li>
* Stream of test files: {@code public static Stream<TestFileInfo> engineTransformations()}; </li> * <li>Provide expected json files (&lt;sourceFilename>"_metadata.json") as resources on the classpath.</li>
* <li> Provide expected json files (&lt;sourceFilename>"_metadata.json") as resources on the classpath.</li> * <li>Override the method {@code testTransformation(TestFileInfo testFileInfo)} such that it calls the super method as a {@code @ParameterizedTest} for example:</li>
* <li> Override the method {@code testTransformation(TestFileInfo testFileInfo)} such that it calls * </ul>
* the super method as a {@code @ParameterizedTest} for example:</li> </ul> *
* <pre> * <pre>
* &#64;ParameterizedTest * &#64;ParameterizedTest
* *
* &#64;MethodSource("engineTransformations") * &#64;MethodSource("engineTransformations")
@@ -69,7 +70,7 @@ import org.springframework.http.ResponseEntity;
* { * {
* super.testTransformation(TestFileInfo testFileInfo) * super.testTransformation(TestFileInfo testFileInfo)
* } * }
* </pre> * </pre>
* *
* @author adavis * @author adavis
* @author dedwards * @author dedwards
@@ -84,7 +85,6 @@ public abstract class AbstractMetadataExtractsIT
private final ObjectMapper jsonObjectMapper = new ObjectMapper(); private final ObjectMapper jsonObjectMapper = new ObjectMapper();
public void testTransformation(TestFileInfo testFileInfo) public void testTransformation(TestFileInfo testFileInfo)
{ {
final String sourceMimetype = testFileInfo.getMimeType(); final String sourceMimetype = testFileInfo.getMimeType();
@@ -106,7 +106,7 @@ public abstract class AbstractMetadataExtractsIT
Map<String, Serializable> expectedMetadata = readExpectedMetadata(metadataFilename, actualMetadataFile); Map<String, Serializable> expectedMetadata = readExpectedMetadata(metadataFilename, actualMetadataFile);
assertEquals(expectedMetadata, actualMetadata, assertEquals(expectedMetadata, actualMetadata,
sourceFile+": The metadata did not match the expected value. It has been saved in "+actualMetadataFile.getAbsolutePath()); sourceFile + ": The metadata did not match the expected value. It has been saved in " + actualMetadataFile.getAbsolutePath());
actualMetadataFile.delete(); actualMetadataFile.delete();
} }
catch (Exception e) catch (Exception e)
@@ -122,8 +122,8 @@ public abstract class AbstractMetadataExtractsIT
{ {
if (inputStream == null) if (inputStream == null)
{ {
fail("The expected metadata file "+filename+" did not exist.\n"+ fail("The expected metadata file " + filename + " did not exist.\n" +
"The actual metadata has been saved in "+actualMetadataFile.getAbsoluteFile()); "The actual metadata has been saved in " + actualMetadataFile.getAbsoluteFile());
} }
return readMetadata(inputStream); return readMetadata(inputStream);
} }

View File

@@ -30,19 +30,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import jakarta.jms.Queue; import jakarta.jms.Queue;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.JmsTemplate;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* @author Lucian Tuca * @author Lucian Tuca created on 15/01/2019
* created on 15/01/2019
*/ */
@Deprecated @Deprecated
@SpringBootTest(properties = {"activemq.url=nio://localhost:61616"}) @SpringBootTest(properties = {"activemq.url=nio://localhost:61616"})
@@ -55,7 +55,7 @@ public abstract class AbstractQueueTransformServiceIT
private JmsTemplate jmsTemplate; private JmsTemplate jmsTemplate;
private final ActiveMQQueue testingQueue = new ActiveMQQueue( private final ActiveMQQueue testingQueue = new ActiveMQQueue(
"org.alfresco.transform.engine.IT"); "org.alfresco.transform.engine.IT");
@Test @Test
public void queueTransformServiceIT() public void queueTransformServiceIT()

View File

@@ -26,38 +26,26 @@
*/ */
package org.alfresco.transformer; package org.alfresco.transformer;
import com.fasterxml.jackson.databind.ObjectMapper; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import static org.hamcrest.Matchers.containsString;
import com.google.common.collect.ImmutableSet; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.alfresco.transform.client.model.InternalContext; import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.alfresco.transform.client.model.TransformReply; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.alfresco.transform.client.model.TransformRequest; import static org.springframework.http.HttpHeaders.ACCEPT;
import org.alfresco.transform.config.SupportedSourceAndTarget; import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import org.alfresco.transform.config.TransformConfig; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.config.TransformOption; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.alfresco.transform.config.TransformOptionGroup; import static org.springframework.http.HttpStatus.OK;
import org.alfresco.transform.config.TransformOptionValue; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import org.alfresco.transform.config.Transformer; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import org.alfresco.transform.messages.TransformStack; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import org.alfresco.transform.registry.TransformServiceRegistry; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient;
import org.alfresco.transformer.model.FileRefEntity; import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import org.alfresco.transformer.model.FileRefResponse; import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import org.alfresco.transformer.probes.ProbeTestTransform; import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG;
import org.junit.jupiter.api.Test; import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG_LATEST;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -70,34 +58,42 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import com.fasterxml.jackson.databind.ObjectMapper;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL; import com.google.common.collect.ImmutableList;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM; import com.google.common.collect.ImmutableMap;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG; import com.google.common.collect.ImmutableSet;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG_LATEST; import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.containsString; import org.junit.jupiter.api.io.TempDir;
import static org.junit.jupiter.api.Assertions.assertEquals; import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.assertNotNull; import org.springframework.beans.factory.annotation.Value;
import static org.junit.jupiter.api.Assertions.assertTrue; import org.springframework.boot.test.mock.mockito.MockBean;
import static org.mockito.ArgumentMatchers.any; import org.springframework.boot.test.mock.mockito.SpyBean;
import static org.mockito.Mockito.when; import org.springframework.core.io.ClassPathResource;
import static org.springframework.http.HttpHeaders.ACCEPT; import org.springframework.mock.web.MockMultipartFile;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import org.springframework.test.util.ReflectionTestUtils;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.http.HttpStatus.CREATED; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.alfresco.transform.client.model.InternalContext;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import org.alfresco.transform.client.model.TransformReply;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import org.alfresco.transform.client.model.TransformRequest;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.alfresco.transform.config.SupportedSourceAndTarget;
import org.alfresco.transform.config.TransformConfig;
import org.alfresco.transform.config.TransformOption;
import org.alfresco.transform.config.TransformOptionGroup;
import org.alfresco.transform.config.TransformOptionValue;
import org.alfresco.transform.config.Transformer;
import org.alfresco.transform.messages.TransformStack;
import org.alfresco.transform.registry.TransformServiceRegistry;
import org.alfresco.transformer.clients.AlfrescoSharedFileStoreClient;
import org.alfresco.transformer.probes.ProbeTestTransform;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Super class for testing controllers without a server. Includes tests for the AbstractTransformerController itself. * Super class for testing controllers without a server. Includes tests for the AbstractTransformerController itself.
*/ */
@Deprecated @Deprecated
public abstract class AbstractTransformerControllerTest public abstract class AbstractTransformerControllerTest
@@ -135,44 +131,46 @@ public abstract class AbstractTransformerControllerTest
/** /**
* The expected result. Taken resting target quick file's bytes. * The expected result. Taken resting target quick file's bytes.
* *
* Note: These checks generally don't work on Windows (Mac and Linux are okay). Possibly to do with byte order * Note: These checks generally don't work on Windows (Mac and Linux are okay). Possibly to do with byte order loading.
* loading.
*/ */
protected byte[] expectedTargetFileBytes; protected byte[] expectedTargetFileBytes;
// Called by sub class // Called by sub class
protected abstract void mockTransformCommand(String sourceExtension, protected abstract void mockTransformCommand(String sourceExtension,
String targetExtension, String sourceMimetype, String targetExtension, String sourceMimetype,
boolean readTargetFileBytes) throws IOException; boolean readTargetFileBytes) throws IOException;
protected abstract AbstractTransformerController getController(); protected abstract AbstractTransformerController getController();
protected abstract void updateTransformRequestWithSpecificOptions( protected abstract void updateTransformRequestWithSpecificOptions(
TransformRequest transformRequest); TransformRequest transformRequest);
/** /**
* This method ends up being the core of the mock. * This method ends up being the core of the mock. It copies content from an existing file in the resources folder to the desired location in order to simulate a successful transformation.
* It copies content from an existing file in the resources folder to the desired location
* in order to simulate a successful transformation.
* *
* @param actualTargetExtension Requested extension. * @param actualTargetExtension
* @param testFile The test file (transformed) - basically the result. * Requested extension.
* @param targetFile The location where the content from the testFile should be copied * @param testFile
* @throws IOException in case of any errors. * The test file (transformed) - basically the result.
* @param targetFile
* The location where the content from the testFile should be copied
* @throws IOException
* in case of any errors.
*/ */
void generateTargetFileFromResourceFile(String actualTargetExtension, File testFile, void generateTargetFileFromResourceFile(String actualTargetExtension, File testFile,
File targetFile) throws IOException File targetFile) throws IOException
{ {
if (testFile != null) if (testFile != null)
{ {
try (var inputStream = new FileInputStream(testFile); try (var inputStream = new FileInputStream(testFile);
var outputStream = new FileOutputStream(targetFile)) var outputStream = new FileOutputStream(targetFile))
{ {
FileChannel source = inputStream.getChannel(); FileChannel source = inputStream.getChannel();
FileChannel target = outputStream.getChannel(); FileChannel target = outputStream.getChannel();
target.transferFrom(source, 0, source.size()); target.transferFrom(source, 0, source.size());
} catch (Exception e) }
catch (Exception e)
{ {
throw e; throw e;
} }
@@ -183,13 +181,14 @@ public abstract class AbstractTransformerControllerTest
if (testFile != null) if (testFile != null)
{ {
try (var inputStream = new FileInputStream(testFile); try (var inputStream = new FileInputStream(testFile);
var outputStream = new FileOutputStream(targetFile)) var outputStream = new FileOutputStream(targetFile))
{ {
FileChannel source = inputStream.getChannel(); FileChannel source = inputStream.getChannel();
FileChannel target = outputStream.getChannel(); FileChannel target = outputStream.getChannel();
target.transferFrom(source, 0, source.size()); target.transferFrom(source, 0, source.size());
} catch (Exception e) }
catch (Exception e)
{ {
throw e; throw e;
} }
@@ -213,11 +212,11 @@ public abstract class AbstractTransformerControllerTest
" does not exist in the resources directory"); " does not exist in the resources directory");
} }
// added as part of ATS-702 to allow test resources to be read from the imported jar files to prevent test resource duplication // added as part of ATS-702 to allow test resources to be read from the imported jar files to prevent test resource duplication
if (testFileUrl!=null) if (testFileUrl != null)
{ {
// Each use of the tempDir should result in a unique directory being used // Each use of the tempDir should result in a unique directory being used
testFile = new File(tempDir, testFilename); testFile = new File(tempDir, testFilename);
Files.copy(classLoader.getResourceAsStream(testFilename), testFile.toPath(),REPLACE_EXISTING); Files.copy(classLoader.getResourceAsStream(testFilename), testFile.toPath(), REPLACE_EXISTING);
} }
return testFileUrl == null ? null : testFile; return testFileUrl == null ? null : testFile;
@@ -236,7 +235,7 @@ public abstract class AbstractTransformerControllerTest
} }
private MockHttpServletRequestBuilder mockMvcRequestWithoutMockMultipartFile(String url, private MockHttpServletRequestBuilder mockMvcRequestWithoutMockMultipartFile(String url,
String... params) String... params)
{ {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM);
@@ -253,10 +252,10 @@ public abstract class AbstractTransformerControllerTest
} }
private MockHttpServletRequestBuilder mockMvcRequestWithMockMultipartFile(String url, MockMultipartFile sourceFile, private MockHttpServletRequestBuilder mockMvcRequestWithMockMultipartFile(String url, MockMultipartFile sourceFile,
String... params) String... params)
{ {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM).file( MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM).file(
sourceFile); sourceFile);
if (params.length % 2 != 0) if (params.length % 2 != 0)
{ {
@@ -298,11 +297,11 @@ public abstract class AbstractTransformerControllerTest
public void simpleTransformTest() throws Exception public void simpleTransformTest() throws Exception
{ {
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
@@ -310,22 +309,22 @@ public abstract class AbstractTransformerControllerTest
{ {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension, mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension,
"testDelay", "400")) "testDelay", "400"))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
long ms = System.currentTimeMillis() - start; long ms = System.currentTimeMillis() - start;
System.out.println("Transform incluing test delay was " + ms); System.out.println("Transform incluing test delay was " + ms);
assertTrue(ms >= 400, "Delay sending the result back was too small " + ms); assertTrue(ms >= 400, "Delay sending the result back was too small " + ms);
assertTrue(ms <= 500,"Delay sending the result back was too big " + ms); assertTrue(ms <= 500, "Delay sending the result back was too big " + ms);
} }
@Test @Test
public void noTargetFileTest() throws Exception public void noTargetFileTest() throws Exception
{ {
mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", "xxx")) mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", "xxx"))
.andExpect(status().is(INTERNAL_SERVER_ERROR.value())); .andExpect(status().is(INTERNAL_SERVER_ERROR.value()));
} }
@Test @Test
@@ -333,14 +332,14 @@ public abstract class AbstractTransformerControllerTest
public void dotDotSourceFilenameTest() throws Exception public void dotDotSourceFilenameTest() throws Exception
{ {
sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype, sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype,
expectedSourceFileBytes); expectedSourceFileBytes);
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
@@ -348,14 +347,14 @@ public abstract class AbstractTransformerControllerTest
public void noExtensionSourceFilenameTest() throws Exception public void noExtensionSourceFilenameTest() throws Exception
{ {
sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype, sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype,
expectedSourceFileBytes); expectedSourceFileBytes);
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*= UTF-8''quick." + targetExtension)); "attachment; filename*= UTF-8''quick." + targetExtension));
} }
@Test @Test
@@ -365,9 +364,9 @@ public abstract class AbstractTransformerControllerTest
sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, expectedSourceFileBytes);
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason(containsString("The source filename was not supplied"))); .andExpect(status().reason(containsString("The source filename was not supplied")));
} }
@Test @Test
@@ -376,17 +375,17 @@ public abstract class AbstractTransformerControllerTest
sourceFile = new MockMultipartFile("file", "", sourceMimetype, expectedSourceFileBytes); sourceFile = new MockMultipartFile("file", "", sourceMimetype, expectedSourceFileBytes);
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile, "targetExtension", targetExtension))
.andExpect(status().is(BAD_REQUEST.value())); .andExpect(status().is(BAD_REQUEST.value()));
} }
@Test @Test
public void noTargetExtensionTest() throws Exception public void noTargetExtensionTest() throws Exception
{ {
mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile)) mockMvc.perform(mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andExpect(status().reason( .andExpect(status().reason(
containsString("Request parameter 'targetExtension' is missing"))); containsString("Request parameter 'targetExtension' is missing")));
} }
@Test @Test
@@ -396,14 +395,14 @@ public abstract class AbstractTransformerControllerTest
probeTestTransform.setLivenessPercent(110); probeTestTransform.setLivenessPercent(110);
long[][] values = new long[][]{ long[][] values = new long[][]{
{5000, 0, Long.MAX_VALUE}, // 1st transform is ignored {5000, 0, Long.MAX_VALUE}, // 1st transform is ignored
{1000, 1000, 2100}, // 1000 + 1000*1.1 {1000, 1000, 2100}, // 1000 + 1000*1.1
{3000, 2000, 4200}, // 2000 + 2000*1.1 {3000, 2000, 4200}, // 2000 + 2000*1.1
{2000, 2000, 4200}, {2000, 2000, 4200},
{6000, 3000, 6300}, {6000, 3000, 6300},
{8000, 4000, 8400}, {8000, 4000, 8400},
{4444, 4000, 8400}, // no longer in the first few, so normal and max times don't change {4444, 4000, 8400}, // no longer in the first few, so normal and max times don't change
{5555, 4000, 8400} {5555, 4000, 8400}
}; };
for (long[] v : values) for (long[] v : values)
@@ -427,16 +426,16 @@ public abstract class AbstractTransformerControllerTest
// Serialize and call the transformer // Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest); String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc String transformationReplyAsString = mockMvc
.perform(MockMvcRequestBuilders .perform(MockMvcRequestBuilders
.post(ENDPOINT_TRANSFORM) .post(ENDPOINT_TRANSFORM)
.header(ACCEPT, APPLICATION_JSON_VALUE) .header(ACCEPT, APPLICATION_JSON_VALUE)
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE) .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.content(tr)) .content(tr))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class); TransformReply.class);
// Assert the reply // Assert the reply
assertEquals(BAD_REQUEST.value(), transformReply.getStatus()); assertEquals(BAD_REQUEST.value(), transformReply.getStatus());
@@ -455,8 +454,8 @@ public abstract class AbstractTransformerControllerTest
public void testGetTransformConfigInfo() throws Exception public void testGetTransformConfigInfo() throws Exception
{ {
TransformConfig expectedTransformConfig = objectMapper TransformConfig expectedTransformConfig = objectMapper
.readValue(getTestFile(getEngineConfigName(), true), .readValue(getTestFile(getEngineConfigName(), true),
TransformConfig.class); TransformConfig.class);
expectedTransformConfig.getTransformers().forEach(transformer -> { expectedTransformConfig.getTransformers().forEach(transformer -> {
transformer.setCoreVersion(coreVersion); transformer.setCoreVersion(coreVersion);
transformer.getTransformOptions().add(DIRECT_ACCESS_URL); transformer.getTransformOptions().add(DIRECT_ACCESS_URL);
@@ -464,13 +463,13 @@ public abstract class AbstractTransformerControllerTest
expectedTransformConfig.getTransformOptions().put(DIRECT_ACCESS_URL, Set.of(new TransformOptionValue(false, DIRECT_ACCESS_URL))); expectedTransformConfig.getTransformOptions().put(DIRECT_ACCESS_URL, Set.of(new TransformOptionValue(false, DIRECT_ACCESS_URL)));
ReflectionTestUtils.setField(transformRegistry, "engineConfig", ReflectionTestUtils.setField(transformRegistry, "engineConfig",
new ClassPathResource(getEngineConfigName())); new ClassPathResource(getEngineConfigName()));
String response = mockMvc String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG_LATEST)) .perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG_LATEST))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE)) .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
assertEquals(expectedTransformConfig, transformConfig); assertEquals(expectedTransformConfig, transformConfig);
@@ -481,17 +480,17 @@ public abstract class AbstractTransformerControllerTest
public void testGetTransformConfigInfoExcludingCoreVersion() throws Exception public void testGetTransformConfigInfoExcludingCoreVersion() throws Exception
{ {
TransformConfig expectedTransformConfig = objectMapper TransformConfig expectedTransformConfig = objectMapper
.readValue(getTestFile(getEngineConfigName(), true), .readValue(getTestFile(getEngineConfigName(), true),
TransformConfig.class); TransformConfig.class);
ReflectionTestUtils.setField(transformRegistry, "engineConfig", ReflectionTestUtils.setField(transformRegistry, "engineConfig",
new ClassPathResource(getEngineConfigName())); new ClassPathResource(getEngineConfigName()));
String response = mockMvc String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG)) .perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE)) .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
assertEquals(expectedTransformConfig, transformConfig); assertEquals(expectedTransformConfig, transformConfig);
@@ -503,13 +502,13 @@ public abstract class AbstractTransformerControllerTest
TransformConfig expectedResult = buildCompleteTransformConfig(); TransformConfig expectedResult = buildCompleteTransformConfig();
ReflectionTestUtils.setField(transformRegistry, "engineConfig", ReflectionTestUtils.setField(transformRegistry, "engineConfig",
new ClassPathResource("engine_config_with_duplicates.json")); new ClassPathResource("engine_config_with_duplicates.json"));
String response = mockMvc String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG)) .perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE)) .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@@ -517,9 +516,9 @@ public abstract class AbstractTransformerControllerTest
assertEquals(expectedResult, transformConfig); assertEquals(expectedResult, transformConfig);
assertEquals(3, transformConfig.getTransformOptions().get("engineXOptions").size()); assertEquals(3, transformConfig.getTransformOptions().get("engineXOptions").size());
assertEquals(1, assertEquals(1,
transformConfig.getTransformers().get(0).getSupportedSourceAndTargetList().size()); transformConfig.getTransformers().get(0).getSupportedSourceAndTargetList().size());
assertEquals(1, assertEquals(1,
transformConfig.getTransformers().get(0).getTransformOptions().size()); transformConfig.getTransformers().get(0).getTransformOptions().size());
} }
@Test @Test
@@ -530,13 +529,13 @@ public abstract class AbstractTransformerControllerTest
expectedResult.setTransformers(ImmutableList.of(transformer)); expectedResult.setTransformers(ImmutableList.of(transformer));
ReflectionTestUtils.setField(transformRegistry, "engineConfig", ReflectionTestUtils.setField(transformRegistry, "engineConfig",
new ClassPathResource("engine_config_incomplete.json")); new ClassPathResource("engine_config_incomplete.json"));
String response = mockMvc String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG)) .perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE)) .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@@ -553,13 +552,13 @@ public abstract class AbstractTransformerControllerTest
expectedResult.setTransformers(ImmutableList.of(transformer)); expectedResult.setTransformers(ImmutableList.of(transformer));
ReflectionTestUtils.setField(transformRegistry, "engineConfig", ReflectionTestUtils.setField(transformRegistry, "engineConfig",
new ClassPathResource("engine_config_no_transform_options.json")); new ClassPathResource("engine_config_no_transform_options.json"));
String response = mockMvc String response = mockMvc
.perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG)) .perform(MockMvcRequestBuilders.get(ENDPOINT_TRANSFORM_CONFIG))
.andExpect(status().is(OK.value())) .andExpect(status().is(OK.value()))
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE)) .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class); TransformConfig transformConfig = objectMapper.readValue(response, TransformConfig.class);
@@ -572,16 +571,16 @@ public abstract class AbstractTransformerControllerTest
TransformConfig expectedResult = new TransformConfig(); TransformConfig expectedResult = new TransformConfig();
Set<TransformOption> transformOptionGroup = ImmutableSet.of( Set<TransformOption> transformOptionGroup = ImmutableSet.of(
new TransformOptionValue(false, "cropGravity")); new TransformOptionValue(false, "cropGravity"));
Set<TransformOption> transformOptions = ImmutableSet.of( Set<TransformOption> transformOptions = ImmutableSet.of(
new TransformOptionValue(false, "page"), new TransformOptionValue(false, "page"),
new TransformOptionValue(false, "width"), new TransformOptionValue(false, "width"),
new TransformOptionGroup(false, transformOptionGroup)); new TransformOptionGroup(false, transformOptionGroup));
Map<String, Set<TransformOption>> transformOptionsMap = ImmutableMap.of("engineXOptions", Map<String, Set<TransformOption>> transformOptionsMap = ImmutableMap.of("engineXOptions",
transformOptions); transformOptions);
Transformer transformer = buildTransformer("application/pdf", "image/png", "engineXOptions", Transformer transformer = buildTransformer("application/pdf", "image/png", "engineXOptions",
"engineX"); "engineX");
List<Transformer> transformers = ImmutableList.of(transformer); List<Transformer> transformers = ImmutableList.of(transformer);
expectedResult.setTransformOptions(transformOptionsMap); expectedResult.setTransformOptions(transformOptionsMap);
@@ -591,7 +590,7 @@ public abstract class AbstractTransformerControllerTest
} }
private Transformer buildTransformer(String sourceMediaType, String targetMediaType, private Transformer buildTransformer(String sourceMediaType, String targetMediaType,
String transformOptions, String transformerName) String transformOptions, String transformerName)
{ {
Transformer transformer = buildTransformer(sourceMediaType, targetMediaType); Transformer transformer = buildTransformer(sourceMediaType, targetMediaType);
transformer.setTransformerName(transformerName); transformer.setTransformerName(transformerName);
@@ -603,10 +602,10 @@ public abstract class AbstractTransformerControllerTest
private Transformer buildTransformer(String sourceMediaType, String targetMediaType) private Transformer buildTransformer(String sourceMediaType, String targetMediaType)
{ {
Set<SupportedSourceAndTarget> supportedSourceAndTargetList = ImmutableSet.of( Set<SupportedSourceAndTarget> supportedSourceAndTargetList = ImmutableSet.of(
SupportedSourceAndTarget.builder() SupportedSourceAndTarget.builder()
.withSourceMediaType(sourceMediaType) .withSourceMediaType(sourceMediaType)
.withTargetMediaType(targetMediaType) .withTargetMediaType(targetMediaType)
.build()); .build());
Transformer transformer = new Transformer(); Transformer transformer = new Transformer();
transformer.setSupportedSourceAndTargetList(supportedSourceAndTargetList); transformer.setSupportedSourceAndTargetList(supportedSourceAndTargetList);

View File

@@ -8,9 +8,11 @@
package org.alfresco.transformer; package org.alfresco.transformer;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import java.util.Map; import java.util.Map;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@@ -31,23 +33,23 @@ import org.springframework.web.client.RestTemplate;
public class EngineClient public class EngineClient
{ {
private static final RestTemplate REST_TEMPLATE = new RestTemplate(); private static final RestTemplate REST_TEMPLATE = new RestTemplate();
public static ResponseEntity<Resource> sendTRequest( public static ResponseEntity<Resource> sendTRequest(
final String engineUrl, final String sourceFile, final String engineUrl, final String sourceFile,
final String sourceMimetype, final String targetMimetype, final String targetExtension) final String sourceMimetype, final String targetMimetype, final String targetExtension)
{ {
return sendTRequest(engineUrl, sourceFile, sourceMimetype, targetMimetype, targetExtension, return sendTRequest(engineUrl, sourceFile, sourceMimetype, targetMimetype, targetExtension,
emptyMap()); emptyMap());
} }
public static ResponseEntity<Resource> sendTRequest( public static ResponseEntity<Resource> sendTRequest(
final String engineUrl, final String sourceFile, final String engineUrl, final String sourceFile,
final String sourceMimetype, final String targetMimetype, final String targetExtension, final String sourceMimetype, final String targetMimetype, final String targetExtension,
final Map<String, String> transformOptions) final Map<String, String> transformOptions)
{ {
final HttpHeaders headers = new HttpHeaders(); final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);
//headers.setAccept(ImmutableList.of(MULTIPART_FORM_DATA)); // headers.setAccept(ImmutableList.of(MULTIPART_FORM_DATA));
final MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); final MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", new ClassPathResource(sourceFile)); body.add("file", new ClassPathResource(sourceFile));

View File

@@ -40,10 +40,6 @@ import jakarta.jms.Destination;
import jakarta.jms.JMSException; import jakarta.jms.JMSException;
import jakarta.jms.Message; import jakarta.jms.Message;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transformer.messaging.TransformMessageConverter;
import org.alfresco.transformer.messaging.TransformReplySender;
import org.apache.activemq.command.ActiveMQObjectMessage; import org.apache.activemq.command.ActiveMQObjectMessage;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@@ -55,6 +51,11 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transformer.messaging.TransformMessageConverter;
import org.alfresco.transformer.messaging.TransformReplySender;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
*/ */
@@ -106,12 +107,12 @@ public class QueueTransformServiceTest
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformReply reply = TransformReply TransformReply reply = TransformReply
.builder() .builder()
.withStatus(INTERNAL_SERVER_ERROR.value()) .withStatus(INTERNAL_SERVER_ERROR.value())
.withErrorDetails( .withErrorDetails(
"JMS exception during T-Request deserialization of message with correlationID " "JMS exception during T-Request deserialization of message with correlationID "
+ msg.getCorrelationId() + ": null") + msg.getCorrelationId() + ": null")
.build(); .build();
doReturn(null).when(transformMessageConverter).fromMessage(msg); doReturn(null).when(transformMessageConverter).fromMessage(msg);
@@ -125,7 +126,7 @@ public class QueueTransformServiceTest
@Test @Test
public void testConvertMessageThrowsMessageConversionExceptionThenReplyWithBadRequest() public void testConvertMessageThrowsMessageConversionExceptionThenReplyWithBadRequest()
throws JMSException throws JMSException
{ {
ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); ActiveMQObjectMessage msg = new ActiveMQObjectMessage();
msg.setCorrelationId("1234"); msg.setCorrelationId("1234");
@@ -133,12 +134,12 @@ public class QueueTransformServiceTest
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformReply reply = TransformReply TransformReply reply = TransformReply
.builder() .builder()
.withStatus(BAD_REQUEST.value()) .withStatus(BAD_REQUEST.value())
.withErrorDetails( .withErrorDetails(
"Message conversion exception during T-Request deserialization of message with correlationID" "Message conversion exception during T-Request deserialization of message with correlationID"
+ msg.getCorrelationId() + ": null") + msg.getCorrelationId() + ": null")
.build(); .build();
doThrow(MessageConversionException.class).when(transformMessageConverter).fromMessage(msg); doThrow(MessageConversionException.class).when(transformMessageConverter).fromMessage(msg);
@@ -152,7 +153,7 @@ public class QueueTransformServiceTest
@Test @Test
public void testConvertMessageThrowsJMSExceptionThenReplyWithInternalServerError() public void testConvertMessageThrowsJMSExceptionThenReplyWithInternalServerError()
throws JMSException throws JMSException
{ {
ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); ActiveMQObjectMessage msg = new ActiveMQObjectMessage();
msg.setCorrelationId("1234"); msg.setCorrelationId("1234");
@@ -160,12 +161,12 @@ public class QueueTransformServiceTest
msg.setJMSReplyTo(destination); msg.setJMSReplyTo(destination);
TransformReply reply = TransformReply TransformReply reply = TransformReply
.builder() .builder()
.withStatus(INTERNAL_SERVER_ERROR.value()) .withStatus(INTERNAL_SERVER_ERROR.value())
.withErrorDetails( .withErrorDetails(
"JMSException during T-Request deserialization of message with correlationID " + "JMSException during T-Request deserialization of message with correlationID " +
msg.getCorrelationId() + ": null") msg.getCorrelationId() + ": null")
.build(); .build();
doThrow(JMSException.class).when(transformMessageConverter).fromMessage(msg); doThrow(JMSException.class).when(transformMessageConverter).fromMessage(msg);
@@ -186,13 +187,13 @@ public class QueueTransformServiceTest
TransformRequest request = new TransformRequest(); TransformRequest request = new TransformRequest();
TransformReply reply = TransformReply TransformReply reply = TransformReply
.builder() .builder()
.withStatus(CREATED.value()) .withStatus(CREATED.value())
.build(); .build();
doReturn(request).when(transformMessageConverter).fromMessage(msg); doReturn(request).when(transformMessageConverter).fromMessage(msg);
doReturn(new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()))) doReturn(new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus())))
.when(transformController).transform(request, null); .when(transformController).transform(request, null);
queueTransformService.receive(msg); queueTransformService.receive(msg);
@@ -217,7 +218,7 @@ public class QueueTransformServiceTest
@Test @Test
public void testWhenExceptionOnCorrelationIdIsThrownThenContinueFlowWithNullCorrelationId() public void testWhenExceptionOnCorrelationIdIsThrownThenContinueFlowWithNullCorrelationId()
throws JMSException throws JMSException
{ {
Message msg = mock(Message.class); Message msg = mock(Message.class);
Destination destination = mock(Destination.class); Destination destination = mock(Destination.class);
@@ -227,13 +228,13 @@ public class QueueTransformServiceTest
TransformRequest request = new TransformRequest(); TransformRequest request = new TransformRequest();
TransformReply reply = TransformReply TransformReply reply = TransformReply
.builder() .builder()
.withStatus(CREATED.value()) .withStatus(CREATED.value())
.build(); .build();
doReturn(request).when(transformMessageConverter).fromMessage(msg); doReturn(request).when(transformMessageConverter).fromMessage(msg);
doReturn(new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()))) doReturn(new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus())))
.when(transformController).transform(request, null); .when(transformController).transform(request, null);
queueTransformService.receive(msg); queueTransformService.receive(msg);

View File

@@ -31,7 +31,7 @@ import java.util.Objects;
/** /**
* @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine. * @deprecated will be removed in a future release. Replaced by alfresco-base-t-engine.
* *
* Source & Target media type pair * Source & Target media type pair
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -50,11 +50,13 @@ public class SourceTarget
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (o == null || getClass() != o.getClass())
return false;
SourceTarget that = (SourceTarget) o; SourceTarget that = (SourceTarget) o;
return Objects.equals(source, that.source) && return Objects.equals(source, that.source) &&
Objects.equals(target, that.target); Objects.equals(target, that.target);
} }
@Override @Override

View File

@@ -39,7 +39,7 @@ public class TestFileInfo
private final boolean exactMimeType; private final boolean exactMimeType;
public TestFileInfo(final String mimeType, final String extension, final String path, public TestFileInfo(final String mimeType, final String extension, final String path,
final boolean exactMimeType) final boolean exactMimeType)
{ {
this.mimeType = mimeType; this.mimeType = mimeType;
this.extension = extension; this.extension = extension;
@@ -68,13 +68,13 @@ public class TestFileInfo
} }
public static TestFileInfo testFile(final String mimeType, final String extension, public static TestFileInfo testFile(final String mimeType, final String extension,
final String path, final boolean exactMimeType) final String path, final boolean exactMimeType)
{ {
return new TestFileInfo(mimeType, extension, path, exactMimeType); return new TestFileInfo(mimeType, extension, path, exactMimeType);
} }
public static TestFileInfo testFile(final String mimeType, final String extension, public static TestFileInfo testFile(final String mimeType, final String extension,
final String path) final String path)
{ {
return new TestFileInfo(mimeType, extension, path, false); return new TestFileInfo(mimeType, extension, path, false);
} }

View File

@@ -26,19 +26,20 @@
*/ */
package org.alfresco.transform.coreaio; package org.alfresco.transform.coreaio;
import org.alfresco.transform.base.TransformEngine; import static org.alfresco.transform.base.logging.StandardMessages.COMMUNITY_LICENCE;
import org.alfresco.transform.base.probes.ProbeTransform; import static org.alfresco.transform.common.Mimetype.MIMETYPE_PDF;
import org.alfresco.transform.config.TransformConfig; import static org.alfresco.transform.common.Mimetype.MIMETYPE_TEXT_PLAIN;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.alfresco.transform.base.logging.StandardMessages.COMMUNITY_LICENCE; import org.springframework.beans.factory.annotation.Autowired;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_PDF; import org.springframework.stereotype.Component;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_TEXT_PLAIN;
import org.alfresco.transform.base.TransformEngine;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.config.TransformConfig;
@Component @Component
public class AIOTransformEngine implements TransformEngine public class AIOTransformEngine implements TransformEngine
@@ -62,7 +63,7 @@ public class AIOTransformEngine implements TransformEngine
message = transformEngines.stream() message = transformEngines.stream()
.filter(transformEngine -> transformEngine != this) .filter(transformEngine -> transformEngine != this)
.map(transformEngine -> transformEngine.getStartupMessage()) .map(transformEngine -> transformEngine.getStartupMessage())
.collect( Collectors.joining("\n")); .collect(Collectors.joining("\n"));
message = message.replace(COMMUNITY_LICENCE, ""); message = message.replace(COMMUNITY_LICENCE, "");
} }
return COMMUNITY_LICENCE + message; return COMMUNITY_LICENCE + message;

View File

@@ -1,33 +1,32 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.imagemagick.ImageMagickTransformationIT; import org.alfresco.transform.imagemagick.ImageMagickTransformationIT;
public class AIOImageMagickIT extends ImageMagickTransformationIT public class AIOImageMagickIT extends ImageMagickTransformationIT
{ {}
}

View File

@@ -1,36 +1,35 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.imagemagick.ImageMagickTest; import org.alfresco.transform.imagemagick.ImageMagickTest;
/** /**
* Test ImageMagick functionality in All-In-One. * Test ImageMagick functionality in All-In-One.
*/ */
public class AIOImageMagickTest extends ImageMagickTest public class AIOImageMagickTest extends ImageMagickTest
{ {}
}

View File

@@ -1,36 +1,35 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.libreoffice.LibreOfficeTest; import org.alfresco.transform.libreoffice.LibreOfficeTest;
/** /**
* Test LibreOffice functionality in All-In-One. * Test LibreOffice functionality in All-In-One.
*/ */
public class AIOLibreOfficeTest extends LibreOfficeTest public class AIOLibreOfficeTest extends LibreOfficeTest
{ {}
}

View File

@@ -1,33 +1,32 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.libreoffice.LibreOfficeTransformationIT; import org.alfresco.transform.libreoffice.LibreOfficeTransformationIT;
public class AIOLibreOfficeTransformationIT extends LibreOfficeTransformationIT public class AIOLibreOfficeTransformationIT extends LibreOfficeTransformationIT
{ {}
}

View File

@@ -29,9 +29,11 @@ package org.alfresco.transform.aio;
import org.alfresco.transform.base.LivenessReadinessProbeTest; import org.alfresco.transform.base.LivenessReadinessProbeTest;
public class AIOLivenessReadinessProbeIT extends LivenessReadinessProbeTest { public class AIOLivenessReadinessProbeIT extends LivenessReadinessProbeTest
{
@Override @Override
protected LivenessReadinessProbeTest.ImagesForTests getImageForTest() { protected LivenessReadinessProbeTest.ImagesForTests getImageForTest()
{
return new ImagesForTests("alfresco-transform-core-aio", "text/plain", "text/plain", "original.txt"); return new ImagesForTests("alfresco-transform-core-aio", "text/plain", "text/plain", "original.txt");
} }
} }

View File

@@ -29,5 +29,4 @@ package org.alfresco.transform.aio;
import org.alfresco.transform.misc.MiscMetadataExtractsIT; import org.alfresco.transform.misc.MiscMetadataExtractsIT;
public class AIOMiscMetadataExtractsIT extends MiscMetadataExtractsIT public class AIOMiscMetadataExtractsIT extends MiscMetadataExtractsIT
{ {}
}

View File

@@ -1,36 +1,35 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.misc.MiscTest; import org.alfresco.transform.misc.MiscTest;
/** /**
* Test Misc functionality in All-In-One. * Test Misc functionality in All-In-One.
*/ */
public class AIOMiscTest extends MiscTest public class AIOMiscTest extends MiscTest
{ {}
}

View File

@@ -1,33 +1,32 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.misc.MiscTransformsIT; import org.alfresco.transform.misc.MiscTransformsIT;
public class AIOMiscTransformsIT extends MiscTransformsIT public class AIOMiscTransformsIT extends MiscTransformsIT
{ {}
}

View File

@@ -1,33 +1,32 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.pdfrenderer.PdfRendererTransformationIT; import org.alfresco.transform.pdfrenderer.PdfRendererTransformationIT;
public class AIOPdfRendererIT extends PdfRendererTransformationIT public class AIOPdfRendererIT extends PdfRendererTransformationIT
{ {}
}

View File

@@ -1,36 +1,35 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.pdfrenderer.PdfRendererTest; import org.alfresco.transform.pdfrenderer.PdfRendererTest;
/** /**
* Test PdfRenderer functionality in All-In-One. * Test PdfRenderer functionality in All-In-One.
*/ */
public class AIOPdfRendererTest extends PdfRendererTest public class AIOPdfRendererTest extends PdfRendererTest
{ {}
}

View File

@@ -1,59 +1,58 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.base.messaging.AbstractQueueIT; import static org.alfresco.transform.common.Mimetype.MIMETYPE_HTML;
import org.alfresco.transform.client.model.TransformRequest; import static org.alfresco.transform.common.Mimetype.MIMETYPE_TEXT_PLAIN;
import java.util.UUID; import java.util.UUID;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_HTML; import org.alfresco.transform.base.messaging.AbstractQueueIT;
import static org.alfresco.transform.common.Mimetype.MIMETYPE_TEXT_PLAIN; import org.alfresco.transform.client.model.TransformRequest;
/** /**
* @author David Edwards * @author David Edwards created on 21/04/2020
* created on 21/04/2020 */
*/ public class AIOQueueIT extends AbstractQueueIT
public class AIOQueueIT extends AbstractQueueIT {
{ @Override
@Override protected TransformRequest buildRequest()
protected TransformRequest buildRequest() {
{ return TransformRequest
return TransformRequest .builder()
.builder() .withRequestId(UUID.randomUUID().toString())
.withRequestId(UUID.randomUUID().toString()) .withSourceMediaType(MIMETYPE_HTML)
.withSourceMediaType(MIMETYPE_HTML) .withTargetMediaType(MIMETYPE_TEXT_PLAIN)
.withTargetMediaType(MIMETYPE_TEXT_PLAIN) .withTargetExtension("txt")
.withTargetExtension("txt") .withSchema(1)
.withSchema(1) .withClientData("ACS")
.withClientData("ACS") .withSourceReference(UUID.randomUUID().toString())
.withSourceReference(UUID.randomUUID().toString()) .withSourceSize(32L)
.withSourceSize(32L) .withInternalContextForTransformEngineTests()
.withInternalContextForTransformEngineTests() .build();
.build(); }
} }
}

View File

@@ -29,5 +29,4 @@ package org.alfresco.transform.aio;
import org.alfresco.transform.tika.TikaMetadataExtractsIT; import org.alfresco.transform.tika.TikaMetadataExtractsIT;
public class AIOTikaMetadataExtractsIT extends TikaMetadataExtractsIT public class AIOTikaMetadataExtractsIT extends TikaMetadataExtractsIT
{ {}
}

View File

@@ -1,33 +1,32 @@
/* /*
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* - * -
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* - * -
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* - * -
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.transform.aio; package org.alfresco.transform.aio;
import org.alfresco.transform.tika.TikaTransformationIT; import org.alfresco.transform.tika.TikaTransformationIT;
public class AIOTikaTransformationIT extends TikaTransformationIT public class AIOTikaTransformationIT extends TikaTransformationIT
{ {}
}

View File

@@ -27,8 +27,6 @@
package org.alfresco.transform.base; package org.alfresco.transform.base;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;

View File

@@ -26,16 +26,14 @@
*/ */
package org.alfresco.transform.base; package org.alfresco.transform.base;
import org.alfresco.transform.config.TransformConfig;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import org.alfresco.transform.config.TransformConfig;
/** /**
* Interface to be implemented by transform specific code. The {@code transformerName} should match the transformerName * Interface to be implemented by transform specific code. The {@code transformerName} should match the transformerName in the {@link TransformConfig} returned by the {@link TransformEngine}. So that it is automatically picked up, it must exist in a package under {@code org.alfresco.transform} and have the Spring {@code @Component} annotation.
* in the {@link TransformConfig} returned by the {@link TransformEngine}. So that it is automatically picked up, it
* must exist in a package under {@code org.alfresco.transform} and have the Spring {@code @Component} annotation.
* *
* Implementations may also use the {@link TransformManager} if they wish to interact with the base t-engine. * Implementations may also use the {@link TransformManager} if they wish to interact with the base t-engine.
*/ */
@@ -44,6 +42,6 @@ public interface CustomTransformer
String getTransformerName(); String getTransformerName();
void transform(String sourceMimetype, InputStream inputStream, void transform(String sourceMimetype, InputStream inputStream,
String targetMimetype, OutputStream outputStream, String targetMimetype, OutputStream outputStream,
Map<String, String> transformOptions, TransformManager transformManager) throws Exception; Map<String, String> transformOptions, TransformManager transformManager) throws Exception;
} }

View File

@@ -26,15 +26,42 @@
*/ */
package org.alfresco.transform.base; package org.alfresco.transform.base;
import org.alfresco.transform.base.logging.LogEntry; import static java.text.MessageFormat.format;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.base.registry.TransformRegistry; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.base.transform.TransformHandler; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import org.alfresco.transform.client.model.TransformReply; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException; import static org.alfresco.transform.base.html.OptionsHelper.getOptionNames;
import org.alfresco.transform.config.TransformConfig; import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION;
import org.alfresco.transform.registry.TransformServiceRegistry; import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION_DEFAULT;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_ERROR;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_LIVE;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_LOG;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_READY;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_ROOT;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TEST;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_VERSION;
import static org.alfresco.transform.common.RequestParamMap.FILE;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE;
import static org.alfresco.transform.config.CoreVersionDecorator.setOrClearCoreVersion;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import jakarta.annotation.PostConstruct;
import jakarta.jms.Destination;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -57,39 +84,14 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import jakarta.annotation.PostConstruct; import org.alfresco.transform.base.logging.LogEntry;
import jakarta.jms.Destination; import org.alfresco.transform.base.probes.ProbeTransform;
import jakarta.servlet.http.HttpServletRequest; import org.alfresco.transform.base.registry.TransformRegistry;
import jakarta.servlet.http.HttpServletResponse; import org.alfresco.transform.base.transform.TransformHandler;
import java.io.IOException; import org.alfresco.transform.client.model.TransformReply;
import java.util.Arrays; import org.alfresco.transform.client.model.TransformRequest;
import java.util.Collection; import org.alfresco.transform.config.TransformConfig;
import java.util.Comparator; import org.alfresco.transform.exceptions.TransformException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import static java.text.MessageFormat.format;
import static org.alfresco.transform.base.html.OptionsHelper.getOptionNames;
import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION;
import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION_DEFAULT;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_ERROR;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_LIVE;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_LOG;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_READY;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_ROOT;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TEST;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM_CONFIG;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_VERSION;
import static org.alfresco.transform.common.RequestParamMap.FILE;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE;
import static org.alfresco.transform.config.CoreVersionDecorator.setOrClearCoreVersion;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
/** /**
* Provides the main endpoints into the t-engine. * Provides the main endpoints into the t-engine.
@@ -126,9 +128,9 @@ public class TransformController
transformEngine = getTransformEngine(); transformEngine = getTransformEngine();
logger.info("TransformEngine: {}", transformEngine.getTransformEngineName()); logger.info("TransformEngine: {}", transformEngine.getTransformEngineName());
transformEngines.stream() transformEngines.stream()
.filter(transformEngineFromStream -> transformEngineFromStream != transformEngine) .filter(transformEngineFromStream -> transformEngineFromStream != transformEngine)
.sorted(Comparator.comparing(TransformEngine::getTransformEngineName)) .sorted(Comparator.comparing(TransformEngine::getTransformEngineName))
.map(sortedTransformEngine -> " "+sortedTransformEngine.getTransformEngineName()).forEach(logger::info); .map(sortedTransformEngine -> " " + sortedTransformEngine.getTransformEngineName()).forEach(logger::info);
} }
} }
@@ -138,9 +140,9 @@ public class TransformController
// CustomTransform code from many t-engines into a single t-engine. In this case, there should be a wrapper // CustomTransform code from many t-engines into a single t-engine. In this case, there should be a wrapper
// TransformEngine (it has no TransformConfig of its own). // TransformEngine (it has no TransformConfig of its own).
return transformEngines.stream() return transformEngines.stream()
.filter(transformEngineFromStream -> transformEngineFromStream.getTransformConfig() == null) .filter(transformEngineFromStream -> transformEngineFromStream.getTransformConfig() == null)
.findFirst() .findFirst()
.orElse(transformEngines.get(0)); .orElse(transformEngines.get(0));
} }
@EventListener(ApplicationReadyEvent.class) @EventListener(ApplicationReadyEvent.class)
@@ -285,8 +287,8 @@ public class TransformController
@PostMapping(value = ENDPOINT_TRANSFORM, produces = APPLICATION_JSON_VALUE) @PostMapping(value = ENDPOINT_TRANSFORM, produces = APPLICATION_JSON_VALUE)
@ResponseBody @ResponseBody
public ResponseEntity<TransformReply> transform(@RequestBody TransformRequest request, public ResponseEntity<TransformReply> transform(@RequestBody TransformRequest request,
@RequestParam(value = "timeout", required = false) Long timeout, @RequestParam(value = "timeout", required = false) Long timeout,
@RequestParam(value = "replyToQueue", required = false) Destination replyToQueue) @RequestParam(value = "replyToQueue", required = false) Destination replyToQueue)
{ {
TransformReply reply = transformHandler.handleMessageRequest(request, timeout, replyToQueue, getProbeTransform()); TransformReply reply = transformHandler.handleMessageRequest(request, timeout, replyToQueue, getProbeTransform());
return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus())); return new ResponseEntity<>(reply, HttpStatus.valueOf(reply.getStatus()));
@@ -317,8 +319,7 @@ public class TransformController
Map<String, String> requestParameters = new HashMap<>(); Map<String, String> requestParameters = new HashMap<>();
sourceMimetype = overrideMimetypeFromExtension(origRequestParameters, SOURCE_MIMETYPE, sourceMimetype); sourceMimetype = overrideMimetypeFromExtension(origRequestParameters, SOURCE_MIMETYPE, sourceMimetype);
targetMimetype = overrideMimetypeFromExtension(origRequestParameters, TARGET_MIMETYPE, targetMimetype); targetMimetype = overrideMimetypeFromExtension(origRequestParameters, TARGET_MIMETYPE, targetMimetype);
origRequestParameters.forEach((name, value) -> origRequestParameters.forEach((name, value) -> {
{
if (!name.startsWith("value")) if (!name.startsWith("value"))
{ {
if (name.startsWith("name")) if (name.startsWith("name"))
@@ -338,7 +339,7 @@ public class TransformController
private String overrideMimetypeFromExtension(Map<String, String> origRequestParameters, String name, String value) private String overrideMimetypeFromExtension(Map<String, String> origRequestParameters, String name, String value)
{ {
String override = origRequestParameters.remove("_"+ name); String override = origRequestParameters.remove("_" + name);
if (override != null && !override.isBlank()) if (override != null && !override.isBlank())
{ {
value = override; value = override;
@@ -349,7 +350,7 @@ public class TransformController
@ExceptionHandler(MissingServletRequestParameterException.class) @ExceptionHandler(MissingServletRequestParameterException.class)
public void handleMissingParams(HttpServletResponse response, MissingServletRequestParameterException e) public void handleMissingParams(HttpServletResponse response, MissingServletRequestParameterException e)
throws IOException throws IOException
{ {
final String message = format("Request parameter ''{0}'' is missing", e.getParameterName()); final String message = format("Request parameter ''{0}'' is missing", e.getParameterName());
logger.error(message, e); logger.error(message, e);

View File

@@ -27,14 +27,11 @@
package org.alfresco.transform.base; package org.alfresco.transform.base;
import org.alfresco.transform.base.probes.ProbeTransform; import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.config.reader.TransformConfigResourceReader;
import org.alfresco.transform.config.TransformConfig; import org.alfresco.transform.config.TransformConfig;
import org.alfresco.transform.config.reader.TransformConfigResourceReader;
/** /**
* Interface to be implemented by transform specific code. Provides information about the t-engine as a whole. * Interface to be implemented by transform specific code. Provides information about the t-engine as a whole. Also see {@link CustomTransformer} which provides the code that performs transformation. There may be several in a single t-engine. So that it is automatically picked up, it must exist in a package under {@code org.alfresco.transform} and have the Spring {@code @Component} annotation.
* Also see {@link CustomTransformer} which provides the code that performs transformation. There may be several
* in a single t-engine. So that it is automatically picked up, it must exist in a package under
* {@code org.alfresco.transform} and have the Spring {@code @Component} annotation.
*/ */
public interface TransformEngine public interface TransformEngine
{ {
@@ -49,10 +46,7 @@ public interface TransformEngine
String getStartupMessage(); String getStartupMessage();
/** /**
* @return a definition of what the t-engine supports. Normally read from a json Resource on the classpath using a * @return a definition of what the t-engine supports. Normally read from a json Resource on the classpath using a {@link TransformConfigResourceReader}. To combine to code from multiple t-engine into a single t-engine include all the TransformEngines and CustomTransform implementations, plus a wrapper TransformEngine for the others. The wrapper should return {@code null} from this method.
* {@link TransformConfigResourceReader}. To combine to code from multiple t-engine into a single t-engine
* include all the TransformEngines and CustomTransform implementations, plus a wrapper TransformEngine for the
* others. The wrapper should return {@code null} from this method.
*/ */
TransformConfig getTransformConfig(); TransformConfig getTransformConfig();

View File

@@ -26,14 +26,14 @@
*/ */
package org.alfresco.transform.base; package org.alfresco.transform.base;
import org.alfresco.transform.exceptions.TransformException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import org.alfresco.transform.exceptions.TransformException;
/** /**
* Allows {@link CustomTransformer} implementations to interact with the base t-engine. * Allows {@link CustomTransformer} implementations to interact with the base t-engine.
*/ */
@@ -45,42 +45,33 @@ public interface TransformManager
String getRequestId(); String getRequestId();
/** /**
* Allows a {@link CustomTransformer} to use a local source {@code File} rather than the supplied {@code InputStream}. * Allows a {@link CustomTransformer} to use a local source {@code File} rather than the supplied {@code InputStream}. The file will be deleted once the request is completed. To avoid creating extra files, if a File has already been created by the base t-engine, it is returned. If possible this method should be avoided as it is better not to leave content on disk.
* The file will be deleted once the request is completed. To avoid creating extra files, if a File has already *
* been created by the base t-engine, it is returned. * @throws IllegalStateException
* If possible this method should be avoided as it is better not to leave content on disk. * if this method has already been called.
* @throws IllegalStateException if this method has already been called.
*/ */
File createSourceFile() throws IllegalStateException; File createSourceFile() throws IllegalStateException;
/** /**
* Allows a {@link CustomTransformer} to use a local target {@code File} rather than the supplied {@code OutputStream}. * Allows a {@link CustomTransformer} to use a local target {@code File} rather than the supplied {@code OutputStream}. The file will be deleted once the request is completed. To avoid creating extra files, if a File has already been created by the base t-engine, it is returned. If possible this method should be avoided as it is better not to leave content on disk.
* The file will be deleted once the request is completed. To avoid creating extra files, if a File has already *
* been created by the base t-engine, it is returned. * @throws IllegalStateException
* If possible this method should be avoided as it is better not to leave content on disk. * if this method has already been called. A call to {@link #respondWithFragment(Integer, boolean)} allows the method to be called again.
* @throws IllegalStateException if this method has already been called. A call to {@link #respondWithFragment(Integer, boolean)}
* allows the method to be called again.
*/ */
File createTargetFile() throws IllegalStateException; File createTargetFile() throws IllegalStateException;
/** /**
* Allows a single transform request to have multiple transform responses. For example, images from a video at * Allows a single transform request to have multiple transform responses. For example, images from a video at different time offsets or different pages of a document. Following a call to this method a transform response is made with the data sent to the current {@code OutputStream}. If this method has been called, there will not be another response when {@link CustomTransformer#transform(String, InputStream, String, OutputStream, Map, TransformManager)} returns and any data written to the final {@code OutputStream} will be ignored.
* different time offsets or different pages of a document. Following a call to this method a transform response is *
* made with the data sent to the current {@code OutputStream}. If this method has been called, there will not be * @param index
* another response when {@link CustomTransformer#transform(String, InputStream, String, OutputStream, Map, * returned with the response, so that the fragment may be distinguished from other responses. Renditions use the index as an offset into elements. A {@code null} value indicates that there is no more output and any data sent to the current {@code outputStream} will be ignored.
* TransformManager)} returns and any data written to the final {@code OutputStream} will be ignored. * @param finished
* @param index returned with the response, so that the fragment may be distinguished from other responses. * indicates this is the final fragment. {@code False} indicates that it is expected there will be more fragments. There need not be a call with this parameter set to {@code true}.
* Renditions use the index as an offset into elements. A {@code null} value indicates that there * @return a new {@code OutputStream} for the next fragment. A {@code null} will be returned if {@code index} was {@code null} or {@code finished} was {@code true}.
* is no more output and any data sent to the current {@code outputStream} will be ignored. * @throws TransformException
* @param finished indicates this is the final fragment. {@code False} indicates that it is expected there will be * if a synchronous (http) request has been made as this only works with requests on queues, or the first call to this method indicated there was no output, or another call is made after it has been indicated that there should be no more fragments.
* more fragments. There need not be a call with this parameter set to {@code true}. * @throws IOException
* @return a new {@code OutputStream} for the next fragment. A {@code null} will be returned if {@code index} was * if there was a problem sending the response.
* {@code null} or {@code finished} was {@code true}.
* @throws TransformException if a synchronous (http) request has been made as this only works with requests
* on queues, or the first call to this method indicated there was no output, or
* another call is made after it has been indicated that there should be no more
* fragments.
* @throws IOException if there was a problem sending the response.
*/ */
// This works because all the state is in the TransformResponse and the t-router will just see each response as // This works because all the state is in the TransformResponse and the t-router will just see each response as
// something to either return to the client or pass to the next stage in a pipeline. We might be able to enhance // something to either return to the client or pass to the next stage in a pipeline. We might be able to enhance

View File

@@ -26,7 +26,15 @@
*/ */
package org.alfresco.transform.base.config; package org.alfresco.transform.base.config;
import org.alfresco.transform.base.WebClientBuilderAdjuster; import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -52,17 +60,11 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.reactive.JettyClientHttpConnector; import org.springframework.http.client.reactive.JettyClientHttpConnector;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import java.io.IOException; import org.alfresco.transform.base.WebClientBuilderAdjuster;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
@Configuration @Configuration
public class MTLSConfig { public class MTLSConfig
{
@Value("${client.ssl.key-store:#{null}}") @Value("${client.ssl.key-store:#{null}}")
private Resource keyStoreResource; private Resource keyStoreResource;
@@ -89,7 +91,7 @@ public class MTLSConfig {
public WebClientBuilderAdjuster webClientBuilderAdjuster(SslContextFactory.Client sslContextFactory) public WebClientBuilderAdjuster webClientBuilderAdjuster(SslContextFactory.Client sslContextFactory)
{ {
return builder -> { return builder -> {
if(isTlsOrMtlsConfigured()) if (isTlsOrMtlsConfigured())
{ {
ClientConnector clientConnector = new ClientConnector(); ClientConnector clientConnector = new ClientConnector();
clientConnector.setSslContextFactory(sslContextFactory); clientConnector.setSslContextFactory(sslContextFactory);
@@ -103,23 +105,26 @@ public class MTLSConfig {
@Bean @Bean
public RestTemplate restTemplate(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException public RestTemplate restTemplate(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException
{ {
if(isTlsOrMtlsConfigured()) if (isTlsOrMtlsConfigured())
{ {
return createRestTemplateWithSslContext(sslContextBuilder); return createRestTemplateWithSslContext(sslContextBuilder);
} else { }
else
{
return new RestTemplate(); return new RestTemplate();
} }
} }
@Bean @Bean
public SSLContextBuilder sslContextBuilder() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException { public SSLContextBuilder sslContextBuilder() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException
{
SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
if(isKeystoreConfigured()) if (isKeystoreConfigured())
{ {
KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword);
sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword);
} }
if(isTruststoreConfigured()) if (isTruststoreConfigured())
{ {
sslContextBuilder sslContextBuilder
.setKeyStoreType(trustStoreType) .setKeyStoreType(trustStoreType)
@@ -156,20 +161,20 @@ public class MTLSConfig {
return keyStoreResource != null; return keyStoreResource != null;
} }
private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException { private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException
final SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = {
SSLConnectionSocketFactoryBuilder.create() final SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(sslContextBuilder.build()) .setSslContext(sslContextBuilder.build())
.setTlsVersions(TLS.V_1_2, TLS.V_1_3); .setTlsVersions(TLS.V_1_2, TLS.V_1_3);
if (hostNameVerificationDisabled) { if (hostNameVerificationDisabled)
{
sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE); sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
} }
final SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryBuilder.build(); final SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryBuilder.build();
final Registry<ConnectionSocketFactory> sslSocketFactoryRegistry = final Registry<ConnectionSocketFactory> sslSocketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslConnectionSocketFactory)
.register("https", sslConnectionSocketFactory) .build();
.build();
final PoolingHttpClientConnectionManager sslConnectionManager = new PoolingHttpClientConnectionManager(sslSocketFactoryRegistry); final PoolingHttpClientConnectionManager sslConnectionManager = new PoolingHttpClientConnectionManager(sslSocketFactoryRegistry);

View File

@@ -26,10 +26,12 @@
*/ */
package org.alfresco.transform.base.config; package org.alfresco.transform.base.config;
import org.alfresco.transform.base.html.TransformInterceptor; import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import org.alfresco.transform.base.registry.TransformConfigSource; import static org.alfresco.transform.config.CoreFunction.standardizeCoreVersion;
import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transform.messages.TransformRequestValidator; import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
@@ -38,11 +40,10 @@ import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList; import org.alfresco.transform.base.html.TransformInterceptor;
import java.util.List; import org.alfresco.transform.base.registry.TransformConfigSource;
import org.alfresco.transform.common.TransformerDebug;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM; import org.alfresco.transform.messages.TransformRequestValidator;
import static org.alfresco.transform.config.CoreFunction.standardizeCoreVersion;
@Configuration @Configuration
@ComponentScan( @ComponentScan(

View File

@@ -26,10 +26,11 @@
*/ */
package org.alfresco.transform.base.executors; package org.alfresco.transform.base.executors;
import static org.alfresco.transform.base.executors.RuntimeExec.ExecutionResult;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.alfresco.transform.base.executors.RuntimeExec.ExecutionResult;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;

View File

@@ -26,12 +26,12 @@
*/ */
package org.alfresco.transform.base.executors; package org.alfresco.transform.base.executors;
import org.alfresco.transform.base.logging.LogEntry;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.transform.base.logging.LogEntry;
/** /**
* Basic interface for executing transformations via Shell commands * Basic interface for executing transformations via Shell commands
* *

View File

@@ -35,24 +35,17 @@ import java.util.Objects;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
* This class is used to tokenize strings used as parameters for {@link RuntimeExec} objects. * This class is used to tokenize strings used as parameters for {@link RuntimeExec} objects. Examples of such strings are as follows (ImageMagick-like parameters):
* Examples of such strings are as follows (ImageMagick-like parameters):
* <ul> * <ul>
* <li><tt>-font Helvetica -pointsize 50</tt></li> * <li><tt>-font Helvetica -pointsize 50</tt></li>
* <li><tt>-font Helvetica -pointsize 50 -draw "circle 100,100 150,150"</tt></li> * <li><tt>-font Helvetica -pointsize 50 -draw "circle 100,100 150,150"</tt></li>
* <li><tt>-font Helvetica -pointsize 50 -draw "gravity south fill black text 0,12 'CopyRight'"</tt></li> * <li><tt>-font Helvetica -pointsize 50 -draw "gravity south fill black text 0,12 'CopyRight'"</tt></li>
* </ul> * </ul>
* The first is the simple case which would be parsed into Strings as follows: * The first is the simple case which would be parsed into Strings as follows: <tt>"-font", "Helvetica", "-pointsize", "50"</tt>
* <tt>"-font", "Helvetica", "-pointsize", "50"</tt>
* <p/> * <p/>
* The second is more complex in that it includes a quoted parameter, which would be parsed as a single String: * The second is more complex in that it includes a quoted parameter, which would be parsed as a single String: <tt>"-font", "Helvetica", "-pointsize", "50", "circle 100,100 150,150"</tt> Note however that the quotation characters will be stripped from the token.
* <tt>"-font", "Helvetica", "-pointsize", "50", "circle 100,100 150,150"</tt>
* Note however that the quotation characters will be stripped from the token.
* <p/> * <p/>
* The third shows an example with embedded quotation marks, which would parse to: * The third shows an example with embedded quotation marks, which would parse to: <tt>"-font", "Helvetica", "-pointsize", "50", "gravity south fill black text 0,12 'CopyRight'"</tt> In this case, the embedded quotation marks (which must be different from those surrounding the parameter) are preserved in the extracted token.
* <tt>"-font", "Helvetica", "-pointsize", "50", "gravity south fill black text 0,12 'CopyRight'"</tt>
* In this case, the embedded quotation marks (which must be different from those surrounding the parameter)
* are preserved in the extracted token.
* <p/> * <p/>
* The class does not understand escaped quotes such as <tt>p1 p2 "a b c \"hello\" d" p4</tt> * The class does not understand escaped quotes such as <tt>p1 p2 "a b c \"hello\" d" p4</tt>
* *
@@ -77,15 +70,12 @@ public class ExecParameterTokenizer
} }
/** /**
* This method returns the tokens in a parameter string. * This method returns the tokens in a parameter string. Any tokens not contained within single or double quotes will be tokenized in the normal way i.e. by using whitespace separators and the standard StringTokenizer algorithm. Any tokens which are contained within single or double quotes will be returned as single String instances and will have their quote marks removed.
* Any tokens not contained within single or double quotes will be tokenized in the normal
* way i.e. by using whitespace separators and the standard StringTokenizer algorithm.
* Any tokens which are contained within single or double quotes will be returned as single
* String instances and will have their quote marks removed.
* <p/> * <p/>
* See above for examples. * See above for examples.
* *
* @throws NullPointerException if the string to be tokenized was null. * @throws NullPointerException
* if the string to be tokenized was null.
*/ */
public List<String> getAllTokens() public List<String> getAllTokens()
{ {
@@ -103,7 +93,7 @@ public class ExecParameterTokenizer
{ {
// Contains no quotes. // Contains no quotes.
for (StringTokenizer standardTokenizer = new StringTokenizer( for (StringTokenizer standardTokenizer = new StringTokenizer(
str); standardTokenizer.hasMoreTokens(); ) str); standardTokenizer.hasMoreTokens();)
{ {
tokens.add(standardTokenizer.nextToken()); tokens.add(standardTokenizer.nextToken());
} }
@@ -114,7 +104,7 @@ public class ExecParameterTokenizer
// So we need to identify the quoted regions within the string. // So we need to identify the quoted regions within the string.
List<Pair<Integer, Integer>> quotedRegions = new ArrayList<>(); List<Pair<Integer, Integer>> quotedRegions = new ArrayList<>();
for (Pair<Integer, Integer> next = identifyNextQuotedRegion(str, 0); next != null; ) for (Pair<Integer, Integer> next = identifyNextQuotedRegion(str, 0); next != null;)
{ {
quotedRegions.add(next); quotedRegions.add(next);
next = identifyNextQuotedRegion(str, next.getSecond() + 1); next = identifyNextQuotedRegion(str, next.getSecond() + 1);
@@ -135,12 +125,10 @@ public class ExecParameterTokenizer
} }
/** /**
* The substrings will be a list of quoted and unquoted substrings. * The substrings will be a list of quoted and unquoted substrings. The unquoted ones need to be further tokenized in the normal way. The quoted ones must not be tokenized, but need their quotes stripped off.
* The unquoted ones need to be further tokenized in the normal way.
* The quoted ones must not be tokenized, but need their quotes stripped off.
*/ */
private List<Substring> getSubstrings(String str, private List<Substring> getSubstrings(String str,
List<Pair<Integer, Integer>> quotedRegionIndices) List<Pair<Integer, Integer>> quotedRegionIndices)
{ {
List<Substring> result = new ArrayList<>(); List<Substring> result = new ArrayList<>();
@@ -151,10 +139,10 @@ public class ExecParameterTokenizer
{ {
int startIndexOfNextQuotedRegion = nextQuotedRegionIndices.getFirst() - 1; int startIndexOfNextQuotedRegion = nextQuotedRegionIndices.getFirst() - 1;
result.add(new UnquotedSubstring( result.add(new UnquotedSubstring(
str.substring(cursorPosition, startIndexOfNextQuotedRegion))); str.substring(cursorPosition, startIndexOfNextQuotedRegion)));
} }
result.add(new QuotedSubstring(str.substring(nextQuotedRegionIndices.getFirst(), result.add(new QuotedSubstring(str.substring(nextQuotedRegionIndices.getFirst(),
nextQuotedRegionIndices.getSecond()))); nextQuotedRegionIndices.getSecond())));
cursorPosition = nextQuotedRegionIndices.getSecond(); cursorPosition = nextQuotedRegionIndices.getSecond();
} }
@@ -198,12 +186,12 @@ public class ExecParameterTokenizer
} }
private Pair<Integer, Integer> findIndexOfClosingQuote(String str, int indexOfStartingQuote, private Pair<Integer, Integer> findIndexOfClosingQuote(String str, int indexOfStartingQuote,
char quoteChar) char quoteChar)
{ {
// So we know which type of quote char we're dealing with. Either ' or ". // So we know which type of quote char we're dealing with. Either ' or ".
// Now we need to find the closing quote. // Now we need to find the closing quote.
int indexAfterClosingQuote = str.indexOf(quoteChar, int indexAfterClosingQuote = str.indexOf(quoteChar,
indexOfStartingQuote + 1) + 1; // + 1 to search after opening quote. + 1 to give result including closing quote. indexOfStartingQuote + 1) + 1; // + 1 to search after opening quote. + 1 to give result including closing quote.
if (indexAfterClosingQuote == 0) // -1 + 1 if (indexAfterClosingQuote == 0) // -1 + 1
{ {
@@ -295,8 +283,10 @@ public class ExecParameterTokenizer
/** /**
* Make a new one. * Make a new one.
* *
* @param first The first member. * @param first
* @param second The second member. * The first member.
* @param second
* The second member.
*/ */
public Pair(F first, S second) public Pair(F first, S second)
{ {
@@ -334,16 +324,20 @@ public class ExecParameterTokenizer
this.second = second; this.second = second;
} }
@Override public boolean equals(Object o) @Override
public boolean equals(Object o)
{ {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (o == null || getClass() != o.getClass())
return false;
Pair<?, ?> pair = (Pair<?, ?>) o; Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) && return Objects.equals(first, pair.first) &&
Objects.equals(second, pair.second); Objects.equals(second, pair.second);
} }
@Override public int hashCode() @Override
public int hashCode()
{ {
return Objects.hash(first, second); return Objects.hash(first, second);
} }
@@ -355,4 +349,3 @@ public class ExecParameterTokenizer
} }
} }
} }

View File

@@ -50,51 +50,50 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* This acts as a session similar to the <code>java.lang.Process</code>, but * This acts as a session similar to the <code>java.lang.Process</code>, but logs the system standard and error streams.
* logs the system standard and error streams.
* <p> * <p>
* The bean can be configured to execute a command directly, or be given a map * The bean can be configured to execute a command directly, or be given a map of commands keyed by the <i>os.name</i> Java system property. In this map, the default key that is used when no match is found is the <b>{@link #KEY_OS_DEFAULT *}</b> key.
* of commands keyed by the <i>os.name</i> Java system property. In this map,
* the default key that is used when no match is found is the
* <b>{@link #KEY_OS_DEFAULT *}</b> key.
* <p> * <p>
* Use the {@link #setProcessDirectory(String) processDirectory} property to change the default location * Use the {@link #setProcessDirectory(String) processDirectory} property to change the default location from which the command executes. The process's environment can be configured using the {@link #setProcessProperties(Map) processProperties} property.
* from which the command executes. The process's environment can be configured using the
* {@link #setProcessProperties(Map) processProperties} property.
* <p> * <p>
* Commands may use placeholders, e.g. * Commands may use placeholders, e.g.
* <pre><code> *
* <pre>
* <code>
* find * find
* -name * -name
* ${filename} * ${filename}
* </code></pre> * </code>
* The <b>filename</b> property will be substituted for any supplied value prior to * </pre>
* each execution of the command. Currently, no checks are made to get or check the *
* properties contained within the command string. It is up to the client code to * The <b>filename</b> property will be substituted for any supplied value prior to each execution of the command. Currently, no checks are made to get or check the properties contained within the command string. It is up to the client code to dynamically extract the properties required if the required properties are not known up front.
* dynamically extract the properties required if the required properties are not
* known up front.
* <p> * <p>
* Sometimes, a variable may contain several arguments. . In this case, the arguments * Sometimes, a variable may contain several arguments. . In this case, the arguments need to be tokenized using a standard <tt>StringTokenizer</tt>. To force tokenization of a value, use:
* need to be tokenized using a standard <tt>StringTokenizer</tt>. To force tokenization *
* of a value, use: * <pre>
* <pre><code> * <code>
* SPLIT:${userArgs} * SPLIT:${userArgs}
* </code></pre> * </code>
* You should not use this just to split up arguments that are known to require tokenization * </pre>
* up front. The <b>SPLIT:</b> directive works for the entire argument and will not do anything *
* if it is not at the beginning of the argument. Do not use <b>SPLIT:</b> to break up arguments * You should not use this just to split up arguments that are known to require tokenization up front. The <b>SPLIT:</b> directive works for the entire argument and will not do anything if it is not at the beginning of the argument. Do not use <b>SPLIT:</b> to break up arguments that are fixed, so avoid doing this:
* that are fixed, so avoid doing this: *
* <pre><code> * <pre>
* <code>
* SPLIT:ls -lih * SPLIT:ls -lih
* </code></pre> * </code>
* </pre>
*
* Instead, break the command up explicitly: * Instead, break the command up explicitly:
* <pre><code> *
* <pre>
* <code>
* ls * ls
* -lih * -lih
* </code></pre> * </code>
* </pre>
* *
* Tokenization of quoted parameter values is handled by ExecParameterTokenizer, which * Tokenization of quoted parameter values is handled by ExecParameterTokenizer, which describes the support in more detail.
* describes the support in more detail.
* *
* @author Derek Hulley * @author Derek Hulley
*/ */
@@ -123,7 +122,7 @@ public class RuntimeExec
private final Timer timer = new Timer(true); private final Timer timer = new Timer(true);
/** /**
* Default constructor. Initialize this instance by setting individual properties. * Default constructor. Initialize this instance by setting individual properties.
*/ */
public RuntimeExec() public RuntimeExec()
{ {
@@ -151,15 +150,16 @@ public class RuntimeExec
sb.append("\n"); sb.append("\n");
} }
sb.append(" env props: ").append(Arrays.toString(processProperties)).append("\n") sb.append(" env props: ").append(Arrays.toString(processProperties)).append("\n")
.append(" dir: ").append(processDirectory).append("\n") .append(" dir: ").append(processDirectory).append("\n")
.append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n"); .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n");
return sb.toString(); return sb.toString();
} }
/** /**
* Set the command to execute regardless of operating system * Set the command to execute regardless of operating system
* *
* @param command an array of strings representing the command (first entry) and arguments * @param command
* an array of strings representing the command (first entry) and arguments
* @since 3.0 * @since 3.0
*/ */
public void setCommand(String[] command) public void setCommand(String[] command)
@@ -168,11 +168,12 @@ public class RuntimeExec
} }
/** /**
* Sets the assumed charset of OUT and ERR streams generated by the executed command. * Sets the assumed charset of OUT and ERR streams generated by the executed command. This defaults to the system default charset: {@link Charset#defaultCharset()}.
* This defaults to the system default charset: {@link Charset#defaultCharset()}.
* *
* @param charsetCode a supported character set code * @param charsetCode
* @throws UnsupportedCharsetException if the characterset code is not recognised by Java * a supported character set code
* @throws UnsupportedCharsetException
* if the characterset code is not recognised by Java
*/ */
public void setCharset(String charsetCode) public void setCharset(String charsetCode)
{ {
@@ -180,14 +181,10 @@ public class RuntimeExec
} }
/** /**
* Set whether to wait for completion of the command or not. If there is no wait for completion, * Set whether to wait for completion of the command or not. If there is no wait for completion, then the return value of <i>out</i> and <i>err</i> buffers cannot be relied upon as the command may still be in progress. Failure is therefore not possible unless the calling thread waits for execution.
* then the return value of <i>out</i> and <i>err</i> buffers cannot be relied upon as the
* command may still be in progress. Failure is therefore not possible unless the calling thread
* waits for execution.
* *
* @param waitForCompletion <tt>true</tt> (default) is to wait for the command to exit, * @param waitForCompletion
* or <tt>false</tt> to just return an exit code of 0 and whatever * <tt>true</tt> (default) is to wait for the command to exit, or <tt>false</tt> to just return an exit code of 0 and whatever output is available at that point.
* output is available at that point.
* @since 2.1 * @since 2.1
*/ */
public void setWaitForCompletion(boolean waitForCompletion) public void setWaitForCompletion(boolean waitForCompletion)
@@ -196,26 +193,17 @@ public class RuntimeExec
} }
/** /**
* Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system * Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used to get a command where there is not direct match to the operating system key.
* property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used
* to get a command where there is not direct match to the operating system key.
* <p> * <p>
* Each command is an array of strings, the first of which represents the command and all subsequent * Each command is an array of strings, the first of which represents the command and all subsequent entries in the array represent the arguments. All elements of the array will be checked for the presence of any substitution parameters (e.g. '{dir}'). The parameters can be set using the {@link #setDefaultProperties(Map) defaults} or by passing the substitution values into the {@link #execute(Map)} command.
* entries in the array represent the arguments. All elements of the array will be checked for
* the presence of any substitution parameters (e.g. '{dir}'). The parameters can be set using the
* {@link #setDefaultProperties(Map) defaults} or by passing the substitution values into the
* {@link #execute(Map)} command.
* <p> * <p>
* If parameters passed may be multiple arguments, or if the values provided in the map are themselves * If parameters passed may be multiple arguments, or if the values provided in the map are themselves collections of arguments (not recommended), then prefix the value with <b>SPLIT:</b> to ensure that the value is tokenized before being passed to the command. Any values that are not split, will be passed to the command as single arguments. For example:<br>
* collections of arguments (not recommended), then prefix the value with <b>SPLIT:</b> to ensure that
* the value is tokenized before being passed to the command. Any values that are not split, will be
* passed to the command as single arguments. For example:<br>
* '<b>SPLIT: dir . ..</b>' becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br> * '<b>SPLIT: dir . ..</b>' becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br>
* '<b>SPLIT: dir ${path}</b>' (if path is '<b>. ..</b>') becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br> * '<b>SPLIT: dir ${path}</b>' (if path is '<b>. ..</b>') becomes '<b>dir</b>', '<b>.</b>' and '<b>..</b>'.<br>
* The splitting occurs post-subtitution. Where the arguments are known, it is advisable to avoid * The splitting occurs post-subtitution. Where the arguments are known, it is advisable to avoid <b>SPLIT:</b>.
* <b>SPLIT:</b>.
* *
* @param commandsByOS a map of command string arrays, keyed by operating system names * @param commandsByOS
* a map of command string arrays, keyed by operating system names
* @see #setDefaultProperties(Map) * @see #setDefaultProperties(Map)
* @since 3.0 * @since 3.0
*/ */
@@ -230,7 +218,7 @@ public class RuntimeExec
// go through the commands keys, looking for one that matches by regular expression matching // go through the commands keys, looking for one that matches by regular expression matching
for (String osName : commandsByOS.keySet()) for (String osName : commandsByOS.keySet())
{ {
// Ignore * options. It is dealt with later. // Ignore * options. It is dealt with later.
if (osName.equals(KEY_OS_DEFAULT)) if (osName.equals(KEY_OS_DEFAULT))
{ {
continue; continue;
@@ -252,26 +240,25 @@ public class RuntimeExec
if (command == null) if (command == null)
{ {
throw new RuntimeException( throw new RuntimeException(
"No command found for OS " + serverOs + " or '" + KEY_OS_DEFAULT + "': \n" + "No command found for OS " + serverOs + " or '" + KEY_OS_DEFAULT + "': \n" +
" commands: " + commandsByOS); " commands: " + commandsByOS);
} }
this.command = command; this.command = command;
} }
/** /**
* Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system * Supply a choice of commands to execute based on a mapping from the <i>os.name</i> system property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used to get a command where there is not direct match to the operating system key.
* property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used
* to get a command where there is not direct match to the operating system key.
* *
* @param commandsByOS a map of command string keyed by operating system names * @param commandsByOS
* a map of command string keyed by operating system names
* @deprecated Use {@link #setCommandsAndArguments(Map)} * @deprecated Use {@link #setCommandsAndArguments(Map)}
*/ */
public void setCommandMap(Map<String, String> commandsByOS) public void setCommandMap(Map<String, String> commandsByOS)
{ {
// This is deprecated, so issue a warning // This is deprecated, so issue a warning
logger.warn( logger.warn(
"The bean RuntimeExec property 'commandMap' has been deprecated;" + "The bean RuntimeExec property 'commandMap' has been deprecated;" +
" use 'commandsAndArguments' instead. See https://issues.alfresco.com/jira/browse/ETHREEOH-579."); " use 'commandsAndArguments' instead. See https://issues.alfresco.com/jira/browse/ETHREEOH-579.");
Map<String, String[]> fixed = new LinkedHashMap<>(); Map<String, String[]> fixed = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : commandsByOS.entrySet()) for (Map.Entry<String, String> entry : commandsByOS.entrySet())
{ {
@@ -289,14 +276,12 @@ public class RuntimeExec
} }
/** /**
* Set the default command-line properties to use when executing the command. * Set the default command-line properties to use when executing the command. These are properties that substitute variables defined in the command string itself. Properties supplied during execution will overwrite the default properties.
* These are properties that substitute variables defined in the command string itself.
* Properties supplied during execution will overwrite the default properties.
* <p> * <p>
* <code>null</code> properties will be treated as an empty string for substitution * <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes.
* *
* @param defaultProperties property values * @param defaultProperties
* property values
*/ */
public void setDefaultProperties(Map<String, String> defaultProperties) public void setDefaultProperties(Map<String, String> defaultProperties)
{ {
@@ -304,14 +289,12 @@ public class RuntimeExec
} }
/** /**
* Set additional runtime properties (environment properties) that will used * Set additional runtime properties (environment properties) that will used by the executing process.
* by the executing process.
* <p> * <p>
* Any keys or properties that start and end with <b>${...}</b> will be removed on the assumption * Any keys or properties that start and end with <b>${...}</b> will be removed on the assumption that these are unset properties. <tt>null</tt> values are translated to empty strings. All keys and values are trimmed of leading and trailing whitespace.
* that these are unset properties. <tt>null</tt> values are translated to empty strings.
* All keys and values are trimmed of leading and trailing whitespace.
* *
* @param processProperties Runtime process properties * @param processProperties
* Runtime process properties
* @see Runtime#exec(String, String[], java.io.File) * @see Runtime#exec(String, String[], java.io.File)
*/ */
public void setProcessProperties(Map<String, String> processProperties) public void setProcessProperties(Map<String, String> processProperties)
@@ -368,13 +351,12 @@ public class RuntimeExec
} }
/** /**
* Adds a property to existed processProperties. * Adds a property to existed processProperties. Property should not be null or empty. If property with the same value already exists then no change is made. If property exists with a different value then old value is replaced with the new one.
* Property should not be null or empty.
* If property with the same value already exists then no change is made.
* If property exists with a different value then old value is replaced with the new one.
* *
* @param name - property name * @param name
* @param value - property value * - property name
* @param value
* - property value
*/ */
public void setProcessProperty(String name, String value) public void setProcessProperty(String name, String value)
{ {
@@ -424,10 +406,10 @@ public class RuntimeExec
/** /**
* Set the runtime location from which the command is executed. * Set the runtime location from which the command is executed.
* <p> * <p>
* If the value is an unsubsititued variable (<b>${...}</b>) then it is ignored. * If the value is an unsubsititued variable (<b>${...}</b>) then it is ignored. If the location is not visible at the time of setting, a warning is issued only.
* If the location is not visible at the time of setting, a warning is issued only.
* *
* @param processDirectory the runtime location from which to execute the command * @param processDirectory
* the runtime location from which to execute the command
*/ */
public void setProcessDirectory(String processDirectory) public void setProcessDirectory(String processDirectory)
{ {
@@ -441,17 +423,18 @@ public class RuntimeExec
if (!this.processDirectory.exists()) if (!this.processDirectory.exists())
{ {
logger.warn( logger.warn(
"The runtime process directory is not visible when setting property " + "The runtime process directory is not visible when setting property " +
"'processDirectory': \n{}", this); "'processDirectory': \n{}",
this);
} }
} }
} }
/** /**
* A comma or space separated list of values that, if returned by the executed command, * A comma or space separated list of values that, if returned by the executed command, indicate an error value. This defaults to <b>"1, 2"</b>.
* indicate an error value. This defaults to <b>"1, 2"</b>.
* *
* @param errCodesStr the error codes for the execution * @param errCodesStr
* the error codes for the execution
*/ */
public void setErrorCodes(String errCodesStr) public void setErrorCodes(String errCodesStr)
{ {
@@ -469,7 +452,7 @@ public class RuntimeExec
catch (NumberFormatException e) catch (NumberFormatException e)
{ {
throw new RuntimeException( throw new RuntimeException(
"Property 'errorCodes' must be comma-separated list of integers: " + errCodesStr); "Property 'errorCodes' must be comma-separated list of integers: " + errCodesStr);
} }
} }
} }
@@ -487,9 +470,8 @@ public class RuntimeExec
/** /**
* Executes the statement that this instance was constructed with. * Executes the statement that this instance was constructed with.
* *
* @param properties the properties that the command might be executed with. * @param properties
* <code>null</code> properties will be treated as an empty string for substitution * the properties that the command might be executed with. <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes.
* @return Returns the full execution results * @return Returns the full execution results
*/ */
public ExecutionResult execute(Map<String, String> properties) public ExecutionResult execute(Map<String, String> properties)
@@ -498,15 +480,12 @@ public class RuntimeExec
} }
/** /**
* Executes the statement that this instance was constructed with an optional * Executes the statement that this instance was constructed with an optional timeout after which the command is asked to
* timeout after which the command is asked to
* *
* @param properties the properties that the command might be executed with. * @param properties
* <code>null</code> properties will be treated as an empty string for substitution * the properties that the command might be executed with. <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes. * @param timeoutMs
* @param timeoutMs a timeout after which {@link Process#destroy()} is called. * a timeout after which {@link Process#destroy()} is called. ignored if less than or equal to zero. Note this method does not guarantee to terminate the process (it is not a kill -9).
* ignored if less than or equal to zero. Note this method does not guarantee
* to terminate the process (it is not a kill -9).
* @return Returns the full execution results * @return Returns the full execution results
*/ */
public ExecutionResult execute(Map<String, String> properties, final long timeoutMs) public ExecutionResult execute(Map<String, String> properties, final long timeoutMs)
@@ -528,13 +507,12 @@ public class RuntimeExec
// execute the command with full property replacement // execute the command with full property replacement
commandToExecute = getCommand(properties); commandToExecute = getCommand(properties);
final Process thisProcess = runtime.exec(commandToExecute, processProperties, final Process thisProcess = runtime.exec(commandToExecute, processProperties,
processDirectory); processDirectory);
process = thisProcess; process = thisProcess;
if (timeoutMs > 0) if (timeoutMs > 0)
{ {
final String[] command = commandToExecute; final String[] command = commandToExecute;
timer.schedule(new TimerTask() timer.schedule(new TimerTask() {
{
@Override @Override
public void run() public void run()
{ {
@@ -546,8 +524,8 @@ public class RuntimeExec
catch (IllegalThreadStateException stillRunning) catch (IllegalThreadStateException stillRunning)
{ {
logger.debug( logger.debug(
"Process has taken too long ({} seconds). Killing process {}", "Process has taken too long ({} seconds). Killing process {}",
timeoutMs / 1000, Arrays.deepToString(command)); timeoutMs / 1000, Arrays.deepToString(command));
} }
} }
}, timeoutMs); }, timeoutMs);
@@ -559,16 +537,16 @@ public class RuntimeExec
String execOut = ""; String execOut = "";
String execErr = e.getMessage(); String execErr = e.getMessage();
ExecutionResult result = new ExecutionResult(null, commandToExecute, errCodes, ExecutionResult result = new ExecutionResult(null, commandToExecute, errCodes,
defaultFailureExitValue, execOut, execErr); defaultFailureExitValue, execOut, execErr);
logFullEnvironmentDump(result); logFullEnvironmentDump(result);
return result; return result;
} }
// create the stream gobblers // create the stream gobblers
InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread( InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread(
process.getInputStream(), charset); process.getInputStream(), charset);
InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread( InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread(
process.getErrorStream(), charset); process.getErrorStream(), charset);
// start gobbling // start gobbling
stdOutGobbler.start(); stdOutGobbler.start();
@@ -603,7 +581,7 @@ public class RuntimeExec
// construct the return value // construct the return value
ExecutionResult result = new ExecutionResult(process, commandToExecute, errCodes, exitValue, ExecutionResult result = new ExecutionResult(process, commandToExecute, errCodes, exitValue,
execOut, execErr); execOut, execErr);
// done // done
logFullEnvironmentDump(result); logFullEnvironmentDump(result);
@@ -646,12 +624,11 @@ public class RuntimeExec
} }
logger.debug("Result: " + result.toString()); logger.debug("Result: " + result.toString());
// close output stream (connected to input stream of native subprocess) // close output stream (connected to input stream of native subprocess)
} }
/** /**
* @return Returns the command that will be executed if no additional properties * @return Returns the command that will be executed if no additional properties were to be supplied
* were to be supplied
*/ */
public String[] getCommand() public String[] getCommand()
{ {
@@ -661,12 +638,11 @@ public class RuntimeExec
/** /**
* Get the command that will be executed post substitution. * Get the command that will be executed post substitution.
* <p> * <p>
* <code>null</code> properties will be treated as an empty string for substitution * <code>null</code> properties will be treated as an empty string for substitution purposes.
* purposes.
* *
* @param properties the properties that the command might be executed with * @param properties
* @return Returns the command that will be executed should the additional properties * the properties that the command might be executed with
* be supplied * @return Returns the command that will be executed should the additional properties be supplied
*/ */
public String[] getCommand(Map<String, String> properties) public String[] getCommand(Map<String, String> properties)
{ {
@@ -715,7 +691,7 @@ public class RuntimeExec
// There may be quoted arguments here (see ALF-7482) // There may be quoted arguments here (see ALF-7482)
ExecParameterTokenizer quoteAwareTokenizer = new ExecParameterTokenizer( ExecParameterTokenizer quoteAwareTokenizer = new ExecParameterTokenizer(
unsplitAdjustedValue); unsplitAdjustedValue);
List<String> tokens = quoteAwareTokenizer.getAllTokens(); List<String> tokens = quoteAwareTokenizer.getAllTokens();
adjustedCommandElements.addAll(tokens); adjustedCommandElements.addAll(tokens);
} }
@@ -743,15 +719,16 @@ public class RuntimeExec
private final String stdErr; private final String stdErr;
/** /**
* @param process the process attached to Java - <tt>null</tt> is allowed * @param process
* the process attached to Java - <tt>null</tt> is allowed
*/ */
private ExecutionResult( private ExecutionResult(
final Process process, final Process process,
final String[] command, final String[] command,
final Set<Integer> errCodes, final Set<Integer> errCodes,
final int exitValue, final int exitValue,
final String stdOut, final String stdOut,
final String stdErr) final String stdErr)
{ {
this.process = process; this.process = process;
this.command = command; this.command = command;
@@ -769,20 +746,18 @@ public class RuntimeExec
StringBuilder sb = new StringBuilder(128); StringBuilder sb = new StringBuilder(128);
sb.append("Execution result: \n") sb.append("Execution result: \n")
.append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n") .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n")
.append(" command: "); .append(" command: ");
appendCommand(sb, command).append("\n") appendCommand(sb, command).append("\n")
.append(" succeeded: ").append(getSuccess()).append("\n") .append(" succeeded: ").append(getSuccess()).append("\n")
.append(" exit code: ").append(exitValue).append("\n") .append(" exit code: ").append(exitValue).append("\n")
.append(" out: ").append(out).append("\n") .append(" out: ").append(out).append("\n")
.append(" err: ").append(err); .append(" err: ").append(err);
return sb.toString(); return sb.toString();
} }
/** /**
* Appends the command in a form that make running from the command line simpler. * Appends the command in a form that make running from the command line simpler. It is not a real attempt at making a command given all the operating system and shell options, but makes copy, paste and edit a bit simpler.
* It is not a real attempt at making a command given all the operating system
* and shell options, but makes copy, paste and edit a bit simpler.
*/ */
private StringBuilder appendCommand(StringBuilder sb, String[] command) private StringBuilder appendCommand(StringBuilder sb, String[] command)
{ {
@@ -818,10 +793,7 @@ public class RuntimeExec
} }
/** /**
* A helper method to force a kill of the process that generated this result. This is * A helper method to force a kill of the process that generated this result. This is useful in cases where the process started is not expected to exit, or doesn't exit quickly. If the {@linkplain RuntimeExec#setWaitForCompletion(boolean) "wait for completion"} flag is <tt>false</tt> then the process may still be running when this result is returned.
* useful in cases where the process started is not expected to exit, or doesn't exit
* quickly. If the {@linkplain RuntimeExec#setWaitForCompletion(boolean) "wait for completion"}
* flag is <tt>false</tt> then the process may still be running when this result is returned.
* *
* @return <tt>true</tt> if the process was killed, otherwise <tt>false</tt> * @return <tt>true</tt> if the process was killed, otherwise <tt>false</tt>
*/ */
@@ -844,7 +816,8 @@ public class RuntimeExec
} }
/** /**
* @param exitValue the command exit value * @param exitValue
* the command exit value
* @return Returns true if the code is a listed failure code * @return Returns true if the code is a listed failure code
* @see #setErrorCodes(String) * @see #setErrorCodes(String)
*/ */
@@ -854,8 +827,7 @@ public class RuntimeExec
} }
/** /**
* @return Returns true if the command was deemed to be successful according to the * @return Returns true if the command was deemed to be successful according to the failure codes returned by the execution.
* failure codes returned by the execution.
*/ */
public boolean getSuccess() public boolean getSuccess()
{ {
@@ -879,8 +851,7 @@ public class RuntimeExec
} }
/** /**
* Gobbles an <code>InputStream</code> and writes it into a * Gobbles an <code>InputStream</code> and writes it into a <code>StringBuffer</code>
* <code>StringBuffer</code>
* <p> * <p>
* The reading of the input stream is buffered. * The reading of the input stream is buffered.
*/ */
@@ -888,12 +859,12 @@ public class RuntimeExec
{ {
private final InputStream is; private final InputStream is;
private final Charset charset; private final Charset charset;
private final StringBuffer buffer; // we require the synchronization private final StringBuffer buffer; // we require the synchronization
private boolean completed; private boolean completed;
/** /**
* @param is an input stream to read - it will be wrapped in a buffer * @param is
* for reading * an input stream to read - it will be wrapped in a buffer for reading
*/ */
public InputStreamReaderThread(InputStream is, Charset charset) public InputStreamReaderThread(InputStream is, Charset charset)
{ {
@@ -935,7 +906,7 @@ public class RuntimeExec
// The thread has finished consuming the stream // The thread has finished consuming the stream
completed = true; completed = true;
// Notify waiters // Notify waiters
this.notifyAll(); // Note: Method is synchronized this.notifyAll(); // Note: Method is synchronized
} }
} }
@@ -954,13 +925,13 @@ public class RuntimeExec
this.wait(1000L); // 200 ms this.wait(1000L); // 200 ms
} }
catch (InterruptedException ignore) catch (InterruptedException ignore)
{ {}
}
} }
} }
/** /**
* @param msg the message to add to the buffer * @param msg
* the message to add to the buffer
*/ */
public void addToBuffer(String msg) public void addToBuffer(String msg)
{ {
@@ -981,4 +952,3 @@ public class RuntimeExec
} }
} }
} }

View File

@@ -26,15 +26,16 @@
*/ */
package org.alfresco.transform.base.html; package org.alfresco.transform.base.html;
import org.alfresco.transform.config.TransformOption;
import org.alfresco.transform.config.TransformOptionGroup;
import org.alfresco.transform.config.TransformOptionValue;
import org.springframework.stereotype.Component;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import org.springframework.stereotype.Component;
import org.alfresco.transform.config.TransformOption;
import org.alfresco.transform.config.TransformOptionGroup;
import org.alfresco.transform.config.TransformOptionValue;
/** /**
* Used in the html test page, which provides a list of known transform option names. * Used in the html test page, which provides a list of known transform option names.
*/ */
@@ -42,14 +43,12 @@ import java.util.TreeSet;
public class OptionsHelper public class OptionsHelper
{ {
private OptionsHelper() private OptionsHelper()
{ {}
}
public static Set<String> getOptionNames(Map<String, Set<TransformOption>> transformOptionsByName) public static Set<String> getOptionNames(Map<String, Set<TransformOption>> transformOptionsByName)
{ {
Set<String> set = new TreeSet<>(); Set<String> set = new TreeSet<>();
transformOptionsByName.forEach(((optionName, optionSet) -> transformOptionsByName.forEach(((optionName, optionSet) -> optionSet.stream().forEach(option -> addOption(set, option))));
optionSet.stream().forEach(option -> addOption(set, option))));
return set; return set;
} }
@@ -57,11 +56,11 @@ public class OptionsHelper
{ {
if (option instanceof TransformOptionGroup) if (option instanceof TransformOptionGroup)
{ {
addGroup(set, (TransformOptionGroup)option); addGroup(set, (TransformOptionGroup) option);
} }
else else
{ {
addValue(set, (TransformOptionValue)option); addValue(set, (TransformOptionValue) option);
} }
} }

View File

@@ -42,7 +42,7 @@ public class TransformInterceptor implements AsyncHandlerInterceptor
{ {
@Override @Override
public void afterCompletion(HttpServletRequest request, public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) HttpServletResponse response, Object handler, Exception ex)
{ {
deleteFile(request, SOURCE_FILE); deleteFile(request, SOURCE_FILE);
deleteFile(request, TARGET_FILE); deleteFile(request, TARGET_FILE);

View File

@@ -26,10 +26,9 @@
*/ */
package org.alfresco.transform.base.logging; package org.alfresco.transform.base.logging;
import org.apache.commons.lang3.StringUtils; import static java.lang.Math.max;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import static org.springframework.http.HttpStatus.OK;
import org.springframework.http.HttpStatus;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collection; import java.util.Collection;
@@ -38,14 +37,13 @@ import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Math.max; import org.apache.commons.lang3.StringUtils;
import static org.springframework.http.HttpStatus.OK; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
/** /**
* Provides setter and getter methods to allow the current Thread to set various log properties and for these * Provides setter and getter methods to allow the current Thread to set various log properties and for these values to be retrieved. The {@link #complete()} method should be called at the end of a request to flush the current entry to an internal log Collection of the latest entries. The {@link #getLog()} method is used to obtain access to this collection.
* values to be retrieved. The {@link #complete()} method should be called at the end of a request to flush the
* current entry to an internal log Collection of the latest entries. The {@link #getLog()} method is used to obtain
* access to this collection.
*/ */
public final class LogEntry public final class LogEntry
{ {
@@ -166,7 +164,7 @@ public final class LogEntry
if (logEntry.statusCode == OK.value()) if (logEntry.statusCode == OK.value())
{ {
logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start -
logEntry.durationStreamIn - max(logEntry.durationTransform, 0); logEntry.durationStreamIn - max(logEntry.durationTransform, 0);
} }
currentLogEntry.remove(); currentLogEntry.remove();
@@ -195,13 +193,14 @@ public final class LogEntry
{ {
long duration = durationStreamIn + max(durationTransform, 0) + max(durationStreamOut, 0); long duration = durationStreamIn + max(durationTransform, 0) + max(durationStreamOut, 0);
return duration <= 5 return duration <= 5
? "" ? ""
: time(duration) + : time(duration) +
" (" + " (" +
(time(durationStreamIn) + ' ' + (time(durationStreamIn) + ' ' +
time(durationTransform) + ' ' + time(durationTransform) + ' ' +
time(durationStreamOut)).trim() + time(durationStreamOut)).trim()
")"; +
")";
} }
public String getSource() public String getSource()
@@ -236,16 +235,18 @@ public final class LogEntry
private String time(long ms) private String time(long ms)
{ {
return ms == -1 ? "" : size(ms, "1ms", return ms == -1 ? ""
new String[]{"ms", "s", "min", "hr"}, : size(ms, "1ms",
new long[]{1000, 60 * 1000, 60 * 60 * 1000, Long.MAX_VALUE}); new String[]{"ms", "s", "min", "hr"},
new long[]{1000, 60 * 1000, 60 * 60 * 1000, Long.MAX_VALUE});
} }
private String size(long size) private String size(long size)
{ {
return size == -1 ? "" : size(size, "1 byte", return size == -1 ? ""
new String[]{"bytes", " KB", " MB", " GB", " TB"}, : size(size, "1 byte",
new long[]{1024, 1024 * 1024, 1024 * 1024 * 1024, 1024L * 1024 * 1024 * 1024, Long.MAX_VALUE}); new String[]{"bytes", " KB", " MB", " GB", " TB"},
new long[]{1024, 1024 * 1024, 1024 * 1024 * 1024, 1024L * 1024 * 1024 * 1024, Long.MAX_VALUE});
} }
private String size(long size, String singleValue, String[] units, long[] dividers) private String size(long size, String singleValue, String[] units, long[] dividers)

View File

@@ -29,19 +29,16 @@ package org.alfresco.transform.base.logging;
public final class StandardMessages public final class StandardMessages
{ {
private StandardMessages() private StandardMessages()
{ {}
}
public static String COMMUNITY_LICENCE = public static String COMMUNITY_LICENCE = "If the Alfresco software was purchased under a paid Alfresco license, the terms of the paid license agreement \n" +
"If the Alfresco software was purchased under a paid Alfresco license, the terms of the paid license agreement \n" + "will prevail. Otherwise, the software is provided under terms of the GNU LGPL v3 license. \n" +
"will prevail. Otherwise, the software is provided under terms of the GNU LGPL v3 license. \n" + "See the license at http://www.gnu.org/licenses/lgpl-3.0.txt. or in /LICENSE.txt \n\n";
"See the license at http://www.gnu.org/licenses/lgpl-3.0.txt. or in /LICENSE.txt \n\n";
public static String ENTERPRISE_LICENCE = public static String ENTERPRISE_LICENCE = "This image is only intended to be used with the Alfresco Enterprise Content Repository which is covered by\n" +
"This image is only intended to be used with the Alfresco Enterprise Content Repository which is covered by\n" + "https://www.alfresco.com/legal/agreements and https://www.alfresco.com/terms-use\n" +
"https://www.alfresco.com/legal/agreements and https://www.alfresco.com/terms-use\n" + "\n" +
"\n" + "License rights for this program may be obtained from Alfresco Software, Ltd. pursuant to a written agreement\n" +
"License rights for this program may be obtained from Alfresco Software, Ltd. pursuant to a written agreement\n" + "and any use of this program without such an agreement is prohibited.\n" +
"and any use of this program without such an agreement is prohibited.\n" + "\n";
"\n";
} }

View File

@@ -26,7 +26,9 @@
*/ */
package org.alfresco.transform.base.messaging; package org.alfresco.transform.base.messaging;
import org.alfresco.transform.messages.TransformRequestValidator; import jakarta.jms.ConnectionFactory;
import jakarta.jms.Queue;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -40,15 +42,12 @@ import org.springframework.lang.NonNull;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import jakarta.jms.ConnectionFactory; import org.alfresco.transform.messages.TransformRequestValidator;
import jakarta.jms.Queue;
/** /**
* JMS and messaging configuration for the T-Engines. Contains the basic config in order to have the * JMS and messaging configuration for the T-Engines. Contains the basic config in order to have the T-Engine able to read from queues and send a reply back.
* T-Engine able to read from queues and send a reply back.
* *
* @author Lucian Tuca * @author Lucian Tuca created on 18/12/2018
* created on 18/12/2018
*/ */
@Configuration @Configuration
@ConditionalOnProperty(name = "activemq.url") @ConditionalOnProperty(name = "activemq.url")
@@ -70,9 +69,9 @@ public class MessagingConfig implements JmsListenerConfigurer
@Bean @Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
final ConnectionFactory connectionFactory, final ConnectionFactory connectionFactory,
final TransformMessageConverter transformMessageConverter, final TransformMessageConverter transformMessageConverter,
final MessagingErrorHandler messagingErrorHandler) final MessagingErrorHandler messagingErrorHandler)
{ {
final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory); factory.setConnectionFactory(connectionFactory);
@@ -92,10 +91,8 @@ public class MessagingConfig implements JmsListenerConfigurer
@Bean @Bean
public Queue engineRequestQueue( public Queue engineRequestQueue(
@Value("${queue.engineRequestQueue}") String engineRequestQueueValue) @Value("${queue.engineRequestQueue}") String engineRequestQueueValue)
{ {
return new ActiveMQQueue(engineRequestQueueValue); return new ActiveMQQueue(engineRequestQueueValue);
} }
} }

View File

@@ -55,7 +55,7 @@ public class MessagingInfo
// Note: as per application.yaml the broker url is appended with "?jms.watchTopicAdvisories=false". If this needs to be fully // Note: as per application.yaml the broker url is appended with "?jms.watchTopicAdvisories=false". If this needs to be fully
// overridden then it would require explicitly setting both "spring.activemq.broker-url" *and* "activemq.url" (latter to non-false value). // overridden then it would require explicitly setting both "spring.activemq.broker-url" *and* "activemq.url" (latter to non-false value).
if ((activemqUrl != null) && (! activemqUrl.equals("false"))) if ((activemqUrl != null) && (!activemqUrl.equals("false")))
{ {
logger.info("JMS client is ENABLED - ACTIVEMQ_URL ='{}'", activemqUrl); logger.info("JMS client is ENABLED - ACTIVEMQ_URL ='{}'", activemqUrl);
} }

View File

@@ -26,10 +26,14 @@
*/ */
package org.alfresco.transform.base.messaging; package org.alfresco.transform.base.messaging;
import org.alfresco.transform.base.TransformController; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.client.model.TransformReply; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException; import java.util.Optional;
import jakarta.jms.Destination;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -39,22 +43,15 @@ import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.jms.Destination; import org.alfresco.transform.base.TransformController;
import jakarta.jms.JMSException; import org.alfresco.transform.client.model.TransformReply;
import jakarta.jms.Message; import org.alfresco.transform.client.model.TransformRequest;
import java.util.Optional; import org.alfresco.transform.exceptions.TransformException;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
/** /**
* Queue Transformer service. * Queue Transformer service. This service reads all the requests for the particular engine, forwards them to the worker component (at this time the injected controller - to be refactored) and sends back the reply to the {@link Message#getJMSReplyTo()} value. If this value is missing we've got to a dead end.
* This service reads all the requests for the particular engine, forwards them to the worker
* component (at this time the injected controller - to be refactored) and sends back the reply
* to the {@link Message#getJMSReplyTo()} value. If this value is missing we've got to a dead end.
* *
* @author Lucian Tuca * @author Lucian Tuca created on 18/12/2018
* created on 18/12/2018
*/ */
@Component @Component
@ConditionalOnProperty(name = "activemq.url") @ConditionalOnProperty(name = "activemq.url")
@@ -87,16 +84,16 @@ public class QueueTransformService
if (replyToQueue == null) if (replyToQueue == null)
{ {
logger.error( logger.error(
"Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ", "Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ",
correlationId); correlationId);
return; return;
} }
} }
catch (JMSException e) catch (JMSException e)
{ {
logger.error( logger.error(
"Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ", "Cannot find 'replyTo' destination queue for message with correlationID {}. Stopping. ",
correlationId); correlationId);
return; return;
} }
@@ -111,7 +108,7 @@ public class QueueTransformService
{ {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
replyWithError(replyToQueue, HttpStatus.valueOf(e.getStatus().value()), replyWithError(replyToQueue, HttpStatus.valueOf(e.getStatus().value()),
e.getMessage(), correlationId); e.getMessage(), correlationId);
return; return;
} }
@@ -119,7 +116,7 @@ public class QueueTransformService
{ {
logger.error("T-Request from message with correlationID {} is null!", correlationId); logger.error("T-Request from message with correlationID {} is null!", correlationId);
replyWithInternalSvErr(replyToQueue, replyWithInternalSvErr(replyToQueue,
"JMS exception during T-Request deserialization: ", correlationId); "JMS exception during T-Request deserialization: ", correlationId);
return; return;
} }
@@ -127,10 +124,10 @@ public class QueueTransformService
} }
/** /**
* Tries to convert the JMS {@link Message} to a {@link TransformRequest} * Tries to convert the JMS {@link Message} to a {@link TransformRequest} If any error occurs, a {@link TransformException} is thrown
* If any error occurs, a {@link TransformException} is thrown
* *
* @param msg Message to be deserialized * @param msg
* Message to be deserialized
* @return The converted {@link TransformRequest} instance * @return The converted {@link TransformRequest} instance
*/ */
private Optional<TransformRequest> convert(final Message msg, String correlationId) private Optional<TransformRequest> convert(final Message msg, String correlationId)
@@ -142,42 +139,39 @@ public class QueueTransformService
} }
catch (MessageConversionException e) catch (MessageConversionException e)
{ {
String message = String message = "MessageConversionException during T-Request deserialization of message with correlationID "
"MessageConversionException during T-Request deserialization of message with correlationID " + correlationId + ": ";
+ correlationId + ": ";
throw new TransformException(BAD_REQUEST, message + e.getMessage()); throw new TransformException(BAD_REQUEST, message + e.getMessage());
} }
catch (JMSException e) catch (JMSException e)
{ {
String message = String message = "JMSException during T-Request deserialization of message with correlationID "
"JMSException during T-Request deserialization of message with correlationID " + correlationId + ": ";
+ correlationId + ": ";
throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage()); throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage());
} }
catch (Exception e) catch (Exception e)
{ {
String message = String message = "Exception during T-Request deserialization of message with correlationID "
"Exception during T-Request deserialization of message with correlationID " + correlationId + ": ";
+ correlationId + ": ";
throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage()); throw new TransformException(INTERNAL_SERVER_ERROR, message + e.getMessage());
} }
} }
private void replyWithInternalSvErr(final Destination destination, final String msg, private void replyWithInternalSvErr(final Destination destination, final String msg,
final String correlationId) final String correlationId)
{ {
replyWithError(destination, INTERNAL_SERVER_ERROR, msg, correlationId); replyWithError(destination, INTERNAL_SERVER_ERROR, msg, correlationId);
} }
private void replyWithError(final Destination replyToQueue, final HttpStatus status, private void replyWithError(final Destination replyToQueue, final HttpStatus status,
final String msg, final String msg,
final String correlationId) final String correlationId)
{ {
final TransformReply reply = TransformReply final TransformReply reply = TransformReply
.builder() .builder()
.withStatus(status.value()) .withStatus(status.value())
.withErrorDetails(msg) .withErrorDetails(msg)
.build(); .build();
transformReplySender.send(replyToQueue, reply, correlationId); transformReplySender.send(replyToQueue, reply, correlationId);
} }

View File

@@ -27,11 +27,13 @@
package org.alfresco.transform.base.messaging; package org.alfresco.transform.base.messaging;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.Session;
import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.converter.MessageConverter;
@@ -39,13 +41,11 @@ import org.springframework.jms.support.converter.MessageType;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.jms.JMSException; import org.alfresco.transform.client.model.TransformReply;
import jakarta.jms.Message; import org.alfresco.transform.client.model.TransformRequest;
import jakarta.jms.Session;
/** /**
* Copied from the t-router. We would need to create a common dependency between t-engine base and t-router that * Copied from the t-router. We would need to create a common dependency between t-engine base and t-router that knows about jms to remove this duplication.
* knows about jms to remove this duplication.
* *
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -53,16 +53,13 @@ import jakarta.jms.Session;
public class TransformMessageConverter implements MessageConverter public class TransformMessageConverter implements MessageConverter
{ {
private static final MappingJackson2MessageConverter converter; private static final MappingJackson2MessageConverter converter;
private static final JavaType TRANSFORM_REQUEST_TYPE = private static final JavaType TRANSFORM_REQUEST_TYPE = TypeFactory.defaultInstance().constructType(TransformRequest.class);
TypeFactory.defaultInstance().constructType(TransformRequest.class);
static static
{ {
converter = new MappingJackson2MessageConverter() converter = new MappingJackson2MessageConverter() {
{
@Override @Override
@NonNull @NonNull protected JavaType getJavaTypeForMessage(final Message message) throws JMSException
protected JavaType getJavaTypeForMessage(final Message message) throws JMSException
{ {
if (message.getStringProperty("_type") == null) if (message.getStringProperty("_type") == null)
{ {
@@ -74,23 +71,20 @@ public class TransformMessageConverter implements MessageConverter
converter.setTargetType(MessageType.BYTES); converter.setTargetType(MessageType.BYTES);
converter.setTypeIdPropertyName("_type"); converter.setTypeIdPropertyName("_type");
converter.setTypeIdMappings(ImmutableMap.of( converter.setTypeIdMappings(ImmutableMap.of(
TransformRequest.class.getName(), TransformRequest.class, TransformRequest.class.getName(), TransformRequest.class,
TransformReply.class.getName(), TransformReply.class) TransformReply.class.getName(), TransformReply.class));
);
} }
@Override @Override
@NonNull @NonNull public Message toMessage(
public Message toMessage( @NonNull final Object object,
@NonNull final Object object, @NonNull final Session session) throws JMSException, MessageConversionException
@NonNull final Session session) throws JMSException, MessageConversionException
{ {
return converter.toMessage(object, session); return converter.toMessage(object, session);
} }
@Override @Override
@NonNull @NonNull public Object fromMessage(@NonNull final Message message) throws JMSException
public Object fromMessage(@NonNull final Message message) throws JMSException
{ {
return converter.fromMessage(message); return converter.fromMessage(message);
} }

View File

@@ -28,13 +28,14 @@ package org.alfresco.transform.base.messaging;
import jakarta.jms.Destination; import jakarta.jms.Destination;
import org.alfresco.transform.client.model.TransformReply;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.alfresco.transform.client.model.TransformReply;
/** /**
* Copied from the t-router. * Copied from the t-router.
* *

View File

@@ -26,12 +26,7 @@
*/ */
package org.alfresco.transform.base.metadata; package org.alfresco.transform.base.metadata;
import com.fasterxml.jackson.core.JsonProcessingException; import static org.alfresco.transform.base.metadata.AbstractMetadataExtractorEmbedder.Type.EMBEDDER;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
import org.slf4j.Logger;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -50,45 +45,38 @@ import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.TreeMap; import java.util.TreeMap;
import static org.alfresco.transform.base.metadata.AbstractMetadataExtractorEmbedder.Type.EMBEDDER; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
/** /**
* Helper methods for metadata extract and embed. * Helper methods for metadata extract and embed.
* <p> * <p>
* <i>Much of the code is based on AbstractMappingMetadataExtracter from the * <i>Much of the code is based on AbstractMappingMetadataExtracter from the content repository. The code has been simplified to only set up mapping one way.</i>
* content repository. The code has been simplified to only set up mapping one way.</i>
* <p> * <p>
* If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-extract"} * If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-extract"} (specified in the {@code engine_config.json}), it is indicating that it can extract metadata from {@code <MIMETYPE>}.
* (specified in the {@code engine_config.json}), it is indicating that it can extract metadata from {@code <MIMETYPE>}.
* *
* The transform results in a Map of extracted properties encoded as json being returned to the content repository. * The transform results in a Map of extracted properties encoded as json being returned to the content repository.
* <ul> * <ul>
* <li>The method extracts ALL available metadata from the document with * <li>The method extracts ALL available metadata from the document with {@link #extractMetadata(String, InputStream, String, OutputStream, Map, TransformManager)} and then calls {@link #mapMetadataAndWrite(OutputStream, Map, Map)}.</li>
* {@link #extractMetadata(String, InputStream, String, OutputStream, Map, TransformManager)} and then calls * <li>Selected values from the available metadata are mapped into content repository property names and values, depending on what is defined in a {@code "<classname>_metadata_extract.properties"} file.</li>
* {@link #mapMetadataAndWrite(OutputStream, Map, Map)}.</li> * <li>The selected values are set back to the content repository as a JSON representation of a Map, where the values are applied to the source node.</li>
* <li>Selected values from the available metadata are mapped into content repository property names and values,
* depending on what is defined in a {@code "<classname>_metadata_extract.properties"} file.</li>
* <li>The selected values are set back to the content repository as a JSON representation of a Map, where the values
* are applied to the source node.</li>
* </ul> * </ul>
* To support the same functionality as metadata extractors configured inside the content repository, * To support the same functionality as metadata extractors configured inside the content repository, extra key value pairs may be returned from {@link #extractMetadata(String, InputStream, String, OutputStream, Map, TransformManager)}. These are:
* extra key value pairs may be returned from {@link #extractMetadata(String, InputStream, String, OutputStream, Map, TransformManager)}.
* These are:
* <ul> * <ul>
* <li>{@code "sys:overwritePolicy"} which can specify the * <li>{@code "sys:overwritePolicy"} which can specify the {@code org.alfresco.repo.content.metadata.MetadataExtracter.OverwritePolicy} name. Defaults to "PRAGMATIC".</li>
* {@code org.alfresco.repo.content.metadata.MetadataExtracter.OverwritePolicy} name. Defaults to "PRAGMATIC".</li> * <li>{@code "sys:enableStringTagging"} if {@code "true"} finds or creates tags for each string mapped to {@code cm:taggable}. Defaults to {@code "false"} to ignore mapping strings to tags.</li>
* <li>{@code "sys:enableStringTagging"} if {@code "true"} finds or creates tags for each string mapped to * <li>{@code "sys:carryAspectProperties"}</li>
* {@code cm:taggable}. Defaults to {@code "false"} to ignore mapping strings to tags.</li> * <li>{@code "sys:stringTaggingSeparators"}</li>
* <li>{@code "sys:carryAspectProperties"} </li>
* <li>{@code "sys:stringTaggingSeparators"} </li>
* </ul> * </ul>
* *
* If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-embed"}, it is * If a transform specifies that it can convert from {@code "<MIMETYPE>"} to {@code "alfresco-metadata-embed"}, it is indicating that it can embed metadata in {@code <MIMETYPE>}.
* indicating that it can embed metadata in {@code <MIMETYPE>}.
* *
* The transform calls {@link #embedMetadata(String, InputStream, String, OutputStream, Map, TransformManager)} * The transform calls {@link #embedMetadata(String, InputStream, String, OutputStream, Map, TransformManager)} which should results in a new version of supplied source file that contains the metadata supplied in the transform options.
* which should results in a new version of supplied source file that contains the metadata supplied in the transform
* options.
* *
* @author Jesper Steen Møller * @author Jesper Steen Møller
* @author Derek Hulley * @author Derek Hulley
@@ -213,7 +201,8 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
logger.debug( logger.debug(
"Converted system model values to metadata values: \n" + "Converted system model values to metadata values: \n" +
" System Properties: {}\n" + " System Properties: {}\n" +
" Metadata Properties: {}", systemMetadata, metadataProperties); " Metadata Properties: {}",
systemMetadata, metadataProperties);
} }
return metadataProperties; return metadataProperties;
} }
@@ -226,9 +215,8 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
/** /**
* Based on AbstractMappingMetadataExtracter#getDefaultMapping. * Based on AbstractMappingMetadataExtracter#getDefaultMapping.
* *
* This method provides a <i>mapping</i> of where to store the values extracted from the documents. The list of * This method provides a <i>mapping</i> of where to store the values extracted from the documents. The list of properties need <b>not</b> include all metadata values extracted from the document. This mapping should be defined in a file based on the class name: {@code "<classname>_metadata_extract.properties"}
* properties need <b>not</b> include all metadata values extracted from the document. This mapping should be *
* defined in a file based on the class name: {@code "<classname>_metadata_extract.properties"}
* @return Returns a static mapping. It may not be null. * @return Returns a static mapping. It may not be null.
*/ */
private Map<String, Set<String>> buildExtractMapping() private Map<String, Set<String>> buildExtractMapping()
@@ -278,13 +266,10 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
/** /**
* Based on AbstractMappingMetadataExtracter#getDefaultEmbedMapping. * Based on AbstractMappingMetadataExtracter#getDefaultEmbedMapping.
* *
* This method provides a <i>mapping</i> of model properties that should be embedded in the content. The list of * This method provides a <i>mapping</i> of model properties that should be embedded in the content. The list of properties need <b>not</b> include all properties. This mapping should be defined in a file based on the class name: {@code "<classname>_metadata_embed.properties"}
* properties need <b>not</b> include all properties. This mapping should be defined in a file based on the class
* name: {@code "<classname>_metadata_embed.properties"}
* <p> * <p>
* If no {@code "<classname>_metadata_embed.properties"} file is found, a reverse of the * If no {@code "<classname>_metadata_embed.properties"} file is found, a reverse of the {@code "<classname>_metadata_extract.properties"} will be assumed. A last win approach will be used for handling duplicates.
* {@code "<classname>_metadata_extract.properties"} will be assumed. A last win approach will be used for handling *
* duplicates.
* @return Returns a static mapping. It may not be null. * @return Returns a static mapping. It may not be null.
*/ */
private Map<String, Set<String>> buildEmbedMapping() private Map<String, Set<String>> buildEmbedMapping()
@@ -388,8 +373,7 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
} }
} }
catch (IOException ignore) catch (IOException ignore)
{ {}
}
return properties; return properties;
} }
@@ -431,21 +415,21 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
} }
/** /**
* Adds a value to the map, conserving null values. Values are converted to null if: * Adds a value to the map, conserving null values. Values are converted to null if:
* <ul> * <ul>
* <li>it is an empty string value after trimming</li> * <li>it is an empty string value after trimming</li>
* <li>it is an empty collection</li> * <li>it is an empty collection</li>
* <li>it is an empty array</li> * <li>it is an empty array</li>
* </ul> * </ul>
* String values are trimmed before being put into the map. * String values are trimmed before being put into the map. Otherwise, it is up to the extracter to ensure that the value is a <tt>Serializable</tt>. It is not appropriate to implicitly convert values in order to make them <tt>Serializable</tt> - the best conversion method will depend on the value's specific meaning.
* Otherwise, it is up to the extracter to ensure that the value is a <tt>Serializable</tt>.
* It is not appropriate to implicitly convert values in order to make them <tt>Serializable</tt>
* - the best conversion method will depend on the value's specific meaning.
* *
* @param key the destination key * @param key
* @param value the serializable value * the destination key
* @param destination the map to put values into * @param value
* @return Returns <tt>true</tt> if set, otherwise <tt>false</tt> * the serializable value
* @param destination
* the map to put values into
* @return Returns <tt>true</tt> if set, otherwise <tt>false</tt>
*/ */
// Copied from the content repository's AbstractMappingMetadataExtracter. // Copied from the content repository's AbstractMappingMetadataExtracter.
protected boolean putRawValue(String key, Serializable value, Map<String, Serializable> destination) protected boolean putRawValue(String key, Serializable value, Map<String, Serializable> destination)
@@ -514,8 +498,7 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
String targetMimetype, OutputStream outputStream, Map<String, String> transformOptions, String targetMimetype, OutputStream outputStream, Map<String, String> transformOptions,
TransformManager transformManager) throws Exception; TransformManager transformManager) throws Exception;
private Map<String, Set<String>> getExtractMappingFromOptions(Map<String, String> transformOptions, Map<String, private Map<String, Set<String>> getExtractMappingFromOptions(Map<String, String> transformOptions, Map<String, Set<String>> defaultExtractMapping)
Set<String>> defaultExtractMapping)
{ {
String extractMappingOption = transformOptions.get(EXTRACT_MAPPING); String extractMappingOption = transformOptions.get(EXTRACT_MAPPING);
if (extractMappingOption != null) if (extractMappingOption != null)
@@ -527,19 +510,19 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
} }
catch (JsonProcessingException e) catch (JsonProcessingException e)
{ {
throw new IllegalArgumentException("Failed to read "+ EXTRACT_MAPPING +" from request", e); throw new IllegalArgumentException("Failed to read " + EXTRACT_MAPPING + " from request", e);
} }
} }
return defaultExtractMapping; return defaultExtractMapping;
} }
public void mapMetadataAndWrite(OutputStream outputStream, Map<String, Serializable> metadata, public void mapMetadataAndWrite(OutputStream outputStream, Map<String, Serializable> metadata,
Map<String, Set<String>> extractMapping) throws IOException Map<String, Set<String>> extractMapping) throws IOException
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Raw metadata:"); logger.debug("Raw metadata:");
metadata.forEach((k,v) -> logger.debug(" {}={}", k, v)); metadata.forEach((k, v) -> logger.debug(" {}={}", k, v));
} }
metadata = mapRawToSystem(metadata, extractMapping); metadata = mapRawToSystem(metadata, extractMapping);
@@ -549,12 +532,14 @@ public abstract class AbstractMetadataExtractorEmbedder implements CustomTransfo
/** /**
* Based on AbstractMappingMetadataExtracter#mapRawToSystem. * Based on AbstractMappingMetadataExtracter#mapRawToSystem.
* *
* @param rawMetadata Metadata keyed by document properties * @param rawMetadata
* @param extractMapping Mapping between document ans system properties * Metadata keyed by document properties
* @return Returns the metadata keyed by the system properties * @param extractMapping
* Mapping between document ans system properties
* @return Returns the metadata keyed by the system properties
*/ */
private Map<String, Serializable> mapRawToSystem(Map<String, Serializable> rawMetadata, private Map<String, Serializable> mapRawToSystem(Map<String, Serializable> rawMetadata,
Map<String, Set<String>> extractMapping) Map<String, Set<String>> extractMapping)
{ {
boolean debugEnabled = logger.isDebugEnabled(); boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) if (debugEnabled)

View File

@@ -37,7 +37,8 @@ public class FileRefEntity
{ {
private String fileRef; private String fileRef;
public FileRefEntity() {} public FileRefEntity()
{}
public FileRefEntity(String fileRef) public FileRefEntity(String fileRef)
{ {
@@ -57,8 +58,10 @@ public class FileRefEntity
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (o == null || getClass() != o.getClass())
return false;
FileRefEntity that = (FileRefEntity) o; FileRefEntity that = (FileRefEntity) o;
return Objects.equals(fileRef, that.fileRef); return Objects.equals(fileRef, that.fileRef);
} }

View File

@@ -35,7 +35,8 @@ public class FileRefResponse
{ {
private FileRefEntity entry; private FileRefEntity entry;
public FileRefResponse() {} public FileRefResponse()
{}
public FileRefResponse(FileRefEntity entry) public FileRefResponse(FileRefEntity entry)
{ {

View File

@@ -26,11 +26,11 @@
*/ */
package org.alfresco.transform.base.probes; package org.alfresco.transform.base.probes;
import org.alfresco.transform.base.transform.TransformHandler; import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE;
import org.alfresco.transform.base.logging.LogEntry; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.alfresco.transform.exceptions.TransformException; import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import static org.alfresco.transform.base.fs.FileManager.TempFileProvider.createTempFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -42,35 +42,33 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static org.alfresco.transform.base.fs.FileManager.TempFileProvider.createTempFile; import org.slf4j.Logger;
import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE; import org.slf4j.LoggerFactory;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; import org.alfresco.transform.base.logging.LogEntry;
import org.alfresco.transform.base.transform.TransformHandler;
import org.alfresco.transform.exceptions.TransformException;
/** /**
* Provides test transformations and the logic used by k8 liveness and readiness probes. * Provides test transformations and the logic used by k8 liveness and readiness probes.
* *
* <p><b>K8s probes</b>: A readiness probe indicates if the pod should accept request. <b>It does not indicate that a * <p>
* pod is ready after startup</b>. The liveness probe indicates when to kill the pod. <b>Both probes are called * <b>K8s probes</b>: A readiness probe indicates if the pod should accept request. <b>It does not indicate that a pod is ready after startup</b>. The liveness probe indicates when to kill the pod. <b>Both probes are called throughout the lifetime of the pod</b> and a <b>liveness probes can take place before a readiness probe.</b> The k8s <b>initialDelaySeconds field is not fully honoured</b> as it is multiplied by a random number, so is actually a maximum initial delay in seconds, but could be 0.
* throughout the lifetime of the pod</b> and a <b>liveness probes can take place before a readiness probe.</b> The k8s * </p>
* <b>initialDelaySeconds field is not fully honoured</b> as it is multiplied by a random number, so is
* actually a maximum initial delay in seconds, but could be 0. </p>
* *
* <p>Live and readiness probes do test transforms. The first 6 requests result in a transformation of a small test * <p>
* file. The average time and size is remembered, but excludes the first one as it is normally slower. This is * Live and readiness probes do test transforms. The first 6 requests result in a transformation of a small test file. The average time and size is remembered, but excludes the first one as it is normally slower. This is used in future requests to discover if transformations are becoming slower or unexpectedly change size.
* used in future requests to discover if transformations are becoming slower or unexpectedly change size.</p> * </p>
* *
* <p>If a transform longer than a maximum time, a maximum number of transforms have been performed, a test transform is * <p>
* an unexpected size or a test transform takes an unexpected time, then a non 200 status code is returned resulting in * If a transform longer than a maximum time, a maximum number of transforms have been performed, a test transform is an unexpected size or a test transform takes an unexpected time, then a non 200 status code is returned resulting in k8s terminating the pod. These are controlled by:
* k8s terminating the pod. These are controlled by:</p> * </p>
* <ul> * <ul>
* <li>expectedLength the expected length of the target file after a test transform</li> * <li>expectedLength the expected length of the target file after a test transform</li>
* <li>plusOrMinus allows for variation in the transformed size - generally caused by dates</li> * <li>plusOrMinus allows for variation in the transformed size - generally caused by dates</li>
* <li>livenessPercent allows for variation in transform time. Up to 2 and a half times is not * <li>livenessPercent allows for variation in transform time. Up to 2 and a half times is not unreasonable under load</li>
* unreasonable under load</li> * <li>maxTransforms the maximum number of transforms (not just test ones) before a restart is triggered</li>
* <li>maxTransforms the maximum number of transforms (not just test ones) before a restart is triggered</li> * <li>maxTransformSeconds a maximum time any transform (not just test ones) is allowed to take before a restart is triggered.</li>
* <li>maxTransformSeconds a maximum time any transform (not just test ones) is allowed to take before
* a restart is triggered.</li>
* <li>livenessTransformPeriodSeconds The number of seconds between test transforms done for live probes</li> * <li>livenessTransformPeriodSeconds The number of seconds between test transforms done for live probes</li>
* </ul> * </ul>
*/ */
@@ -114,8 +112,8 @@ public class ProbeTransform
} }
public ProbeTransform(String sourceFilename, String sourceMimetype, String targetMimetype, Map<String, String> transformOptions, public ProbeTransform(String sourceFilename, String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
long expectedLength, long plusOrMinus, int livenessPercent, long maxTransforms, long maxTransformSeconds, long expectedLength, long plusOrMinus, int livenessPercent, long maxTransforms, long maxTransformSeconds,
long livenessTransformPeriodSeconds) long livenessTransformPeriodSeconds)
{ {
this.sourceFilename = sourceFilename; this.sourceFilename = sourceFilename;
this.sourceMimetype = sourceMimetype; this.sourceMimetype = sourceMimetype;
@@ -128,7 +126,7 @@ public class ProbeTransform
maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms); maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms);
maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds) * 1000; maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds) * 1000;
livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds", livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds",
livenessTransformPeriodSeconds) * 1000; livenessTransformPeriodSeconds) * 1000;
livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false); livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false);
} }
@@ -139,8 +137,7 @@ public class ProbeTransform
return Boolean.parseBoolean(System.getenv(name)); return Boolean.parseBoolean(System.getenv(name));
} }
catch (Exception ignore) catch (Exception ignore)
{ {}
}
return defaultValue; return defaultValue;
} }
@@ -155,8 +152,7 @@ public class ProbeTransform
l = Long.parseLong(env); l = Long.parseLong(env);
} }
catch (NumberFormatException ignore) catch (NumberFormatException ignore)
{ {}
}
} }
if (l <= 0) if (l <= 0)
{ {
@@ -182,9 +178,9 @@ public class ProbeTransform
String result = (isLiveProbe && livenessTransformPeriod > 0 && String result = (isLiveProbe && livenessTransformPeriod > 0 &&
(transCount <= AVERAGE_OVER_TRANSFORMS || nextTransformTime < System.currentTimeMillis())) (transCount <= AVERAGE_OVER_TRANSFORMS || nextTransformTime < System.currentTimeMillis()))
|| !initialised.get() || !initialised.get()
? doTransform(isLiveProbe, transformHandler) ? doTransform(isLiveProbe, transformHandler)
: doNothing(isLiveProbe); : doNothing(isLiveProbe);
// Check if the test transformation was too slow. // Check if the test transformation was too slow.
checkMaxTransformTimeAndCount(isLiveProbe); checkMaxTransformTimeAndCount(isLiveProbe);
@@ -211,8 +207,7 @@ public class ProbeTransform
do do
{ {
nextTransformTime += livenessTransformPeriod; nextTransformTime += livenessTransformPeriod;
} } while (nextTransformTime < start);
while (nextTransformTime < start);
} }
File sourceFile = getSourceFile(isLiveProbe); File sourceFile = getSourceFile(isLiveProbe);
@@ -228,15 +223,15 @@ public class ProbeTransform
if (time > maxTime) if (time > maxTime)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
getMessagePrefix(isLiveProbe) + getMessagePrefix(isLiveProbe) +
message + " which is more than " + livenessPercent + message + " which is more than " + livenessPercent +
"% slower than the normal value of " + normalTime + "ms"); "% slower than the normal value of " + normalTime + "ms");
} }
// We don't care if the ready or live probe works out if we are 'ready' to take requests. // We don't care if the ready or live probe works out if we are 'ready' to take requests.
initialised.set(true); initialised.set(true);
return getProbeMessage(isLiveProbe) + "Success - "+message; return getProbeMessage(isLiveProbe) + "Success - " + message;
} }
private void checkMaxTransformTimeAndCount(boolean isLiveProbe) private void checkMaxTransformTimeAndCount(boolean isLiveProbe)
@@ -244,15 +239,15 @@ public class ProbeTransform
if (die.get()) if (die.get())
{ {
throw new TransformException(TOO_MANY_REQUESTS, throw new TransformException(TOO_MANY_REQUESTS,
getMessagePrefix(isLiveProbe) + "Transformer requested to die. A transform took " + getMessagePrefix(isLiveProbe) + "Transformer requested to die. A transform took " +
"longer than " + (maxTransformTime / 1000) + " seconds"); "longer than " + (maxTransformTime / 1000) + " seconds");
} }
if (maxTransformCount > 0 && transformCount.get() > maxTransformCount) if (maxTransformCount > 0 && transformCount.get() > maxTransformCount)
{ {
throw new TransformException(TOO_MANY_REQUESTS, throw new TransformException(TOO_MANY_REQUESTS,
getMessagePrefix(isLiveProbe) + "Transformer requested to die. It has performed " + getMessagePrefix(isLiveProbe) + "Transformer requested to die. It has performed " +
"more than " + maxTransformCount + " transformations"); "more than " + maxTransformCount + " transformations");
} }
} }
@@ -266,7 +261,7 @@ public class ProbeTransform
catch (IOException e) catch (IOException e)
{ {
throw new TransformException(INSUFFICIENT_STORAGE, throw new TransformException(INSUFFICIENT_STORAGE,
getMessagePrefix(isLiveProbe) + "Failed to store the source file", e); getMessagePrefix(isLiveProbe) + "Failed to store the source file", e);
} }
long length = sourceFile.length(); long length = sourceFile.length();
LogEntry.setSource(sourceFile.getName(), length); LogEntry.setSource(sourceFile.getName(), length);
@@ -301,7 +296,7 @@ public class ProbeTransform
maxTime = (normalTime * (livenessPercent + 100)) / 100; maxTime = (normalTime * (livenessPercent + 100)) / 100;
if ((!isLiveProbe && !readySent.getAndSet( if ((!isLiveProbe && !readySent.getAndSet(
true)) || transCount > AVERAGE_OVER_TRANSFORMS) true)) || transCount > AVERAGE_OVER_TRANSFORMS)
{ {
nextTransformTime = System.currentTimeMillis() + livenessTransformPeriod; nextTransformTime = System.currentTimeMillis() + livenessTransformPeriod;
logger.trace("{} - {}ms+{}%={}ms", message, normalTime, livenessPercent, maxTime); logger.trace("{} - {}ms+{}%={}ms", message, normalTime, livenessPercent, maxTime);
@@ -320,16 +315,16 @@ public class ProbeTransform
if (!targetFile.exists() || !targetFile.isFile()) if (!targetFile.exists() || !targetFile.isFile())
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
probeMessage + "Target File \"" + targetFile.getAbsolutePath() + "\" did not exist"); probeMessage + "Target File \"" + targetFile.getAbsolutePath() + "\" did not exist");
} }
long length = targetFile.length(); long length = targetFile.length();
targetFile.delete(); targetFile.delete();
if (length < minExpectedLength || length > maxExpectedLength) if (length < minExpectedLength || length > maxExpectedLength)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
probeMessage + "Target File \"" + targetFile.getAbsolutePath() + probeMessage + "Target File \"" + targetFile.getAbsolutePath() +
"\" was the wrong size (" + length + "). Needed to be between " + "\" was the wrong size (" + length + "). Needed to be between " +
minExpectedLength + " and " + maxExpectedLength); minExpectedLength + " and " + maxExpectedLength);
} }
} }

View File

@@ -56,4 +56,4 @@ public abstract class AbstractTransformConfigSource implements TransformConfigSo
{ {
return baseUrl; return baseUrl;
} }
} }

View File

@@ -26,18 +26,19 @@
*/ */
package org.alfresco.transform.base.registry; package org.alfresco.transform.base.registry;
import org.alfresco.transform.base.CustomTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.alfresco.transform.base.CustomTransformer;
@Component @Component
public class CustomTransformers public class CustomTransformers
@@ -54,22 +55,21 @@ public class CustomTransformers
{ {
if (customTransformerList != null) if (customTransformerList != null)
{ {
customTransformerList.forEach(customTransformer -> customTransformerList.forEach(customTransformer -> customTransformersByName.put(customTransformer.getTransformerName(), customTransformer));
customTransformersByName.put(customTransformer.getTransformerName(), customTransformer));
List<String> nonNullTransformerNames = customTransformerList.stream() List<String> nonNullTransformerNames = customTransformerList.stream()
.map(CustomTransformer::getTransformerName) .map(CustomTransformer::getTransformerName)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!nonNullTransformerNames.isEmpty()) if (!nonNullTransformerNames.isEmpty())
{ {
logger.info("Custom Transformers:"); logger.info("Custom Transformers:");
nonNullTransformerNames nonNullTransformerNames
.stream() .stream()
.sorted() .sorted()
.map(name -> " "+name) .map(name -> " " + name)
.forEach(logger::debug); .forEach(logger::debug);
} }
} }
} }
@@ -80,7 +80,7 @@ public class CustomTransformers
return customTransformer == null ? customTransformersByName.get(null) : customTransformer; return customTransformer == null ? customTransformersByName.get(null) : customTransformer;
} }
public void put(String name, CustomTransformer customTransformer) public void put(String name, CustomTransformer customTransformer)
{ {
customTransformersByName.put(name, customTransformer); customTransformersByName.put(name, customTransformer);
} }

View File

@@ -26,14 +26,14 @@
*/ */
package org.alfresco.transform.base.registry; package org.alfresco.transform.base.registry;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
@Configuration @Configuration
@ConfigurationProperties(prefix = "transform.config") @ConfigurationProperties(prefix = "transform.config")
public class TransformConfigFiles public class TransformConfigFiles

View File

@@ -26,18 +26,18 @@
*/ */
package org.alfresco.transform.base.registry; package org.alfresco.transform.base.registry;
import org.springframework.beans.factory.annotation.Value; import static org.alfresco.transform.base.registry.TransformConfigFromFiles.retrieveResource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.alfresco.transform.base.registry.TransformConfigFromFiles.retrieveResource; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/** /**
* Similar to {@link TransformConfigFiles} but uses the names historically used by the t-router. * Similar to {@link TransformConfigFiles} but uses the names historically used by the t-router.
@@ -50,8 +50,9 @@ public class TransformConfigFilesHistoric
// environment variables like TRANSFORMER_ROUTES_ADDITIONAL_<engineName>. // environment variables like TRANSFORMER_ROUTES_ADDITIONAL_<engineName>.
private final Map<String, String> additional = new HashMap<>(); private final Map<String, String> additional = new HashMap<>();
//Used by ConfigurationProperties annotation // Used by ConfigurationProperties annotation
public Map<String, String> getAdditional() { public Map<String, String> getAdditional()
{
return additional; return additional;
} }

View File

@@ -26,8 +26,14 @@
*/ */
package org.alfresco.transform.base.registry; package org.alfresco.transform.base.registry;
import org.alfresco.transform.config.reader.TransformConfigResourceReader; import static java.util.stream.Collectors.toList;
import org.alfresco.transform.config.TransformConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@@ -35,13 +41,8 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct; import org.alfresco.transform.config.TransformConfig;
import java.util.ArrayList; import org.alfresco.transform.config.reader.TransformConfigResourceReader;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static java.util.stream.Collectors.toList;
/** /**
* Makes {@link TransformConfig} from files on the classpath or externally available to the {@link TransformRegistry}. * Makes {@link TransformConfig} from files on the classpath or externally available to the {@link TransformRegistry}.
@@ -66,30 +67,29 @@ public class TransformConfigFromFiles
final List<Resource> resources = new ArrayList<>(); final List<Resource> resources = new ArrayList<>();
resources.addAll(transformConfigFiles.retrieveResources()); resources.addAll(transformConfigFiles.retrieveResources());
resources.addAll(transformConfigFilesHistoric.retrieveResources()); resources.addAll(transformConfigFilesHistoric.retrieveResources());
resources.forEach(resource -> resources.forEach(resource -> {
{
String filename = resource.getFilename(); String filename = resource.getFilename();
transformConfigSources.add( transformConfigSources.add(
new AbstractTransformConfigSource(filename, filename, isTRouter ? null : "---") new AbstractTransformConfigSource(filename, filename, isTRouter ? null : "---") {
{ @Override
@Override public TransformConfig getTransformConfig() public TransformConfig getTransformConfig()
{ {
return transformConfigResourceReader.read(resource); return transformConfigResourceReader.read(resource);
} }
}); });
}); });
} }
public static List<Resource> retrieveResources(Map<String, String> additional) public static List<Resource> retrieveResources(Map<String, String> additional)
{ {
return additional return additional
.values() .values()
.stream() .stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(String::trim) .map(String::trim)
.filter(s -> !s.isBlank()) .filter(s -> !s.isBlank())
.map(TransformConfigFromFiles::retrieveResource) .map(TransformConfigFromFiles::retrieveResource)
.collect(toList()); .collect(toList());
} }
public static Resource retrieveResource(final String filename) public static Resource retrieveResource(final String filename)

View File

@@ -26,14 +26,15 @@
*/ */
package org.alfresco.transform.base.registry; package org.alfresco.transform.base.registry;
import org.alfresco.transform.base.TransformEngine; import java.util.List;
import org.alfresco.transform.config.TransformConfig; import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct; import org.alfresco.transform.base.TransformEngine;
import java.util.List; import org.alfresco.transform.config.TransformConfig;
/** /**
* Makes {@link TransformConfig} from {@link TransformEngine}s available to the {@link TransformRegistry}. * Makes {@link TransformConfig} from {@link TransformEngine}s available to the {@link TransformRegistry}.
@@ -54,21 +55,21 @@ public class TransformConfigFromTransformEngines
if (transformEngines != null) if (transformEngines != null)
{ {
transformEngines.stream() transformEngines.stream()
.forEach(transformEngine -> { .forEach(transformEngine -> {
TransformConfig transformConfig = transformEngine.getTransformConfig(); TransformConfig transformConfig = transformEngine.getTransformConfig();
if (transformConfig != null) // if not a wrapping TransformEngine like all-in-one if (transformConfig != null) // if not a wrapping TransformEngine like all-in-one
{ {
String engineName = transformEngine.getTransformEngineName(); String engineName = transformEngine.getTransformEngineName();
transformConfigSources.add( transformConfigSources.add(
new AbstractTransformConfigSource(engineName, engineName, isTRouter ? null : "---") new AbstractTransformConfigSource(engineName, engineName, isTRouter ? null : "---") {
{ @Override
@Override public TransformConfig getTransformConfig() public TransformConfig getTransformConfig()
{ {
return transformEngine.getTransformConfig(); return transformEngine.getTransformConfig();
} }
}); });
} }
}); });
} }
} }
} }

View File

@@ -25,17 +25,26 @@
*/ */
package org.alfresco.transform.base.registry; package org.alfresco.transform.base.registry;
import org.alfresco.transform.base.CustomTransformer; import static java.util.Collections.emptyMap;
import org.alfresco.transform.config.TransformConfig; import static java.util.Objects.isNull;
import org.alfresco.transform.config.TransformOption; import static java.util.stream.Collectors.toUnmodifiableMap;
import org.alfresco.transform.config.TransformOptionGroup; import static java.util.stream.Collectors.toUnmodifiableSet;
import org.alfresco.transform.config.TransformOptionValue;
import org.alfresco.transform.config.Transformer; import static org.springframework.util.CollectionUtils.isEmpty;
import org.alfresco.transform.registry.AbstractTransformRegistry;
import org.alfresco.transform.registry.CombinedTransformConfig; import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnSingleStepTransformers;
import org.alfresco.transform.registry.Origin; import static org.alfresco.transform.registry.TransformerType.FAILOVER_TRANSFORMER;
import org.alfresco.transform.registry.TransformCache; import static org.alfresco.transform.registry.TransformerType.PIPELINE_TRANSFORMER;
import org.alfresco.transform.registry.TransformerType;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -49,26 +58,20 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import org.alfresco.transform.base.CustomTransformer;
import java.util.Map.Entry; import org.alfresco.transform.config.TransformConfig;
import java.util.concurrent.atomic.AtomicBoolean; import org.alfresco.transform.config.TransformOption;
import java.util.concurrent.locks.ReadWriteLock; import org.alfresco.transform.config.TransformOptionGroup;
import java.util.concurrent.locks.ReentrantReadWriteLock; import org.alfresco.transform.config.TransformOptionValue;
import java.util.function.Supplier; import org.alfresco.transform.config.Transformer;
import java.util.stream.Collectors; import org.alfresco.transform.registry.AbstractTransformRegistry;
import java.util.stream.Stream; import org.alfresco.transform.registry.CombinedTransformConfig;
import org.alfresco.transform.registry.Origin;
import static java.util.Collections.emptyMap; import org.alfresco.transform.registry.TransformCache;
import static java.util.Objects.isNull; import org.alfresco.transform.registry.TransformerType;
import static java.util.stream.Collectors.toUnmodifiableMap;
import static java.util.stream.Collectors.toUnmodifiableSet;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnSingleStepTransformers;
import static org.alfresco.transform.registry.TransformerType.FAILOVER_TRANSFORMER;
import static org.alfresco.transform.registry.TransformerType.PIPELINE_TRANSFORMER;
import static org.springframework.util.CollectionUtils.isEmpty;
@Service @Service
@Scope( proxyMode = ScopedProxyMode.TARGET_CLASS ) @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TransformRegistry extends AbstractTransformRegistry public class TransformRegistry extends AbstractTransformRegistry
{ {
private static final Logger logger = LoggerFactory.getLogger(TransformRegistry.class); private static final Logger logger = LoggerFactory.getLogger(TransformRegistry.class);
@@ -91,7 +94,7 @@ public class TransformRegistry extends AbstractTransformRegistry
{ {
private TransformConfig transformConfig; private TransformConfig transformConfig;
private TransformConfig uncombinedTransformConfig; private TransformConfig uncombinedTransformConfig;
private Map<String,Origin<Transformer>> transformerByNameMap; private Map<String, Origin<Transformer>> transformerByNameMap;
public TransformConfig getTransformConfig() public TransformConfig getTransformConfig()
{ {
@@ -143,8 +146,7 @@ public class TransformRegistry extends AbstractTransformRegistry
} }
/** /**
* Load the registry on application startup. This allows Components in projects that extend the t-engine base * Load the registry on application startup. This allows Components in projects that extend the t-engine base to use @PostConstruct to add to {@code transformConfigSources}, before the registry is loaded.
* to use @PostConstruct to add to {@code transformConfigSources}, before the registry is loaded.
*/ */
@Async @Async
void initRegistryOnAppStartup(final ContextRefreshedEvent event) void initRegistryOnAppStartup(final ContextRefreshedEvent event)
@@ -170,7 +172,7 @@ public class TransformRegistry extends AbstractTransformRegistry
fixedDelayString = "#{${transform.engine.config.retry.timeout} * 1000}") fixedDelayString = "#{${transform.engine.config.retry.timeout} * 1000}")
public void retrieveEngineConfigsAfterFailure() public void retrieveEngineConfigsAfterFailure()
{ {
if(isRecoveryModeOn.get()) if (isRecoveryModeOn.get())
{ {
logger.trace("Recovery mode, attempting to retrieve configs for all registered T-Engines."); logger.trace("Recovery mode, attempting to retrieve configs for all registered T-Engines.");
retrieveConfig(); retrieveConfig();
@@ -191,8 +193,7 @@ public class TransformRegistry extends AbstractTransformRegistry
TransformConfig transformConfig = source.getTransformConfig(); TransformConfig transformConfig = source.getTransformConfig();
availableTransformers.put( availableTransformers.put(
sortOnName, sortOnName,
new LocalTransformConfigSource(transformConfig, sortOnName, source.getReadFrom(), source.getBaseUrl()) new LocalTransformConfigSource(transformConfig, sortOnName, source.getReadFrom(), source.getBaseUrl()));
);
} }
catch (IllegalStateException e) catch (IllegalStateException e)
{ {
@@ -204,14 +205,13 @@ public class TransformRegistry extends AbstractTransformRegistry
{ {
logger.warn( logger.warn(
"Failed to retrieved TransformConfig during refreshment. Stops refreshing TransformRegistry. {}", "Failed to retrieved TransformConfig during refreshment. Stops refreshing TransformRegistry. {}",
e.getMessage() e.getMessage());
);
return; return;
} }
} }
} }
if(transformConfigSources.size() == availableTransformers.size() if (transformConfigSources.size() == availableTransformers.size()
&& isRecoveryModeOn.compareAndExchange(true, false)) && isRecoveryModeOn.compareAndExchange(true, false))
{ {
logger.trace("All TransformConfigSources have been retrieved, turning off recovery mode."); logger.trace("All TransformConfigSources have been retrieved, turning off recovery mode.");
@@ -219,11 +219,11 @@ public class TransformRegistry extends AbstractTransformRegistry
logger.debug("Creating CombinedTransformConfig."); logger.debug("Creating CombinedTransformConfig.");
availableTransformers.values().forEach(source -> { availableTransformers.values().forEach(source -> {
TransformConfig transformConfig = source.getTransformConfig(); TransformConfig transformConfig = source.getTransformConfig();
setCoreVersionOnSingleStepTransformers(transformConfig, coreVersion); setCoreVersionOnSingleStepTransformers(transformConfig, coreVersion);
combinedTransformConfig.addTransformConfig(transformConfig, source.getReadFrom(), source.getBaseUrl(), combinedTransformConfig.addTransformConfig(transformConfig, source.getReadFrom(), source.getBaseUrl(),
this); this);
}); });
TransformConfig uncombinedTransformConfig = combinedTransformConfig.buildTransformConfig(); TransformConfig uncombinedTransformConfig = combinedTransformConfig.buildTransformConfig();
combinedTransformConfig.combineTransformerConfig(this); combinedTransformConfig.combineTransformerConfig(this);
@@ -239,48 +239,48 @@ public class TransformRegistry extends AbstractTransformRegistry
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
{ {
Set<String> customTransformerNames = new HashSet<>(customTransformerList == null Set<String> customTransformerNames = new HashSet<>(customTransformerList == null
? Collections.emptySet() ? Collections.emptySet()
: customTransformerList.stream().map(CustomTransformer::getTransformerName).collect(Collectors.toSet())); : customTransformerList.stream().map(CustomTransformer::getTransformerName).collect(Collectors.toSet()));
List<String> nonNullTransformerNames = uncombinedTransformConfig.getTransformers().stream() List<String> nonNullTransformerNames = uncombinedTransformConfig.getTransformers().stream()
.map(Transformer::getTransformerName) .map(Transformer::getTransformerName)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
ArrayList<String> logMessages = new ArrayList<>(); ArrayList<String> logMessages = new ArrayList<>();
if (!nonNullTransformerNames.isEmpty()) if (!nonNullTransformerNames.isEmpty())
{ {
logMessages.add("Transformers (" + nonNullTransformerNames.size() + ") Transforms (" + getData().getTransformCount()+ "):"); logMessages.add("Transformers (" + nonNullTransformerNames.size() + ") Transforms (" + getData().getTransformCount() + "):");
nonNullTransformerNames nonNullTransformerNames
.stream() .stream()
.sorted(String.CASE_INSENSITIVE_ORDER) .sorted(String.CASE_INSENSITIVE_ORDER)
.map(name -> { .map(name -> {
Origin<Transformer> transformerOrigin = transformerByNameMap.get(name); Origin<Transformer> transformerOrigin = transformerByNameMap.get(name);
String message = " " + name + (transformerOrigin == null String message = " " + name + (transformerOrigin == null
? " -- unavailable: see previous messages" ? " -- unavailable: see previous messages"
: isTRouter : isTRouter
? "" ? ""
: TransformerType.valueOf(transformerOrigin.get()) == PIPELINE_TRANSFORMER : TransformerType.valueOf(transformerOrigin.get()) == PIPELINE_TRANSFORMER
? " -- unavailable: pipeline only available via t-router" ? " -- unavailable: pipeline only available via t-router"
: TransformerType.valueOf(transformerOrigin.get()) == FAILOVER_TRANSFORMER : TransformerType.valueOf(transformerOrigin.get()) == FAILOVER_TRANSFORMER
? " -- unavailable: failover only available via t-router" ? " -- unavailable: failover only available via t-router"
: !customTransformerNames.contains(name) : !customTransformerNames.contains(name)
? " -- missing: CustomTransformer" ? " -- missing: CustomTransformer"
: ""); : "");
customTransformerNames.remove(name); customTransformerNames.remove(name);
return message; return message;
}) })
.forEach(logMessages::add); .forEach(logMessages::add);
List<String> unusedCustomTransformNames = customTransformerNames.stream() List<String> unusedCustomTransformNames = customTransformerNames.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!unusedCustomTransformNames.isEmpty()) if (!unusedCustomTransformNames.isEmpty())
{ {
logMessages.add("Unused CustomTransformers (" + unusedCustomTransformNames.size() + ") - name is not in the transform config:"); logMessages.add("Unused CustomTransformers (" + unusedCustomTransformNames.size() + ") - name is not in the transform config:");
unusedCustomTransformNames unusedCustomTransformNames
.stream() .stream()
.map(name -> " " + name) .map(name -> " " + name)
.forEach(logMessages::add); .forEach(logMessages::add);
} }
int logMessageHashCode = logMessages.hashCode(); int logMessageHashCode = logMessages.hashCode();
@@ -301,8 +301,8 @@ public class TransformRegistry extends AbstractTransformRegistry
{ {
Data data = getData(); Data data = getData();
return isTRouter return isTRouter
? data.getTransformConfig() ? data.getTransformConfig()
: data.getUncombinedTransformConfig(); : data.getUncombinedTransformConfig();
} }
/** /**
@@ -321,15 +321,15 @@ public class TransformRegistry extends AbstractTransformRegistry
@Override @Override
public Data getData() public Data getData()
{ {
return concurrentRead(() -> data ); return concurrentRead(() -> data);
} }
/** /**
* Lock for reads while updating, use {@link #concurrentRead} to access locked fields * Lock for reads while updating, use {@link #concurrentRead} to access locked fields
*/ */
private void concurrentUpdate(CombinedTransformConfig combinedTransformConfig, private void concurrentUpdate(CombinedTransformConfig combinedTransformConfig,
TransformConfig uncombinedTransformConfig, TransformConfig transformConfig, TransformConfig uncombinedTransformConfig, TransformConfig transformConfig,
Map<String, Origin<Transformer>> transformerByNameMap) Map<String, Origin<Transformer>> transformerByNameMap)
{ {
configRefreshLock.writeLock().lock(); configRefreshLock.writeLock().lock();
try try
@@ -372,10 +372,9 @@ public class TransformRegistry extends AbstractTransformRegistry
} }
public Transformer getTransformer(final String sourceMediaType, final Long fileSizeBytes, public Transformer getTransformer(final String sourceMediaType, final Long fileSizeBytes,
final String targetMediaType, final Map<String, String> transformOptions) final String targetMediaType, final Map<String, String> transformOptions)
{ {
return concurrentRead(() -> return concurrentRead(() -> {
{
long fileSize = fileSizeBytes == null ? 0 : fileSizeBytes; long fileSize = fileSizeBytes == null ? 0 : fileSizeBytes;
String transformerName = findTransformerName(sourceMediaType, fileSize, targetMediaType, transformOptions, null); String transformerName = findTransformerName(sourceMediaType, fileSize, targetMediaType, transformOptions, null);
return getTransformer(transformerName); return getTransformer(transformerName);
@@ -395,15 +394,10 @@ public class TransformRegistry extends AbstractTransformRegistry
public boolean checkSourceSize(String transformerName, String sourceMediaType, Long sourceSize, String targetMediaType) public boolean checkSourceSize(String transformerName, String sourceMediaType, Long sourceSize, String targetMediaType)
{ {
return Optional.ofNullable(getTransformer(transformerName)). return Optional.ofNullable(getTransformer(transformerName)).map(transformer -> transformer.getSupportedSourceAndTargetList().stream().filter(supported -> supported.getSourceMediaType().equals(sourceMediaType) &&
map(transformer -> transformer.getSupportedSourceAndTargetList().stream(). supported.getTargetMediaType().equals(targetMediaType)).findFirst().map(supported -> supported.getMaxSourceSizeBytes() == -1 ||
filter(supported -> supported.getSourceMediaType().equals(sourceMediaType) && supported.getMaxSourceSizeBytes() >= sourceSize)
supported.getTargetMediaType().equals(targetMediaType)). .orElse(false)).orElse(false);
findFirst().
map(supported -> supported.getMaxSourceSizeBytes() == -1 ||
supported.getMaxSourceSizeBytes() >= sourceSize).
orElse(false)).
orElse(false);
} }
public String getEngineName(String transformerName) public String getEngineName(String transformerName)
@@ -447,9 +441,9 @@ public class TransformRegistry extends AbstractTransformRegistry
if (option instanceof TransformOptionGroup) if (option instanceof TransformOptionGroup)
{ {
return ((TransformOptionGroup) option) return ((TransformOptionGroup) option)
.getTransformOptions() .getTransformOptions()
.stream() .stream()
.flatMap(TransformRegistry::retrieveOptionsStrings); .flatMap(TransformRegistry::retrieveOptionsStrings);
} }
return Stream.of(((TransformOptionValue) option).getName()); return Stream.of(((TransformOptionValue) option).getName());
} }

View File

@@ -33,10 +33,9 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import java.io.File; import java.io.File;
import javax.net.ssl.SSLException;
import jakarta.annotation.PostConstruct;
import org.alfresco.transform.base.WebClientBuilderAdjuster;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transform.base.model.FileRefResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -54,8 +53,9 @@ import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import jakarta.annotation.PostConstruct; import org.alfresco.transform.base.WebClientBuilderAdjuster;
import javax.net.ssl.SSLException; import org.alfresco.transform.base.model.FileRefResponse;
import org.alfresco.transform.exceptions.TransformException;
/** /**
* Simple Rest client that call Alfresco Shared File Store * Simple Rest client that call Alfresco Shared File Store
@@ -77,19 +77,21 @@ public class SharedFileStoreClient
private WebClient client; private WebClient client;
@PostConstruct @PostConstruct
public void init() throws SSLException { public void init() throws SSLException
{
final WebClient.Builder clientBuilder = WebClient.builder(); final WebClient.Builder clientBuilder = WebClient.builder();
adjuster.adjust(clientBuilder); adjuster.adjust(clientBuilder);
client = clientBuilder.baseUrl(url.endsWith("/") ? url : url + "/") client = clientBuilder.baseUrl(url.endsWith("/") ? url : url + "/")
.defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE)
.build(); .build();
} }
/** /**
* Retrieves a file from Shared File Store using given file reference * Retrieves a file from Shared File Store using given file reference
* *
* @param fileRef File reference * @param fileRef
* File reference
* @return ResponseEntity<Resource> * @return ResponseEntity<Resource>
*/ */
public ResponseEntity<Resource> retrieveFile(String fileRef) public ResponseEntity<Resource> retrieveFile(String fileRef)
@@ -97,7 +99,7 @@ public class SharedFileStoreClient
try try
{ {
return restTemplate.getForEntity(url + "/" + fileRef, return restTemplate.getForEntity(url + "/" + fileRef,
org.springframework.core.io.Resource.class); org.springframework.core.io.Resource.class);
} }
catch (HttpClientErrorException e) catch (HttpClientErrorException e)
{ {
@@ -108,7 +110,8 @@ public class SharedFileStoreClient
/** /**
* Stores given file in Shared File Store * Stores given file in Shared File Store
* *
* @param file File to be stored * @param file
* File to be stored
* @return A FileRefResponse containing detail about file's reference * @return A FileRefResponse containing detail about file's reference
*/ */
public FileRefResponse saveFile(File file) public FileRefResponse saveFile(File file)
@@ -121,9 +124,9 @@ public class SharedFileStoreClient
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map,
headers); headers);
ResponseEntity<FileRefResponse> responseEntity = restTemplate ResponseEntity<FileRefResponse> responseEntity = restTemplate
.exchange(url, POST, requestEntity, FileRefResponse.class); .exchange(url, POST, requestEntity, FileRefResponse.class);
return responseEntity.getBody(); return responseEntity.getBody();
} }
catch (HttpClientErrorException e) catch (HttpClientErrorException e)
@@ -140,7 +143,7 @@ public class SharedFileStoreClient
logger.debug(" Deleting intermediate file {}", fileReference); logger.debug(" Deleting intermediate file {}", fileReference);
client.delete().uri(fileReference) client.delete().uri(fileReference)
.exchange().block(); .exchange().block();
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -26,12 +26,12 @@
*/ */
package org.alfresco.transform.base.transform; package org.alfresco.transform.base.transform;
import org.alfresco.transform.exceptions.TransformException; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import org.alfresco.transform.exceptions.TransformException;
/** /**
* Separation of transform fragments logic from the {@link ProcessHandler} logic and {@link StreamHandler}. * Separation of transform fragments logic from the {@link ProcessHandler} logic and {@link StreamHandler}.
@@ -42,8 +42,7 @@ public abstract class FragmentHandler extends StreamHandler
private boolean noMoreFragments; private boolean noMoreFragments;
protected void initTarget() protected void initTarget()
{ {}
}
public OutputStream respondWithFragment(Integer index, boolean finished) throws IOException public OutputStream respondWithFragment(Integer index, boolean finished) throws IOException
{ {
@@ -74,8 +73,7 @@ public abstract class FragmentHandler extends StreamHandler
} }
protected void logFragment(Integer index, Long outputLength) protected void logFragment(Integer index, Long outputLength)
{ {}
}
@Override @Override
protected void handleSuccessfulTransform() throws IOException protected void handleSuccessfulTransform() throws IOException

View File

@@ -26,22 +26,17 @@
*/ */
package org.alfresco.transform.base.transform; package org.alfresco.transform.base.transform;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
/** /**
* Separation of InputStream, OutputStream, sourceFile and targetFile from the {@link ProcessHandler} logic. Allows * Separation of InputStream, OutputStream, sourceFile and targetFile from the {@link ProcessHandler} logic. Allows {@link CustomTransformer} implementations to call {@link TransformManager#createSourceFile()} and {@link TransformManager#createTargetFile()} so that extra Files are not created if there was one already in existence.
* {@link CustomTransformer} implementations to call {@link TransformManager#createSourceFile()} and
* {@link TransformManager#createTargetFile()} so that extra Files are not created if there was one already in
* existence.
* *
* Subclasses MUST call transformManager.setSourceFile(File) and transformManager.setSourceFile(File) if they start * Subclasses MUST call transformManager.setSourceFile(File) and transformManager.setSourceFile(File) if they start with files rather than streams, before calling the {@link #init()} method which calls transformManager.setOutputStream(InputStream) and transformManager.setOutputStream(OutputStream).
* with files rather than streams, before calling the {@link #init()} method which calls
* transformManager.setOutputStream(InputStream) and transformManager.setOutputStream(OutputStream).
*/ */
public abstract class StreamHandler public abstract class StreamHandler
{ {
@@ -95,8 +90,7 @@ public abstract class StreamHandler
} }
protected void onSuccessfulTransform() protected void onSuccessfulTransform()
{ {}
}
protected void closeOutputStream() throws IOException protected void closeOutputStream() throws IOException
{ {
@@ -112,8 +106,7 @@ public abstract class StreamHandler
inputStream.close(); inputStream.close();
} }
catch (IOException ignore) catch (IOException ignore)
{ {}
}
} }
} }

View File

@@ -26,34 +26,18 @@
*/ */
package org.alfresco.transform.base.transform; package org.alfresco.transform.base.transform;
import org.alfresco.transform.base.sfs.SharedFileStoreClient; import static java.util.stream.Collectors.joining;
import org.alfresco.transform.base.messaging.TransformReplySender;
import org.alfresco.transform.base.model.FileRefResponse; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.base.probes.ProbeTransform; import static org.springframework.http.HttpStatus.CREATED;
import org.alfresco.transform.base.registry.CustomTransformers; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.alfresco.transform.client.model.InternalContext;
import org.alfresco.transform.client.model.TransformReply; import static org.alfresco.transform.base.fs.FileManager.createAttachment;
import org.alfresco.transform.client.model.TransformRequest; import static org.alfresco.transform.base.fs.FileManager.createTargetFile;
import org.alfresco.transform.common.ExtensionService; import static org.alfresco.transform.base.fs.FileManager.getDirectAccessUrlInputStream;
import org.alfresco.transform.exceptions.TransformException; import static org.alfresco.transform.base.fs.FileManager.getMultipartFileInputStream;
import org.alfresco.transform.common.TransformerDebug; import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import org.alfresco.transform.messages.TransformRequestValidator;
import org.alfresco.transform.messages.TransformStack;
import org.alfresco.transform.registry.TransformServiceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.validation.DirectFieldBindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.multipart.MultipartFile;
import jakarta.jms.Destination;
import jakarta.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
@@ -66,16 +50,35 @@ import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import jakarta.jms.Destination;
import jakarta.servlet.http.HttpServletRequest;
import static java.util.stream.Collectors.joining; import org.slf4j.Logger;
import static org.alfresco.transform.base.fs.FileManager.createAttachment; import org.slf4j.LoggerFactory;
import static org.alfresco.transform.base.fs.FileManager.createTargetFile; import org.springframework.beans.factory.annotation.Autowired;
import static org.alfresco.transform.base.fs.FileManager.getDirectAccessUrlInputStream; import org.springframework.core.io.Resource;
import static org.alfresco.transform.base.fs.FileManager.getMultipartFileInputStream; import org.springframework.http.HttpStatus;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL; import org.springframework.http.ResponseEntity;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import org.springframework.stereotype.Component;
import static org.springframework.http.HttpStatus.CREATED; import org.springframework.validation.DirectFieldBindingResult;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import org.springframework.validation.Errors;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.multipart.MultipartFile;
import org.alfresco.transform.base.messaging.TransformReplySender;
import org.alfresco.transform.base.model.FileRefResponse;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.base.registry.CustomTransformers;
import org.alfresco.transform.base.sfs.SharedFileStoreClient;
import org.alfresco.transform.client.model.InternalContext;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.common.ExtensionService;
import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transform.messages.TransformRequestValidator;
import org.alfresco.transform.messages.TransformStack;
import org.alfresco.transform.registry.TransformServiceRegistry;
/** /**
* Handles the transform requests from either http or a message. * Handles the transform requests from either http or a message.
@@ -109,9 +112,8 @@ public class TransformHandler
AtomicReference<ResponseEntity<Resource>> responseEntity = new AtomicReference<>(); AtomicReference<ResponseEntity<Resource>> responseEntity = new AtomicReference<>();
new ProcessHandler(sourceMimetype, targetMimetype, requestParameters, new ProcessHandler(sourceMimetype, targetMimetype, requestParameters,
"e" + httpRequestCount.getAndIncrement(), transformRegistry, "e" + httpRequestCount.getAndIncrement(), transformRegistry,
transformerDebug, probeTransform, customTransformers) transformerDebug, probeTransform, customTransformers) {
{
@Override @Override
protected void init() throws IOException protected void init() throws IOException
{ {
@@ -143,7 +145,7 @@ public class TransformHandler
protected void sendTransformResponse(TransformManagerImpl transformManager) protected void sendTransformResponse(TransformManagerImpl transformManager)
{ {
String extension = ExtensionService.getExtensionForTargetMimetype(targetMimetype, sourceMimetype); String extension = ExtensionService.getExtensionForTargetMimetype(targetMimetype, sourceMimetype);
responseEntity.set(createAttachment("transform."+extension, transformManager.getTargetFile())); responseEntity.set(createAttachment("transform." + extension, transformManager.getTargetFile()));
} }
}.handleTransformRequest(); }.handleTransformRequest();
@@ -151,12 +153,11 @@ public class TransformHandler
} }
public void handleProbeRequest(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions, public void handleProbeRequest(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
File sourceFile, File targetFile, ProbeTransform probeTransform) File sourceFile, File targetFile, ProbeTransform probeTransform)
{ {
new ProcessHandler(sourceMimetype, targetMimetype, transformOptions, new ProcessHandler(sourceMimetype, targetMimetype, transformOptions,
"p" + httpRequestCount.getAndIncrement(), transformRegistry, "p" + httpRequestCount.getAndIncrement(), transformRegistry,
transformerDebug, probeTransform, customTransformers) transformerDebug, probeTransform, customTransformers) {
{
@Override @Override
protected void init() throws IOException protected void init() throws IOException
{ {
@@ -187,13 +188,12 @@ public class TransformHandler
} }
public TransformReply handleMessageRequest(TransformRequest request, Long timeout, Destination replyToQueue, public TransformReply handleMessageRequest(TransformRequest request, Long timeout, Destination replyToQueue,
ProbeTransform probeTransform) ProbeTransform probeTransform)
{ {
TransformReply reply = createBasicTransformReply(request); TransformReply reply = createBasicTransformReply(request);
new ProcessHandler(request.getSourceMediaType(), request.getTargetMediaType(), new ProcessHandler(request.getSourceMediaType(), request.getTargetMediaType(),
request.getTransformRequestOptions(),"unset", transformRegistry, request.getTransformRequestOptions(), "unset", transformRegistry,
transformerDebug, probeTransform, customTransformers) transformerDebug, probeTransform, customTransformers) {
{
@Override @Override
protected void init() throws IOException protected void init() throws IOException
{ {
@@ -337,12 +337,12 @@ public class TransformHandler
} }
private InputStream getInputStreamForHandleHttpRequest(Map<String, String> requestParameters, private InputStream getInputStreamForHandleHttpRequest(Map<String, String> requestParameters,
MultipartFile sourceMultipartFile) MultipartFile sourceMultipartFile)
{ {
final String directUrl = requestParameters.getOrDefault(DIRECT_ACCESS_URL, ""); final String directUrl = requestParameters.getOrDefault(DIRECT_ACCESS_URL, "");
return new BufferedInputStream(directUrl.isBlank() return new BufferedInputStream(directUrl.isBlank()
? getMultipartFileInputStream(sourceMultipartFile) ? getMultipartFileInputStream(sourceMultipartFile)
: getDirectAccessUrlInputStream(directUrl)); : getDirectAccessUrlInputStream(directUrl));
} }
private InputStream getInputStreamForHandleProbeRequest(File sourceFile) private InputStream getInputStreamForHandleProbeRequest(File sourceFile)
@@ -419,8 +419,8 @@ public class TransformHandler
{ {
e = e.getCause(); e = e.getCause();
sb.append(", cause ") sb.append(", cause ")
.append(e.getClass().getSimpleName()).append(": ") .append(e.getClass().getSimpleName()).append(": ")
.append(e.getMessage()); .append(e.getMessage());
} }
return sb.toString(); return sb.toString();

View File

@@ -26,17 +26,16 @@
*/ */
package org.alfresco.transform.base.util; package org.alfresco.transform.base.util;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import org.alfresco.transform.base.CustomTransformer;
import org.alfresco.transform.base.TransformManager;
/** /**
* Helper interface for older code that uses Files rather than InputStreams and OutputStreams. * Helper interface for older code that uses Files rather than InputStreams and OutputStreams. If you can, refactor your code to NOT use Files.
* If you can, refactor your code to NOT use Files.
*/ */
public interface CustomTransformerFileAdaptor extends CustomTransformer public interface CustomTransformerFileAdaptor extends CustomTransformer
{ {
@@ -52,4 +51,4 @@ public interface CustomTransformerFileAdaptor extends CustomTransformer
void transform(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions, void transform(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
File sourceFile, File targetFile, TransformManager transformManager) throws Exception; File sourceFile, File targetFile, TransformManager transformManager) throws Exception;
} }

View File

@@ -61,4 +61,4 @@ public class OutputStreamLengthRecorder extends FilterOutputStream
{ {
super.write(b, off, len); super.write(b, off, len);
} }
} }

View File

@@ -29,13 +29,13 @@ package org.alfresco.transform.base.util;
public class Util public class Util
{ {
private Util() private Util()
{ {}
}
/** /**
* Safely converts a {@link String} to an {@link Integer} * Safely converts a {@link String} to an {@link Integer}
* *
* @param param String to be converted * @param param
* String to be converted
* @return Null if param is null or converted value as {@link Integer} * @return Null if param is null or converted value as {@link Integer}
*/ */
public static Integer stringToInteger(final String param) public static Integer stringToInteger(final String param)
@@ -46,7 +46,8 @@ public class Util
/** /**
* Safely converts a {@link String} to a {@link Boolean} * Safely converts a {@link String} to a {@link Boolean}
* *
* @param param String to be converted * @param param
* String to be converted
* @return Null if param is null or converted value as {@link Boolean} * @return Null if param is null or converted value as {@link Boolean}
*/ */
public static Boolean stringToBoolean(final String param) public static Boolean stringToBoolean(final String param)
@@ -57,7 +58,8 @@ public class Util
/** /**
* Safely converts a {@link String} to a {@link Long} * Safely converts a {@link String} to a {@link Long}
* *
* @param param String to be converted * @param param
* String to be converted
* @return Null if param is null or converted value as {@link Boolean} * @return Null if param is null or converted value as {@link Boolean}
*/ */
public static Long stringToLong(final String param) public static Long stringToLong(final String param)

View File

@@ -26,30 +26,22 @@
*/ */
package org.alfresco.transform.base; package org.alfresco.transform.base;
import com.fasterxml.jackson.databind.ObjectMapper; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import org.alfresco.transform.base.sfs.SharedFileStoreClient;
import org.alfresco.transform.base.executors.CommandExecutor; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.alfresco.transform.base.executors.RuntimeExec; import static org.mockito.ArgumentMatchers.any;
import org.alfresco.transform.base.model.FileRefEntity; import static org.mockito.Mockito.when;
import org.alfresco.transform.base.model.FileRefResponse; import static org.springframework.http.HttpHeaders.ACCEPT;
import org.alfresco.transform.base.probes.ProbeTransform; import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import org.alfresco.transform.base.transform.TransformHandler; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.client.model.TransformReply; import static org.springframework.http.HttpStatus.CREATED;
import org.alfresco.transform.client.model.TransformRequest; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import org.alfresco.transform.registry.TransformServiceRegistry; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import org.junit.jupiter.api.Test; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import org.junit.jupiter.api.io.TempDir; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import org.springframework.boot.test.context.SpringBootTest; import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -64,25 +56,36 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import com.fasterxml.jackson.databind.ObjectMapper;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL; import org.junit.jupiter.api.Test;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM; import org.junit.jupiter.api.io.TempDir;
import static org.junit.jupiter.api.Assertions.assertEquals; import org.springframework.beans.factory.annotation.Autowired;
import static org.mockito.ArgumentMatchers.any; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import static org.mockito.Mockito.when; import org.springframework.boot.test.context.SpringBootTest;
import static org.springframework.http.HttpHeaders.ACCEPT; import org.springframework.boot.test.mock.mockito.MockBean;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import org.springframework.boot.test.mock.mockito.SpyBean;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import org.springframework.mock.web.MockMultipartFile;
import static org.springframework.http.HttpStatus.CREATED; import org.springframework.test.util.ReflectionTestUtils;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.alfresco.transform.base.executors.CommandExecutor;
import org.alfresco.transform.base.executors.RuntimeExec;
import org.alfresco.transform.base.model.FileRefEntity;
import org.alfresco.transform.base.model.FileRefResponse;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.base.sfs.SharedFileStoreClient;
import org.alfresco.transform.base.transform.TransformHandler;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.registry.TransformServiceRegistry;
/** /**
* Super class for unit testing. * Super class for unit testing.
*/ */
@SpringBootTest(classes={org.alfresco.transform.base.Application.class}) @SpringBootTest(classes = {org.alfresco.transform.base.Application.class})
@AutoConfigureMockMvc @AutoConfigureMockMvc
public abstract class AbstractBaseTest public abstract class AbstractBaseTest
{ {
@@ -123,8 +126,7 @@ public abstract class AbstractBaseTest
/** /**
* The expected result. Taken resting target quick file's bytes. * The expected result. Taken resting target quick file's bytes.
* *
* Note: These checks generally don't work on Windows (Mac and Linux are okay). Possibly to do with byte order * Note: These checks generally don't work on Windows (Mac and Linux are okay). Possibly to do with byte order loading.
* loading.
*/ */
protected byte[] expectedTargetFileBytes; protected byte[] expectedTargetFileBytes;
@@ -134,7 +136,7 @@ public abstract class AbstractBaseTest
private RuntimeExec origCheckCommand; private RuntimeExec origCheckCommand;
protected void setMockExternalCommandsOnTransformer(CommandExecutor commandExecutor, RuntimeExec mockTransformCommand, protected void setMockExternalCommandsOnTransformer(CommandExecutor commandExecutor, RuntimeExec mockTransformCommand,
RuntimeExec mockCheckCommand) RuntimeExec mockCheckCommand)
{ {
this.commandExecutor = commandExecutor; this.commandExecutor = commandExecutor;
origTransformCommand = (RuntimeExec) ReflectionTestUtils.getField(commandExecutor, "transformCommand"); origTransformCommand = (RuntimeExec) ReflectionTestUtils.getField(commandExecutor, "transformCommand");
@@ -150,27 +152,27 @@ public abstract class AbstractBaseTest
} }
protected void mockTransformCommand(String sourceExtension, protected void mockTransformCommand(String sourceExtension,
String targetExtension, String sourceMimetype, String targetExtension, String sourceMimetype,
boolean readTargetFileBytes) throws IOException boolean readTargetFileBytes) throws IOException
{ {}
}
protected void updateTransformRequestWithSpecificOptions(TransformRequest transformRequest) protected void updateTransformRequestWithSpecificOptions(TransformRequest transformRequest)
{ {}
}
/** /**
* This method ends up being the core of the mock. * This method ends up being the core of the mock. It copies content from an existing file in the resources folder to the desired location in order to simulate a successful transformation.
* It copies content from an existing file in the resources folder to the desired location
* in order to simulate a successful transformation.
* *
* @param actualTargetExtension Requested extension. * @param actualTargetExtension
* @param testFile The test file (transformed) - basically the result. * Requested extension.
* @param targetFile The location where the content from the testFile should be copied * @param testFile
* @throws IOException in case of any errors. * The test file (transformed) - basically the result.
* @param targetFile
* The location where the content from the testFile should be copied
* @throws IOException
* in case of any errors.
*/ */
public void generateTargetFileFromResourceFile(String actualTargetExtension, File testFile, public void generateTargetFileFromResourceFile(String actualTargetExtension, File testFile,
File targetFile) throws IOException File targetFile) throws IOException
{ {
if (testFile == null) if (testFile == null)
{ {
@@ -179,13 +181,14 @@ public abstract class AbstractBaseTest
if (testFile != null) if (testFile != null)
{ {
try (var inputStream = new FileInputStream(testFile); try (var inputStream = new FileInputStream(testFile);
var outputStream = new FileOutputStream(targetFile)) var outputStream = new FileOutputStream(targetFile))
{ {
FileChannel source = inputStream.getChannel(); FileChannel source = inputStream.getChannel();
FileChannel target = outputStream.getChannel(); FileChannel target = outputStream.getChannel();
target.transferFrom(source, 0, source.size()); target.transferFrom(source, 0, source.size());
} catch (Exception e) }
catch (Exception e)
{ {
throw e; throw e;
} }
@@ -218,7 +221,7 @@ public abstract class AbstractBaseTest
{ {
// Each use of the tempDir should result in a unique directory being used // Each use of the tempDir should result in a unique directory being used
testFile = new File(tempDir, testFilename); testFile = new File(tempDir, testFilename);
Files.copy(classLoader.getResourceAsStream(testFilename), testFile.toPath(),REPLACE_EXISTING); Files.copy(classLoader.getResourceAsStream(testFilename), testFile.toPath(), REPLACE_EXISTING);
} }
return testFileUrl == null ? null : testFile; return testFileUrl == null ? null : testFile;
@@ -253,7 +256,7 @@ public abstract class AbstractBaseTest
} }
private MockHttpServletRequestBuilder mockMvcRequestWithMockMultipartFile(String url, MockMultipartFile sourceFile, private MockHttpServletRequestBuilder mockMvcRequestWithMockMultipartFile(String url, MockMultipartFile sourceFile,
String... params) String... params)
{ {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM).file(sourceFile); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM).file(sourceFile);
@@ -272,18 +275,18 @@ public abstract class AbstractBaseTest
protected TransformRequest createTransformRequest(String sourceFileRef, File sourceFile) protected TransformRequest createTransformRequest(String sourceFileRef, File sourceFile)
{ {
return TransformRequest.builder() return TransformRequest.builder()
.withRequestId("1") .withRequestId("1")
.withSchema(1) .withSchema(1)
.withClientData("Alfresco Digital Business Platform") .withClientData("Alfresco Digital Business Platform")
.withTransformRequestOptions(options) .withTransformRequestOptions(options)
.withSourceReference(sourceFileRef) .withSourceReference(sourceFileRef)
.withSourceExtension(sourceExtension) .withSourceExtension(sourceExtension)
.withSourceMediaType(sourceMimetype) .withSourceMediaType(sourceMimetype)
.withSourceSize(sourceFile.length()) .withSourceSize(sourceFile.length())
.withTargetExtension(targetExtension) .withTargetExtension(targetExtension)
.withTargetMediaType(targetMimetype) .withTargetMediaType(targetMimetype)
.withInternalContextForTransformEngineTests() .withInternalContextForTransformEngineTests()
.build(); .build();
} }
public static void resetProbeForTesting(ProbeTransform probe) public static void resetProbeForTesting(ProbeTransform probe)
@@ -294,21 +297,21 @@ public abstract class AbstractBaseTest
ReflectionTestUtils.setField(probe, "maxTime", Long.MAX_VALUE); ReflectionTestUtils.setField(probe, "maxTime", Long.MAX_VALUE);
ReflectionTestUtils.setField(probe, "nextTransformTime", 0); ReflectionTestUtils.setField(probe, "nextTransformTime", 0);
((AtomicBoolean)ReflectionTestUtils.getField(probe, "initialised")).set(false); ((AtomicBoolean) ReflectionTestUtils.getField(probe, "initialised")).set(false);
((AtomicBoolean)ReflectionTestUtils.getField(probe, "readySent")).set(false); ((AtomicBoolean) ReflectionTestUtils.getField(probe, "readySent")).set(false);
((AtomicLong)ReflectionTestUtils.getField(probe, "transformCount")).set(0); ((AtomicLong) ReflectionTestUtils.getField(probe, "transformCount")).set(0);
((AtomicBoolean)ReflectionTestUtils.getField(probe, "die")).set(false); ((AtomicBoolean) ReflectionTestUtils.getField(probe, "die")).set(false);
} }
@Test @Test
public void simpleTransformTest() throws Exception public void simpleTransformTest() throws Exception
{ {
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform." + targetExtension)); "attachment; filename*=UTF-8''transform." + targetExtension));
} }
@Test @Test
@@ -317,11 +320,11 @@ public abstract class AbstractBaseTest
sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype, sourceFileBytes); sourceFile = new MockMultipartFile("file", "../quick." + sourceExtension, sourceMimetype, sourceFileBytes);
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform." + targetExtension)); "attachment; filename*=UTF-8''transform." + targetExtension));
} }
@Test @Test
@@ -330,11 +333,11 @@ public abstract class AbstractBaseTest
sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype, sourceFileBytes); sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype, sourceFileBytes);
mockMvc.perform( mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile)) mockMvcRequest(ENDPOINT_TRANSFORM, sourceFile))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().bytes(expectedTargetFileBytes)) .andExpect(content().bytes(expectedTargetFileBytes))
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform." + targetExtension)); "attachment; filename*=UTF-8''transform." + targetExtension));
} }
@Test @Test
@@ -345,14 +348,14 @@ public abstract class AbstractBaseTest
probeTransform.setLivenessPercent(110); probeTransform.setLivenessPercent(110);
long[][] values = new long[][]{ long[][] values = new long[][]{
{5000, 0, Long.MAX_VALUE}, // 1st transform is ignored {5000, 0, Long.MAX_VALUE}, // 1st transform is ignored
{1000, 1000, 2100}, // 1000 + 1000*1.1 {1000, 1000, 2100}, // 1000 + 1000*1.1
{3000, 2000, 4200}, // 2000 + 2000*1.1 {3000, 2000, 4200}, // 2000 + 2000*1.1
{2000, 2000, 4200}, {2000, 2000, 4200},
{6000, 3000, 6300}, {6000, 3000, 6300},
{8000, 4000, 8400}, {8000, 4000, 8400},
{4444, 4000, 8400}, // no longer in the first few, so normal and max times don't change {4444, 4000, 8400}, // no longer in the first few, so normal and max times don't change
{5555, 4000, 8400} {5555, 4000, 8400}
}; };
for (long[] v : values) for (long[] v : values)
@@ -376,16 +379,16 @@ public abstract class AbstractBaseTest
// Serialize and call the transformer // Serialize and call the transformer
String tr = objectMapper.writeValueAsString(transformRequest); String tr = objectMapper.writeValueAsString(transformRequest);
String transformationReplyAsString = mockMvc String transformationReplyAsString = mockMvc
.perform(MockMvcRequestBuilders .perform(MockMvcRequestBuilders
.post(ENDPOINT_TRANSFORM) .post(ENDPOINT_TRANSFORM)
.header(ACCEPT, APPLICATION_JSON_VALUE) .header(ACCEPT, APPLICATION_JSON_VALUE)
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE) .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.content(tr)) .content(tr))
.andExpect(status().is(BAD_REQUEST.value())) .andExpect(status().is(BAD_REQUEST.value()))
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, TransformReply transformReply = objectMapper.readValue(transformationReplyAsString,
TransformReply.class); TransformReply.class);
// Assert the reply // Assert the reply
assertEquals(BAD_REQUEST.value(), transformReply.getStatus()); assertEquals(BAD_REQUEST.value(), transformReply.getStatus());
@@ -437,11 +440,11 @@ public abstract class AbstractBaseTest
String directUrl = "file://" + dauSourceFile.toPath(); String directUrl = "file://" + dauSourceFile.toPath();
ResultActions resultActions = mockMvc.perform( ResultActions resultActions = mockMvc.perform(
mockMvcRequest(ENDPOINT_TRANSFORM, null) mockMvcRequest(ENDPOINT_TRANSFORM, null)
.param(DIRECT_ACCESS_URL, directUrl)) .param(DIRECT_ACCESS_URL, directUrl))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform."+targetExtension)); "attachment; filename*=UTF-8''transform." + targetExtension));
if (expectedTargetFileBytes != null) if (expectedTargetFileBytes != null)
{ {

View File

@@ -1,24 +1,19 @@
package org.alfresco.transform.base; package org.alfresco.transform.base;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import java.net.URISyntaxException;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
import java.net.URISyntaxException;
import java.util.Objects;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
public abstract class LivenessReadinessProbeTest public abstract class LivenessReadinessProbeTest
{ {
@@ -34,12 +29,9 @@ public abstract class LivenessReadinessProbeTest
env.start(); env.start();
var url = "http://localhost:" + env.getFirstMappedPort(); var url = "http://localhost:" + env.getFirstMappedPort();
/* /* Asserts that /ready probe hasn't died before sending a transformation request. Each /ready request creates a valid transformation and increases the counter of used transformations, hence the need to divide MAX_TRANSFORMS */
Asserts that /ready probe hasn't died before sending a transformation request. for (int i = 0; i < MAX_TRANSFORMS / 2; i++)
Each /ready request creates a valid transformation and increases the counter of {
used transformations, hence the need to divide MAX_TRANSFORMS
*/
for (int i = 0; i<MAX_TRANSFORMS/2; i++) {
assertProbeIsOk(url); assertProbeIsOk(url);
sendTransformRequest(url, testData.sourceMimetype, testData.targetMimetype, testData.filename); sendTransformRequest(url, testData.sourceMimetype, testData.targetMimetype, testData.filename);
} }
@@ -56,13 +48,13 @@ public abstract class LivenessReadinessProbeTest
private GenericContainer<?> createEnv(String image) throws URISyntaxException private GenericContainer<?> createEnv(String image) throws URISyntaxException
{ {
System.out.println(image); System.out.println(image);
final GenericContainer<?> transformCore = new GenericContainer<>("alfresco/"+image+":latest"); final GenericContainer<?> transformCore = new GenericContainer<>("alfresco/" + image + ":latest");
return transformCore.withEnv("livenessTransformEnabled", "true") return transformCore.withEnv("livenessTransformEnabled", "true")
.withEnv("maxTransforms", MAX_TRANSFORMS.toString()) .withEnv("maxTransforms", MAX_TRANSFORMS.toString())
.withNetworkAliases(image) .withNetworkAliases(image)
.withExposedPorts(8090) .withExposedPorts(8090)
.waitingFor(Wait.forListeningPort()); .waitingFor(Wait.forListeningPort());
} }
protected static class ImagesForTests protected static class ImagesForTests
@@ -106,7 +98,7 @@ public abstract class LivenessReadinessProbeTest
private static void assertProbeDied(String url) private static void assertProbeDied(String url)
{ {
WebTestClient client = WebTestClient.bindToServer().baseUrl(url+"/ready").build(); WebTestClient client = WebTestClient.bindToServer().baseUrl(url + "/ready").build();
client.get() client.get()
.exchange() .exchange()
.expectStatus().isEqualTo(TOO_MANY_REQUESTS); .expectStatus().isEqualTo(TOO_MANY_REQUESTS);
@@ -114,9 +106,9 @@ public abstract class LivenessReadinessProbeTest
private static void assertProbeIsOk(String url) private static void assertProbeIsOk(String url)
{ {
WebTestClient client = WebTestClient.bindToServer().baseUrl(url+"/ready").build(); WebTestClient client = WebTestClient.bindToServer().baseUrl(url + "/ready").build();
client.get() client.get()
.exchange() .exchange()
.expectStatus().isEqualTo(OK); .expectStatus().isEqualTo(OK);
} }
} }

View File

@@ -26,6 +26,18 @@
*/ */
package org.alfresco.transform.base; package org.alfresco.transform.base;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
@@ -42,19 +54,8 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext; public class MtlsTestUtils
import java.io.File; {
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
public class MtlsTestUtils {
private static final boolean MTLS_ENABLED = Boolean.parseBoolean(System.getProperty("test-mtls-enabled")); private static final boolean MTLS_ENABLED = Boolean.parseBoolean(System.getProperty("test-mtls-enabled"));
private static final boolean HOSTNAME_VERIFICATION_DISABLED = Boolean.parseBoolean(System.getProperty("test-client-disable-hostname-verification")); private static final boolean HOSTNAME_VERIFICATION_DISABLED = Boolean.parseBoolean(System.getProperty("test-client-disable-hostname-verification"));
@@ -96,20 +97,20 @@ public class MtlsTestUtils {
return HttpClients.custom().setConnectionManager(buildSslConnectionManager(sslContext)).build(); return HttpClients.custom().setConnectionManager(buildSslConnectionManager(sslContext)).build();
} }
private static HttpClientConnectionManager buildSslConnectionManager(SSLContext sslContext) { private static HttpClientConnectionManager buildSslConnectionManager(SSLContext sslContext)
final SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = {
SSLConnectionSocketFactoryBuilder.create() final SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(sslContext) .setSslContext(sslContext)
.setTlsVersions(TLS.V_1_2, TLS.V_1_3); .setTlsVersions(TLS.V_1_2, TLS.V_1_3);
if (HOSTNAME_VERIFICATION_DISABLED) { if (HOSTNAME_VERIFICATION_DISABLED)
{
sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE); sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
} }
final SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryBuilder.build(); final SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryBuilder.build();
final Registry<ConnectionSocketFactory> sslSocketFactoryRegistry = final Registry<ConnectionSocketFactory> sslSocketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslConnectionSocketFactory)
.register("https", sslConnectionSocketFactory) .build();
.build();
return new PoolingHttpClientConnectionManager(sslSocketFactoryRegistry); return new PoolingHttpClientConnectionManager(sslSocketFactoryRegistry);
} }
@@ -117,9 +118,12 @@ public class MtlsTestUtils {
public static RestTemplate restTemplateWithMtls() public static RestTemplate restTemplateWithMtls()
{ {
ClientHttpRequestFactory requestFactory = null; ClientHttpRequestFactory requestFactory = null;
try { try
{
requestFactory = new HttpComponentsClientHttpRequestFactory(httpClientWithMtls()); requestFactory = new HttpComponentsClientHttpRequestFactory(httpClientWithMtls());
} catch (Exception e) { }
catch (Exception e)
{
e.printStackTrace(); e.printStackTrace();
} }
return new RestTemplate(requestFactory); return new RestTemplate(requestFactory);
@@ -130,7 +134,8 @@ public class MtlsTestUtils {
return MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.restTemplateWithMtls() : new RestTemplate(); return MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.restTemplateWithMtls() : new RestTemplate();
} }
public static CloseableHttpClient getHttpClient() throws UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { public static CloseableHttpClient getHttpClient() throws UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException
{
return MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault(); return MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault();
} }
} }

View File

@@ -8,12 +8,13 @@
package org.alfresco.transform.base.clients; package org.alfresco.transform.base.clients;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_TRANSFORM;
import java.util.Map; import java.util.Map;
import org.alfresco.transform.base.MtlsTestUtils;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
@@ -23,6 +24,8 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.alfresco.transform.base.MtlsTestUtils;
/** /**
* @author Cezar Leahu * @author Cezar Leahu
*/ */
@@ -31,16 +34,16 @@ public class HttpClient
private static final RestTemplate REST_TEMPLATE = MtlsTestUtils.getRestTemplate(); private static final RestTemplate REST_TEMPLATE = MtlsTestUtils.getRestTemplate();
public static ResponseEntity<Resource> sendTRequest( public static ResponseEntity<Resource> sendTRequest(
final String engineUrl, final String sourceFile, final String engineUrl, final String sourceFile,
final String sourceMimetype, final String targetMimetype, final String targetExtension) final String sourceMimetype, final String targetMimetype, final String targetExtension)
{ {
return sendTRequest(engineUrl, sourceFile, sourceMimetype, targetMimetype, targetExtension, emptyMap()); return sendTRequest(engineUrl, sourceFile, sourceMimetype, targetMimetype, targetExtension, emptyMap());
} }
public static ResponseEntity<Resource> sendTRequest( public static ResponseEntity<Resource> sendTRequest(
final String engineUrl, final String sourceFile, final String engineUrl, final String sourceFile,
final String sourceMimetype, final String targetMimetype, final String targetExtension, final String sourceMimetype, final String targetMimetype, final String targetExtension,
final Map<String, String> transformOptions) final Map<String, String> transformOptions)
{ {
final HttpHeaders headers = new HttpHeaders(); final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MULTIPART_FORM_DATA); headers.setContentType(MULTIPART_FORM_DATA);

View File

@@ -34,7 +34,7 @@ public class JacksonSerializer
public static <T> byte[] serialize(T value) throws Exception public static <T> byte[] serialize(T value) throws Exception
{ {
try (final ByteArrayOutputStream stream = new ByteArrayOutputStream(1024); try (final ByteArrayOutputStream stream = new ByteArrayOutputStream(1024);
final OutputStreamWriter writer = new OutputStreamWriter(stream, UTF_8)) final OutputStreamWriter writer = new OutputStreamWriter(stream, UTF_8))
{ {
MAPPER.writer().writeValue(writer, value); MAPPER.writer().writeValue(writer, value);
return stream.toByteArray(); return stream.toByteArray();

View File

@@ -18,17 +18,16 @@ import jakarta.jms.MessageProducer;
import jakarta.jms.Session; import jakarta.jms.Session;
import jakarta.jms.TextMessage; import jakarta.jms.TextMessage;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.alfresco.transform.client.model.TransformReply;
import org.alfresco.transform.client.model.TransformRequest;
/** /**
* JMSClient * JMSClient
* *
* Contains the bare minimum logic necessary for sending and receiving T-Request/Reply messages * Contains the bare minimum logic necessary for sending and receiving T-Request/Reply messages through the basic vanilla ActiveMQ client.
* through the basic vanilla ActiveMQ client.
* *
* Used by Aspose t-engine and t-router, but likely to be useful in other t-engines. * Used by Aspose t-engine and t-router, but likely to be useful in other t-engines.
* *
@@ -51,29 +50,28 @@ public class JmsClient
} }
public void sendBytesMessage(final TransformRequest request) public void sendBytesMessage(final TransformRequest request)
throws Exception throws Exception
{ {
sendBytesMessage(request, request.getRequestId()); sendBytesMessage(request, request.getRequestId());
} }
public void sendBytesMessage(final TransformRequest request, final String correlationID) public void sendBytesMessage(final TransformRequest request, final String correlationID)
throws Exception throws Exception
{ {
sendBytesMessage(JacksonSerializer.serialize(request), correlationID); sendBytesMessage(JacksonSerializer.serialize(request), correlationID);
} }
public void sendBytesMessage(final TransformRequest request, final String correlationID, public void sendBytesMessage(final TransformRequest request, final String correlationID,
final Destination replyTo) throws Exception final Destination replyTo) throws Exception
{ {
sendBytesMessage(JacksonSerializer.serialize(request), correlationID, replyTo); sendBytesMessage(JacksonSerializer.serialize(request), correlationID, replyTo);
} }
public void sendBytesMessage(final byte[] data, final String correlationID) throws public void sendBytesMessage(final byte[] data, final String correlationID) throws Exception
Exception
{ {
try (final Connection connection = factory.createConnection(); try (final Connection connection = factory.createConnection();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageProducer producer = session.createProducer(queue)) final MessageProducer producer = session.createProducer(queue))
{ {
producer.setDeliveryMode(DeliveryMode.PERSISTENT); producer.setDeliveryMode(DeliveryMode.PERSISTENT);
final BytesMessage message = session.createBytesMessage(); final BytesMessage message = session.createBytesMessage();
@@ -87,11 +85,11 @@ public class JmsClient
} }
public void sendBytesMessage(final byte[] data, final String correlationID, public void sendBytesMessage(final byte[] data, final String correlationID,
final Destination replyTo) throws Exception final Destination replyTo) throws Exception
{ {
try (final Connection connection = factory.createConnection(); try (final Connection connection = factory.createConnection();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageProducer producer = session.createProducer(queue)) final MessageProducer producer = session.createProducer(queue))
{ {
producer.setDeliveryMode(DeliveryMode.PERSISTENT); producer.setDeliveryMode(DeliveryMode.PERSISTENT);
final BytesMessage message = session.createBytesMessage(); final BytesMessage message = session.createBytesMessage();
@@ -109,23 +107,22 @@ public class JmsClient
} }
public void sendTextMessage(final TransformRequest request) public void sendTextMessage(final TransformRequest request)
throws Exception throws Exception
{ {
sendTextMessage(request, request.getRequestId()); sendTextMessage(request, request.getRequestId());
} }
public void sendTextMessage(final TransformRequest request, final String correlationID) public void sendTextMessage(final TransformRequest request, final String correlationID)
throws Exception throws Exception
{ {
sendTextMessage(new String(JacksonSerializer.serialize(request)), correlationID); sendTextMessage(new String(JacksonSerializer.serialize(request)), correlationID);
} }
public void sendTextMessage(final String data, final String correlationID) throws public void sendTextMessage(final String data, final String correlationID) throws Exception
Exception
{ {
try (final Connection connection = factory.createConnection(); try (final Connection connection = factory.createConnection();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageProducer producer = session.createProducer(queue)) final MessageProducer producer = session.createProducer(queue))
{ {
producer.setDeliveryMode(DeliveryMode.PERSISTENT); producer.setDeliveryMode(DeliveryMode.PERSISTENT);
final TextMessage message = session.createTextMessage(data); final TextMessage message = session.createTextMessage(data);
@@ -143,11 +140,11 @@ public class JmsClient
} }
public TransformReply receiveMessage(final long timeout) public TransformReply receiveMessage(final long timeout)
throws Exception throws Exception
{ {
try (final Connection connection = factory.createConnection(); try (final Connection connection = factory.createConnection();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageConsumer consumer = session.createConsumer(queue)) final MessageConsumer consumer = session.createConsumer(queue))
{ {
connection.start(); connection.start();
@@ -167,11 +164,9 @@ public class JmsClient
try try
{ {
while (receiveMessage(2 * 1000) != null) while (receiveMessage(2 * 1000) != null)
{ {}
}
} }
catch (Exception ignore) catch (Exception ignore)
{ {}
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More