diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..c6b58f2c8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Shell scripts must always use LF (Unix) line endings +*.sh text eol=lf + +# Windows batch files must use CRLF +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b11e778f..4e0e9d3d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,7 +204,7 @@ jobs: run: chmod +x output/build/${{ matrix.config.preset }}/bin/* - name: Run benchmarks - run: ctest --preset ${{ matrix.config.preset }} -L benchmark --output-on-failure --timeout 600 + run: ctest --preset ${{ matrix.config.preset }} -L ^benchmark$ --output-on-failure --timeout 600 - name: Upload test results if: failure() @@ -227,12 +227,11 @@ jobs: strategy: fail-fast: false matrix: - # Use release build for integration tests (one config to avoid excessive combinations) + # Use no-SSL release build for non-SSL integration tests config: - - { preset: x64-linux-release-test, build_type: Release } + - { preset: x64-linux-release-no-ssl-test, build_type: Release } # Test with multiple ActiveMQ versions activemq: - - { name: "AMQ-5.15", image: "rmohr/activemq:5.15.9" } - { name: "AMQ-5.18", image: "apache/activemq-classic:5.18.7" } - { name: "AMQ-6.2", image: "apache/activemq-classic:6.2.0" } @@ -282,8 +281,11 @@ jobs: - name: Show broker status run: docker compose ps - - name: Run integration tests (${{ matrix.activemq.name }}) - run: ctest --preset ${{ matrix.config.preset }} -L integration --output-on-failure --timeout 900 + - name: Run OpenWire integration tests (${{ matrix.activemq.name }}) + run: ctest --preset ${{ matrix.config.preset }} -L ^integration-openwire$ --output-on-failure --timeout 900 + + - name: Run STOMP integration tests (${{ matrix.activemq.name }}) + run: ctest --preset ${{ matrix.config.preset }} -L ^integration-stomp$ --output-on-failure --timeout 900 - name: Show broker logs on failure if: failure() @@ -304,10 +306,103 @@ jobs: output/build/${{ matrix.config.preset }}/Testing/Temporary/ output/build/${{ matrix.config.preset }}/**/*.log - # Linux system tests - run separately with multiple ActiveMQ versions + # Linux SSL integration tests - run with SSL-enabled broker (Linux only) + linux-ssl-integration-test: + name: Linux SSL Integration Test + runs-on: ubuntu-latest + needs: linux-build-test + + env: + VCPKG_BINARY_SOURCES: "clear;files,${{ github.workspace }}/.cache/vcpkg/archives,read" + + strategy: + fail-fast: false + matrix: + # Only test with SSL-enabled presets (release build) + config: + - { preset: x64-linux-release-test, build_type: Release } + # Test with ActiveMQ versions that support SSL + activemq: + - { name: "AMQ-5.18", image: "apache/activemq-classic:5.18.7" } + - { name: "AMQ-6.2", image: "apache/activemq-classic:6.2.0" } + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y ninja-build + + - name: Set VCPKG_ROOT environment variable + run: echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV + + - name: Create vcpkg binary cache directory + run: mkdir -p "${{ github.workspace }}/.cache/vcpkg/archives" + + - name: Cache vcpkg binary packages + uses: actions/cache@v5 + with: + path: ${{ github.workspace }}/.cache/vcpkg/archives + key: vcpkg-binary-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + vcpkg-binary-${{ runner.os }}- + + - name: Configure CMake (for CTest) + run: cmake --preset ${{ matrix.config.preset }} + + - name: Download build artifacts + uses: actions/download-artifact@v7 + with: + name: linux-build-${{ matrix.config.preset }} + path: output/build/${{ matrix.config.preset }}/ + + - name: Make test executables runnable + run: chmod +x output/build/${{ matrix.config.preset }}/bin/* + + - name: Start SSL broker (${{ matrix.activemq.name }}) + run: | + echo "Starting SSL-enabled ActiveMQ using image: ${{ matrix.activemq.image }}" + ACTIVEMQ_IMAGE=${{ matrix.activemq.image }} docker compose --profile ssl up -d + echo "Waiting for SSL broker to be ready..." + timeout 60 bash -c 'until docker compose --profile ssl ps | grep -q "healthy"; do sleep 2; done' || true + sleep 10 + + - name: Show broker status + run: docker compose --profile ssl ps + + - name: Show broker logs (startup) + run: docker compose --profile ssl logs --tail 50 + + - name: Run OpenWire SSL integration tests (${{ matrix.activemq.name }}) + run: ctest --preset ${{ matrix.config.preset }} -L integration-openwire-ssl --output-on-failure --timeout 900 + env: + SSL_CERT_FILE: ${{ github.workspace }}/docker/ssl/certs/ca.pem + + - name: Show broker logs on failure + if: failure() + run: | + echo "=== ActiveMQ SSL broker logs ===" + docker compose --profile ssl logs --tail 200 || true + + - name: Stop SSL broker + if: always() + run: docker compose --profile ssl down + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v6 + with: + name: test-results-ssl-integration-${{ matrix.activemq.name }} + path: | + output/build/${{ matrix.config.preset }}/Testing/Temporary/ + output/build/${{ matrix.config.preset }}/**/*.log + + # Linux integration benchmark tests - run separately with multiple ActiveMQ versions # Tests failover, multi-connection, and high-volume scenarios - linux-system-test: - name: Linux System Test + linux-integration-benchmark-test: + name: Linux Integration Benchmark Test runs-on: ubuntu-latest needs: linux-build-test @@ -317,12 +412,11 @@ jobs: strategy: fail-fast: false matrix: - # Use release build for system tests (one config to avoid excessive combinations) + # Use release build for integration benchmark tests (one config to avoid excessive combinations) config: - { preset: x64-linux-release-test, build_type: Release } # Test with multiple ActiveMQ versions activemq: - - { name: "AMQ-5.15", image: "rmohr/activemq:5.15.9" } - { name: "AMQ-5.18", image: "apache/activemq-classic:5.18.7" } - { name: "AMQ-6.2", image: "apache/activemq-classic:6.2.0" } @@ -371,8 +465,8 @@ jobs: - name: Show broker status run: docker compose --profile failover ps - - name: Run system tests (${{ matrix.activemq.name }}) - run: ctest --preset ${{ matrix.config.preset }} -L system --output-on-failure --timeout 1800 + - name: Run integration benchmark tests (${{ matrix.activemq.name }}) + run: ctest --preset ${{ matrix.config.preset }} -L integration-benchmark --output-on-failure --timeout 1800 - name: Show broker logs on failure if: failure() @@ -392,7 +486,7 @@ jobs: if: failure() uses: actions/upload-artifact@v6 with: - name: test-results-system-${{ matrix.activemq.name }} + name: test-results-integration-benchmark-${{ matrix.activemq.name }} path: | output/build/${{ matrix.config.preset }}/Testing/Temporary/ output/build/${{ matrix.config.preset }}/**/*.log @@ -657,7 +751,7 @@ jobs: path: output/build/${{ matrix.config.preset }}/ - name: Run benchmarks - run: ctest --preset ${{ matrix.config.preset }} -C ${{ matrix.config.build_type }} -L benchmark --output-on-failure --timeout 600 + run: ctest --preset ${{ matrix.config.preset }} -C ${{ matrix.config.build_type }} -L ^benchmark$ --output-on-failure --timeout 600 - name: Upload test results if: failure() diff --git a/.gitignore b/.gitignore index a86e3847d..c7c31a24a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,8 @@ configure output/ .cache/ +# Generated SSL certificates for integration testing +docker/ssl/certs/ + diff --git a/.vscode/settings.json b/.vscode/settings.json index 946690eec..37f0bda0a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -141,5 +141,6 @@ "test": { "enabled": true } - } + }, + "cmake.ctest.testSuiteDelimiter": "/" } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bfdab1a96..82420b2e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,18 +174,7 @@ option(AMQCPP_USE_SSL "Enable OpenSSL support (required when ON)" OFF) if(AMQCPP_USE_SSL) find_package(OpenSSL REQUIRED) - message(STATUS "AMQCPP: SSL support enabled; OpenSSL will be required.") - - # If OpenSSL was found, define HAVE_OPENSSL for the code so sources - # can use `#ifdef HAVE_OPENSSL` to enable OpenSSL-dependent features. - if(OpenSSL_FOUND) - message(STATUS "AMQCPP: Found OpenSSL ${OPENSSL_VERSION}") - add_compile_definitions(HAVE_OPENSSL) - set(AMQCPP_HAVE_OPENSSL ON CACHE INTERNAL "OpenSSL available") - else() - message(STATUS "AMQCPP: OpenSSL not found; HAVE_OPENSSL will not be defined") - set(AMQCPP_HAVE_OPENSSL OFF CACHE INTERNAL "OpenSSL available") - endif() + message(STATUS "AMQCPP: SSL support enabled; found OpenSSL ${OPENSSL_VERSION}") else() find_package(OpenSSL QUIET) message(STATUS "AMQCPP: SSL support disabled; OpenSSL will be optional.") @@ -213,7 +202,7 @@ if(AMQCPP_BUILD_TESTS) add_subdirectory(activemq-cpp/src/test) add_subdirectory(activemq-cpp/src/test-benchmarks) add_subdirectory(activemq-cpp/src/test-integration) - add_subdirectory(activemq-cpp/src/test-system) + add_subdirectory(activemq-cpp/src/test-integration-benchmarks) # ========================================================================= # Integration & System Test Workflow Targets @@ -232,21 +221,21 @@ if(AMQCPP_BUILD_TESTS) COMMENT "Full integration test workflow (start broker, test, stop broker)..." ) - # System tests: start all brokers (failover profile), run tests, stop brokers - add_custom_target(system-full + # Integration benchmark tests: start all brokers (failover profile), run tests, stop brokers + add_custom_target(integration-benchmark-full COMMAND docker compose --profile failover up -d COMMAND ${CMAKE_COMMAND} -E sleep 15 - COMMAND ${CMAKE_COMMAND} -E echo "Running system tests..." - COMMAND $ + COMMAND ${CMAKE_COMMAND} -E echo "Running integration benchmark tests..." + COMMAND $ COMMAND docker compose --profile failover down WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - DEPENDS neoactivemq-system-test - COMMENT "Full system test workflow (start all brokers, test, stop brokers)..." + DEPENDS neoactivemq-integration-benchmark-test + COMMENT "Full integration benchmark test workflow (start all brokers, test, stop brokers)..." ) message(STATUS "Test workflow targets:") - message(STATUS " cmake --build --preset --target integration-full # Integration tests (auto Docker)") - message(STATUS " cmake --build --preset --target system-full # System tests (auto Docker)") + message(STATUS " cmake --build --preset --target integration-full # Integration tests (auto Docker)") + message(STATUS " cmake --build --preset --target integration-benchmark-full # Integration benchmark tests (auto Docker)") endif() # Installation rules diff --git a/README.md b/README.md new file mode 100644 index 000000000..5a02ffb0b --- /dev/null +++ b/README.md @@ -0,0 +1,303 @@ +# NeoActiveMQ CPP Library + +NeoActiveMQ CPP is a modernized C++17 messaging library that can use multiple protocols to talk to a MOM (e.g. ActiveMQ). This is a fork of Apache ActiveMQ CPP with updated build system and modern C++ standards. + +## 1. Prerequisites + +### 1.1 Required Tools + +| Tool | Recommended Version | +|------|-------------------| +| CMake | >= 3.15 | +| vcpkg | latest | +| C++ Compiler | C++17 support required | +| MSVC | >= 2019 (Windows) | +| GCC | >= 7.0 (Linux) | +| Clang | >= 5.0 (macOS/Linux) | + +### 1.2 Automatic Dependency Management + +This project uses vcpkg for dependency management. All dependencies are automatically downloaded and built during the CMake configuration phase. No manual installation is required. + +Dependencies managed by vcpkg: +- **OpenSSL** - cryptography +- **asio** - async I/O +- **Google Test** - testing +- **zlib** - compression + +## 2. Building with CMake Presets + +This project uses CMake presets for simplified configuration and building. The build system automatically manages all dependencies through vcpkg. + +### 2.1 Available Presets + +To see all available presets: + +```bash +cmake --list-presets +``` + +Common presets: + +| Preset | Description | +|--------|-------------| +| `x86-windows-debug-test` | Windows 32-bit Debug with tests | +| `x86-windows-release` | Windows 32-bit Release | +| `x64-windows-debug-test` | Windows 64-bit Debug with tests | +| `x64-windows-release` | Windows 64-bit Release | +| `x64-linux-debug-test` | Linux 64-bit Debug with tests | +| `x64-linux-release` | Linux 64-bit Release | + +### 2.2 Quick Start - Windows + +1. Configure the project: + + ```bash + cmake --preset x86-windows-debug-test + ``` + + This will: + - Download and build all dependencies via vcpkg + - Configure the build with tests enabled + - Generate build files in `output/build/x86-windows-debug-test/` + +2. Build the project: + + ```bash + cmake --build --preset x86-windows-debug-test + ``` + + The build output will be in: + - Libraries: `output/build//lib/` + - Executables: `output/build//bin/` + +### 2.3 Quick Start - Linux/macOS + +1. Configure the project: + + ```bash + cmake --preset x64-linux-debug-test + ``` + +2. Build the project: + + ```bash + cmake --build --preset x64-linux-debug-test + ``` + +### 2.4 Installation + +To install the library to the system: + +```bash +cmake --install output/build/ --prefix /usr/local +``` + +Or on Windows with administrator privileges: + +```bash +cmake --install output/build/ --prefix "C:/Program Files/neoactivemq-cpp" +``` + +This installs: +- Headers to `/include/` +- Libraries to `/lib/` +- CMake config files to `/lib/cmake/neoactivemq-cpp/` + +## 3. Running Tests + +### 3.1 Unit Tests + +The test executables are built automatically when using a preset with `-test` in the name (e.g., `x86-windows-debug-test`). + +To run unit tests via CTest: + +```bash +ctest --preset x86-windows-debug-test -L unit +``` + +### 3.2 Integration Tests + +Integration tests run against a real ActiveMQ broker. Running without a broker will result in failed tests. + +The tests connect to: +- **Stomp:** `tcp://localhost:61613` +- **OpenWire:** `tcp://localhost:61616` + +To run integration tests: + +```bash +# Start an ActiveMQ broker +docker compose up -d + +# Run all integration tests (OpenWire + STOMP) +ctest --preset -L integration --output-on-failure + +# Or run specific protocols: +# Run only OpenWire tests (non-SSL) +ctest --preset -L integration-openwire --output-on-failure + +# Run only STOMP tests +ctest --preset -L integration-stomp --output-on-failure + +# Stop the broker +docker compose down +``` + +Or use the CMake helper target: + +```bash +cmake --build --preset --target integration-full +``` + +### 3.3 SSL Integration Tests (Linux only) + +SSL integration tests provide **comprehensive validation** of the SSL/TLS transport layer against an SSL-enabled ActiveMQ broker. The SSL test suite mirrors the complete OpenWire test suite (29 tests) to ensure SSL transport works for all features. + +```bash +# Start the SSL-enabled broker (certificates are generated automatically) +docker compose --profile ssl up -d + +# Run all OpenWire SSL integration tests (29 comprehensive tests) +SSL_CERT_FILE=docker/ssl/certs/ca.pem ctest --preset -L integration-openwire-ssl --output-on-failure + +# Stop the broker +docker compose --profile ssl down +``` + +**SSL Test Coverage:** +- ✅ All acknowledgment modes (client, individual, optimized) +- ✅ Advisory messages +- ✅ Async sending and callbacks +- ✅ Message selectors and groups +- ✅ Durable subscriptions +- ✅ Transactions (local and XA) +- ✅ Redelivery policies and session recovery +- ✅ Temporary destinations +- ✅ Message compression and priority +- ✅ Queue browsing and virtual topics +- ✅ Slow consumers and expiration +- ✅ Enhanced connection features + +### 3.4 Integration Benchmark Tests + +Integration benchmark tests exercise failover, multi-connection, and high-volume scenarios. They require multiple broker instances. + +```bash +# Start all brokers (failover profile) +docker compose --profile failover up -d + +# Run integration benchmark tests +ctest --preset -L integration-benchmark --output-on-failure --timeout 1800 + +# Stop all brokers +docker compose --profile failover down +``` + +Or use the CMake helper target: + +```bash +cmake --build --preset --target integration-benchmark-full +``` + +## 4. Examples + +Example applications are located in `activemq-cpp/src/examples/` and are built automatically. + +After building, examples are located in: + +``` +output/build//bin/examples/ +``` + +Example executables follow the naming pattern `example__`: + +- `example_advisories_AdvisoryConsumerMain` +- `example_cmstemplate_CMSTemplateReceiverMain` +- `example_producers_SimpleProducerMain` + +## 5. Using in Your Project + +### 5.1 With CMake + +After installation, use `find_package` in your `CMakeLists.txt`: + +```cmake +find_package(neoactivemq-cpp REQUIRED) + +add_executable(myapp main.cpp) +target_link_libraries(myapp PRIVATE neoactivemq-cpp::activemq-cpp) +``` + +### 5.2 Build Options + +The following CMake options can be configured: + +| Option | Default | Description | +|--------|---------|-------------| +| `BUILD_TESTS` | ON for `-test` presets | Build test executables | +| `BUILD_EXAMPLES` | ON | Build example applications | +| `AMQCPP_SHARED_LIB` | OFF | Build as shared library (default: static) | + +To customize a preset, pass options during configuration: + +```bash +cmake --preset x86-windows-debug-test -DBUILD_EXAMPLES=OFF +``` + +## 6. Project Structure + +``` +activemq-cpp/src/main/ Main library source code + activemq/ ActiveMQ protocol implementation + cms/ CMS (Common Messaging System) API + decaf/ Foundation library (I/O, threading, etc.) +activemq-cpp/src/test/ Unit tests +activemq-cpp/src/test-integration/ Integration tests + activemq/test/openwire/ OpenWire protocol tests + activemq/test/openwire_ssl/ OpenWire SSL protocol tests + activemq/test/stomp/ STOMP protocol tests +activemq-cpp/src/test-integration-benchmarks/ Failover & high-volume benchmark tests +activemq-cpp/src/examples/ Example applications +cmake/ CMake configuration files +docker/ssl/ SSL certificate generation and broker config +output/build/ Build output directory (created by CMake) +``` + +## 7. Notes for Windows Users + +- Visual Studio 2019 or later is required for C++17 support +- No need to manually install dependencies -- vcpkg handles everything +- The Platform SDK is included with Visual Studio 2019+ +- When linking applications: + - **Static library** (default): No special considerations + - **Shared library**: Ensure runtime library matches (MD vs MT flags) + +## 8. Notes for Linux/macOS Users + +- GCC 7+ or Clang 5+ required for C++17 support +- vcpkg automatically downloads and builds all dependencies +- No need for manual `apt-get` or `yum` package installations +- For system-wide installation, use `sudo` with `cmake --install` + +## 9. Troubleshooting + +### 9.1 CMake Configuration Fails + +If CMake can't find the compiler: +- Ensure Visual Studio is installed (Windows) +- Ensure GCC/Clang is in PATH (Linux/macOS) +- Try running from Visual Studio Developer Command Prompt (Windows) + +### 9.2 Build Fails + +If build fails with missing dependencies: +- Delete `output/build/` and reconfigure +- vcpkg will re-download dependencies + +### 9.3 Preset Not Found + +If preset is not recognized: +- Ensure you're in the project root directory +- Check `CMakePresets.json` exists +- Update CMake to version 3.15 or later diff --git a/README.txt b/README.txt deleted file mode 100644 index ff98f962a..000000000 --- a/README.txt +++ /dev/null @@ -1,259 +0,0 @@ --------------------------------------------------------------------------- -NeoActiveMQ CPP Library --------------------------------------------------------------------------- - -NeoActiveMQ CPP is a modernized C++17 messaging library that can use -multiple protocols to talk to a MOM (e.g. ActiveMQ). This is a fork of -Apache ActiveMQ CPP with updated build system and modern C++ standards. - -1 Prerequisites --------------------------------------------------------------------------- - -1.1 Required Tools --------------------------------------------------------------------------- - -Tool Recommended Version -------------------------------- -CMake >= 3.15 -vcpkg latest -C++ Compiler C++17 support required - - MSVC >= 2019 (Windows) - - GCC >= 7.0 (Linux) - - Clang >= 5.0 (macOS/Linux) - -1.2 Automatic Dependency Management --------------------------------------------------------------------------- - -This project uses vcpkg for dependency management. All dependencies are -automatically downloaded and built during the CMake configuration phase. -No manual installation is required. - -Dependencies managed by vcpkg: - - OpenSSL (cryptography) - - asio (async I/O) - - CppUnit (testing) - - zlib (compression) - -2 Building with CMake Presets --------------------------------------------------------------------------- - -This project uses CMake presets for simplified configuration and building. -The build system automatically manages all dependencies through vcpkg. - -2.1 Available Presets --------------------------------------------------------------------------- - -To see all available presets: - - cmake --list-presets - -Common presets: - - x86-windows-debug-test (Windows 32-bit Debug with tests) - - x86-windows-release (Windows 32-bit Release) - - x64-windows-debug-test (Windows 64-bit Debug with tests) - - x64-windows-release (Windows 64-bit Release) - - x64-linux-debug-test (Linux 64-bit Debug with tests) - - x64-linux-release (Linux 64-bit Release) - -2.2 Quick Start - Windows --------------------------------------------------------------------------- - -1. Configure the project: - - cmake --preset x86-windows-debug-test - - This will: - - Download and build all dependencies via vcpkg - - Configure the build with tests enabled - - Generate build files in output/build/x86-windows-debug-test/ - -2. Build the project: - - cmake --build --preset x86-windows-debug-test - - The build output will be in: - - Libraries: output/build//lib/ - - Executables: output/build//bin/ - - Tests: output/build//bin/ - -2.3 Quick Start - Linux/macOS --------------------------------------------------------------------------- - -1. Configure the project: - - cmake --preset x64-linux-debug-test - -2. Build the project: - - cmake --build --preset x64-linux-debug-test - -2.4 Installation --------------------------------------------------------------------------- - -To install the library to the system: - - cmake --install output/build/ --prefix /usr/local - -Or on Windows with administrator privileges: - - cmake --install output/build/ --prefix "C:/Program Files/neoactivemq-cpp" - -This installs: - - Headers to /include/ - - Libraries to /lib/ - - CMake config files to /lib/cmake/neoactivemq-cpp/ - -3 Running Tests --------------------------------------------------------------------------- - -3.1 Unit Tests --------------------------------------------------------------------------- - -The test executable is built automatically when using a preset with "-test" -in the name (e.g., x86-windows-debug-test). - -To run the unit tests: - - Windows: - .\output\build\x86-windows-debug-test\bin\neoactivemq-test.exe - - Linux/macOS: - ./output/build/x64-linux-debug-test/bin/neoactivemq-test - -This will verify that the library is functioning correctly on the target -platform. - -3.2 Integration Tests --------------------------------------------------------------------------- - -The library also contains tests that run against a real ActiveMQ broker. -These validate the distribution against your broker. Running without a -broker will result in failed tests. - -The tests currently connect to: - - Stomp: tcp://localhost:61613 - - Openwire: tcp://localhost:61616 - -To run integration tests: - -1. Start an ActiveMQ broker -2. Run the integration test executable: - - Windows: - .\output\build\x86-windows-debug-test\bin\neoactivemq-integration-test.exe - - Linux/macOS: - ./output/build/x64-linux-debug-test/bin/neoactivemq-integration-test - -Note: This takes considerable time. It's recommended to restart the broker -between successive test runs. - -4 Examples --------------------------------------------------------------------------- - -Example applications are located in activemq-cpp/src/examples/ and are -built automatically. - -After building, examples are located in: - output/build//bin/examples/ - -Example executables follow the naming pattern: - example__ - -For instance: - - example_advisories_AdvisoryConsumerMain - - example_cmstemplate_CMSTemplateReceiverMain - - example_producers_SimpleProducerMain - -5 Using in Your Project --------------------------------------------------------------------------- - -5.1 With CMake --------------------------------------------------------------------------- - -After installation, use find_package in your CMakeLists.txt: - - find_package(neoactivemq-cpp REQUIRED) - - add_executable(myapp main.cpp) - target_link_libraries(myapp PRIVATE neoactivemq-cpp::activemq-cpp) - -5.2 Build Options --------------------------------------------------------------------------- - -The following CMake options can be configured: - - -DBUILD_TESTS=ON/OFF Build test executables (default: ON for -test presets) - -DBUILD_EXAMPLES=ON/OFF Build example applications (default: ON) - -DAMQCPP_SHARED_LIB=ON/OFF Build as shared library (default: OFF, builds static) - -To customize a preset, pass options during configuration: - - cmake --preset x86-windows-debug-test -DBUILD_EXAMPLES=OFF - -6 Project Structure --------------------------------------------------------------------------- - -Key directories: - activemq-cpp/src/main/ Main library source code - activemq/ ActiveMQ protocol implementation - cms/ CMS (Common Messaging System) API - decaf/ Foundation library (I/O, threading, etc.) - activemq-cpp/src/test/ Unit tests - activemq-cpp/src/test-integration/ Integration tests - activemq-cpp/src/examples/ Example applications - cmake/ CMake configuration files - output/build/ Build output directory (created by CMake) - -7 Notes for Windows Users --------------------------------------------------------------------------- - -* Visual Studio 2019 or later is required for C++17 support -* No need to manually install dependencies - vcpkg handles everything -* The Platform SDK is included with Visual Studio 2019+ -* When linking applications: - - Static library (default): No special considerations - - Shared library: Ensure runtime library matches (MD vs MT flags) - -8 Notes for Linux/macOS Users --------------------------------------------------------------------------- - -* GCC 7+ or Clang 5+ required for C++17 support -* vcpkg automatically downloads and builds all dependencies -* No need for manual apt-get or yum package installations -* For system-wide installation, use sudo with cmake --install - -9 Troubleshooting --------------------------------------------------------------------------- - -9.1 CMake Configuration Fails --------------------------------------------------------------------------- - -If CMake can't find the compiler: - - Ensure Visual Studio is installed (Windows) - - Ensure GCC/Clang is in PATH (Linux/macOS) - - Try running from Visual Studio Developer Command Prompt (Windows) - -9.2 Build Fails --------------------------------------------------------------------------- - -If build fails with missing dependencies: - - Delete output/build/ and reconfigure - - vcpkg will re-download dependencies - -9.3 Preset Not Found --------------------------------------------------------------------------- - -If preset is not recognized: - - Ensure you're in the project root directory - - Check CMakePresets.json exists - - Update CMake to version 3.15 or later - -10 Legacy Build System --------------------------------------------------------------------------- - -The original autotools-based build system has been replaced with CMake. -Legacy files are preserved in activemq-cpp/legacy-autotools/ for reference -but are no longer maintained or supported. - -For the modern build system, always use CMake with presets as documented above. diff --git a/activemq-cpp/README.txt b/activemq-cpp/README.txt deleted file mode 100644 index 75cbd5cda..000000000 --- a/activemq-cpp/README.txt +++ /dev/null @@ -1,297 +0,0 @@ --------------------------------------------------------------------------- -ActiveMQ CPP Library --------------------------------------------------------------------------- - -ActiveMQ CPP is a messaging library that can use multiple protocols to -talk to a MOM (e.g. ActiveMQ). - -1 Dependencies --------------------------------------------------------------------------- - -There are several dependencies that need to be met in order to build and -install ActiveMQ-CPP on a Unix type system, the short list is shown below, -read the sections that follow for more detailed information. On Windows -you will not need the Auto Tools since the library is built using Microsft's -Visual Studio product. - -Tool Recommended Version -------------------------------- -autoconf >= 2.61 -automake >= 1.10 -libtool >= 1.5.24 -CPPUnit >= 1.10.2* ( 1.12.1 is recommended ) -OpenSSL >= 0.9.8m* ( 1.0.0 or higher is recommended, this is an optional dependency) - -* Requires that the Development package also be installed. - -1.1 CppUnit --------------------------------------------------------------------------- - -The package contains a complete set of CppUnit tests. In order for you to -build an run the tests, you will need to download and install the CppUnit -suite. See http://cppunit.sourceforge.net/cppunit-wiki - -On Fedora, type the following: - - sudo yum install cppunit cppunit-devel - -On Debian/Ubuntu, type the following: - - sudo apt-get install libcppunit-dev - -Make sure that the paths to the installed CppUnit library and includes are -visible in your current shell before you try building the tests. - -Windows users will need to build the CppUnit library using the CPPUnit -MSVC project files. A discussion of the build process can be found -on the CPPUnit wiki under: - -http://cppunit.sourceforge.net/cppunit-wiki/BuildingCppUnit1 - -This covers both MSVC along with many other platforms and tool suites. -The included Visual Studio projects are configured with the assumption -that you will configure Visual Studio with the locations of the Platform -SDK and the CPPUnit libraries and headers. - -1.2 OpenSSL --------------------------------------------------------------------------- - -If you wish to use the SSL Transport then you will need to have OpenSSL and -its includes installed on your system. We recommend that you use version 1.0.0 -or higher for best performance and security, but version from 0.9.8 are also -known to work. The autoconf script will search for the library and enabled support -automatically if it is found. - -You can disable the OpenSSL checks in the configure script with the --disable-ssl -option or you can specify a custom location for the OpenSSL package with the ---with-openssl option. See the --help option on the configure script for more -information. - -On Windows you need to obtain an OpenSSL binary package and place the libraries on -the system path or in the System32 directory. In the Visual Studio project you must -add the HAVE_OPENSSL flag to the preprocessor directives and add the paths for the -includes and libraries so that the compiler and linker can find them. - -1.3 GNU Build System (for building on Unix/Linux/OS X) --------------------------------------------------------------------------- - -To Generate the ./configure script use to create the Makefiles, you need -the following software installed: - -Tool Recommended Version -------------------------------- -autoconf >= 2.63 -automake >= 1.10 -libtool >= 1.5.24 - -On Debian/Ubuntu, multiple versions of autoconf and automake are available -in separate packages. If you have multiple versions of autoconf or automake -installed on your system, you may have to configure the versions to use -using /usr/sbin/update-alternatives. - - ----------------------------------------------------------------------- - |Ubuntu Note: | - | | - |In order to build you will need the Build Essentials Package | - | | - | sudo apt-get install build-essential | - ----------------------------------------------------------------------- - -2 Building on Unix/Linux/OS X --------------------------------------------------------------------------- - - ----------------------------------------------------------------------- - |Solaris 10 Note: CppUnit might not build until you correct the file | - | libstdc++.la to contain the correct data, see this discussion: | - | http://forum.sun.com/jive/thread.jspa?threadID=73150 | - | Also you must pass --enable-shared=no for Solaris GCC builds | - | For Solaris builds using the Sun Compiler you must set the env | - | values CC and CXX to point to the cc and CC commands respectively. | - ----------------------------------------------------------------------- - -The configure script will customize the way the software is built and -installed into your system along with detecting the available libraries -that have been installed. To use the default configuration just run: - - ./configure - -For more help on how to customize the build configuration, run: - - ./configure --help - -Once the configure script has run successfully, you are ready to build. -Run: - - make - -This will build all of the core ActiveMQ CPP source code. To build and -install the code into the system directories, run: - - make install - -You will have to become the superuser in order to be able to install the -files. - -** A Note For ActiveMQ-CPP Developers ** - -If you need to make any changes to the configure.ac or any of the included -m4 files then you need to regenerate the configure script. - - ./autogen.sh - -This should be run the first time and any time you change configure.ac or -any of the Makefile.am files. - - ----------------------------------------------------------------------- - |MacOS X Note: | - | Make sure to set the LIBTOOLIZE environment variable to point to | - | /usr/bin/glibtoolize for the build to complete successfully. Below | - | is an example: | - | | - | $ export LIBTOOLIZE=/usr/bin/glibtoolize | - | | - | If you do not use this environment variable you will encounter an | - | error stating: | - | | - | Can't exec "libtoolize": No such file or directory at | - | /opt/local/share/autoconf/Autom4te/FileUtils.pm line 290... | - ----------------------------------------------------------------------- - -3 Doxygen --------------------------------------------------------------------------- - -To generate the Doxygen documentation for the project, just run: - - make doxygen-run - -4 Running Tests --------------------------------------------------------------------------- - -4.1 Unit Tests --------------------------------------------------------------------------- -In order to build and run the suite of unit tests, run: - - make check - ./src/test/activemq-test - -This will verify that the library is functioning correctly on the target -platform. In addition, it will generate the integration tests binary. - -4.2 Integration Tests --------------------------------------------------------------------------- -The library also contains a set of tests that are run against a real AMQ -broker. These allow you to validate this distribution of ActiveMQ CPP -against your broker. Running these without a broker will result in failed -tests. The tests currently hard-code the broker url to be -tcp://localhost:61613 for Stomp and tcp://localhost:61616 for Openwire. - -The integration tests are built via "make check". To run them, first -start a broker and then - - ./sr/test-integration/activemq-test-integration - -This will take quite some time to complete, so be patient. It is recommended -that you restart the broker between successive runs of the integration tests. - -5 Example --------------------------------------------------------------------------- -There are example applications that ship with the distribution in -src/examples. The examples are compiled by default with the "make" -command on Unix systems. Only one sample is included in the Visual Studio -projects supplied, the others can be easily added by examining the settings -of the one supplied. - -6 Notes for Windows users --------------------------------------------------------------------------- -We do not support using the GNU compiler on Windows, using the Cygwin package -or the MinGW platform, several issues with sockets and threading were found to -exist when trying to use these solutions. - -However we do support using the MSVC compiler on Windows, we provide a set of -Visual Studio project files for v2010, newer version should be able to upgrade -these files to the newest format. - -There are a couple or things that you will need to setup to ensure that -the MSVC compile succeeds. - -* When linking your application to the DLL version of the ActiveMQ-CPP library - you must link your app the the same runtime version that the DLL is linked to, - otherwise your application will cause heap corruption when you delete objects - that are created in the ActiveMQ-CPP DLL's heap. - -* You need to download and install the Platform SDK if you don't have it - installed already. On machines where you intend to use the built libraries - and executable you will also need to install the MS Redistributable for the - version of Visual Studio which you used to build the library. - -6.1 Project Settings - -The Visual Studio 2010 Project files included with the code have been configured to look -for the various headers and library files of the APR, OpenSSL and CPPUnit projects using -a predefined directory structure and a set of environment variables which can be used to -define the exact location on disk. - -The directory structure for each dependency is prefixed with an environment variable, the -set of variables is as follows: - - Library Recommended Version Windows Env Var - ------------------------------------------------------------ - CPPUNIT >= 1.10.2 ${CPPUNIT_DIST} - OpenSSL >= 1.5.24 ${OPENSSL_DIST} - Platform SDK {varies} ${PLATFORM_SDK} - -Under each of the library dependencies the structure should be layed out as follows: - - ${CPPUNIT_DIST}\ - win32\ - include\ - lib\ - x64\ - include\ - lib\ - ${OPENSSL_DIST}\ - win32\ - include\ - lib\ - bin\ - exp\ - x64\ - include\ - lib\ - bin\ - exp\ - -6.2 Obtaining and building the dependent libraries - -Before you can build the ActiveMQ-CPP library you need to get builds of the required libraries -installed on you system. This is not always simple on windows, we will offer a few tips to try -and make getting a copy of each here but you might also want to consult Google and ask on the -mailing list for more help. - -6.2.1 CPPUnit - -To build the CPPUnit library for later Visual Studio versions such as 2010+ you need to download the -most up to date code for CPPUnit v1.12.1+ from its SVN location. The current location is: - - https://svn.code.sf.net/p/cppunit/code/trunk/cppunit - -Once you have checked out the code you can use the included Visual Studio 2010 project files to -build the various target (Debug, Release, Debug DLL, Release DLL) and copy each to the location -you have chosen, just ensure you follow the directory layout as specified above. - -6.2.3 OpenSSL - -OpenSSL can be the harder one to obtain on Windows. If you choose to use SSL on Windows you need -to locate a build of OpenSSL that matches your Visual Stidio version (2010 at the time of this -writing) and install it into the location matching the directory layout configured in the ActiveMQ-CPP -project files. Builds for visual studio 2008 were available online at this location: - - http://slproweb.com/products/Win32OpenSSL.html - -Before attempting to build OpenSSL yourself you should check to see if newer 2010 versions are -available. If you find you need to build it yourself you should consult Google to find useful -Blog postings that detail how to get started with the build process for OpenSSL on Windows. - - - - diff --git a/activemq-cpp/src/examples/main.cpp b/activemq-cpp/src/examples/main.cpp index 16b0d6a3f..5da5a2ddb 100644 --- a/activemq-cpp/src/examples/main.cpp +++ b/activemq-cpp/src/examples/main.cpp @@ -355,10 +355,6 @@ int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) { // the trusted root. If using client authentication you also need to specify // the location of the client Certificate. // - // System::setProperty( "decaf.net.ssl.keyStore", "/client.pem" ); - // System::setProperty( "decaf.net.ssl.keyStorePassword", "password" ); - // System::setProperty( "decaf.net.ssl.trustStore", "/rootCA.pem" ); - // // The you just specify the ssl transport in the URI, for example: // // ssl://localhost:61617 diff --git a/activemq-cpp/src/main/CMakeLists.txt b/activemq-cpp/src/main/CMakeLists.txt index 67d646580..8e619c80a 100644 --- a/activemq-cpp/src/main/CMakeLists.txt +++ b/activemq-cpp/src/main/CMakeLists.txt @@ -675,7 +675,7 @@ else() endif() # SSL/TLS support - conditionally add SSL sources when OpenSSL is available -if(OpenSSL_FOUND) +if(AMQCPP_USE_SSL) list(APPEND ACTIVEMQ_CPP_SOURCES # activemq SSL transport activemq/transport/tcp/SslTransport.cpp @@ -722,8 +722,8 @@ target_include_directories(neoactivemq-cpp ) # OpenSSL support -if(OpenSSL_FOUND) - target_compile_definitions(neoactivemq-cpp PUBLIC HAVE_OPENSSL) +if(AMQCPP_USE_SSL) + target_compile_definitions(neoactivemq-cpp PUBLIC AMQCPP_USE_SSL) target_link_libraries(neoactivemq-cpp PUBLIC OpenSSL::SSL OpenSSL::Crypto) endif() diff --git a/activemq-cpp/src/main/activemq/library/ActiveMQCPP.cpp b/activemq-cpp/src/main/activemq/library/ActiveMQCPP.cpp index 15211c0e2..8fd774efe 100644 --- a/activemq-cpp/src/main/activemq/library/ActiveMQCPP.cpp +++ b/activemq-cpp/src/main/activemq/library/ActiveMQCPP.cpp @@ -29,7 +29,7 @@ #include #include -#ifdef HAVE_OPENSSL +#ifdef AMQCPP_USE_SSL #include #endif #include @@ -111,11 +111,11 @@ void ActiveMQCPP::registerTransports() { TransportRegistry::initialize(); TransportRegistry::getInstance().registerFactory("tcp", new TcpTransportFactory()); -#ifdef HAVE_OPENSSL +#ifdef AMQCPP_USE_SSL TransportRegistry::getInstance().registerFactory("ssl", new SslTransportFactory()); #endif TransportRegistry::getInstance().registerFactory("nio", new TcpTransportFactory()); -#ifdef HAVE_OPENSSL +#ifdef AMQCPP_USE_SSL TransportRegistry::getInstance().registerFactory("nio+ssl", new SslTransportFactory()); #endif TransportRegistry::getInstance().registerFactory("mock", new MockTransportFactory()); diff --git a/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.cpp b/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.cpp index 9c9e342cc..d8bde40e1 100644 --- a/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.cpp +++ b/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.cpp @@ -39,7 +39,13 @@ using namespace decaf::lang::exceptions; //////////////////////////////////////////////////////////////////////////////// SslTransport::SslTransport(const Pointer next, const decaf::net::URI& location) : - TcpTransport(next, location), sslSocket(NULL) { + TcpTransport(next, location), sslSocket(NULL), properties() { +} + +//////////////////////////////////////////////////////////////////////////////// +SslTransport::SslTransport(const Pointer next, const decaf::net::URI& location, + const decaf::util::Properties& properties) : + TcpTransport(next, location), sslSocket(NULL), properties(properties) { } //////////////////////////////////////////////////////////////////////////////// @@ -83,6 +89,13 @@ void SslTransport::configureSocket(Socket* socket) { serverNames.push_back(this->getLocation().getHost()); params.setServerNames(serverNames); + // Apply peer verification setting from URI properties if specified + if (this->properties.hasProperty("socket.disablePeerVerification")) { + bool disablePeerVerification = Boolean::parseBoolean( + this->properties.getProperty("socket.disablePeerVerification", "false")); + params.setPeerVerificationEnabled(!disablePeerVerification); + } + sslSocket->setSSLParameters(params); TcpTransport::configureSocket(socket); diff --git a/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.h b/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.h index 012b856e6..13116a918 100644 --- a/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.h +++ b/activemq-cpp/src/main/activemq/transport/tcp/SslTransport.h @@ -44,6 +44,7 @@ namespace tcp { private: decaf::net::ssl::SSLSocket* sslSocket; + decaf::util::Properties properties; SslTransport(const SslTransport&); SslTransport& operator=(const SslTransport&); @@ -61,6 +62,19 @@ namespace tcp { */ SslTransport(const Pointer next, const decaf::net::URI& location); + /** + * Creates a new instance of the SslTransport with URI properties. + * + * @param next + * The next transport in the chain + * @param location + * The URI of the host this transport is to connect to. + * @param properties + * The properties parsed from the URI query string. + */ + SslTransport(const Pointer next, const decaf::net::URI& location, + const decaf::util::Properties& properties); + virtual ~SslTransport(); protected: diff --git a/activemq-cpp/src/main/activemq/transport/tcp/SslTransportFactory.cpp b/activemq-cpp/src/main/activemq/transport/tcp/SslTransportFactory.cpp index 992b44709..9dc030958 100644 --- a/activemq-cpp/src/main/activemq/transport/tcp/SslTransportFactory.cpp +++ b/activemq-cpp/src/main/activemq/transport/tcp/SslTransportFactory.cpp @@ -52,7 +52,7 @@ Pointer SslTransportFactory::doCreateComposite(const decaf::net::URI& Pointer transport(new IOTransport(wireFormat)); - transport.reset(new SslTransport(transport, location)); + transport.reset(new SslTransport(transport, location, properties)); // Give this class and any derived classes a chance to apply value that // are set in the properties object. diff --git a/activemq-cpp/src/main/decaf/CMakeLists.txt b/activemq-cpp/src/main/decaf/CMakeLists.txt index 94bb2fb15..12e97f3f3 100644 --- a/activemq-cpp/src/main/decaf/CMakeLists.txt +++ b/activemq-cpp/src/main/decaf/CMakeLists.txt @@ -37,8 +37,8 @@ target_include_directories(decaf $ ) -if(OpenSSL_FOUND) - target_compile_definitions(decaf PUBLIC HAVE_OPENSSL) +if(AMQCPP_USE_SSL) + target_compile_definitions(decaf PUBLIC AMQCPP_USE_SSL) target_link_libraries(decaf PUBLIC OpenSSL::SSL OpenSSL::Crypto) endif() diff --git a/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.cpp b/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.cpp index e6df3e6d3..2afd6393d 100644 --- a/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.cpp @@ -16,6 +16,7 @@ */ #include "SocketFileDescriptor.h" +#include using namespace decaf; using namespace decaf::io; @@ -23,7 +24,7 @@ using namespace decaf::internal; using namespace decaf::internal::net; //////////////////////////////////////////////////////////////////////////////// -SocketFileDescriptor::SocketFileDescriptor( long value ) : FileDescriptor( value, false ) { +SocketFileDescriptor::SocketFileDescriptor( intptr_t value ) : FileDescriptor( value, false ) { } //////////////////////////////////////////////////////////////////////////////// @@ -31,6 +32,6 @@ SocketFileDescriptor::~SocketFileDescriptor() { } //////////////////////////////////////////////////////////////////////////////// -long SocketFileDescriptor::getValue() const { +intptr_t SocketFileDescriptor::getValue() const { return this->descriptor; } diff --git a/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.h b/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.h index 81d0b1131..d1b63dd54 100644 --- a/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.h +++ b/activemq-cpp/src/main/decaf/internal/net/SocketFileDescriptor.h @@ -18,6 +18,7 @@ #ifndef _DECAF_INTERNAL_NET_SOCKETFILEDESCRIPTOR_H_ #define _DECAF_INTERNAL_NET_SOCKETFILEDESCRIPTOR_H_ +#include #include #include @@ -34,7 +35,7 @@ namespace net { class DECAF_API SocketFileDescriptor : public decaf::io::FileDescriptor { public: - SocketFileDescriptor( long value ); + SocketFileDescriptor( intptr_t value ); virtual ~SocketFileDescriptor(); @@ -43,7 +44,7 @@ namespace net { * * @return a FileDescriptor value. */ - long getValue() const; + intptr_t getValue() const; }; diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/DefaultSSLContext.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/DefaultSSLContext.cpp index 1accdd179..f2df71d9c 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/DefaultSSLContext.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/DefaultSSLContext.cpp @@ -74,8 +74,6 @@ DefaultSSLContext::~DefaultSSLContext() { //////////////////////////////////////////////////////////////////////////////// SSLContext* DefaultSSLContext::getContext() { -#ifdef HAVE_OPENSSL - if( defaultSSLContext == NULL ) { std::unique_ptr random( new SecureRandom() ); @@ -93,7 +91,5 @@ SSLContext* DefaultSSLContext::getContext() { Network::getNetworkRuntime()->addShutdownTask( new ShutdownTask( &defaultSSLContext ) ); } -#endif - return defaultSSLContext; } diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLContextSpi.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLContextSpi.cpp index 85035483b..736c7cad5 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLContextSpi.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLContextSpi.cpp @@ -21,33 +21,18 @@ #include #include #include -#include #include -#include #include #include #include #include #include -#ifdef HAVE_STRINGS_H -#include -#endif - -#ifdef HAVE_STRING_H -#include -#endif +#include -#ifdef HAVE_OPENSSL #include -#include #include -#include #include -#define SSL_LOCK_MUTEX CRYPTO_LOCK -#else -#define SSL_LOCK_MUTEX 1 -#endif #ifdef _WIN32 #include @@ -84,14 +69,9 @@ namespace openssl { Pointer random; std::string password; - static Mutex* locks; static std::string defaultCipherList; -#ifdef HAVE_OPENSSL SSL_CTX* openSSLContext; -#else - void* openSSLContext; -#endif private: @@ -100,34 +80,19 @@ namespace openssl { public: - ContextData( int size ) : monitor(), - clientSocketFactory(), - serverSocketFactory(), - random(), - password(), - openSSLContext(NULL) { - - ContextData::locks = new Mutex[size]; + ContextData() : monitor(), + clientSocketFactory(), + serverSocketFactory(), + random(), + password(), + openSSLContext(NULL) { } ~ContextData() { -#ifdef HAVE_OPENSSL try{ SSL_CTX_free( this->openSSLContext ); } catch(...) {} -#endif - delete [] locks; - } - - // Used to allow OpenSSL to tell us when to lock / unlock a mutex for it. - static void lockCallback( int mode, int type, const char *, int ) { - - if( mode & SSL_LOCK_MUTEX ) { - locks[type].lock(); - } else { - locks[type].unlock(); - } } // Returns to OpenSSL the password for a Certificate. @@ -145,15 +110,8 @@ namespace openssl { return 0; } - static unsigned long getThreadId() { - // Convert std::thread::id to unsigned long for OpenSSL callback - std::thread::id tid = Thread::currentThread()->getId(); - return static_cast(std::hash{}(tid)); - } - }; - Mutex* ContextData::locks = NULL; std::string ContextData::defaultCipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; }}}}} @@ -166,13 +124,7 @@ OpenSSLContextSpi::OpenSSLContextSpi() : data( NULL ) { OpenSSLContextSpi::~OpenSSLContextSpi() { try{ -#ifdef HAVE_OPENSSL - // Clean up all the OpenSSL resources. -#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L) - CRYPTO_set_locking_callback( 0 ); -#endif - EVP_cleanup(); -#endif + // OpenSSL 1.1+ cleans up its own resources; nothing to do here. delete this->data; } @@ -190,96 +142,53 @@ void OpenSSLContextSpi::providerInit( SecureRandom* random ) { try{ -#ifdef HAVE_OPENSSL - - // General library initialization. - #ifdef WIN32 - /* CRYPTO_malloc_init was removed/changed in OpenSSL 1.1+. Only call - * it on older OpenSSL versions. For OpenSSL >= 1.1 the library - * initializes allocation functions internally. Use the - * OPENSSL_VERSION_NUMBER macro to detect the version. - */ -#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L) - CRYPTO_malloc_init(); -#endif - #endif - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - - // Initialize our data object and store the RNG for later. - /* CRYPTO_num_locks and custom locking callbacks are required for - * OpenSSL < 1.1.0. Newer OpenSSL versions manage internal locking - * themselves and these functions are no-ops or removed. Guard - * usage to support both older and newer OpenSSL builds. - */ - #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L) - this->data = new ContextData( CRYPTO_num_locks() ); - #else - this->data = new ContextData( 1 ); - #endif + + // OpenSSL 1.1+ initialises itself; no explicit init calls are needed. + this->data = new ContextData(); this->data->random.reset( random ); - this->data->openSSLContext = SSL_CTX_new( SSLv23_method() ); - - // Setup the Crypto Library Thread callbacks. - /* Only set the id callback for OpenSSL < 1.1.0. Newer versions - * manage thread id/locking internally. - */ - #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L) - CRYPTO_set_id_callback( &ContextData::getThreadId ); - #endif - /* Only install a locking callback on OpenSSL < 1.1.0; the API is - * no-op or unnecessary on newer OpenSSL versions. - */ - #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L) - CRYPTO_set_locking_callback( &ContextData::lockCallback ); - #endif + this->data->openSSLContext = SSL_CTX_new( TLS_method() ); // Load the Library default CA paths and Context Options. SSL_CTX_set_default_verify_paths( this->data->openSSLContext ); #ifdef _WIN32 - // On Windows, also load certificates from the Windows Certificate Store - // This allows OpenSSL to trust certificates that Windows trusts (like Amazon's certificates) - HCERTSTORE hStore = CertOpenSystemStoreA(NULL, "ROOT"); - if (hStore) { + // On Windows, OpenSSL does not consult the Windows Certificate Store + // automatically, so enumerate ROOT and CA system stores and inject + // each certificate into the context's trust store. + { X509_STORE* store = SSL_CTX_get_cert_store(this->data->openSSLContext); - PCCERT_CONTEXT pContext = NULL; - int certsAdded = 0; - - while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) { - const unsigned char* encoded = pContext->pbCertEncoded; - X509* x509 = d2i_X509(NULL, &encoded, pContext->cbCertEncoded); - if (x509) { - if (X509_STORE_add_cert(store, x509) == 1) { - certsAdded++; - } - X509_free(x509); - } - } - - CertCloseStore(hStore, 0); - - // Also load intermediate certificates from CA store - hStore = CertOpenSystemStoreA(NULL, "CA"); - if (hStore) { - pContext = NULL; - while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) { - const unsigned char* encoded = pContext->pbCertEncoded; - X509* x509 = d2i_X509(NULL, &encoded, pContext->cbCertEncoded); - if (x509) { - if (X509_STORE_add_cert(store, x509) == 1) { - certsAdded++; + const char* winStores[] = { "ROOT", "CA" }; + for (const char* storeName : winStores) { + HCERTSTORE hStore = CertOpenSystemStoreA(NULL, storeName); + if (hStore) { + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) { + const unsigned char* encoded = pContext->pbCertEncoded; + X509* x509 = d2i_X509(NULL, &encoded, pContext->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); } - X509_free(x509); } + CertCloseStore(hStore, 0); } - CertCloseStore(hStore, 0); } } #endif - SSL_CTX_set_options( this->data->openSSLContext, SSL_OP_ALL | SSL_OP_NO_SSLv2 ); + // SSL_OP_NO_TLSv1_3: Force TLS 1.2 and below. + // + // TLS 1.3 introduces post-handshake messages (NewSessionTicket, KeyUpdate) + // that cause SSL_read() to internally trigger SSL_write() calls for responses. + // This codebase uses separate readMutex/writeMutex to allow concurrent + // SSL_read + SSL_write (full-duplex), which is safe in TLS 1.2. However, + // with TLS 1.3 an SSL_read() call on the IOTransport reader thread may + // internally write while the main thread is simultaneously calling SSL_write(), + // corrupting the TLS write sequence numbers and producing a bad_record_mac + // alert at the broker. Disabling TLS 1.3 avoids this race until the + // transport layer is refactored to use asio::ssl::stream (which handles + // TLS 1.3 post-handshake I/O correctly). + SSL_CTX_set_options( this->data->openSSLContext, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_TLSv1_3 ); SSL_CTX_set_mode( this->data->openSSLContext, SSL_MODE_AUTO_RETRY ); // The Password Callback for cases where we need to open a Cert. @@ -307,31 +216,11 @@ void OpenSSLContextSpi::providerInit( SecureRandom* random ) { } } - // Here we load the configured TrustStore, this is where the trusted certificates are stored - // and are used to validate that we trust the Certificate sent by the server or client. - // A server might not need this if its not going to enforce client authentication. - std::string trustStorePath = System::getProperty( "decaf.net.ssl.trustStore" ); - - // OpenSSL sort of assumes that the trust store files won't require a password so we just - // ignore the trustStorePassword for now. - // std::string trustStorePassword = System::getProperty( "decaf.net.ssl.trustStorePassword" ); - - // We only consider trust store's that consist of a PEM encoded file, we could try and - // check for the extension and assume its a directory if not there, but the OpenSSL - // directory restrictions for Certificates make using a directory rather complicated - // for the user so only do it if someone asks really nicely. - if( !trustStorePath.empty() ) { - if( SSL_CTX_load_verify_locations( this->data->openSSLContext, trustStorePath.c_str(), NULL ) != 1 ) { - throw OpenSSLSocketException( __FILE__, __LINE__ ); - } - } - // Now seed the OpenSSL RNG. std::vector seed( 128 ); random->nextBytes( seed ); RAND_seed( (void*)( &seed[0] ), (int)seed.size() ); -#endif } DECAF_CATCH_RETHROW( NullPointerException ) DECAF_CATCH_RETHROW( KeyManagementException ) diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.cpp index c4c875275..6a3fdc22e 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.cpp @@ -19,9 +19,7 @@ #include -#ifdef HAVE_OPENSSL #include -#endif #include @@ -33,12 +31,11 @@ using namespace decaf::internal::net; using namespace decaf::internal::net::ssl; using namespace decaf::internal::net::ssl::openssl; -#ifdef HAVE_OPENSSL - //////////////////////////////////////////////////////////////////////////////// OpenSSLParameters::OpenSSLParameters(SSL_CTX* context) : needClientAuth(false), wantClientAuth(false), useClientMode(true), + peerVerificationEnabled(true), context(context), ssl(NULL), enabledCipherSuites(), @@ -53,15 +50,11 @@ OpenSSLParameters::OpenSSLParameters(SSL_CTX* context) : needClientAuth(false), this->ssl = SSL_new(context); } -#endif - //////////////////////////////////////////////////////////////////////////////// OpenSSLParameters::~OpenSSLParameters() { try { -#ifdef HAVE_OPENSSL SSL_free(this->ssl); -#endif } DECAF_CATCH_NOTHROW(Exception) DECAF_CATCHALL_NOTHROW() @@ -113,8 +106,6 @@ void OpenSSLParameters::setServerNames(const std::vector& serverNam //////////////////////////////////////////////////////////////////////////////// OpenSSLParameters* OpenSSLParameters::clone() const { -#ifdef HAVE_OPENSSL - std::unique_ptr cloned( new OpenSSLParameters( this->context ) ); cloned->enabledProtocols = this->enabledProtocols; @@ -123,12 +114,7 @@ OpenSSLParameters* OpenSSLParameters::clone() const { cloned->needClientAuth = this->needClientAuth; cloned->wantClientAuth = this->wantClientAuth; cloned->useClientMode = this->useClientMode; + cloned->peerVerificationEnabled = this->peerVerificationEnabled; return cloned.release(); - -#else - - return NULL; - -#endif } diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.h b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.h index 1b7c722e6..7a301c0f0 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.h +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLParameters.h @@ -23,9 +23,7 @@ #include #include -#ifdef HAVE_OPENSSL #include -#endif namespace decaf { namespace internal { @@ -44,11 +42,10 @@ namespace openssl { bool needClientAuth; bool wantClientAuth; bool useClientMode; + bool peerVerificationEnabled; -#ifdef HAVE_OPENSSL SSL_CTX* context; SSL* ssl; -#endif std::vector enabledCipherSuites; std::vector enabledProtocols; @@ -61,9 +58,7 @@ namespace openssl { public: -#ifdef HAVE_OPENSSL OpenSSLParameters(SSL_CTX* context); -#endif virtual ~OpenSSLParameters(); @@ -93,6 +88,14 @@ namespace openssl { this->useClientMode = value; } + bool getPeerVerificationEnabled() const { + return this->peerVerificationEnabled; + } + + void setPeerVerificationEnabled(bool value) { + this->peerVerificationEnabled = value; + } + std::vector getSupportedCipherSuites() const; std::vector getSupportedProtocols() const; @@ -109,8 +112,6 @@ namespace openssl { void setServerNames(const std::vector& serverNames); -#ifdef HAVE_OPENSSL - SSL_CTX* getSSLContext() const { return this->context; } @@ -119,8 +120,6 @@ namespace openssl { return this->ssl; } -#endif - /** * Creates a clone of this object such that all settings are transferred to a new * instance of an SSL object whose parent is the same SSL_CTX as this object's. diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocket.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocket.cpp index 58cb77a81..5ddd450c9 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocket.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocket.cpp @@ -17,12 +17,10 @@ #include "OpenSSLServerSocket.h" -#ifdef HAVE_OPENSSL - #include - #include - #include - #include -#endif +#include +#include +#include +#include #include #include diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocketFactory.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocketFactory.cpp index c7dbb23fe..74125ee77 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocketFactory.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLServerSocketFactory.cpp @@ -26,9 +26,7 @@ #include -#ifdef HAVE_OPENSSL #include -#endif using namespace decaf; using namespace decaf::lang; diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp index e931596c5..8ea9147ed 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp @@ -17,15 +17,18 @@ #include "OpenSSLSocket.h" -#ifdef HAVE_OPENSSL - #include - #include - #include - #include - #include -#endif +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include #include #include @@ -33,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +44,15 @@ #include #include +#include +#include +#include +#include + +#ifdef _WIN32 + #include +#endif + using namespace decaf; using namespace decaf::lang; using namespace decaf::lang::exceptions; @@ -52,6 +65,22 @@ using namespace decaf::internal::util; using namespace decaf::internal::net; using namespace decaf::internal::net::ssl; using namespace decaf::internal::net::ssl::openssl; +// Define TcpSocketImpl inline to access ASIO socket for SSL +// This must match the actual TcpSocketImpl in TcpSocket.cpp +namespace decaf { +namespace internal { +namespace net { +namespace tcp { + // Minimal TcpSocketImpl interface for SSL access + // Only include members we need to access + class TcpSocketImpl { + public: + asio::io_context& ioContext; + std::unique_ptr socket; + // Other members exist but we don't need them here + }; +}}}} + //////////////////////////////////////////////////////////////////////////////// namespace decaf { @@ -69,17 +98,36 @@ namespace openssl { Mutex handshakeLock; + // Separate mutexes for read and write paths. + // + // In TLS 1.2 (the only version negotiated — TLS 1.3 is disabled via + // SSL_OP_NO_TLSv1_3 in OpenSSLContextSpi), concurrent SSL_read + + // SSL_write on the same SSL* from different threads is safe: OpenSSL + // 1.1.0+ allows it and the two directions use independent keys and + // sequence numbers. These mutexes prevent concurrent SSL_read + SSL_read + // and concurrent SSL_write + SSL_write, which are NOT allowed. + // + // NOTE: Do NOT enable TLS 1.3 without replacing these two mutexes with a + // single I/O lock. TLS 1.3 allows SSL_read() to internally call + // SSL_write() (e.g. for KeyUpdate responses), which would race with an + // application SSL_write() that only holds writeMutex. + std::mutex readMutex; + std::mutex writeMutex; + public: SocketData() : handshakeStarted(false), handshakeCompleted(false), commonName(), - handshakeLock() { + handshakeLock(), + readMutex(), + writeMutex() + { } - ~SocketData() {} + ~SocketData() { + } -#ifdef HAVE_OPENSSL static int verifyCallback(int verified, X509_STORE_CTX* store DECAF_UNUSED) { if (!verified) { // Trap debug info here about why the Certificate failed to validate. @@ -87,7 +135,6 @@ namespace openssl { return verified; } -#endif }; @@ -145,13 +192,6 @@ OpenSSLSocket::~OpenSSLSocket() { SSLSocket::close(); -#ifdef HAVE_OPENSSL - if (this->parameters->getSSL()) { - SSL_set_shutdown(this->parameters->getSSL(), SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); - SSL_shutdown(this->parameters->getSSL()); - } -#endif - delete data; delete parameters; delete input; @@ -165,36 +205,56 @@ void OpenSSLSocket::connect(const std::string& host, int port, int timeout) { try { -#ifdef HAVE_OPENSSL // Perform the actual Socket connection work SSLSocket::connect(host, port, timeout); - // If we actually connected then we can connect the Socket to an OpenSSL - // BIO filter so that we can use it in OpenSSL APIs. + // If we actually connected, configure the SSL object for this socket if (isConnected()) { - BIO* bio = BIO_new(BIO_s_socket()); - if (!bio) { - throw SocketException(__FILE__, __LINE__, "Failed to create SSL IO Bindings"); + // Get the underlying TcpSocket and its ASIO socket + decaf::internal::net::tcp::TcpSocket* tcpSocket = + dynamic_cast(this->impl); + + if (tcpSocket == NULL) { + throw SocketException(__FILE__, __LINE__, "Socket implementation is not a TcpSocket"); } - const SocketFileDescriptor* fd = dynamic_cast(this->impl->getFileDescriptor()); + decaf::internal::net::tcp::TcpSocketImpl* socketImpl = tcpSocket->getSocketImpl(); + if (socketImpl == NULL || socketImpl->socket == nullptr) { + throw SocketException(__FILE__, __LINE__, "Invalid socket implementation"); + } - if (fd == NULL) { - throw SocketException(__FILE__, __LINE__, "Invalid File Descriptor returned from Socket"); + // Set socket to blocking mode so that SSL_read / SSL_write behave + // as true synchronous blocking calls. + socketImpl->socket->non_blocking(false); + + // Get the raw OS socket handle. + // On x86 Windows, SOCKET is 32-bit (UINT_PTR == unsigned int), so + // the static_cast is safe. On x64 builds this would need + // BIO_new_socket / SSL_set_bio instead. + auto nativeHandle = socketImpl->socket->native_handle(); + + // Retrieve the SSL object that OpenSSLParameters already created + // from the shared SSL_CTX (which has Windows cert store loaded, + // default verify paths set, cipher list configured, etc.). + SSL* ssl = this->parameters->getSSL(); + if (ssl == NULL) { + throw SocketException(__FILE__, __LINE__, "SSL object not available from parameters"); } - BIO_set_fd(bio, (int )fd->getValue(), BIO_NOCLOSE); - SSL_set_bio(this->parameters->getSSL(), bio, bio); + // Attach the socket to the SSL object via a socket BIO. + // After this call, SSL_read / SSL_write go directly through the OS + // socket rather than through ASIO's memory-BIO layer. + if (SSL_set_fd(ssl, static_cast(nativeHandle)) != 1) { + throw SocketException(__FILE__, __LINE__, "SSL_set_fd failed"); + } - // Later when startHandshake is called we will check for this common name - // in the provided certificate + // Store the common name for certificate hostname validation this->data->commonName = host; + + AMQ_LOG_DEBUG("OpenSSLSocket", "SSL socket configured with socket BIO, set to blocking mode"); } -#else - throw SocketException( __FILE__, __LINE__, "Not Supported" ); -#endif } DECAF_CATCH_RETHROW(IOException) DECAF_CATCH_RETHROW(IllegalArgumentException) @@ -211,6 +271,9 @@ void OpenSSLSocket::close() { return; } + // Close the underlying TCP socket. Any blocking SSL_read call that is + // currently waiting in the reader thread will get a socket error and + // return, allowing the reader thread to exit cleanly. SSLSocket::close(); if (this->input != NULL) { @@ -287,6 +350,7 @@ decaf::net::ssl::SSLParameters OpenSSLSocket::getSSLParameters() const { params.setServerNames(this->parameters->getServerNames()); params.setNeedClientAuth(this->parameters->getNeedClientAuth()); params.setWantClientAuth(this->parameters->getWantClientAuth()); + params.setPeerVerificationEnabled(this->parameters->getPeerVerificationEnabled()); return params; } @@ -296,6 +360,7 @@ void OpenSSLSocket::setSSLParameters(const decaf::net::ssl::SSLParameters& value this->parameters->setEnabledCipherSuites(value.getCipherSuites()); this->parameters->setEnabledProtocols(value.getProtocols()); this->parameters->setServerNames(value.getServerNames()); + this->parameters->setPeerVerificationEnabled(value.getPeerVerificationEnabled()); } //////////////////////////////////////////////////////////////////////////////// @@ -341,7 +406,6 @@ void OpenSSLSocket::startHandshake() { try { -#ifdef HAVE_OPENSSL synchronized( &(this->data->handshakeLock ) ) { if (this->data->handshakeStarted) { @@ -350,49 +414,62 @@ void OpenSSLSocket::startHandshake() { this->data->handshakeStarted = true; - bool peerVerifyDisabled = Boolean::parseBoolean(System::getProperty("decaf.net.ssl.disablePeerVerification", "false")); + bool peerVerifyEnabled = this->parameters->getPeerVerificationEnabled(); if (this->parameters->getUseClientMode()) { - // Since we are a client we want to enforce peer verification, we set a - // callback so we can collect data on why a verify failed for debugging. - if (!peerVerifyDisabled) { - // Check host https://wiki.openssl.org/index.php/Hostname_validation - X509_VERIFY_PARAM *param = SSL_get0_param(this->parameters->getSSL()); - - X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - X509_VERIFY_PARAM_set1_host(param, this->data->commonName.c_str(), 0); + SSL* ssl = this->parameters->getSSL(); + if (!ssl) { + AMQ_LOG_ERROR("OpenSSLSocket", "SSL object not available"); + SSLSocket::close(); + throw OpenSSLSocketException(__FILE__, __LINE__, + "SSL object not available. connect() must be called first."); + } - SSL_set_verify(this->parameters->getSSL(), SSL_VERIFY_PEER, SocketData::verifyCallback); + // Configure peer certificate verification + if (peerVerifyEnabled) { + // Enable host-name checking per RFC 6125 + X509_VERIFY_PARAM *param = SSL_get0_param(ssl); + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + X509_VERIFY_PARAM_set1_host(param, this->data->commonName.c_str(), 0); + SSL_set_verify(ssl, SSL_VERIFY_PEER, SocketData::verifyCallback); } else { - SSL_set_verify(this->parameters->getSSL(), SSL_VERIFY_NONE, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); } + // Set SNI hostname (needed for virtual hosting on the broker) std::vector serverNames = this->parameters->getServerNames(); if (!serverNames.empty()) { - std::string serverName = serverNames.at(0); - SSL_set_tlsext_host_name(this->parameters->getSSL(), serverName.c_str()); + SSL_set_tlsext_host_name(ssl, serverNames.at(0).c_str()); + } else { + SSL_set_tlsext_host_name(ssl, this->data->commonName.c_str()); } - int result = SSL_connect(this->parameters->getSSL()); - - // Checks the error status - switch (SSL_get_error(this->parameters->getSSL(), result)) { - case SSL_ERROR_NONE: - break; - case SSL_ERROR_SSL: - case SSL_ERROR_ZERO_RETURN: - case SSL_ERROR_SYSCALL: - default: + AMQ_LOG_DEBUG("OpenSSLSocket", "Starting SSL handshake using SSL_connect"); + + // Perform the TLS handshake synchronously. The underlying + // socket is in blocking mode so this call blocks until the + // handshake completes or fails. + int rc = SSL_connect(ssl); + if (rc != 1) { + int sslErr = SSL_get_error(ssl, rc); + unsigned long errCode = ERR_get_error(); + char errStr[256] = {0}; + ERR_error_string_n(errCode, errStr, sizeof(errStr)); + AMQ_LOG_ERROR("OpenSSLSocket", "SSL handshake failed: sslErr=" << sslErr + << " msg=" << errStr); SSLSocket::close(); - throw OpenSSLSocketException(__FILE__, __LINE__); + throw OpenSSLSocketException(__FILE__, __LINE__, + errStr[0] ? errStr : "SSL_connect failed"); } - } else { // We are in Server Mode. + AMQ_LOG_DEBUG("OpenSSLSocket", "SSL handshake completed successfully"); + + } else { // Server mode int mode = SSL_VERIFY_NONE; - if (!peerVerifyDisabled) { + if (peerVerifyEnabled) { if (this->parameters->getWantClientAuth()) { mode = SSL_VERIFY_PEER; @@ -403,13 +480,14 @@ void OpenSSLSocket::startHandshake() { } } - // Since we are a server we want to enforce peer verification, we set a - // callback so we can collect data on why a verify failed for debugging. - SSL_set_verify(this->parameters->getSSL(), mode, SocketData::verifyCallback); + SSL* ssl = this->parameters->getSSL(); + SSL_set_verify(ssl, mode, SocketData::verifyCallback); - int result = SSL_accept(this->parameters->getSSL()); + int result = SSL_accept(ssl); - if (result != SSL_ERROR_NONE) { + int sslError = SSL_get_error(ssl, result); + if (sslError != SSL_ERROR_NONE) { + AMQ_LOG_ERROR("OpenSSLSocket", "SSL_accept() failed with error code " << sslError); SSLSocket::close(); throw OpenSSLSocketException(__FILE__, __LINE__); } @@ -417,9 +495,6 @@ void OpenSSLSocket::startHandshake() { this->data->handshakeCompleted = true; } -#else - throw IOException( __FILE__, __LINE__, "SSL Not Supported." ); -#endif } DECAF_CATCH_RETHROW(IOException) DECAF_CATCHALL_THROW(IOException) @@ -498,32 +573,69 @@ int OpenSSLSocket::read(unsigned char* buffer, int size, int offset, int length) "length parameter out of Bounds: %d.", length); } -#ifdef HAVE_OPENSSL if (!this->data->handshakeCompleted) { this->startHandshake(); } - // Read data from the socket. - int result = SSL_read(this->parameters->getSSL(), buffer + offset, length); + SSL* ssl = this->parameters->getSSL(); + if (!ssl) { + throw IOException(__FILE__, __LINE__, "SSL object not initialized"); + } + + // Only one thread may call SSL_read at a time (prevents concurrent reads). + // A concurrent SSL_write from the writer thread is safe in OpenSSL 1.1+. + std::lock_guard readLock(this->data->readMutex); + + int bytesRead = SSL_read(ssl, buffer + offset, length); + + if (bytesRead > 0) { + return bytesRead; + } + + // bytesRead <= 0 — interrogate the SSL error stack + int sslError = SSL_get_error(ssl, bytesRead); - int sslError = SSL_get_error(this->parameters->getSSL(), result); switch (sslError) { - case SSL_ERROR_NONE: - return result; - case SSL_ERROR_ZERO_RETURN: - AMQ_LOG_DEBUG("OpenSSLSocket", "SSL_read returned SSL_ERROR_ZERO_RETURN, shutting down input"); - if (!isClosed()) { - this->shutdownInput(); + + case SSL_ERROR_ZERO_RETURN: + // Peer sent a TLS close_notify alert — clean EOF + AMQ_LOG_DEBUG("OpenSSLSocket", "SSL read: TLS close_notify received (clean close)"); return -1; - } - default: - AMQ_LOG_ERROR("OpenSSLSocket", "SSL_read failed: result=" << result << " sslError=" << sslError); - throw OpenSSLSocketException(__FILE__, __LINE__); - } + + case SSL_ERROR_SYSCALL: { + unsigned long errCode = ERR_get_error(); + if (errCode == 0 && bytesRead == 0) { + // Peer closed the TCP connection without sending close_notify. + // Treat as EOF. + AMQ_LOG_DEBUG("OpenSSLSocket", "SSL read: TCP EOF without close_notify"); + return -1; + } + char errStr[256] = {0}; + if (errCode != 0) { + ERR_error_string_n(errCode, errStr, sizeof(errStr)); + } else { +#ifdef _WIN32 + snprintf(errStr, sizeof(errStr), "SSL_ERROR_SYSCALL WSA=%d", WSAGetLastError()); #else - throw SocketException( __FILE__, __LINE__, "Not Supported" ); + snprintf(errStr, sizeof(errStr), "SSL_ERROR_SYSCALL errno=%d", errno); #endif + } + AMQ_LOG_ERROR("OpenSSLSocket", "SSL read failed: " << errStr); + throw OpenSSLSocketException(__FILE__, __LINE__, errStr); + } + + default: { + unsigned long errCode = ERR_get_error(); + char errStr[256] = {0}; + ERR_error_string_n(errCode, errStr, sizeof(errStr)); + AMQ_LOG_ERROR("OpenSSLSocket", "SSL read failed: sslError=" << sslError + << " msg=" << errStr); + throw OpenSSLSocketException(__FILE__, __LINE__, + errStr[0] ? errStr : "SSL_read failed"); + } + } + } DECAF_CATCH_RETHROW(IOException) DECAF_CATCH_RETHROW(NullPointerException) @@ -565,35 +677,40 @@ void OpenSSLSocket::write(const unsigned char* buffer, int size, int offset, int "length parameter out of Bounds: %d.", length); } -#ifdef HAVE_OPENSSL if (!this->data->handshakeCompleted) { this->startHandshake(); } - int remaining = length; - - while (remaining > 0 && !isClosed()) { - - int written = SSL_write(this->parameters->getSSL(), buffer + offset, remaining); + SSL* ssl = this->parameters->getSSL(); + if (!ssl) { + throw IOException(__FILE__, __LINE__, "SSL object not initialized"); + } - int sslError = SSL_get_error(this->parameters->getSSL(), written); - switch (sslError) { - case SSL_ERROR_NONE: - offset += written; - remaining -= written; - break; - case SSL_ERROR_ZERO_RETURN: - AMQ_LOG_ERROR("OpenSSLSocket", "SSL_write SSL_ERROR_ZERO_RETURN: connection broken unexpectedly"); - throw SocketException(__FILE__, __LINE__, "The connection was broken unexpectedly."); - default: - AMQ_LOG_ERROR("OpenSSLSocket", "SSL_write failed: written=" << written << " sslError=" << sslError << " remaining=" << remaining); - throw OpenSSLSocketException(__FILE__, __LINE__); + // Only one thread may call SSL_write at a time. + // A concurrent SSL_read from the reader thread is safe in OpenSSL 1.1+. + std::lock_guard writeLock(this->data->writeMutex); + + // In blocking mode without SSL_MODE_ENABLE_PARTIAL_WRITE, SSL_write + // either writes all bytes or returns an error. Loop for robustness. + int totalWritten = 0; + while (totalWritten < length) { + int written = SSL_write(ssl, buffer + offset + totalWritten, + length - totalWritten); + if (written > 0) { + totalWritten += written; + } else { + int sslError = SSL_get_error(ssl, written); + unsigned long errCode = ERR_get_error(); + char errStr[256] = {0}; + ERR_error_string_n(errCode, errStr, sizeof(errStr)); + AMQ_LOG_ERROR("OpenSSLSocket", "SSL write failed: sslError=" << sslError + << " msg=" << errStr); + throw OpenSSLSocketException(__FILE__, __LINE__, + errStr[0] ? errStr : "SSL_write failed"); } } -#else - throw SocketException( __FILE__, __LINE__, "Not Supported" ); -#endif + } DECAF_CATCH_RETHROW(IOException) DECAF_CATCH_RETHROW(NullPointerException) @@ -606,13 +723,12 @@ int OpenSSLSocket::available() { try { -#ifdef HAVE_OPENSSL if (!isClosed()) { - return SSL_pending(this->parameters->getSSL()); + SSL* ssl = this->parameters->getSSL(); + if (ssl) { + return SSL_pending(ssl); + } } -#else - throw SocketException( __FILE__, __LINE__, "Not Supported" ); -#endif return -1; } diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketException.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketException.cpp index 5658a179e..f3b680d96 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketException.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketException.cpp @@ -17,9 +17,7 @@ #include "OpenSSLSocketException.h" -#ifdef HAVE_OPENSSL #include -#endif #include @@ -104,14 +102,12 @@ std::string OpenSSLSocketException::getErrorString() const { std::string returnValue = "Error occurred while accessing an OpenSSL library method:"; -#ifdef HAVE_OPENSSL for( unsigned long e = ERR_get_error(); e; e = ERR_get_error() ) { char msg[256]; ERR_error_string_n(e, msg, sizeof msg); returnValue += "\n"; returnValue += msg; } -#endif return returnValue; } diff --git a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketFactory.cpp b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketFactory.cpp index 3c2277187..d8ccf40e8 100644 --- a/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketFactory.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocketFactory.cpp @@ -26,9 +26,7 @@ #include -#ifdef HAVE_OPENSSL #include -#endif using namespace decaf; using namespace decaf::lang; @@ -71,14 +69,10 @@ Socket* OpenSSLSocketFactory::createSocket() { try{ -#ifdef HAVE_OPENSSL // Create a new SSL object for the Socket then create a new unconnected Socket. SSL_CTX* ctx = static_cast( this->parent->getOpenSSLCtx() ); std::unique_ptr parameters( new OpenSSLParameters( ctx ) ); return new OpenSSLSocket( parameters.release() ); -#else - return NULL; -#endif } DECAF_CATCH_RETHROW( IOException ) DECAF_CATCH_EXCEPTION_CONVERT( Exception, IOException ) @@ -90,15 +84,11 @@ Socket* OpenSSLSocketFactory::createSocket( const decaf::net::InetAddress* host, try{ -#ifdef HAVE_OPENSSL // Create a new SSL object for the Socket then create a new unconnected Socket. SSL_CTX* ctx = static_cast( this->parent->getOpenSSLCtx() ); std::unique_ptr parameters( new OpenSSLParameters( ctx ) ); std::unique_ptr socket( new OpenSSLSocket( parameters.release(), host, port ) ); return socket.release(); -#else - return NULL; -#endif } DECAF_CATCH_RETHROW( IOException ) DECAF_CATCH_EXCEPTION_CONVERT( Exception, IOException ) @@ -111,16 +101,12 @@ Socket* OpenSSLSocketFactory::createSocket( const decaf::net::InetAddress* host, try{ -#ifdef HAVE_OPENSSL // Create a new SSL object for the Socket then create a new unconnected Socket. SSL_CTX* ctx = static_cast( this->parent->getOpenSSLCtx() ); std::unique_ptr parameters( new OpenSSLParameters( ctx ) ); std::unique_ptr socket( new OpenSSLSocket( parameters.release(), host, port, ifAddress, localPort ) ); return socket.release(); -#else - return NULL; -#endif } DECAF_CATCH_RETHROW( IOException ) DECAF_CATCH_EXCEPTION_CONVERT( Exception, IOException ) @@ -132,15 +118,11 @@ Socket* OpenSSLSocketFactory::createSocket( const std::string& hostname, int por try{ -#ifdef HAVE_OPENSSL // Create a new SSL object for the Socket then create a new unconnected Socket. SSL_CTX* ctx = static_cast( this->parent->getOpenSSLCtx() ); std::unique_ptr parameters( new OpenSSLParameters( ctx ) ); std::unique_ptr socket( new OpenSSLSocket( parameters.release(), hostname, port ) ); return socket.release(); -#else - return NULL; -#endif } DECAF_CATCH_RETHROW( IOException ) DECAF_CATCH_EXCEPTION_CONVERT( Exception, IOException ) @@ -153,16 +135,12 @@ Socket* OpenSSLSocketFactory::createSocket( const std::string& hostname, int por try{ -#ifdef HAVE_OPENSSL // Create a new SSL object for the Socket then create a new unconnected Socket. SSL_CTX* ctx = static_cast( this->parent->getOpenSSLCtx() ); std::unique_ptr parameters( new OpenSSLParameters( ctx ) ); std::unique_ptr socket( new OpenSSLSocket( parameters.release(), hostname, port, ifAddress, localPort ) ); return socket.release(); -#else - return NULL; -#endif } DECAF_CATCH_RETHROW( IOException ) DECAF_CATCH_EXCEPTION_CONVERT( Exception, IOException ) diff --git a/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.cpp b/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.cpp index 62753629c..3c2b6f35f 100644 --- a/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.cpp +++ b/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.cpp @@ -180,8 +180,10 @@ void TcpSocket::create() { } // Initialize the Socket's FileDescriptor with the native handle + // On Windows x64, native_handle() returns SOCKET which is 64-bit (UINT_PTR) + // Use intptr_t to properly store the handle without truncation auto nativeHandle = this->impl->socket->native_handle(); - this->fd = new SocketFileDescriptor(static_cast(nativeHandle)); + this->fd = new SocketFileDescriptor(static_cast(nativeHandle)); AMQ_LOG_DEBUG("TcpSocket", "create() completed, handle=" << nativeHandle); } DECAF_CATCH_RETHROW(decaf::io::IOException) @@ -409,10 +411,14 @@ void TcpSocket::connect(const std::string& hostname, int port, int timeout) { asio::error_code ec; - // Resolve the hostname - AMQ_LOG_DEBUG("TcpSocket", "connect() resolving hostname..."); + // Resolve the hostname using IPv4 only to match the IPv4 socket opened in create(). + // Resolving without a protocol hint returns both IPv6 and IPv4 endpoints; when the + // broker only listens on IPv4 (e.g. Docker on Windows), the IPv6 attempt can take + // ~2 seconds to fail before falling back to IPv4. Restricting to IPv4 avoids this + // delay, which matters for timing-sensitive tests (e.g. message expiration tests). + AMQ_LOG_DEBUG("TcpSocket", "connect() resolving hostname (IPv4)..."); asio::ip::tcp::resolver resolver(this->impl->ioContext); - auto endpoints = resolver.resolve(hostname, std::to_string(port), ec); + auto endpoints = resolver.resolve(asio::ip::tcp::v4(), hostname, std::to_string(port), ec); if (ec) { AMQ_LOG_ERROR("TcpSocket", "connect() resolve failed: " << ec.message()); @@ -1152,3 +1158,8 @@ bool TcpSocket::isConnected() const { bool TcpSocket::isClosed() const { return this->impl->closed.get(); } + +//////////////////////////////////////////////////////////////////////////////// +TcpSocketImpl* TcpSocket::getSocketImpl() { + return this->impl; +} diff --git a/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.h b/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.h index 786ecfd5e..65b61d8e8 100644 --- a/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.h +++ b/activemq-cpp/src/main/decaf/internal/net/tcp/TcpSocket.h @@ -183,8 +183,19 @@ namespace tcp { */ void write(const unsigned char* buffer, int size, int offset, int length); + /** + * Get access to the underlying TcpSocketImpl for advanced operations. + * This is primarily used by SSL socket implementations that need direct + * access to the ASIO socket. + * + * @return pointer to the TcpSocketImpl, or NULL if not available + */ + TcpSocketImpl* getSocketImpl(); + }; }}}} #endif /*_DECAF_INTERNAL_NET_TCP_TCPSOCKET_H_*/ + + diff --git a/activemq-cpp/src/main/decaf/io/FileDescriptor.cpp b/activemq-cpp/src/main/decaf/io/FileDescriptor.cpp index 7fd99e0ec..1f1ccf7ba 100644 --- a/activemq-cpp/src/main/decaf/io/FileDescriptor.cpp +++ b/activemq-cpp/src/main/decaf/io/FileDescriptor.cpp @@ -30,7 +30,7 @@ FileDescriptor::FileDescriptor() : descriptor( -1 ), readonly( false ) { } //////////////////////////////////////////////////////////////////////////////// -FileDescriptor::FileDescriptor(long value, bool readonly) : descriptor(value), readonly(readonly) { +FileDescriptor::FileDescriptor(intptr_t value, bool readonly) : descriptor(value), readonly(readonly) { } //////////////////////////////////////////////////////////////////////////////// diff --git a/activemq-cpp/src/main/decaf/io/FileDescriptor.h b/activemq-cpp/src/main/decaf/io/FileDescriptor.h index 5e6ff23ee..7d2dc0f21 100644 --- a/activemq-cpp/src/main/decaf/io/FileDescriptor.h +++ b/activemq-cpp/src/main/decaf/io/FileDescriptor.h @@ -18,6 +18,7 @@ #ifndef _DECAF_IO_FILEDESCRIPTOR_H_ #define _DECAF_IO_FILEDESCRIPTOR_H_ +#include #include namespace decaf { @@ -52,12 +53,14 @@ namespace io { protected: - long descriptor; + // Use intptr_t to properly store file/socket handles on all platforms + // On Windows x64, SOCKET is 64-bit (UINT_PTR) which doesn't fit in 32-bit long + intptr_t descriptor; bool readonly; protected: - FileDescriptor(long value, bool readonly); + FileDescriptor(intptr_t value, bool readonly); public: diff --git a/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.cpp b/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.cpp index 9b8558bdb..7ebf6756e 100644 --- a/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.cpp +++ b/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.cpp @@ -23,17 +23,17 @@ using namespace decaf::net::ssl; //////////////////////////////////////////////////////////////////////////////// SSLParameters::SSLParameters() : - cipherSuites(), protocols(), serverNames(), needClientAuth(false), wantClientAuth(false) { + cipherSuites(), protocols(), serverNames(), needClientAuth(false), wantClientAuth(false), peerVerificationEnabled(true) { } //////////////////////////////////////////////////////////////////////////////// SSLParameters::SSLParameters(const std::vector& cipherSuites) : - cipherSuites(cipherSuites), protocols(), serverNames(), needClientAuth(false), wantClientAuth(false) { + cipherSuites(cipherSuites), protocols(), serverNames(), needClientAuth(false), wantClientAuth(false), peerVerificationEnabled(true) { } //////////////////////////////////////////////////////////////////////////////// SSLParameters::SSLParameters(const std::vector& cipherSuites, const std::vector& protocols) : - cipherSuites(cipherSuites), protocols(protocols), serverNames(), needClientAuth(false), wantClientAuth(false) { + cipherSuites(cipherSuites), protocols(protocols), serverNames(), needClientAuth(false), wantClientAuth(false), peerVerificationEnabled(true) { } //////////////////////////////////////////////////////////////////////////////// diff --git a/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.h b/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.h index 9be15d9bd..49dcec7ec 100644 --- a/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.h +++ b/activemq-cpp/src/main/decaf/net/ssl/SSLParameters.h @@ -35,6 +35,7 @@ namespace ssl { std::vector serverNames; bool needClientAuth; bool wantClientAuth; + bool peerVerificationEnabled; public: @@ -161,6 +162,27 @@ namespace ssl { return this->serverNames; } + /** + * Sets whether peer certificate verification should be enabled. + * When disabled, the SSL connection will not verify the server's certificate. + * This should only be disabled for testing purposes. + * + * @param enabled + * true to enable peer verification (default), false to disable. + */ + void setPeerVerificationEnabled(bool enabled) { + this->peerVerificationEnabled = enabled; + } + + /** + * Gets whether peer certificate verification is enabled. + * + * @return true if peer verification is enabled, false otherwise. + */ + bool getPeerVerificationEnabled() const { + return this->peerVerificationEnabled; + } + }; }}} diff --git a/activemq-cpp/src/test-system/CMakeLists.txt b/activemq-cpp/src/test-integration-benchmarks/CMakeLists.txt similarity index 59% rename from activemq-cpp/src/test-system/CMakeLists.txt rename to activemq-cpp/src/test-integration-benchmarks/CMakeLists.txt index 730e800d6..828a87662 100644 --- a/activemq-cpp/src/test-system/CMakeLists.txt +++ b/activemq-cpp/src/test-integration-benchmarks/CMakeLists.txt @@ -1,6 +1,6 @@ -# Build system tests under src/test-system +# Build integration benchmark tests under src/test-integration-benchmarks # -# IMPORTANT: System tests require multiple running ActiveMQ brokers. +# IMPORTANT: Integration benchmark tests require multiple running ActiveMQ brokers. # Before running these tests, start all brokers using Docker Compose: # # docker compose --profile failover up -d @@ -11,15 +11,15 @@ # - activemq3: Independent broker on port 61619 # # To run specific test suites: -# ./neoactivemq-system-test -test OpenwireFailoverIntegrationTest -# ./neoactivemq-system-test -test OpenwireMultiConnectionTest -# ./neoactivemq-system-test -test OpenwireHighVolumeListenerTest +# ./neoactivemq-integration-benchmark-test -test OpenwireFailoverIntegrationTest +# ./neoactivemq-integration-benchmark-test -test OpenwireMultiConnectionTest +# ./neoactivemq-integration-benchmark-test -test OpenwireHighVolumeListenerTest # # To stop the brokers after testing: # docker compose --profile failover down # -# NOTE: System tests are designed to run SEPARATELY from unit and integration -# tests because they: +# NOTE: Integration benchmark tests are designed to run SEPARATELY from unit and +# integration tests because they: # - Require multiple broker instances # - Test high-volume scenarios (10k+ messages) # - May take longer to complete @@ -28,7 +28,7 @@ # Reuse utility files from test-integration set(TEST_INTEGRATION_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../test-integration) -set(SYSTEM_TEST_SRCS +set(INTEGRATION_BENCHMARK_TEST_SRCS # Top level launcher ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp @@ -38,18 +38,18 @@ set(SYSTEM_TEST_SRCS ${TEST_INTEGRATION_DIR}/activemq/util/CMSProvider.cpp ${TEST_INTEGRATION_DIR}/activemq/util/IntegrationCommon.cpp - # System tests - Failover and Multi-Connection + # Integration benchmark tests - Failover and Multi-Connection activemq/test/openwire/OpenwireFailoverIntegrationTest.cpp activemq/test/openwire/OpenwireMultiConnectionTest.cpp activemq/test/openwire/OpenwireHighVolumeListenerTest.cpp ) -set(SYSTEM_TEST_MAIN "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp") +set(INTEGRATION_BENCHMARK_TEST_MAIN "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp") -# Build a single system test harness -list(REMOVE_ITEM SYSTEM_TEST_SRCS ${SYSTEM_TEST_MAIN}) -add_executable(neoactivemq-system-test ${SYSTEM_TEST_MAIN} ${SYSTEM_TEST_SRCS}) -target_include_directories(neoactivemq-system-test PRIVATE +# Build a single integration benchmark test harness +list(REMOVE_ITEM INTEGRATION_BENCHMARK_TEST_SRCS ${INTEGRATION_BENCHMARK_TEST_MAIN}) +add_executable(neoactivemq-integration-benchmark-test ${INTEGRATION_BENCHMARK_TEST_MAIN} ${INTEGRATION_BENCHMARK_TEST_SRCS}) +target_include_directories(neoactivemq-integration-benchmark-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../main ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. @@ -62,14 +62,14 @@ target_include_directories(neoactivemq-system-test PRIVATE # Add /bigobj flag for MSVC to handle large object files with many sections if(MSVC) - target_compile_options(neoactivemq-system-test PRIVATE /bigobj) + target_compile_options(neoactivemq-integration-benchmark-test PRIVATE /bigobj) # Allow duplicate symbols - templates instantiated in both DLL and test exe - target_link_options(neoactivemq-system-test PRIVATE /FORCE:MULTIPLE) + target_link_options(neoactivemq-integration-benchmark-test PRIVATE /FORCE:MULTIPLE) endif() # Define DLL import macros when linking against shared library on Windows if(AMQCPP_SHARED_LIB AND WIN32) - target_compile_definitions(neoactivemq-system-test PRIVATE + target_compile_definitions(neoactivemq-integration-benchmark-test PRIVATE DECAF_DLL CMS_DLL AMQCPP_DLL @@ -77,16 +77,16 @@ if(AMQCPP_SHARED_LIB AND WIN32) endif() # Link to the consolidated library and Google Test -target_link_libraries(neoactivemq-system-test PRIVATE neoactivemq-cpp GTest::gtest) +target_link_libraries(neoactivemq-integration-benchmark-test PRIVATE neoactivemq-cpp GTest::gtest) include(StaticTestDiscovery) -static_discover_tests(neoactivemq-system-test - TEST_PREFIX "system/" +static_discover_tests(neoactivemq-integration-benchmark-test + TEST_PREFIX "integration-benchmark/" SOURCES activemq/test/openwire/OpenwireFailoverIntegrationTest.cpp activemq/test/openwire/OpenwireHighVolumeListenerTest.cpp activemq/test/openwire/OpenwireMultiConnectionTest.cpp - LABELS system + LABELS integration-benchmark PROPERTIES TIMEOUT 600 ENVIRONMENT "CTEST_OUTPUT_ON_FAILURE=1" diff --git a/activemq-cpp/src/test-system/activemq/test/openwire/OpenwireFailoverIntegrationTest.cpp b/activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireFailoverIntegrationTest.cpp similarity index 100% rename from activemq-cpp/src/test-system/activemq/test/openwire/OpenwireFailoverIntegrationTest.cpp rename to activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireFailoverIntegrationTest.cpp diff --git a/activemq-cpp/src/test-system/activemq/test/openwire/OpenwireFailoverIntegrationTest.h b/activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireFailoverIntegrationTest.h similarity index 100% rename from activemq-cpp/src/test-system/activemq/test/openwire/OpenwireFailoverIntegrationTest.h rename to activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireFailoverIntegrationTest.h diff --git a/activemq-cpp/src/test-system/activemq/test/openwire/OpenwireHighVolumeListenerTest.cpp b/activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireHighVolumeListenerTest.cpp similarity index 100% rename from activemq-cpp/src/test-system/activemq/test/openwire/OpenwireHighVolumeListenerTest.cpp rename to activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireHighVolumeListenerTest.cpp diff --git a/activemq-cpp/src/test-system/activemq/test/openwire/OpenwireHighVolumeListenerTest.h b/activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireHighVolumeListenerTest.h similarity index 100% rename from activemq-cpp/src/test-system/activemq/test/openwire/OpenwireHighVolumeListenerTest.h rename to activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireHighVolumeListenerTest.h diff --git a/activemq-cpp/src/test-system/activemq/test/openwire/OpenwireMultiConnectionTest.cpp b/activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireMultiConnectionTest.cpp similarity index 100% rename from activemq-cpp/src/test-system/activemq/test/openwire/OpenwireMultiConnectionTest.cpp rename to activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireMultiConnectionTest.cpp diff --git a/activemq-cpp/src/test-system/activemq/test/openwire/OpenwireMultiConnectionTest.h b/activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireMultiConnectionTest.h similarity index 100% rename from activemq-cpp/src/test-system/activemq/test/openwire/OpenwireMultiConnectionTest.h rename to activemq-cpp/src/test-integration-benchmarks/activemq/test/openwire/OpenwireMultiConnectionTest.h diff --git a/activemq-cpp/src/test-system/main.cpp b/activemq-cpp/src/test-integration-benchmarks/main.cpp similarity index 98% rename from activemq-cpp/src/test-system/main.cpp rename to activemq-cpp/src/test-integration-benchmarks/main.cpp index 5010ae3c7..f0e4883fd 100644 --- a/activemq-cpp/src/test-system/main.cpp +++ b/activemq-cpp/src/test-integration-benchmarks/main.cpp @@ -36,7 +36,7 @@ int main( int argc, char **argv ) { // Enable record-only mode: skip formatting overhead, only record to flight recorder activemq::util::AMQLogger::setRecordOnlyMode(true); - long long testTimeoutSeconds = 600; // Per-test timeout: 10 minutes default for system tests + long long testTimeoutSeconds = 600; // Per-test timeout: 10 minutes default for integration benchmark tests bool useTeamCity = false; // Let GTest parse --gtest_* flags first @@ -77,7 +77,7 @@ int main( int argc, char **argv ) { // Add watchdog listener for per-test timeout (0 = disabled) std::unique_ptr watchdog; if( testTimeoutSeconds > 0 ) { - watchdog.reset( new test::util::TestWatchdog( testTimeoutSeconds, true, "System test" ) ); + watchdog.reset( new test::util::TestWatchdog( testTimeoutSeconds, true, "Integration benchmark test" ) ); listeners.Append( watchdog.get() ); } diff --git a/activemq-cpp/src/test-integration/CMakeLists.txt b/activemq-cpp/src/test-integration/CMakeLists.txt index 798e2f93b..48976de86 100644 --- a/activemq-cpp/src/test-integration/CMakeLists.txt +++ b/activemq-cpp/src/test-integration/CMakeLists.txt @@ -14,9 +14,9 @@ # apache/activemq-classic:5.18.7 # # NOTE: Failover, multi-connection, and high-volume tests have been moved to -# test-system directory. Run those with: +# test-integration-benchmarks directory. Run those with: # docker compose --profile failover up -d -# ./neoactivemq-system-test +# ./neoactivemq-integration-benchmark-test # # To stop the broker after testing: # docker-compose down @@ -83,6 +83,36 @@ set(INTEGRATION_TEST_SRCS # Additional OpenWire protocol specification tests activemq/test/openwire/OpenwireMessageSelectorTest.cpp + # OpenWire SSL protocol tests (require SSL broker: docker compose --profile ssl up) + activemq/test/openwire_ssl/OpenwireSslAdvisoryTest.cpp + activemq/test/openwire_ssl/OpenwireSslAsyncSenderTest.cpp + activemq/test/openwire_ssl/OpenwireSslClientAckTest.cpp + activemq/test/openwire_ssl/OpenwireSslCmsConnectionStartStopTest.cpp + activemq/test/openwire_ssl/OpenwireSslCmsSendWithAsyncCallbackTest.cpp + activemq/test/openwire_ssl/OpenwireSslCmsTemplateTest.cpp + activemq/test/openwire_ssl/OpenwireSslDurableTest.cpp + activemq/test/openwire_ssl/OpenwireSslEnhancedConnectionTest.cpp + activemq/test/openwire_ssl/OpenwireSslExpirationTest.cpp + activemq/test/openwire_ssl/OpenwireSslIndividualAckTest.cpp + activemq/test/openwire_ssl/OpenwireSslJmsMessageGroupsTest.cpp + activemq/test/openwire_ssl/OpenwireSslJmsRecoverTest.cpp + activemq/test/openwire_ssl/OpenwireSslMapMessageTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessageCompressionTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessageListenerRedeliveryTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessagePriorityTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessageSelectorTest.cpp + activemq/test/openwire_ssl/OpenwireSslNonBlockingRedeliveryTest.cpp + activemq/test/openwire_ssl/OpenwireSslOptimizedAckTest.cpp + activemq/test/openwire_ssl/OpenwireSslQueueBrowserTest.cpp + activemq/test/openwire_ssl/OpenwireSslRedeliveryPolicyTest.cpp + activemq/test/openwire_ssl/OpenwireSslSimpleRollbackTest.cpp + activemq/test/openwire_ssl/OpenwireSslSimpleTest.cpp + activemq/test/openwire_ssl/OpenwireSslSlowListenerTest.cpp + activemq/test/openwire_ssl/OpenwireSslTempDestinationTest.cpp + activemq/test/openwire_ssl/OpenwireSslTransactionTest.cpp + activemq/test/openwire_ssl/OpenwireSslVirtualTopicTest.cpp + activemq/test/openwire_ssl/OpenwireSslXATransactionsTest.cpp + # STOMP protocol tests activemq/test/stomp/StompAdvisoryTest.cpp activemq/test/stomp/StompAsyncSenderTest.cpp @@ -134,9 +164,9 @@ target_link_libraries(neoactivemq-integration-test PRIVATE neoactivemq-cpp GTest include(StaticTestDiscovery) -# Collect openwire + stomp test sources for static test discovery -# (these files contain the TEST_F macros after header merge) -set(INTEGRATION_TEST_DISCOVERY_SRCS +# OpenWire integration tests (non-SSL) +# Requires: docker compose up (default broker on port 61616) +set(INTEGRATION_OPENWIRE_TEST_DISCOVERY_SRCS activemq/test/openwire/OpenwireAdvisoryTest.cpp activemq/test/openwire/OpenwireAsyncSenderTest.cpp activemq/test/openwire/OpenwireClientAckTest.cpp @@ -165,6 +195,65 @@ set(INTEGRATION_TEST_DISCOVERY_SRCS activemq/test/openwire/OpenwireVirtualTopicTest.cpp activemq/test/openwire/OpenwireXATransactionsTest.cpp activemq/test/openwire/OpenwireMessageSelectorTest.cpp +) + +static_discover_tests(neoactivemq-integration-test + TEST_PREFIX "integration/activemq/test/openwire/" + SOURCES ${INTEGRATION_OPENWIRE_TEST_DISCOVERY_SRCS} + LABELS "integration;integration-openwire" + PROPERTIES + TIMEOUT 300 + ENVIRONMENT "CTEST_OUTPUT_ON_FAILURE=1" +) + +# OpenWire SSL integration tests +# Requires: docker compose --profile ssl up (SSL broker on port 61617) +set(INTEGRATION_OPENWIRE_SSL_TEST_DISCOVERY_SRCS + activemq/test/openwire_ssl/OpenwireSslAdvisoryTest.cpp + activemq/test/openwire_ssl/OpenwireSslAsyncSenderTest.cpp + activemq/test/openwire_ssl/OpenwireSslClientAckTest.cpp + activemq/test/openwire_ssl/OpenwireSslCmsConnectionStartStopTest.cpp + activemq/test/openwire_ssl/OpenwireSslCmsSendWithAsyncCallbackTest.cpp + activemq/test/openwire_ssl/OpenwireSslCmsTemplateTest.cpp + activemq/test/openwire_ssl/OpenwireSslDurableTest.cpp + activemq/test/openwire_ssl/OpenwireSslEnhancedConnectionTest.cpp + activemq/test/openwire_ssl/OpenwireSslExpirationTest.cpp + activemq/test/openwire_ssl/OpenwireSslIndividualAckTest.cpp + activemq/test/openwire_ssl/OpenwireSslJmsMessageGroupsTest.cpp + activemq/test/openwire_ssl/OpenwireSslJmsRecoverTest.cpp + activemq/test/openwire_ssl/OpenwireSslMapMessageTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessageCompressionTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessageListenerRedeliveryTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessagePriorityTest.cpp + activemq/test/openwire_ssl/OpenwireSslMessageSelectorTest.cpp + activemq/test/openwire_ssl/OpenwireSslNonBlockingRedeliveryTest.cpp + activemq/test/openwire_ssl/OpenwireSslOptimizedAckTest.cpp + activemq/test/openwire_ssl/OpenwireSslQueueBrowserTest.cpp + activemq/test/openwire_ssl/OpenwireSslRedeliveryPolicyTest.cpp + activemq/test/openwire_ssl/OpenwireSslSimpleRollbackTest.cpp + activemq/test/openwire_ssl/OpenwireSslSimpleTest.cpp + activemq/test/openwire_ssl/OpenwireSslSlowListenerTest.cpp + activemq/test/openwire_ssl/OpenwireSslTempDestinationTest.cpp + activemq/test/openwire_ssl/OpenwireSslTransactionTest.cpp + activemq/test/openwire_ssl/OpenwireSslVirtualTopicTest.cpp + activemq/test/openwire_ssl/OpenwireSslXATransactionsTest.cpp +) + +# Set SSL certificate file path (convert to native path for Windows compatibility) +file(TO_NATIVE_PATH "${CMAKE_SOURCE_DIR}/docker/ssl/certs/ca.pem" SSL_CERT_FILE_PATH) + +static_discover_tests(neoactivemq-integration-test + TEST_PREFIX "integration/activemq/test/openwire-ssl/" + SOURCES ${INTEGRATION_OPENWIRE_SSL_TEST_DISCOVERY_SRCS} + LABELS "integration;integration-openwire-ssl" + PROPERTIES + TIMEOUT 300 + ENVIRONMENT "SSL_CERT_FILE=${SSL_CERT_FILE_PATH}" +) + +# STOMP integration tests +# Requires: docker compose up (default broker on port 61613) +set(INTEGRATION_STOMP_TEST_DISCOVERY_SRCS activemq/test/stomp/StompAdvisoryTest.cpp activemq/test/stomp/StompAsyncSenderTest.cpp activemq/test/stomp/StompBulkMessageTest.cpp @@ -180,9 +269,9 @@ set(INTEGRATION_TEST_DISCOVERY_SRCS ) static_discover_tests(neoactivemq-integration-test - TEST_PREFIX "integration/" - SOURCES ${INTEGRATION_TEST_DISCOVERY_SRCS} - LABELS integration + TEST_PREFIX "integration/activemq/test/stomp/" + SOURCES ${INTEGRATION_STOMP_TEST_DISCOVERY_SRCS} + LABELS "integration;integration-stomp" PROPERTIES TIMEOUT 300 ENVIRONMENT "CTEST_OUTPUT_ON_FAILURE=1" diff --git a/activemq-cpp/src/test-integration/activemq/test/CmsConnectionStartStopTest.h b/activemq-cpp/src/test-integration/activemq/test/CmsConnectionStartStopTest.h index f9eb49ce2..00d2bcc96 100644 --- a/activemq-cpp/src/test-integration/activemq/test/CmsConnectionStartStopTest.h +++ b/activemq-cpp/src/test-integration/activemq/test/CmsConnectionStartStopTest.h @@ -28,7 +28,7 @@ namespace activemq{ namespace test{ class CmsConnectionStartStopTest : public CMSTestFixture { - private: + protected: decaf::lang::Pointer startedConnection; decaf::lang::Pointer stoppedConnection; diff --git a/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.cpp b/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.cpp index f37a3a80b..4125ad895 100644 --- a/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.cpp @@ -46,10 +46,6 @@ using namespace decaf::lang; //////////////////////////////////////////////////////////////////////////////// namespace { - std::unique_ptr factory; - std::unique_ptr connection; - std::unique_ptr destination; - class MyMessageListener: public cms::MessageListener { public: @@ -84,11 +80,11 @@ namespace { } }; - double benchmarkNonCallbackRate(int count) { + double benchmarkNonCallbackRate(int count, cms::Connection* connection, cms::Destination* destination) { std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); std::unique_ptr producer( - dynamic_cast(session->createProducer(destination.get()))); + dynamic_cast(session->createProducer(destination))); producer->setDeliveryMode(DeliveryMode::PERSISTENT); long long start = System::currentTimeMillis(); @@ -101,13 +97,13 @@ namespace { return 1000.0 * count / (double)((System::currentTimeMillis() - start)); } - double benchmarkCallbackRate(int count) { + double benchmarkCallbackRate(int count, cms::Connection* connection, cms::Destination* destination) { std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); CountDownLatch messagesSent(count); MyAsyncCallback onComplete(&messagesSent); std::unique_ptr producer( - dynamic_cast(session->createProducer(destination.get()))); + dynamic_cast(session->createProducer(destination))); producer->setDeliveryMode(DeliveryMode::PERSISTENT); long long start = System::currentTimeMillis(); @@ -146,9 +142,9 @@ void CmsSendWithAsyncCallbackTest::SetUp() { //////////////////////////////////////////////////////////////////////////////// void CmsSendWithAsyncCallbackTest::TearDown() { - factory.reset(NULL); - connection.reset(NULL); - destination.reset(NULL); + factory.reset(); + connection.reset(); + destination.reset(); } //////////////////////////////////////////////////////////////////////////////// @@ -165,11 +161,11 @@ void CmsSendWithAsyncCallbackTest::testAsyncCallbackIsFaster() { consumer->setMessageListener(&listener); // warmup... - benchmarkNonCallbackRate(20); - benchmarkCallbackRate(20); + benchmarkNonCallbackRate(20, connection.get(), destination.get()); + benchmarkCallbackRate(20, connection.get(), destination.get()); - double callbackRate = benchmarkCallbackRate(30); - double nonCallbackRate = benchmarkNonCallbackRate(30); + double callbackRate = benchmarkCallbackRate(30, connection.get(), destination.get()); + double nonCallbackRate = benchmarkNonCallbackRate(30, connection.get(), destination.get()); ASSERT_TRUE(callbackRate > 0); ASSERT_TRUE(nonCallbackRate > 0); diff --git a/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.h b/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.h index 81616c7f5..f91875418 100644 --- a/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.h +++ b/activemq-cpp/src/test-integration/activemq/test/CmsSendWithAsyncCallbackTest.h @@ -20,10 +20,21 @@ #include +#include +#include +#include +#include + namespace activemq { namespace test { class CmsSendWithAsyncCallbackTest : public CMSTestFixture { + protected: + + std::unique_ptr factory; + std::unique_ptr connection; + std::unique_ptr destination; + public: CmsSendWithAsyncCallbackTest(); diff --git a/activemq-cpp/src/test-integration/activemq/test/DurableTest.h b/activemq-cpp/src/test-integration/activemq/test/DurableTest.h index 77ba99a40..7af77d926 100644 --- a/activemq-cpp/src/test-integration/activemq/test/DurableTest.h +++ b/activemq-cpp/src/test-integration/activemq/test/DurableTest.h @@ -25,7 +25,7 @@ namespace activemq{ namespace test{ class DurableTest : public CMSTestFixture { - private: + protected: static const int MSG_COUNT = 10; diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireCmsSendWithAsyncCallbackTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireCmsSendWithAsyncCallbackTest.cpp index 193aa2470..0dd7fce2b 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireCmsSendWithAsyncCallbackTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireCmsSendWithAsyncCallbackTest.cpp @@ -22,28 +22,16 @@ namespace activemq { namespace test { namespace openwire { class OpenWireCmsSendWithAsyncCallbackTest : public CmsSendWithAsyncCallbackTest { - private: -public: - OpenWireCmsSendWithAsyncCallbackTest(); - virtual ~OpenWireCmsSendWithAsyncCallbackTest(); + public: virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } }; }}} -using namespace activemq; -using namespace activemq::test; using namespace activemq::test::openwire; //////////////////////////////////////////////////////////////////////////////// -OpenWireCmsSendWithAsyncCallbackTest::OpenWireCmsSendWithAsyncCallbackTest() { +TEST_F(OpenWireCmsSendWithAsyncCallbackTest, testAsyncCallbackIsFaster) { + testAsyncCallbackIsFaster(); } - -//////////////////////////////////////////////////////////////////////////////// -OpenWireCmsSendWithAsyncCallbackTest::~OpenWireCmsSendWithAsyncCallbackTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenWireCmsSendWithAsyncCallbackTest, testAsyncCallbackIsFaster) { testAsyncCallbackIsFaster(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireMessageListenerRedeliveryTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireMessageListenerRedeliveryTest.cpp index faaa09289..b61ef25fe 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireMessageListenerRedeliveryTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireMessageListenerRedeliveryTest.cpp @@ -22,17 +22,12 @@ namespace activemq { namespace test { namespace openwire { class OpenWireMessageListenerRedeliveryTest : public CMSTestFixture { - private: -public: - OpenWireMessageListenerRedeliveryTest(); - virtual ~OpenWireMessageListenerRedeliveryTest(); + public: + void SetUp() override {} + void TearDown() override {} virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } - void testQueueRollbackConsumerListener(); - void testQueueSessionListenerExceptionRetry(); - void testQueueSessionListenerExceptionDlq(); - void testTransactedQueueSessionListenerExceptionDlq(); }; }}} @@ -249,15 +244,7 @@ namespace { } //////////////////////////////////////////////////////////////////////////////// -OpenWireMessageListenerRedeliveryTest::OpenWireMessageListenerRedeliveryTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenWireMessageListenerRedeliveryTest::~OpenWireMessageListenerRedeliveryTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenWireMessageListenerRedeliveryTest::testQueueRollbackConsumerListener() { +TEST_F(OpenWireMessageListenerRedeliveryTest, testQueueRollbackConsumerListener) { std::unique_ptr connection(createConnection(getBrokerURL())); connection->start(); @@ -313,7 +300,7 @@ void OpenWireMessageListenerRedeliveryTest::testQueueRollbackConsumerListener() } //////////////////////////////////////////////////////////////////////////////// -void OpenWireMessageListenerRedeliveryTest::testQueueSessionListenerExceptionRetry() { +TEST_F(OpenWireMessageListenerRedeliveryTest, testQueueSessionListenerExceptionRetry) { std::unique_ptr connection(createConnection(getBrokerURL())); connection->start(); @@ -352,7 +339,7 @@ void OpenWireMessageListenerRedeliveryTest::testQueueSessionListenerExceptionRet } //////////////////////////////////////////////////////////////////////////////// -void OpenWireMessageListenerRedeliveryTest::testQueueSessionListenerExceptionDlq() { +TEST_F(OpenWireMessageListenerRedeliveryTest, testQueueSessionListenerExceptionDlq) { const std::string TEST_NAME = "testQueueSessionListenerExceptionDlq"; @@ -402,7 +389,7 @@ void OpenWireMessageListenerRedeliveryTest::testQueueSessionListenerExceptionDlq } //////////////////////////////////////////////////////////////////////////////// -void OpenWireMessageListenerRedeliveryTest::testTransactedQueueSessionListenerExceptionDlq() { +TEST_F(OpenWireMessageListenerRedeliveryTest, testTransactedQueueSessionListenerExceptionDlq) { const std::string TEST_NAME = "testTransactedQueueSessionListenerExceptionDlq"; @@ -451,10 +438,3 @@ void OpenWireMessageListenerRedeliveryTest::testTransactedQueueSessionListenerEx connection->close(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenWireMessageListenerRedeliveryTest, testQueueRollbackConsumerListener) { testQueueRollbackConsumerListener(); } -TEST_F(OpenWireMessageListenerRedeliveryTest, testQueueSessionListenerExceptionRetry) { testQueueSessionListenerExceptionRetry(); } -TEST_F(OpenWireMessageListenerRedeliveryTest, testQueueSessionListenerExceptionDlq) { testQueueSessionListenerExceptionDlq(); } -TEST_F(OpenWireMessageListenerRedeliveryTest, testTransactedQueueSessionListenerExceptionDlq) { testTransactedQueueSessionListenerExceptionDlq(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireRedeliveryPolicyTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireRedeliveryPolicyTest.cpp index d94bd978d..45811ac90 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireRedeliveryPolicyTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenWireRedeliveryPolicyTest.cpp @@ -22,25 +22,12 @@ namespace activemq { namespace test { namespace openwire { class OpenWireRedeliveryPolicyTest : public CMSTestFixture { -public: - OpenWireRedeliveryPolicyTest(); - virtual ~OpenWireRedeliveryPolicyTest(); + public: void SetUp() override {} void TearDown() override {} - virtual std::string getBrokerURL() const; - void testGetNext(); - void testGetNextWithInitialDelay(); - void testExponentialRedeliveryPolicyDelaysDeliveryOnRollback(); - void testNornalRedeliveryPolicyDelaysDeliveryOnRollback(); - void testDLQHandling(); - void testInfiniteMaximumNumberOfRedeliveries(); - void testMaximumRedeliveryDelay(); - void testZeroMaximumNumberOfRedeliveries(); - void testRepeatedRedeliveryReceiveNoCommit(); - void testRepeatedRedeliveryOnMessageNoCommit(); - void testInitialRedeliveryDelayZero(); - void testInitialRedeliveryDelayOne(); - void testRedeliveryDelayOne(); + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); + } }; }}} @@ -75,20 +62,7 @@ using namespace decaf::util::concurrent; using namespace decaf::util::concurrent::atomic; //////////////////////////////////////////////////////////////////////////////// -OpenWireRedeliveryPolicyTest::OpenWireRedeliveryPolicyTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenWireRedeliveryPolicyTest::~OpenWireRedeliveryPolicyTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -std::string OpenWireRedeliveryPolicyTest::getBrokerURL() const { - return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testGetNext() { +TEST_F(OpenWireRedeliveryPolicyTest, testGetNext) { DefaultRedeliveryPolicy policy; policy.setInitialRedeliveryDelay(0); @@ -109,7 +83,7 @@ void OpenWireRedeliveryPolicyTest::testGetNext() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testGetNextWithInitialDelay() { +TEST_F(OpenWireRedeliveryPolicyTest, testGetNextWithInitialDelay) { DefaultRedeliveryPolicy policy; policy.setInitialRedeliveryDelay(500); @@ -123,7 +97,7 @@ void OpenWireRedeliveryPolicyTest::testGetNextWithInitialDelay() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testExponentialRedeliveryPolicyDelaysDeliveryOnRollback() { +TEST_F(OpenWireRedeliveryPolicyTest, testExponentialRedeliveryPolicyDelaysDeliveryOnRollback) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -185,7 +159,7 @@ void OpenWireRedeliveryPolicyTest::testExponentialRedeliveryPolicyDelaysDelivery } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testNornalRedeliveryPolicyDelaysDeliveryOnRollback() { +TEST_F(OpenWireRedeliveryPolicyTest, testNornalRedeliveryPolicyDelaysDeliveryOnRollback) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -244,7 +218,7 @@ void OpenWireRedeliveryPolicyTest::testNornalRedeliveryPolicyDelaysDeliveryOnRol } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testDLQHandling() { +TEST_F(OpenWireRedeliveryPolicyTest, testDLQHandling) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -315,7 +289,7 @@ void OpenWireRedeliveryPolicyTest::testDLQHandling() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testInfiniteMaximumNumberOfRedeliveries() { +TEST_F(OpenWireRedeliveryPolicyTest, testInfiniteMaximumNumberOfRedeliveries) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -389,7 +363,7 @@ void OpenWireRedeliveryPolicyTest::testInfiniteMaximumNumberOfRedeliveries() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testMaximumRedeliveryDelay() { +TEST_F(OpenWireRedeliveryPolicyTest, testMaximumRedeliveryDelay) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -449,7 +423,7 @@ void OpenWireRedeliveryPolicyTest::testMaximumRedeliveryDelay() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testZeroMaximumNumberOfRedeliveries() { +TEST_F(OpenWireRedeliveryPolicyTest, testZeroMaximumNumberOfRedeliveries) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -493,7 +467,7 @@ void OpenWireRedeliveryPolicyTest::testZeroMaximumNumberOfRedeliveries() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testRepeatedRedeliveryReceiveNoCommit() { +TEST_F(OpenWireRedeliveryPolicyTest, testRepeatedRedeliveryReceiveNoCommit) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -589,7 +563,7 @@ namespace { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testRepeatedRedeliveryOnMessageNoCommit() { +TEST_F(OpenWireRedeliveryPolicyTest, testRepeatedRedeliveryOnMessageNoCommit) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -658,7 +632,7 @@ void OpenWireRedeliveryPolicyTest::testRepeatedRedeliveryOnMessageNoCommit() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testInitialRedeliveryDelayZero() { +TEST_F(OpenWireRedeliveryPolicyTest, testInitialRedeliveryDelayZero) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -706,7 +680,7 @@ void OpenWireRedeliveryPolicyTest::testInitialRedeliveryDelayZero() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testInitialRedeliveryDelayOne() { +TEST_F(OpenWireRedeliveryPolicyTest, testInitialRedeliveryDelayOne) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -756,7 +730,7 @@ void OpenWireRedeliveryPolicyTest::testInitialRedeliveryDelayOne() { } //////////////////////////////////////////////////////////////////////////////// -void OpenWireRedeliveryPolicyTest::testRedeliveryDelayOne() { +TEST_F(OpenWireRedeliveryPolicyTest, testRedeliveryDelayOne) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -811,19 +785,3 @@ void OpenWireRedeliveryPolicyTest::testRedeliveryDelayOne() { ASSERT_EQ(std::string("2nd"), textMessage->getText()); session->commit(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenWireRedeliveryPolicyTest, testGetNext) { testGetNext(); } -TEST_F(OpenWireRedeliveryPolicyTest, testGetNextWithInitialDelay) { testGetNextWithInitialDelay(); } -TEST_F(OpenWireRedeliveryPolicyTest, testExponentialRedeliveryPolicyDelaysDeliveryOnRollback) { testExponentialRedeliveryPolicyDelaysDeliveryOnRollback(); } -TEST_F(OpenWireRedeliveryPolicyTest, testNornalRedeliveryPolicyDelaysDeliveryOnRollback) { testNornalRedeliveryPolicyDelaysDeliveryOnRollback(); } -TEST_F(OpenWireRedeliveryPolicyTest, testDLQHandling) { testDLQHandling(); } -TEST_F(OpenWireRedeliveryPolicyTest, testInfiniteMaximumNumberOfRedeliveries) { testInfiniteMaximumNumberOfRedeliveries(); } -TEST_F(OpenWireRedeliveryPolicyTest, testZeroMaximumNumberOfRedeliveries) { testZeroMaximumNumberOfRedeliveries(); } -TEST_F(OpenWireRedeliveryPolicyTest, testRepeatedRedeliveryReceiveNoCommit) { testRepeatedRedeliveryReceiveNoCommit(); } -TEST_F(OpenWireRedeliveryPolicyTest, testRepeatedRedeliveryOnMessageNoCommit) { testRepeatedRedeliveryOnMessageNoCommit(); } -TEST_F(OpenWireRedeliveryPolicyTest, testInitialRedeliveryDelayZero) { testInitialRedeliveryDelayZero(); } -TEST_F(OpenWireRedeliveryPolicyTest, testInitialRedeliveryDelayOne) { testInitialRedeliveryDelayOne(); } -TEST_F(OpenWireRedeliveryPolicyTest, testRedeliveryDelayOne) { testRedeliveryDelayOne(); } -TEST_F(OpenWireRedeliveryPolicyTest, testMaximumRedeliveryDelay) { testMaximumRedeliveryDelay(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAdvisoryTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAdvisoryTest.cpp index aaaf3a8b4..33c625039 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAdvisoryTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAdvisoryTest.cpp @@ -18,9 +18,15 @@ #include #include #include +#include +#include +#include #include #include +#include #include +#include +#include #include #include @@ -38,6 +44,7 @@ #include #include #include +#include #include #include @@ -49,16 +56,68 @@ namespace test { namespace openwire { class OpenwireAdvisoryTest : public AdvisoryTest { public: - OpenwireAdvisoryTest(); - virtual ~OpenwireAdvisoryTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } - void testConnectionAdvisories(); - void testConcurrentTempDestCreation(); }; }}} +namespace { + + class ConnectionLoadThread : public decaf::lang::Thread { + private: + + cms::ConnectionFactory* factory; + bool noErrors; + std::string errorMessage; + + public: + + ConnectionLoadThread(cms::ConnectionFactory* factory) : + Thread(), factory(factory), noErrors(true), errorMessage() { + } + + virtual ~ConnectionLoadThread() {} + + bool isNoErrors() const { + return this->noErrors; + } + + std::string getErrorMessage() const { + return this->errorMessage; + } + + virtual void run() { + + try { + for (unsigned int i = 0; i < 50; ++i) { + std::unique_ptr connection(factory->createConnection()); + connection->start(); + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + + for (unsigned int j = 0; j < 100; ++j) { + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + } + + decaf::util::concurrent::TimeUnit::MILLISECONDS.sleep(20); + connection->close(); + } + } catch (cms::CMSException& e) { + noErrors = false; + errorMessage = std::string("CMSException: ") + e.what(); + } catch (std::exception& e) { + noErrors = false; + errorMessage = std::string("std::exception: ") + e.what(); + } catch(...) { + noErrors = false; + errorMessage = "Unknown exception"; + } + } + }; + +} + using namespace cms; using namespace std; using namespace decaf; @@ -72,17 +131,10 @@ using namespace activemq::commands; using namespace activemq::exceptions; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::util; //////////////////////////////////////////////////////////////////////////////// -OpenwireAdvisoryTest::OpenwireAdvisoryTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireAdvisoryTest::~OpenwireAdvisoryTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireAdvisoryTest::testConnectionAdvisories() { +TEST_F(OpenwireAdvisoryTest, testConnectionAdvisories) { std::unique_ptr factory(ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); ASSERT_TRUE(factory.get() != NULL); @@ -131,83 +183,74 @@ void OpenwireAdvisoryTest::testConnectionAdvisories() { } //////////////////////////////////////////////////////////////////////////////// -namespace { +TEST_F(OpenwireAdvisoryTest, testConcurrentTempDestCreation) { - class ConnectionLoadThread : public Thread { - private: + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); - ConnectionFactory* factory; - bool noErrors; - std::string errorMessage; + ConnectionLoadThread thread1(factory.get()); + ConnectionLoadThread thread2(factory.get()); - public: + thread1.start(); + thread2.start(); - ConnectionLoadThread(ConnectionFactory* factory) : - Thread(), factory(factory), noErrors(true), errorMessage() { - } + thread1.join(); + thread2.join(); - virtual ~ConnectionLoadThread() {} + ASSERT_TRUE(thread1.isNoErrors()) << (std::string("Thread1 error: ") + thread1.getErrorMessage()); + ASSERT_TRUE(thread2.isNoErrors()) << (std::string("Thread2 error: ") + thread2.getErrorMessage()); +} - bool isNoErrors() const { - return this->noErrors; - } +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireAdvisoryTest, testTempDestinationCompositeAdvisoryTopic) { - std::string getErrorMessage() const { - return this->errorMessage; - } + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); + ASSERT_TRUE(factory.get() != NULL); - virtual void run() { + std::unique_ptr connection(factory->createConnection()); + ASSERT_TRUE(connection.get() != NULL); - try { - for (unsigned int i = 0; i < 50; ++i) { - std::unique_ptr connection(factory->createConnection()); - connection->start(); - std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr session(connection->createSession()); + ASSERT_TRUE(session.get() != NULL); - for (unsigned int j = 0; j < 100; ++j) { - std::unique_ptr queue(session->createTemporaryQueue()); - std::unique_ptr producer(session->createProducer(queue.get())); - } + std::unique_ptr composite( + AdvisorySupport::getTempDestinationCompositeAdvisoryTopic()); - TimeUnit::MILLISECONDS.sleep(20); - connection->close(); - } - } catch (cms::CMSException& e) { - noErrors = false; - errorMessage = std::string("CMSException: ") + e.what(); - } catch (std::exception& e) { - noErrors = false; - errorMessage = std::string("std::exception: ") + e.what(); - } catch(...) { - noErrors = false; - errorMessage = "Unknown exception"; - } - } - }; + std::unique_ptr consumer(session->createConsumer(dynamic_cast(composite.get()))); -} + connection->start(); -//////////////////////////////////////////////////////////////////////////////// -void OpenwireAdvisoryTest::testConcurrentTempDestCreation() { + // Create one of each + std::unique_ptr tempTopic(session->createTemporaryTopic()); + std::unique_ptr tempQueue(session->createTemporaryQueue()); - std::unique_ptr factory( - ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); + // Create a consumer to ensure destination creation based on protocol. + std::unique_ptr tempTopicConsumer(session->createConsumer(tempTopic.get())); + std::unique_ptr tempQueueConsumer(session->createConsumer(tempQueue.get())); - ConnectionLoadThread thread1(factory.get()); - ConnectionLoadThread thread2(factory.get()); + // Should be an advisory for each + std::unique_ptr advisory1(consumer->receive(2000)); + ASSERT_TRUE(advisory1.get() != NULL); + std::unique_ptr advisory2(consumer->receive(2000)); + ASSERT_TRUE(advisory2.get() != NULL); - thread1.start(); - thread2.start(); + ActiveMQMessage* tempTopicAdvisory = dynamic_cast(advisory1.get()); + ActiveMQMessage* tempQueueAdvisory = dynamic_cast(advisory2.get()); - thread1.join(); - thread2.join(); + // Create one of each + std::unique_ptr topic(session->createTopic(UUID::randomUUID().toString())); + std::unique_ptr queue(session->createQueue(UUID::randomUUID().toString())); - ASSERT_TRUE(thread1.isNoErrors()) << (std::string("Thread1 error: ") + thread1.getErrorMessage()); - ASSERT_TRUE(thread2.isNoErrors()) << (std::string("Thread2 error: ") + thread2.getErrorMessage()); -} + // Create a producer to ensure destination creation based on protocol. + std::unique_ptr topicProducer(session->createProducer(topic.get())); + std::unique_ptr queueProducer(session->createProducer(queue.get())); -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireAdvisoryTest, testConnectionAdvisories) { testConnectionAdvisories(); } -TEST_F(OpenwireAdvisoryTest, testConcurrentTempDestCreation) { testConcurrentTempDestCreation(); } -TEST_F(OpenwireAdvisoryTest, testTempDestinationCompositeAdvisoryTopic) { testTempDestinationCompositeAdvisoryTopic(); } + // Should not be an advisory for each + std::unique_ptr advisory3(consumer->receive(500)); + ASSERT_TRUE(advisory3.get() == NULL); + std::unique_ptr advisory4(consumer->receive(500)); + ASSERT_TRUE(advisory4.get() == NULL); + + connection->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAsyncSenderTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAsyncSenderTest.cpp index 835eb3e5d..1e527b69b 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAsyncSenderTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireAsyncSenderTest.cpp @@ -17,25 +17,22 @@ #include #include +#include +#include +#include namespace activemq { namespace test { namespace openwire { class OpenwireAsyncSenderTest : public AsyncSenderTest { public: - OpenwireAsyncSenderTest(); - virtual ~OpenwireAsyncSenderTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL() + "&connection.useAsyncSend=true"; } - virtual void testOpenWireConnector(); }; }}} -#include -#include - using namespace std; using namespace cms; using namespace activemq; @@ -45,15 +42,44 @@ using namespace activemq::core; using namespace activemq::util; //////////////////////////////////////////////////////////////////////////////// -OpenwireAsyncSenderTest::OpenwireAsyncSenderTest() { -} +TEST_F(OpenwireAsyncSenderTest, testAsyncSends) { -//////////////////////////////////////////////////////////////////////////////// -OpenwireAsyncSenderTest::~OpenwireAsyncSenderTest() { + try { + + // Create CMS Object for Comms + cms::Session* session( cmsProvider->getSession() ); + + CMSListener listener( session ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener( &listener ); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + std::unique_ptr txtMessage( session->createTextMessage( "TEST MESSAGE" ) ); + std::unique_ptr bytesMessage( session->createBytesMessage() ); + + for( unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i ) { + producer->send( txtMessage.get() ); + } + + for( unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i ) { + producer->send( bytesMessage.get() ); + } + + // Wait for the messages to get here + listener.asyncWaitForMessages( IntegrationCommon::defaultMsgCount * 2 ); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount * 2); + + } catch(...) { + ASSERT_TRUE(false); + } } //////////////////////////////////////////////////////////////////////////////// -void OpenwireAsyncSenderTest::testOpenWireConnector() { +TEST_F(OpenwireAsyncSenderTest, testOpenWireConnector) { try{ @@ -76,8 +102,3 @@ void OpenwireAsyncSenderTest::testOpenWireConnector() { ASSERT_TRUE(false); } } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireAsyncSenderTest, testAsyncSends) { testAsyncSends(); } -TEST_F(OpenwireAsyncSenderTest, testOpenWireConnector) { testOpenWireConnector(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireClientAckTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireClientAckTest.cpp index e294973f8..b41e0909a 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireClientAckTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireClientAckTest.cpp @@ -23,16 +23,9 @@ namespace test { namespace openwire { class OpenwireClientAckTest : public CMSTestFixture { public: - OpenwireClientAckTest(); - virtual ~OpenwireClientAckTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } - void testAckedMessageAreConsumed(); - void testLastMessageAcked(); - void testUnAckedMessageAreNotConsumedOnSessionClose(); - void testAckedMessageAreConsumedAsync(); - void testUnAckedMessageAreNotConsumedOnSessionCloseAsync(); }; }}} @@ -85,15 +78,7 @@ namespace { } //////////////////////////////////////////////////////////////////////////////// -OpenwireClientAckTest::OpenwireClientAckTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireClientAckTest::~OpenwireClientAckTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireClientAckTest::testAckedMessageAreConsumed() { +TEST_F(OpenwireClientAckTest, testAckedMessageAreConsumed) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -124,7 +109,7 @@ void OpenwireClientAckTest::testAckedMessageAreConsumed() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireClientAckTest::testLastMessageAcked() { +TEST_F(OpenwireClientAckTest, testLastMessageAcked) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -163,7 +148,7 @@ void OpenwireClientAckTest::testLastMessageAcked() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireClientAckTest::testUnAckedMessageAreNotConsumedOnSessionClose() { +TEST_F(OpenwireClientAckTest, testUnAckedMessageAreNotConsumedOnSessionClose) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -195,7 +180,7 @@ void OpenwireClientAckTest::testUnAckedMessageAreNotConsumedOnSessionClose() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireClientAckTest::testAckedMessageAreConsumedAsync() { +TEST_F(OpenwireClientAckTest, testAckedMessageAreConsumedAsync) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -229,7 +214,7 @@ void OpenwireClientAckTest::testAckedMessageAreConsumedAsync() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireClientAckTest::testUnAckedMessageAreNotConsumedOnSessionCloseAsync() { +TEST_F(OpenwireClientAckTest, testUnAckedMessageAreNotConsumedOnSessionCloseAsync) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -263,11 +248,3 @@ void OpenwireClientAckTest::testUnAckedMessageAreNotConsumedOnSessionCloseAsync( session->close(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireClientAckTest, testAckedMessageAreConsumed) { testAckedMessageAreConsumed(); } -TEST_F(OpenwireClientAckTest, testLastMessageAcked) { testLastMessageAcked(); } -TEST_F(OpenwireClientAckTest, testUnAckedMessageAreNotConsumedOnSessionClose) { testUnAckedMessageAreNotConsumedOnSessionClose(); } -TEST_F(OpenwireClientAckTest, testUnAckedMessageAreNotConsumedOnSessionCloseAsync) { testUnAckedMessageAreNotConsumedOnSessionCloseAsync(); } -TEST_F(OpenwireClientAckTest, testAckedMessageAreConsumedAsync) { testAckedMessageAreConsumedAsync(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsConnectionStartStopTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsConnectionStartStopTest.cpp index e2e282eff..17b9e3222 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsConnectionStartStopTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsConnectionStartStopTest.cpp @@ -15,45 +15,168 @@ * limitations under the License. */ -#include #include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::core; +using namespace activemq::exceptions; + +using namespace decaf; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::lang; + +namespace { + + class CreateSessionRunnable : public Runnable { + private: + + Connection* connection; + Random rand; + CopyOnWriteArrayList* exceptions; + + private: + + CreateSessionRunnable(const CreateSessionRunnable&); + CreateSessionRunnable& operator= (const CreateSessionRunnable&); + + public: + + CreateSessionRunnable(Connection* connection, CopyOnWriteArrayList* exceptions) : + Runnable(), connection(connection), rand(), exceptions(exceptions) { + } + + virtual ~CreateSessionRunnable() {} + + virtual void run() { + try { + TimeUnit::MILLISECONDS.sleep(rand.nextInt(10)); + Pointer(connection->createSession()); + } catch (CMSException& e) { + exceptions->add(e.getMessage()); + } + } + }; + + class StartStopRunnable : public Runnable { + private: + + Connection* connection; + Random rand; + CopyOnWriteArrayList* exceptions; + + private: + + StartStopRunnable(const StartStopRunnable&); + StartStopRunnable& operator= (const StartStopRunnable&); + + public: + + StartStopRunnable(Connection* connection, CopyOnWriteArrayList* exceptions) : + Runnable(), connection(connection), rand(), exceptions(exceptions) { + } + + virtual ~StartStopRunnable() {} + + virtual void run() { + try { + TimeUnit::MILLISECONDS.sleep(rand.nextInt(10)); + connection->start(); + connection->stop(); + } catch (CMSException& e) { + exceptions->add(e.getMessage()); + } + + } + }; + +} namespace activemq { namespace test { namespace openwire { + class OpenwireCmsConnectionStartStopTest : public CmsConnectionStartStopTest { - private: -public: - OpenwireCmsConnectionStartStopTest(); - virtual ~OpenwireCmsConnectionStartStopTest(); - virtual std::string getBrokerURL() const { + public: + std::string getBrokerURL() const override { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } }; + }}} -#include +using activemq::test::openwire::OpenwireCmsConnectionStartStopTest; -using namespace std; -using namespace cms; -using namespace activemq; -using namespace activemq::test; -using namespace activemq::test::openwire; -using namespace activemq::exceptions; +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireCmsConnectionStartStopTest, testStoppedConsumerHoldsMessagesTillStarted) { -using namespace decaf; -using namespace decaf::lang; + Pointer startedSession(startedConnection->createSession()); + Pointer stoppedSession(stoppedConnection->createSession()); -//////////////////////////////////////////////////////////////////////////////// -OpenwireCmsConnectionStartStopTest::OpenwireCmsConnectionStartStopTest() { + // Setup the consumers. + Pointer topic(startedSession->createTopic("test")); + Pointer startedConsumer(startedSession->createConsumer(topic.get())); + Pointer stoppedConsumer(stoppedSession->createConsumer(topic.get())); + + // Send the message. + Pointer producer(startedSession->createProducer(topic.get())); + Pointer message(startedSession->createTextMessage("Hello")); + producer->send(message.get()); + + // Test the assertions. + Pointer m(startedConsumer->receive(2000)); + ASSERT_TRUE(m != NULL); + + m.reset(stoppedConsumer->receive(2000)); + ASSERT_TRUE(m == NULL); + + stoppedConnection->start(); + m.reset(stoppedConsumer->receive(5000)); + ASSERT_TRUE(m != NULL); + + startedSession->close(); + stoppedSession->close(); } //////////////////////////////////////////////////////////////////////////////// -OpenwireCmsConnectionStartStopTest::~OpenwireCmsConnectionStartStopTest() { +TEST_F(OpenwireCmsConnectionStartStopTest, testMultipleConnectionStops) { + + testStoppedConsumerHoldsMessagesTillStarted(); + stoppedConnection->stop(); + testStoppedConsumerHoldsMessagesTillStarted(); + stoppedConnection->stop(); + testStoppedConsumerHoldsMessagesTillStarted(); } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireCmsConnectionStartStopTest, testStoppedConsumerHoldsMessagesTillStarted) { testStoppedConsumerHoldsMessagesTillStarted(); } -TEST_F(OpenwireCmsConnectionStartStopTest, testMultipleConnectionStops) { testMultipleConnectionStops(); } -TEST_F(OpenwireCmsConnectionStartStopTest, testConcurrentSessionCreateWithStart) { testConcurrentSessionCreateWithStart(); } +TEST_F(OpenwireCmsConnectionStartStopTest, testConcurrentSessionCreateWithStart) { + + ThreadPoolExecutor executor(50, Integer::MAX_VALUE, 60LL, TimeUnit::SECONDS, new LinkedBlockingQueue()); + + CopyOnWriteArrayList exceptions; + Random rand; + + for (int i=0; i<2000; i++) { + executor.execute(new CreateSessionRunnable(stoppedConnection.get(), &exceptions)); + executor.execute(new StartStopRunnable(stoppedConnection.get(), &exceptions)); + } + + executor.shutdown(); + ASSERT_TRUE(executor.awaitTermination(45, TimeUnit::SECONDS)) << ("executor terminated"); + ASSERT_TRUE(exceptions.isEmpty()) << ("no exceptions: " + exceptions.toString()); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsTemplateTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsTemplateTest.cpp index 0a2989685..63a5d672f 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsTemplateTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireCmsTemplateTest.cpp @@ -15,36 +15,233 @@ * limitations under the License. */ -#include #include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::util; +using namespace activemq::core; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::lang; + +namespace activemq { +namespace test { + + class TextMessageCreator : public activemq::cmsutil::MessageCreator { + private: + + std::string text; + + public: + + TextMessageCreator(const std::string& text) : + activemq::cmsutil::MessageCreator(), text(text) { + } + + virtual ~TextMessageCreator() { + } + + std::string getText() const { + return text; + } + + virtual cms::Message* createMessage(cms::Session* session) { + + return session->createTextMessage(text); + } + }; + + class Sender: public decaf::lang::Runnable { + private: + + activemq::core::ActiveMQConnectionFactory cf; + activemq::cmsutil::CmsTemplate cmsTemplate; + int count; + + public: + + Sender(const std::string& url, bool pubSub, const std::string& destName, int count) : + decaf::lang::Runnable(), cf(), cmsTemplate(), count(count) { + + cf.setBrokerURI(url); + cmsTemplate.setConnectionFactory(&cf); + cmsTemplate.setPubSubDomain(pubSub); + cmsTemplate.setDefaultDestinationName(destName); + cmsTemplate.setDeliveryPersistent(false); + } + + virtual ~Sender() {} + + virtual void run() { + try { + + // Send a batch of messages. + TextMessageCreator tmc("hello world"); + for (int ix = 0; ix < count; ++ix) { + cmsTemplate.send(&tmc); + } + + } catch (cms::CMSException& ex) { + ex.printStackTrace(); + } + } + }; + + class Receiver: public decaf::lang::Runnable { + private: + + activemq::core::ActiveMQConnectionFactory cf; + activemq::cmsutil::CmsTemplate cmsTemplate; + int count; + int numReceived; + decaf::util::concurrent::CountDownLatch ready; + + public: + + Receiver(const std::string& url, bool pubSub, const std::string& destName, int count) : + decaf::lang::Runnable(), cf(), cmsTemplate(), count(count), numReceived(), ready(1) { + + cf.setBrokerURI(url); + cmsTemplate.setConnectionFactory(&cf); + cmsTemplate.setPubSubDomain(pubSub); + cmsTemplate.setDefaultDestinationName(destName); + cmsTemplate.setDeliveryPersistent(false); + } + + virtual ~Receiver() { + } + + int getNumReceived() const { + return numReceived; + } + + virtual void waitUntilReady() { + ready.await(); + } + + virtual void run() { + + try { + numReceived = 0; + + ready.countDown(); + // Receive a batch of messages. + for (int ix = 0; ix < count; ++ix) { + cms::Message* message = cmsTemplate.receive(); + numReceived++; + delete message; + } + + } catch (cms::CMSException& ex) { + ex.printStackTrace(); + } + } + }; + +}} + +namespace activemq { +namespace test { +namespace openwire { -namespace activemq{ -namespace test{ -namespace openwire{ class OpenwireCmsTemplateTest : public CmsTemplateTest { -public: - OpenwireCmsTemplateTest(); - virtual ~OpenwireCmsTemplateTest(); - virtual std::string getBrokerURL() const { + public: + std::string getBrokerURL() const override { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } }; + }}} -using namespace activemq; -using namespace activemq::test; -using namespace activemq::test::openwire; +using activemq::test::openwire::OpenwireCmsTemplateTest; //////////////////////////////////////////////////////////////////////////////// -OpenwireCmsTemplateTest::OpenwireCmsTemplateTest() { +TEST_F(OpenwireCmsTemplateTest, testBasics) { + + const unsigned int NUM_MESSAGES = IntegrationCommon::defaultMsgCount; + + Receiver receiver(this->getBrokerURL(), false, "testBasics1", NUM_MESSAGES); + Thread rt(&receiver); + rt.start(); + + // Wait for receiver thread to start. + receiver.waitUntilReady(); + + Sender sender(this->getBrokerURL(), false, "testBasics1", NUM_MESSAGES); + Thread st(&sender); + st.start(); + + st.join(); + rt.join(); + + unsigned int numReceived = receiver.getNumReceived(); + ASSERT_TRUE(numReceived == NUM_MESSAGES); } //////////////////////////////////////////////////////////////////////////////// -OpenwireCmsTemplateTest::~OpenwireCmsTemplateTest() { +TEST_F(OpenwireCmsTemplateTest, testReceiveException) { + + // First, try receiving from a bad url + activemq::core::ActiveMQConnectionFactory cf("tcp://localhost:61666"); + activemq::cmsutil::CmsTemplate cmsTemplate(&cf); + cmsTemplate.setDefaultDestinationName("testReceive1"); + + try { + cmsTemplate.receive(); + FAIL() << ("failed to throw expected exception"); + } catch (CMSException& ex) { + // Expected. + } + + // Now change to a good url and verify that we can reuse the same + // CmsTemplate successfully. + activemq::core::ActiveMQConnectionFactory cf2(this->getBrokerURL()); + cmsTemplate.setConnectionFactory(&cf2); + + // Send 1 message. + Sender sender(this->getBrokerURL(), false, "testReceive1", 1); + Thread st(&sender); + st.start(); + st.join(); + + // Receive the message. + cms::Message* message = cmsTemplate.receive(); + ASSERT_TRUE(message != NULL); + delete message; } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireCmsTemplateTest, testBasics) { testBasics(); } -TEST_F(OpenwireCmsTemplateTest, testReceiveException) { testReceiveException(); } -TEST_F(OpenwireCmsTemplateTest, testSendException) { testSendException(); } +TEST_F(OpenwireCmsTemplateTest, testSendException) { + + // First, try sending to a bad url. + activemq::core::ActiveMQConnectionFactory cf("tcp://localhost:61666"); + activemq::cmsutil::CmsTemplate cmsTemplate(&cf); + cmsTemplate.setDefaultDestinationName("testSend1"); + try { + TextMessageCreator msgCreator("hello world"); + cmsTemplate.send(&msgCreator); + FAIL() << ("failed to throw expected exception"); + } catch (CMSException& ex) { + // Expected. + } + + // Now change to a good url and verify that we can reuse the same + // CmsTemplate successfully. + activemq::core::ActiveMQConnectionFactory cf2(this->getBrokerURL()); + cmsTemplate.setConnectionFactory(&cf2); + TextMessageCreator msgCreator("hello world"); + cmsTemplate.send(&msgCreator); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireDurableTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireDurableTest.cpp index 5f3fc2c5f..f1238e188 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireDurableTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireDurableTest.cpp @@ -16,14 +16,15 @@ */ #include +#include + +#include namespace activemq{ namespace test{ namespace openwire{ class OpenwireDurableTest : public DurableTest { public: - OpenwireDurableTest(); - virtual ~OpenwireDurableTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -31,20 +32,12 @@ namespace openwire{ }; }}} -#include - using namespace activemq; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::exceptions; using namespace decaf::util; - -//////////////////////////////////////////////////////////////////////////////// -OpenwireDurableTest::OpenwireDurableTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireDurableTest::~OpenwireDurableTest() { -} +using namespace cms; //////////////////////////////////////////////////////////////////////////////// std::string OpenwireDurableTest::getSubscriptionName() const { @@ -52,5 +45,49 @@ std::string OpenwireDurableTest::getSubscriptionName() const { } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireDurableTest, testDurableConsumer) { testDurableConsumer(); } +TEST_F(OpenwireDurableTest, testDurableConsumer) { + try { + + // Create CMS Object for Comms + cms::Session* session( cmsProvider->getSession() ); + cmsProvider->setSubscription( this->getSubscriptionName() ); + cmsProvider->setDurable( true ); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + // Send a text message to the consumer while its active + std::unique_ptr txtMessage( session->createTextMessage( "TEST MESSAGE" ) ); + producer->send( txtMessage.get() ); + std::unique_ptr received( consumer->receive( 3000 ) ); + + ASSERT_TRUE(received.get() != NULL); + + cmsProvider->reconnectSession(); + session = cmsProvider->getSession(); + producer = cmsProvider->getProducer(); + + // Send some messages while there is no consumer active. + for( int i = 0; i < MSG_COUNT; ++i ) { + producer->send( txtMessage.get() ); + } + + consumer = cmsProvider->getConsumer(); + + // Send some messages while there is no consumer active. + for( int i = 0; i < MSG_COUNT; ++i ) { + producer->send( txtMessage.get() ); + } + + for( int i = 0; i < MSG_COUNT * 2; i++ ) { + received.reset( consumer->receive( 1000 * 5 ) ); + + ASSERT_TRUE(received.get() != NULL) << ("Failed to receive all messages in batch"); + } + + // Remove the subscription after the consumer is forcibly closed. + cmsProvider->unsubscribe(); + } + catch( ActiveMQException& ex ) { + ASSERT_TRUE(false) << (ex.getStackTraceString()); + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireEnhancedConnectionTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireEnhancedConnectionTest.cpp index 3e5608b5c..ed75d0236 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireEnhancedConnectionTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireEnhancedConnectionTest.cpp @@ -57,20 +57,9 @@ namespace openwire { class OpenwireEnhancedConnectionTest : public ::testing::Test { public: - - OpenwireEnhancedConnectionTest(); - virtual ~OpenwireEnhancedConnectionTest(); - virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } - - void SetUp() override {} - void TearDown() override {} - - void testDestinationSource(); - void testDestinationSourceGetters(); - }; }}} @@ -78,14 +67,6 @@ namespace openwire { using namespace activemq::test; using namespace activemq::test::openwire; -//////////////////////////////////////////////////////////////////////////////// -OpenwireEnhancedConnectionTest::OpenwireEnhancedConnectionTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireEnhancedConnectionTest::~OpenwireEnhancedConnectionTest() { -} - //////////////////////////////////////////////////////////////////////////////// namespace { @@ -152,7 +133,65 @@ namespace { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireEnhancedConnectionTest::testDestinationSourceGetters() { +TEST_F(OpenwireEnhancedConnectionTest, testDestinationSource) { + + TestDestinationListener listener; + + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createSession() ); + ASSERT_TRUE(session.get() != NULL); + + ActiveMQConnection* amq = dynamic_cast(connection.get()); + ASSERT_TRUE(amq != NULL); + + cms::EnhancedConnection* enhanced = dynamic_cast(connection.get()); + ASSERT_TRUE(enhanced != NULL); + + std::unique_ptr source(enhanced->getDestinationSource()); + ASSERT_TRUE(source.get() != NULL); + + source->setListener(&listener); + + connection->start(); + source->start(); + + TimeUnit::SECONDS.sleep(2); + + int currTempQueueCount = (int)source->getTemporaryQueues().size(); + int currTempTopicCount = (int)source->getTemporaryTopics().size(); + + std::unique_ptr destination1(session->createTemporaryQueue()); + std::unique_ptr destination2(session->createTemporaryTopic()); + std::unique_ptr destination3(session->createTemporaryQueue()); + std::unique_ptr destination4(session->createTemporaryTopic()); + std::unique_ptr destination5(session->createTemporaryQueue()); + std::unique_ptr destination6(session->createTemporaryTopic()); + + TimeUnit::SECONDS.sleep(2); + + std::vector tempQueues = source->getTemporaryQueues(); + std::vector tempTopics = source->getTemporaryTopics(); + + ASSERT_EQ(currTempQueueCount + 3, (int)tempQueues.size()); + ASSERT_EQ(currTempTopicCount + 3, (int)tempTopics.size()); + + for (int i = 0; i < 3; ++i) { + delete tempQueues[i]; + delete tempTopics[i]; + } + + source->stop(); + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireEnhancedConnectionTest, testDestinationSourceGetters) { TestDestinationListener listener; @@ -234,66 +273,3 @@ void OpenwireEnhancedConnectionTest::testDestinationSourceGetters() { source->stop(); connection->close(); } - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireEnhancedConnectionTest::testDestinationSource() { - - TestDestinationListener listener; - - std::unique_ptr factory( - ConnectionFactory::createCMSConnectionFactory( getBrokerURL() ) ); - ASSERT_TRUE(factory.get() != NULL); - - std::unique_ptr connection( factory->createConnection() ); - ASSERT_TRUE(connection.get() != NULL); - - std::unique_ptr session( connection->createSession() ); - ASSERT_TRUE(session.get() != NULL); - - ActiveMQConnection* amq = dynamic_cast(connection.get()); - ASSERT_TRUE(amq != NULL); - - cms::EnhancedConnection* enhanced = dynamic_cast(connection.get()); - ASSERT_TRUE(enhanced != NULL); - - std::unique_ptr source(enhanced->getDestinationSource()); - ASSERT_TRUE(source.get() != NULL); - - source->setListener(&listener); - - connection->start(); - source->start(); - - TimeUnit::SECONDS.sleep(2); - - int currTempQueueCount = (int)source->getTemporaryQueues().size(); - int currTempTopicCount = (int)source->getTemporaryTopics().size(); - - std::unique_ptr destination1(session->createTemporaryQueue()); - std::unique_ptr destination2(session->createTemporaryTopic()); - std::unique_ptr destination3(session->createTemporaryQueue()); - std::unique_ptr destination4(session->createTemporaryTopic()); - std::unique_ptr destination5(session->createTemporaryQueue()); - std::unique_ptr destination6(session->createTemporaryTopic()); - - TimeUnit::SECONDS.sleep(2); - - std::vector tempQueues = source->getTemporaryQueues(); - std::vector tempTopics = source->getTemporaryTopics(); - - ASSERT_EQ(currTempQueueCount + 3, (int)tempQueues.size()); - ASSERT_EQ(currTempTopicCount + 3, (int)tempTopics.size()); - - for (int i = 0; i < 3; ++i) { - delete tempQueues[i]; - delete tempTopics[i]; - } - - source->stop(); - connection->close(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireEnhancedConnectionTest, testDestinationSource) { testDestinationSource(); } -TEST_F(OpenwireEnhancedConnectionTest, testDestinationSourceGetters) { testDestinationSourceGetters(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireExpirationTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireExpirationTest.cpp index 445ba0c90..8bff01a8e 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireExpirationTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireExpirationTest.cpp @@ -17,35 +17,236 @@ #include -namespace activemq{ -namespace test{ -namespace openwire{ +#include +#include +#include +#include + +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::util; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + +namespace { + + class Producer: public decaf::lang::Runnable { + private: + + std::unique_ptr cmsProvider; + int numMessages; + long long timeToLive; + bool disableTimeStamps; + + public: + + Producer(const std::string& brokerURL, const std::string& destination, int numMessages, long long timeToLive) : + Runnable(), cmsProvider(), numMessages(numMessages), timeToLive(timeToLive), disableTimeStamps(false) { + + this->cmsProvider.reset(new CMSProvider(brokerURL)); + this->cmsProvider->setDestinationName(destination); + this->cmsProvider->setTopic(false); + } + + virtual ~Producer() { + } + + virtual bool getDisableTimeStamps() const { + return this->disableTimeStamps; + } + + virtual void setDisableTimeStamps(bool value) { + this->disableTimeStamps = value; + } + + virtual void run() { + try { + + cms::Session* session = cmsProvider->getSession(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + producer->setDisableMessageTimeStamp(disableTimeStamps); + + if (!this->disableTimeStamps) { + producer->setTimeToLive(timeToLive); + } + + // Create the Thread Id String + std::ostringstream oss; + oss << Thread::currentThread()->getId(); + string threadIdStr = oss.str(); + + // Create a messages + string text = (string) "Hello world! from thread " + threadIdStr; + + for (int ix = 0; ix < numMessages; ++ix) { + TextMessage* message = session->createTextMessage(text); + producer->send(message); + delete message; + } + + } catch (CMSException& e) { + e.printStackTrace(); + } + } + }; + + class Consumer: public cms::MessageListener, public decaf::lang::Runnable { + private: + + std::unique_ptr cmsProvider; + long initialDelay; + long waitMillis; + int numReceived; + + public: + + Consumer(const std::string& brokerURL, const std::string& destination, long waitMillis) : + Runnable(), cmsProvider(), initialDelay(0), waitMillis(waitMillis), numReceived(0) { + + this->cmsProvider.reset(new CMSProvider(brokerURL)); + this->cmsProvider->setTopic(false); + this->cmsProvider->setDestinationName(destination); + } + + virtual ~Consumer() { + } + + int getNumReceived() const { + return numReceived; + } + + void setInitialDelay(long delay) { + initialDelay = delay; + } + + long getInitialDelay() { + return initialDelay; + } + + virtual void run() { + + try { + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + + if (getInitialDelay() > 0) { + Thread::sleep(getInitialDelay()); + } + + consumer->setMessageListener(this); + + // Sleep while asynchronous messages come in. + Thread::sleep(waitMillis); + + } catch (CMSException& e) { + e.printStackTrace(); + } + } + + virtual void onMessage(const cms::Message* message) { + + try { + const TextMessage* textMessage = dynamic_cast(message); + textMessage->getText(); + numReceived++; + } catch (CMSException& e) { + e.printStackTrace(); + } + } + }; + +} + +namespace activemq { +namespace test { +namespace openwire { + class OpenwireExpirationTest : public ExpirationTest { -public: - OpenwireExpirationTest(); - virtual ~OpenwireExpirationTest(); - virtual std::string getBrokerURL() const { + public: + std::string getBrokerURL() const override { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } }; + }}} -using namespace std; -using namespace cms; -using namespace activemq; -using namespace activemq::test; -using namespace activemq::test::openwire; +using activemq::test::openwire::OpenwireExpirationTest; //////////////////////////////////////////////////////////////////////////////// -OpenwireExpirationTest::OpenwireExpirationTest() { +TEST_F(OpenwireExpirationTest, testExpired) { + + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 1000); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL(), destination, 2000); + consumer.setInitialDelay(1500); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(0, consumer.getNumReceived()); } //////////////////////////////////////////////////////////////////////////////// -OpenwireExpirationTest::~OpenwireExpirationTest() { +TEST_F(OpenwireExpirationTest, testExpiredWithChecksDisabled) { + + { + // Try it once enabled to prove the expiration processing works. + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 1000); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL() + "?connection.consumerExpiryCheckEnabled=true", destination, 2000); + consumer.setInitialDelay(1500); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(0, consumer.getNumReceived()); + } + { + // Now lets try it disabled. + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 1000); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL() + "?connection.consumerExpiryCheckEnabled=false", destination, 2000); + consumer.setInitialDelay(1500); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(2, consumer.getNumReceived()); + } } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireExpirationTest, testExpired) { testExpired(); } -TEST_F(OpenwireExpirationTest, testExpiredWithChecksDisabled) { testExpiredWithChecksDisabled(); } -TEST_F(OpenwireExpirationTest, testNotExpired) { testNotExpired(); } +TEST_F(OpenwireExpirationTest, testNotExpired) { + + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 2000); + producer.setDisableTimeStamps(true); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL(), destination, 3000); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(2, consumer.getNumReceived()); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireIndividualAckTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireIndividualAckTest.cpp index 00bce5c0d..010641d57 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireIndividualAckTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireIndividualAckTest.cpp @@ -22,16 +22,7 @@ namespace activemq { namespace test { namespace openwire { class OpenwireIndividualAckTest : public CMSTestFixture { -public: - OpenwireIndividualAckTest(); - virtual ~OpenwireIndividualAckTest(); - void testAckedMessageAreConsumed(); - void testLastMessageAcked(); - void testUnAckedMessageAreNotConsumedOnSessionClose(); - void testIndividualAcknowledgeMultiMessages_AcknowledgeFirstTest(); - void testManyMessageAckedAfterMessageConsumption(); - void testManyMessageAckedAfterAllConsumption(); - void tesIndividualAcksWithClosedConsumerAndAudit(); + public: virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -52,15 +43,7 @@ using namespace activemq::test::openwire; using namespace activemq::exceptions; //////////////////////////////////////////////////////////////////////////////// -OpenwireIndividualAckTest::OpenwireIndividualAckTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireIndividualAckTest::~OpenwireIndividualAckTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::testAckedMessageAreConsumed() { +TEST_F(OpenwireIndividualAckTest, testAckedMessageAreConsumed) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -91,7 +74,7 @@ void OpenwireIndividualAckTest::testAckedMessageAreConsumed() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::testLastMessageAcked() { +TEST_F(OpenwireIndividualAckTest, testLastMessageAcked) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -137,7 +120,7 @@ void OpenwireIndividualAckTest::testLastMessageAcked() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::testUnAckedMessageAreNotConsumedOnSessionClose() { +TEST_F(OpenwireIndividualAckTest, testUnAckedMessageAreNotConsumedOnSessionClose) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -169,7 +152,7 @@ void OpenwireIndividualAckTest::testUnAckedMessageAreNotConsumedOnSessionClose() } //////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::testIndividualAcknowledgeMultiMessages_AcknowledgeFirstTest() { +TEST_F(OpenwireIndividualAckTest, testIndividualAcknowledgeMultiMessages_AcknowledgeFirstTest) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -215,7 +198,7 @@ void OpenwireIndividualAckTest::testIndividualAcknowledgeMultiMessages_Acknowled } //////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::testManyMessageAckedAfterMessageConsumption() { +TEST_F(OpenwireIndividualAckTest, testManyMessageAckedAfterMessageConsumption) { int messageCount = 20; std::unique_ptr msg; @@ -255,7 +238,7 @@ void OpenwireIndividualAckTest::testManyMessageAckedAfterMessageConsumption() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::testManyMessageAckedAfterAllConsumption() { +TEST_F(OpenwireIndividualAckTest, testManyMessageAckedAfterAllConsumption) { int messageCount = 20; std::unique_ptr msg; @@ -301,7 +284,7 @@ void OpenwireIndividualAckTest::testManyMessageAckedAfterAllConsumption() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireIndividualAckTest::tesIndividualAcksWithClosedConsumerAndAudit() { +TEST_F(OpenwireIndividualAckTest, tesIndividualAcksWithClosedConsumerAndAudit) { int messageCount = 20; std::unique_ptr msg; @@ -339,13 +322,3 @@ void OpenwireIndividualAckTest::tesIndividualAcksWithClosedConsumerAndAudit() { connection->close(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireIndividualAckTest, testAckedMessageAreConsumed) { testAckedMessageAreConsumed(); } -TEST_F(OpenwireIndividualAckTest, testLastMessageAcked) { testLastMessageAcked(); } -TEST_F(OpenwireIndividualAckTest, testUnAckedMessageAreNotConsumedOnSessionClose) { testUnAckedMessageAreNotConsumedOnSessionClose(); } -TEST_F(OpenwireIndividualAckTest, testIndividualAcknowledgeMultiMessages_AcknowledgeFirstTest) { testIndividualAcknowledgeMultiMessages_AcknowledgeFirstTest(); } -TEST_F(OpenwireIndividualAckTest, testManyMessageAckedAfterMessageConsumption) { testManyMessageAckedAfterMessageConsumption(); } -TEST_F(OpenwireIndividualAckTest, testManyMessageAckedAfterAllConsumption) { testManyMessageAckedAfterAllConsumption(); } -TEST_F(OpenwireIndividualAckTest, tesIndividualAcksWithClosedConsumerAndAudit) { tesIndividualAcksWithClosedConsumerAndAudit(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsMessageGroupsTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsMessageGroupsTest.cpp index 6fd00751e..aa19a63fd 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsMessageGroupsTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsMessageGroupsTest.cpp @@ -16,14 +16,16 @@ */ #include +#include + +#include +#include namespace activemq { namespace test { namespace openwire { class OpenwireJmsMessageGroupsTest : public JmsMessageGroupsTest { public: - OpenwireJmsMessageGroupsTest(); - virtual ~OpenwireJmsMessageGroupsTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -33,15 +35,32 @@ namespace openwire { using namespace activemq; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::exceptions; +using namespace cms; //////////////////////////////////////////////////////////////////////////////// -OpenwireJmsMessageGroupsTest::OpenwireJmsMessageGroupsTest() { -} +TEST_F(OpenwireJmsMessageGroupsTest, testMessageSend) { -//////////////////////////////////////////////////////////////////////////////// -OpenwireJmsMessageGroupsTest::~OpenwireJmsMessageGroupsTest() { -} + try { -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireJmsMessageGroupsTest, testMessageSend) { testMessageSend(); } + std::string GROUPID = "TEST-GROUP-ID"; + + // Create CMS Object for Comms + cms::Session* session( cmsProvider->getSession() ); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + std::unique_ptr txtMessage( session->createTextMessage( "TEST MESSAGE" ) ); + txtMessage->setStringProperty( "JMSXGroupID", GROUPID ); + + // Send some text messages + producer->send( txtMessage.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + ASSERT_TRUE(message->getStringProperty( "JMSXGroupID" ) == GROUPID); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsRecoverTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsRecoverTest.cpp index ec621fd9b..85da3385b 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsRecoverTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireJmsRecoverTest.cpp @@ -60,21 +60,11 @@ namespace test { namespace openwire { class OpenwireJmsRecoverTest : public ::testing::Test { - private: - - cms::ConnectionFactory* factory; - cms::Connection* connection; - cms::Destination* destination; + protected: - private: - - OpenwireJmsRecoverTest(const OpenwireJmsRecoverTest&); - OpenwireJmsRecoverTest& operator= (const OpenwireJmsRecoverTest&); - - public: - - OpenwireJmsRecoverTest(); - virtual ~OpenwireJmsRecoverTest(); + cms::ConnectionFactory* factory = nullptr; + cms::Connection* connection = nullptr; + cms::Destination* destination = nullptr; virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); @@ -83,15 +73,6 @@ namespace openwire { void SetUp() override; void TearDown() override; - void testQueueSynchRecover(); - void testQueueAsynchRecover(); - void testTopicSynchRecover(); - void testTopicAsynchRecover(); - void testQueueAsynchRecoverWithAutoAck(); - void testTopicAsynchRecoverWithAutoAck(); - - private: - void doTestSynchRecover(); void doTestAsynchRecover(); void doTestAsynchRecoverWithAutoAck(); @@ -103,15 +84,6 @@ namespace openwire { using namespace activemq::test; using namespace activemq::test::openwire; -//////////////////////////////////////////////////////////////////////////////// -OpenwireJmsRecoverTest::OpenwireJmsRecoverTest() : - factory(), connection(), destination() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireJmsRecoverTest::~OpenwireJmsRecoverTest() { -} - //////////////////////////////////////////////////////////////////////////////// void OpenwireJmsRecoverTest::SetUp() { @@ -127,42 +99,6 @@ void OpenwireJmsRecoverTest::TearDown() { delete destination; } -//////////////////////////////////////////////////////////////////////////////// -void OpenwireJmsRecoverTest::testQueueSynchRecover() { - destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); - doTestSynchRecover(); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireJmsRecoverTest::testQueueAsynchRecover() { - destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); - doTestAsynchRecover(); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireJmsRecoverTest::testTopicSynchRecover() { - destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); - doTestSynchRecover(); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireJmsRecoverTest::testTopicAsynchRecover() { - destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); - doTestAsynchRecover(); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireJmsRecoverTest::testQueueAsynchRecoverWithAutoAck() { - destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); - doTestAsynchRecoverWithAutoAck(); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireJmsRecoverTest::testTopicAsynchRecoverWithAutoAck() { - destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); - doTestAsynchRecoverWithAutoAck(); -} - //////////////////////////////////////////////////////////////////////////////// void OpenwireJmsRecoverTest::doTestSynchRecover() { @@ -366,10 +302,32 @@ void OpenwireJmsRecoverTest::doTestAsynchRecoverWithAutoAck() { } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireJmsRecoverTest, testQueueSynchRecover) { testQueueSynchRecover(); } -TEST_F(OpenwireJmsRecoverTest, testQueueAsynchRecover) { testQueueAsynchRecover(); } -TEST_F(OpenwireJmsRecoverTest, testTopicSynchRecover) { testTopicSynchRecover(); } -TEST_F(OpenwireJmsRecoverTest, testTopicAsynchRecover) { testTopicAsynchRecover(); } -TEST_F(OpenwireJmsRecoverTest, testQueueAsynchRecoverWithAutoAck) { testQueueAsynchRecoverWithAutoAck(); } -TEST_F(OpenwireJmsRecoverTest, testTopicAsynchRecoverWithAutoAck) { testTopicAsynchRecoverWithAutoAck(); } +TEST_F(OpenwireJmsRecoverTest, testQueueSynchRecover) { + destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); + doTestSynchRecover(); +} + +TEST_F(OpenwireJmsRecoverTest, testQueueAsynchRecover) { + destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecover(); +} + +TEST_F(OpenwireJmsRecoverTest, testTopicSynchRecover) { + destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); + doTestSynchRecover(); +} + +TEST_F(OpenwireJmsRecoverTest, testTopicAsynchRecover) { + destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecover(); +} + +TEST_F(OpenwireJmsRecoverTest, testQueueAsynchRecoverWithAutoAck) { + destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecoverWithAutoAck(); +} + +TEST_F(OpenwireJmsRecoverTest, testTopicAsynchRecoverWithAutoAck) { + destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecoverWithAutoAck(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMapMessageTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMapMessageTest.cpp index cf4c9929a..12c651487 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMapMessageTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMapMessageTest.cpp @@ -17,36 +17,168 @@ #include +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::commands; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + namespace activemq { namespace test { namespace openwire { + class OpenwireMapMessageTest : public MapMessageTest { - private: -public: - OpenwireMapMessageTest(); - virtual ~OpenwireMapMessageTest(); - virtual std::string getBrokerURL() const { + public: + std::string getBrokerURL() const override { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } }; + }}} -using namespace activemq; -using namespace activemq::test; -using namespace activemq::test::openwire; +using activemq::test::openwire::OpenwireMapMessageTest; //////////////////////////////////////////////////////////////////////////////// -OpenwireMapMessageTest::OpenwireMapMessageTest() { +TEST_F(OpenwireMapMessageTest, testEmptyMapSendReceive) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("SomeKey") == false); } //////////////////////////////////////////////////////////////////////////////// -OpenwireMapMessageTest::~OpenwireMapMessageTest() { +TEST_F(OpenwireMapMessageTest, testMapWithEmptyStringValue) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setString("String1", ""); + mapMessage->setString("String2", "value"); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("String1") == true); + ASSERT_TRUE(recvMapMessage->getString("String1") == ""); + ASSERT_TRUE(recvMapMessage->itemExists("String2") == true); + ASSERT_TRUE(recvMapMessage->itemExists("String3") == false); + ASSERT_TRUE(recvMapMessage->getString("String2") == string("value")); } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireMapMessageTest, testEmptyMapSendReceive) { testEmptyMapSendReceive(); } -TEST_F(OpenwireMapMessageTest, testMapWithEmptyStringValue) { testMapWithEmptyStringValue(); } -TEST_F(OpenwireMapMessageTest, testMapSetEmptyBytesVector) { testMapSetEmptyBytesVector(); } -TEST_F(OpenwireMapMessageTest, testMapWithSingleCharEntry) { testMapWithSingleCharEntry(); } -TEST_F(OpenwireMapMessageTest, testMapWithCharAndStringEntry) { testMapWithCharAndStringEntry(); } +TEST_F(OpenwireMapMessageTest, testMapSetEmptyBytesVector) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + std::vector bytes; + + mapMessage->setBytes("BYTES", bytes); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("BYTES") == true); + ASSERT_TRUE(recvMapMessage->getBytes("BYTES").empty() == true); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireMapMessageTest, testMapWithSingleCharEntry) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setChar("Char1", 'a'); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("Char1") == true); + ASSERT_TRUE(recvMapMessage->getChar("Char1") == 'a'); + ASSERT_TRUE(recvMapMessage->itemExists("Char2") == false); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireMapMessageTest, testMapWithCharAndStringEntry) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setChar("Char1", 'a'); + mapMessage->setString("String1", "string"); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("Char1") == true); + ASSERT_TRUE(recvMapMessage->getChar("Char1") == 'a'); + ASSERT_TRUE(recvMapMessage->itemExists("Char2") == false); + ASSERT_TRUE(recvMapMessage->itemExists("String1") == true); + ASSERT_TRUE(recvMapMessage->itemExists("String3") == false); + ASSERT_TRUE(recvMapMessage->getString("String1") == string("string")); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageCompressionTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageCompressionTest.cpp index d2e377be9..cb13deb72 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageCompressionTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageCompressionTest.cpp @@ -17,36 +17,284 @@ #include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::commands; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + +namespace { + + // The following text should compress well + const string TEXT = std::string() + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. "; + + bool a = true; + unsigned char b = 123; + char c = 'c'; + short d = 0x1234; + int e = 0x12345678; + long long f = 0x1234567812345678LL; + string g = "Hello World!"; + bool h = false; + unsigned char i = 0xFF; + short j = -0x1234; + int k = -0x12345678; + long long l = -0x1234567812345678LL; + float m = 2.1F; + double n = 2.3; +} + namespace activemq { namespace test { namespace openwire { + class OpenwireMessageCompressionTest : public MessageCompressionTest { - private: -public: - OpenwireMessageCompressionTest(); - virtual ~OpenwireMessageCompressionTest(); - virtual std::string getBrokerURL() const { + public: + std::string getBrokerURL() const override { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL() + "&connection.useCompression=true"; } }; + }}} -using namespace activemq; -using namespace activemq::test; -using namespace activemq::test::openwire; +using activemq::test::openwire::OpenwireMessageCompressionTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireMessageCompressionTest, testTextMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createTextMessage( TEXT ) ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + TextMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a TextMessage"); + + ASSERT_EQ(sent->getText(), recvd->getText()) << ("Received text differs from sent text."); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); +} //////////////////////////////////////////////////////////////////////////////// -OpenwireMessageCompressionTest::OpenwireMessageCompressionTest() { +TEST_F(OpenwireMessageCompressionTest, testBytesMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createBytesMessage() ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + sent->writeBoolean( a ); + sent->writeByte( b ); + sent->writeChar( c ); + sent->writeShort( d ); + sent->writeInt( e ); + sent->writeLong( f ); + sent->writeString( g ); + sent->writeBoolean( h ); + sent->writeByte( i ); + sent->writeShort( j ); + sent->writeInt( k ); + sent->writeLong( l ); + sent->writeFloat( m ); + sent->writeDouble( n ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + BytesMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a BytesMessage"); + + ASSERT_EQ(a, recvd->readBoolean()); + ASSERT_EQ(b, recvd->readByte()); + ASSERT_EQ(c, recvd->readChar()); + ASSERT_EQ(d, recvd->readShort()); + ASSERT_EQ(e, recvd->readInt()); + ASSERT_EQ(f, recvd->readLong()); + ASSERT_EQ(g, recvd->readString()); + ASSERT_EQ(h, recvd->readBoolean()); + ASSERT_EQ(i, recvd->readByte()); + ASSERT_EQ(j, recvd->readShort()); + ASSERT_EQ(k, recvd->readInt()); + ASSERT_EQ(l, recvd->readLong()); + ASSERT_EQ(m, recvd->readFloat()); + ASSERT_EQ(n, recvd->readDouble()); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); } //////////////////////////////////////////////////////////////////////////////// -OpenwireMessageCompressionTest::~OpenwireMessageCompressionTest() { +TEST_F(OpenwireMessageCompressionTest, testStreamMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createStreamMessage() ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + sent->writeBoolean( a ); + sent->writeByte( b ); + sent->writeChar( c ); + sent->writeShort( d ); + sent->writeInt( e ); + sent->writeLong( f ); + sent->writeString( g ); + sent->writeBoolean( h ); + sent->writeByte( i ); + sent->writeShort( j ); + sent->writeInt( k ); + sent->writeLong( l ); + sent->writeFloat( m ); + sent->writeDouble( n ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + StreamMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a StreamMessage"); + + ASSERT_EQ(a, recvd->readBoolean()); + ASSERT_EQ(b, recvd->readByte()); + ASSERT_EQ(c, recvd->readChar()); + ASSERT_EQ(d, recvd->readShort()); + ASSERT_EQ(e, recvd->readInt()); + ASSERT_EQ(f, recvd->readLong()); + ASSERT_EQ(g, recvd->readString()); + ASSERT_EQ(h, recvd->readBoolean()); + ASSERT_EQ(i, recvd->readByte()); + ASSERT_EQ(j, recvd->readShort()); + ASSERT_EQ(k, recvd->readInt()); + ASSERT_EQ(l, recvd->readLong()); + ASSERT_EQ(m, recvd->readFloat()); + ASSERT_EQ(n, recvd->readDouble()); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireMessageCompressionTest, testTextMessageCompression) { testTextMessageCompression(); } -TEST_F(OpenwireMessageCompressionTest, testBytesMessageCompression) { testBytesMessageCompression(); } -TEST_F(OpenwireMessageCompressionTest, testStreamMessageCompression) { testStreamMessageCompression(); } -TEST_F(OpenwireMessageCompressionTest, testMapMessageCompression) { testMapMessageCompression(); } +TEST_F(OpenwireMessageCompressionTest, testMapMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createMapMessage() ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + sent->setBoolean( "a", a ); + sent->setByte( "b", b ); + sent->setChar( "c", c ); + sent->setShort( "d", d ); + sent->setInt( "e", e ); + sent->setLong( "f", f ); + sent->setString( "g", g ); + sent->setBoolean( "h", h ); + sent->setByte( "i", i ); + sent->setShort( "j", j ); + sent->setInt( "k", k ); + sent->setLong( "l", l ); + sent->setFloat( "m", m ); + sent->setDouble( "n", n ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + MapMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a MapMessage"); + + ASSERT_EQ(a, recvd->getBoolean( "a" )); + ASSERT_EQ(b, recvd->getByte( "b" )); + ASSERT_EQ(c, recvd->getChar( "c" )); + ASSERT_EQ(d, recvd->getShort( "d" )); + ASSERT_EQ(e, recvd->getInt( "e" )); + ASSERT_EQ(f, recvd->getLong( "f" )); + ASSERT_EQ(g, recvd->getString( "g" )); + ASSERT_EQ(h, recvd->getBoolean( "h" )); + ASSERT_EQ(i, recvd->getByte( "i" )); + ASSERT_EQ(j, recvd->getShort( "j" )); + ASSERT_EQ(k, recvd->getInt( "k" )); + ASSERT_EQ(l, recvd->getLong( "l" )); + ASSERT_EQ(m, recvd->getFloat( "m" )); + ASSERT_EQ(n, recvd->getDouble( "n" )); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessagePriorityTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessagePriorityTest.cpp index d23321ccb..11e4ffa6c 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessagePriorityTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessagePriorityTest.cpp @@ -16,14 +16,57 @@ */ #include +#include +#include +#include +#include + +#include +#include +#include + +namespace { + + class ProducerThread : public decaf::lang::Thread { + private: + + cms::Session* session; + cms::Destination* destination; + int num; + int priority; + + private: + + ProducerThread(const ProducerThread&); + ProducerThread& operator= (const ProducerThread&); + + public: + + ProducerThread(cms::Session* session, cms::Destination* destination, int num, int priority) : + session(session), destination(destination), num(num), priority(priority) { + } + + virtual ~ProducerThread() {} + + virtual void run() { + + decaf::lang::Pointer producer(session->createProducer(destination)); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + producer->setPriority(priority); + + for (int i = 0; i < num; ++i) { + decaf::lang::Pointer message(session->createTextMessage("Test Message")); + producer->send(message.get()); + } + } + }; +} namespace activemq { namespace test { namespace openwire { class OpenwireMessagePriorityTest : public MessagePriorityTest { public: - OpenwireMessagePriorityTest(); - virtual ~OpenwireMessagePriorityTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -33,15 +76,45 @@ namespace openwire { using namespace activemq; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace cms; +using namespace decaf::lang; +using namespace decaf::util; //////////////////////////////////////////////////////////////////////////////// -OpenwireMessagePriorityTest::OpenwireMessagePriorityTest() { -} +TEST_F(OpenwireMessagePriorityTest, testMessagePrioritySendReceive) { -//////////////////////////////////////////////////////////////////////////////// -OpenwireMessagePriorityTest::~OpenwireMessagePriorityTest() { -} + const int MSG_COUNT = 25; -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireMessagePriorityTest, testMessagePrioritySendReceive) { testMessagePrioritySendReceive(); } + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + connectionFactory->setMessagePrioritySupported(true); + + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + connection->start(); + + ProducerThread thread1(session.get(), destination.get(), MSG_COUNT, 9); + ProducerThread thread2(session.get(), destination.get(), MSG_COUNT, 1); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + Thread::sleep(3000); + + for (int i = 0; i < MSG_COUNT * 2; ++i) { + Pointer message(consumer->receive(2000)); + ASSERT_TRUE(message != NULL); + ASSERT_TRUE(message->getCMSPriority() == (i < MSG_COUNT ? 9 : 1)); + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageSelectorTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageSelectorTest.cpp index b1a22f61a..a6cb60c38 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageSelectorTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireMessageSelectorTest.cpp @@ -18,64 +18,6 @@ #include #include -namespace activemq { -namespace test { -namespace openwire { - /** - * Tests the OpenWire message selector feature. - * Message selectors use SQL92-like syntax to filter messages - * based on message properties and headers. - */ - class OpenwireMessageSelectorTest : public CMSTestFixture { -public: - OpenwireMessageSelectorTest(); - virtual ~OpenwireMessageSelectorTest(); - virtual std::string getBrokerURL() const { - return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); - } - /** - * Test selector with string property comparison. - */ - void testStringPropertySelector(); - /** - * Test selector with integer property comparison. - */ - void testIntPropertySelector(); - /** - * Test selector with boolean property. - */ - void testBooleanPropertySelector(); - /** - * Test compound selector with AND/OR operators. - */ - void testCompoundSelector(); - /** - * Test selector with LIKE operator for pattern matching. - */ - void testSelectorWithLike(); - /** - * Test selector with IN operator for value lists. - */ - void testSelectorWithIn(); - /** - * Test selector with BETWEEN operator for ranges. - */ - void testSelectorWithBetween(); - /** - * Test selector with IS NULL / IS NOT NULL operators. - */ - void testSelectorWithIsNull(); - /** - * Test selector on JMSType header. - */ - void testJMSTypeSelector(); - /** - * Test selector on JMSPriority header. - */ - void testJMSPrioritySelector(); - }; -}}} - #include #include @@ -94,18 +36,27 @@ using namespace decaf::lang; using namespace activemq; using namespace activemq::core; using namespace activemq::test; -using namespace activemq::test::openwire; -//////////////////////////////////////////////////////////////////////////////// -OpenwireMessageSelectorTest::OpenwireMessageSelectorTest() { -} +namespace activemq { +namespace test { +namespace openwire { + /** + * Tests the OpenWire message selector feature. + * Message selectors use SQL92-like syntax to filter messages + * based on message properties and headers. + */ + class OpenwireMessageSelectorTest : public CMSTestFixture { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); + } + }; +}}} -//////////////////////////////////////////////////////////////////////////////// -OpenwireMessageSelectorTest::~OpenwireMessageSelectorTest() { -} +using namespace activemq::test::openwire; //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testStringPropertySelector() { +TEST_F(OpenwireMessageSelectorTest, testStringPropertySelector) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -149,7 +100,7 @@ void OpenwireMessageSelectorTest::testStringPropertySelector() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testIntPropertySelector() { +TEST_F(OpenwireMessageSelectorTest, testIntPropertySelector) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -186,7 +137,7 @@ void OpenwireMessageSelectorTest::testIntPropertySelector() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testBooleanPropertySelector() { +TEST_F(OpenwireMessageSelectorTest, testBooleanPropertySelector) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -221,7 +172,7 @@ void OpenwireMessageSelectorTest::testBooleanPropertySelector() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testCompoundSelector() { +TEST_F(OpenwireMessageSelectorTest, testCompoundSelector) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -264,7 +215,7 @@ void OpenwireMessageSelectorTest::testCompoundSelector() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testSelectorWithLike() { +TEST_F(OpenwireMessageSelectorTest, testSelectorWithLike) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -305,7 +256,7 @@ void OpenwireMessageSelectorTest::testSelectorWithLike() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testSelectorWithIn() { +TEST_F(OpenwireMessageSelectorTest, testSelectorWithIn) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -346,7 +297,7 @@ void OpenwireMessageSelectorTest::testSelectorWithIn() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testSelectorWithBetween() { +TEST_F(OpenwireMessageSelectorTest, testSelectorWithBetween) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -381,7 +332,7 @@ void OpenwireMessageSelectorTest::testSelectorWithBetween() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testSelectorWithIsNull() { +TEST_F(OpenwireMessageSelectorTest, testSelectorWithIsNull) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -416,7 +367,7 @@ void OpenwireMessageSelectorTest::testSelectorWithIsNull() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testJMSTypeSelector() { +TEST_F(OpenwireMessageSelectorTest, testJMSTypeSelector) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -451,7 +402,7 @@ void OpenwireMessageSelectorTest::testJMSTypeSelector() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireMessageSelectorTest::testJMSPrioritySelector() { +TEST_F(OpenwireMessageSelectorTest, testJMSPrioritySelector) { Connection* connection = this->cmsProvider->getConnection(); connection->start(); @@ -486,16 +437,3 @@ void OpenwireMessageSelectorTest::testJMSPrioritySelector() { session->close(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireMessageSelectorTest, testStringPropertySelector) { testStringPropertySelector(); } -TEST_F(OpenwireMessageSelectorTest, testIntPropertySelector) { testIntPropertySelector(); } -TEST_F(OpenwireMessageSelectorTest, testBooleanPropertySelector) { testBooleanPropertySelector(); } -TEST_F(OpenwireMessageSelectorTest, testCompoundSelector) { testCompoundSelector(); } -TEST_F(OpenwireMessageSelectorTest, testSelectorWithLike) { testSelectorWithLike(); } -TEST_F(OpenwireMessageSelectorTest, testSelectorWithIn) { testSelectorWithIn(); } -TEST_F(OpenwireMessageSelectorTest, testSelectorWithBetween) { testSelectorWithBetween(); } -TEST_F(OpenwireMessageSelectorTest, testSelectorWithIsNull) { testSelectorWithIsNull(); } -TEST_F(OpenwireMessageSelectorTest, testJMSTypeSelector) { testJMSTypeSelector(); } -TEST_F(OpenwireMessageSelectorTest, testJMSPrioritySelector) { testJMSPrioritySelector(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireNonBlockingRedeliveryTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireNonBlockingRedeliveryTest.cpp index 741b65a37..58bc2836a 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireNonBlockingRedeliveryTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireNonBlockingRedeliveryTest.cpp @@ -22,19 +22,13 @@ namespace activemq { namespace test { namespace openwire { class OpenwireNonBlockingRedeliveryTest : public CMSTestFixture { -public: - OpenwireNonBlockingRedeliveryTest(); - virtual ~OpenwireNonBlockingRedeliveryTest(); + public: void SetUp() override {} void TearDown() override {} - virtual std::string getBrokerURL() const; - void testConsumerMessagesAreNotOrdered(); - void testMessageDeleiveredWhenNonBlockingEnabled(); - void testMessageRedeliveriesAreInOrder(); - void testMessageDeleiveryDoesntStop(); - void testNonBlockingMessageDeleiveryIsDelayed(); - void testNonBlockingMessageDeleiveryWithRollbacks(); - void testNonBlockingMessageDeleiveryWithAllRolledBack(); + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getOpenwireURL() + + "?connection.nonBlockingRedelivery=true"; + } }; }}} @@ -269,24 +263,92 @@ namespace { } } }; -} -//////////////////////////////////////////////////////////////////////////////// -OpenwireNonBlockingRedeliveryTest::OpenwireNonBlockingRedeliveryTest() { -} + class ReceivedListener : public cms::MessageListener { + private: -//////////////////////////////////////////////////////////////////////////////// -OpenwireNonBlockingRedeliveryTest::~OpenwireNonBlockingRedeliveryTest() { -} + LinkedHashSet< Pointer >* received; -//////////////////////////////////////////////////////////////////////////////// -std::string OpenwireNonBlockingRedeliveryTest::getBrokerURL() const { - return activemq::util::IntegrationCommon::getInstance().getOpenwireURL() + - "?connection.nonBlockingRedelivery=true"; + public: + + ReceivedListener(LinkedHashSet< Pointer >* received) : + cms::MessageListener(), received(received) { + } + + virtual ~ReceivedListener() { + } + + virtual void onMessage(const cms::Message* message) { + const commands::Message* amqMessage = + dynamic_cast(message); + + received->add(amqMessage->getMessageId()); + } + + }; + + class SomeRollbacksListener : public cms::MessageListener { + private: + + int count; + Pointer session; + LinkedHashSet< Pointer >* received; + + public: + + SomeRollbacksListener(Pointer session, LinkedHashSet< Pointer >* received) : + cms::MessageListener(), count(0), session(session), received(received) { + } + + virtual ~SomeRollbacksListener() {} + + virtual void onMessage(const cms::Message* message) { + const commands::Message* amqMessage = + dynamic_cast(message); + + if (++count > 10) { + try { + session->rollback(); + count = 0; + } catch (CMSException& e) { + } + } else { + received->add(amqMessage->getMessageId()); + try { + session->commit(); + } catch (CMSException& e) { + } + } + } + + }; + + class RollbacksListener : public cms::MessageListener { + private: + + Pointer session; + + public: + + RollbacksListener(Pointer session) : + cms::MessageListener(), session(session) { + } + + virtual ~RollbacksListener() { + } + + virtual void onMessage(const cms::Message* message) { + try { + session->rollback(); + } catch (CMSException& e) { + } + } + + }; } //////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testConsumerMessagesAreNotOrdered() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testConsumerMessagesAreNotOrdered) { LinkedList messages; @@ -322,34 +384,7 @@ void OpenwireNonBlockingRedeliveryTest::testConsumerMessagesAreNotOrdered() { } //////////////////////////////////////////////////////////////////////////////// -namespace { - - class ReceivedListener : public cms::MessageListener { - private: - - LinkedHashSet< Pointer >* received; - - public: - - ReceivedListener(LinkedHashSet< Pointer >* received) : - cms::MessageListener(), received(received) { - } - - virtual ~ReceivedListener() { - } - - virtual void onMessage(const cms::Message* message) { - const commands::Message* amqMessage = - dynamic_cast(message); - - received->add(amqMessage->getMessageId()); - } - - }; -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testMessageDeleiveredWhenNonBlockingEnabled() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testMessageDeleiveredWhenNonBlockingEnabled) { LinkedHashSet< Pointer > received; LinkedHashSet< Pointer > beforeRollback; @@ -391,7 +426,7 @@ void OpenwireNonBlockingRedeliveryTest::testMessageDeleiveredWhenNonBlockingEnab } //////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testMessageRedeliveriesAreInOrder() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testMessageRedeliveriesAreInOrder) { LinkedHashSet< Pointer > received; LinkedHashSet< Pointer > beforeRollback; @@ -447,7 +482,7 @@ void OpenwireNonBlockingRedeliveryTest::testMessageRedeliveriesAreInOrder() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testMessageDeleiveryDoesntStop() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testMessageDeleiveryDoesntStop) { LinkedHashSet< Pointer > received; LinkedHashSet< Pointer > beforeRollback; @@ -491,7 +526,7 @@ void OpenwireNonBlockingRedeliveryTest::testMessageDeleiveryDoesntStop() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testNonBlockingMessageDeleiveryIsDelayed() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryIsDelayed) { LinkedHashSet< Pointer > received; @@ -531,47 +566,7 @@ void OpenwireNonBlockingRedeliveryTest::testNonBlockingMessageDeleiveryIsDelayed } //////////////////////////////////////////////////////////////////////////////// -namespace { - - class SomeRollbacksListener : public cms::MessageListener { - private: - - int count; - Pointer session; - LinkedHashSet< Pointer >* received; - - public: - - SomeRollbacksListener(Pointer session, LinkedHashSet< Pointer >* received) : - cms::MessageListener(), count(0), session(session), received(received) { - } - - virtual ~SomeRollbacksListener() {} - - virtual void onMessage(const cms::Message* message) { - const commands::Message* amqMessage = - dynamic_cast(message); - - if (++count > 10) { - try { - session->rollback(); - count = 0; - } catch (CMSException& e) { - } - } else { - received->add(amqMessage->getMessageId()); - try { - session->commit(); - } catch (CMSException& e) { - } - } - } - - }; -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testNonBlockingMessageDeleiveryWithRollbacks() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryWithRollbacks) { LinkedHashSet< Pointer > received; @@ -612,34 +607,7 @@ void OpenwireNonBlockingRedeliveryTest::testNonBlockingMessageDeleiveryWithRollb } //////////////////////////////////////////////////////////////////////////////// -namespace { - - class RollbacksListener : public cms::MessageListener { - private: - - Pointer session; - - public: - - RollbacksListener(Pointer session) : - cms::MessageListener(), session(session) { - } - - virtual ~RollbacksListener() { - } - - virtual void onMessage(const cms::Message* message) { - try { - session->rollback(); - } catch (CMSException& e) { - } - } - - }; -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireNonBlockingRedeliveryTest::testNonBlockingMessageDeleiveryWithAllRolledBack() { +TEST_F(OpenwireNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryWithAllRolledBack) { LinkedHashSet< Pointer > received; LinkedHashSet< Pointer > dlqed; @@ -684,12 +652,3 @@ void OpenwireNonBlockingRedeliveryTest::testNonBlockingMessageDeleiveryWithAllRo destroyDestination(getBrokerURL(), destinationName); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireNonBlockingRedeliveryTest, testConsumerMessagesAreNotOrdered) { testConsumerMessagesAreNotOrdered(); } -TEST_F(OpenwireNonBlockingRedeliveryTest, testMessageDeleiveredWhenNonBlockingEnabled) { testMessageDeleiveredWhenNonBlockingEnabled(); } -TEST_F(OpenwireNonBlockingRedeliveryTest, testMessageDeleiveryDoesntStop) { testMessageDeleiveryDoesntStop(); } -TEST_F(OpenwireNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryIsDelayed) { testNonBlockingMessageDeleiveryIsDelayed(); } -TEST_F(OpenwireNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryWithRollbacks) { testNonBlockingMessageDeleiveryWithRollbacks(); } -TEST_F(OpenwireNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryWithAllRolledBack) { testNonBlockingMessageDeleiveryWithAllRolledBack(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireOptimizedAckTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireOptimizedAckTest.cpp index 4b7bb1924..dee274e51 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireOptimizedAckTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireOptimizedAckTest.cpp @@ -22,16 +22,13 @@ namespace activemq { namespace test { namespace openwire { class OpenwireOptimizedAckTest : public CMSTestFixture { -public: - OpenwireOptimizedAckTest(); - virtual ~OpenwireOptimizedAckTest(); + public: void SetUp() override {} void TearDown() override {} - virtual std::string getBrokerURL() const; - void testOptimizedAckSettings(); - void testOptimizedAckWithExpiredMsgs(); - void testOptimizedAckWithExpiredMsgsSync(); - void testOptimizedAckWithExpiredMsgsSync2(); + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getOpenwireURL() + + "?connection.optimizeAcknowledge=true&cms.prefetchPolicy.all=100"; + } }; }}} @@ -85,21 +82,7 @@ namespace { } //////////////////////////////////////////////////////////////////////////////// -OpenwireOptimizedAckTest::OpenwireOptimizedAckTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -OpenwireOptimizedAckTest::~OpenwireOptimizedAckTest() { -} - -//////////////////////////////////////////////////////////////////////////////// -std::string OpenwireOptimizedAckTest::getBrokerURL() const { - return activemq::util::IntegrationCommon::getInstance().getOpenwireURL() + - "?connection.optimizeAcknowledge=true&cms.prefetchPolicy.all=100"; -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireOptimizedAckTest::testOptimizedAckSettings() { +TEST_F(OpenwireOptimizedAckTest, testOptimizedAckSettings) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -136,7 +119,7 @@ void OpenwireOptimizedAckTest::testOptimizedAckSettings() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireOptimizedAckTest::testOptimizedAckWithExpiredMsgs() { +TEST_F(OpenwireOptimizedAckTest, testOptimizedAckWithExpiredMsgs) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -186,7 +169,7 @@ void OpenwireOptimizedAckTest::testOptimizedAckWithExpiredMsgs() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireOptimizedAckTest::testOptimizedAckWithExpiredMsgsSync() { +TEST_F(OpenwireOptimizedAckTest, testOptimizedAckWithExpiredMsgsSync) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -229,7 +212,7 @@ void OpenwireOptimizedAckTest::testOptimizedAckWithExpiredMsgsSync() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireOptimizedAckTest::testOptimizedAckWithExpiredMsgsSync2() { +TEST_F(OpenwireOptimizedAckTest, testOptimizedAckWithExpiredMsgsSync2) { Pointer connectionFactory( new ActiveMQConnectionFactory(getBrokerURL())); @@ -274,10 +257,3 @@ void OpenwireOptimizedAckTest::testOptimizedAckWithExpiredMsgsSync2() { session->close(); connection->close(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireOptimizedAckTest, testOptimizedAckSettings) { testOptimizedAckSettings(); } -TEST_F(OpenwireOptimizedAckTest, testOptimizedAckWithExpiredMsgs) { testOptimizedAckWithExpiredMsgs(); } -TEST_F(OpenwireOptimizedAckTest, testOptimizedAckWithExpiredMsgsSync) { testOptimizedAckWithExpiredMsgsSync(); } -TEST_F(OpenwireOptimizedAckTest, testOptimizedAckWithExpiredMsgsSync2) { testOptimizedAckWithExpiredMsgsSync2(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireQueueBrowserTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireQueueBrowserTest.cpp index 4ec6ccf70..5110ad598 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireQueueBrowserTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireQueueBrowserTest.cpp @@ -17,14 +17,25 @@ #include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + namespace activemq { namespace test { namespace openwire { class OpenwireQueueBrowserTest : public QueueBrowserTest { - private: -public: - OpenwireQueueBrowserTest(); - virtual ~OpenwireQueueBrowserTest(); + public: virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -34,20 +45,277 @@ namespace openwire { using namespace activemq; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::core; +using namespace decaf; +using namespace decaf::lang; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireQueueBrowserTest, testReceiveBrowseReceive) { + + cms::Session* session(cmsProvider->getSession()); + + std::unique_ptr queue(session->createQueue("testReceiveBrowseReceive")); + + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr message1(session->createTextMessage("First Message")); + std::unique_ptr message2(session->createTextMessage("Second Message")); + std::unique_ptr message3(session->createTextMessage("Third Message")); + + // lets consume any outstanding messages from previous test runs + cms::Message* message; + while ((message = consumer->receive(1000)) != NULL) { + delete message; + } + + producer->send(message1.get()); + producer->send(message2.get()); + producer->send(message3.get()); + + // Get the first. + std::unique_ptr inbound(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message1->getText(), inbound->getText()); + consumer->close(); + + std::unique_ptr browser(session->createBrowser(queue.get())); + cms::MessageEnumeration* enumeration = browser->getEnumeration(); + + // browse the second + ASSERT_TRUE(enumeration->hasMoreMessages()) << ("should have received the second message"); + inbound.reset(dynamic_cast(enumeration->nextMessage())); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message2->getText(), inbound->getText()); + + // browse the third. + ASSERT_TRUE(enumeration->hasMoreMessages()) << ("should have received the third message"); + inbound.reset(dynamic_cast(enumeration->nextMessage())); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message3->getText(), inbound->getText()); + + // There should be no more. + bool tooMany = false; + while (enumeration->hasMoreMessages()) { + tooMany = true; + } + ASSERT_TRUE(!tooMany) << ("Should not have browsed any more messages"); + browser->close(); + + // Re-open the consumer + consumer.reset(session->createConsumer(queue.get())); + // Receive the second. + inbound.reset(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message2->getText(), inbound->getText()); + // Receive the third. + inbound.reset(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message3->getText(), inbound->getText()); + + consumer->close(); + browser->close(); + producer->close(); + cmsProvider->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireQueueBrowserTest, testBrowseReceive) { + + std::unique_ptr inbound; + + cms::Session* session(cmsProvider->getSession()); + std::unique_ptr queue(session->createQueue("testBrowseReceive")); + std::unique_ptr message1(session->createTextMessage("First Message")); + std::unique_ptr producer(session->createProducer(queue.get())); + + producer->send(message1.get()); + + // create browser first + std::unique_ptr browser(session->createBrowser(queue.get())); + cms::MessageEnumeration* enumeration = browser->getEnumeration(); + + // create consumer + std::unique_ptr consumer(session->createConsumer(queue.get())); + + // browse the first message + ASSERT_TRUE(enumeration->hasMoreMessages()) << ("should have received the first message"); + inbound.reset(dynamic_cast(enumeration->nextMessage())); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message1->getText(), inbound->getText()); + + // Receive the first message. + inbound.reset(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message1->getText(), inbound->getText()); + + consumer->close(); + browser->close(); + producer->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireQueueBrowserTest, testQueueBrowserWith2Consumers) { + + const int numMessages = 100; + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + connection->setAlwaysSyncSend(false); + + std::unique_ptr session(connection->createSession(cms::Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testQueueBrowserWith2Consumers")); + + std::unique_ptr queuePrefetch10( + session->createQueue("testQueueBrowserWith2Consumers?consumer.prefetchSize=10")); + std::unique_ptr queuePrefetch1( + session->createQueue("testQueueBrowserWith2Consumers?consumer.prefetchSize=1")); + + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection2(dynamic_cast(factory->createConnection())); + connection2->start(); + + std::unique_ptr session2(connection2->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr consumer(session->createConsumer(queuePrefetch10.get())); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + for (int i = 0; i < numMessages; i++) { + std::unique_ptr message( + session->createTextMessage(std::string("Message: ") + Integer::toString(i))); + producer->send(message.get()); + } + + std::unique_ptr browser(session2->createBrowser(queuePrefetch1.get())); + cms::MessageEnumeration* browserView = browser->getEnumeration(); + + std::vector messages; + for (int i = 0; i < numMessages; i++) { + cms::Message* m1 = consumer->receive(5000); + ASSERT_TRUE(m1 != NULL) << (std::string("m1 is null for index: ") + Integer::toString(i)); + messages.push_back(m1); + } + + for (int i = 0; i < numMessages && browserView->hasMoreMessages(); i++) { + cms::Message* m1 = messages[i]; + cms::Message* m2 = browserView->nextMessage(); + ASSERT_TRUE(m2 != NULL) << (std::string("m2 is null for index: ") + Integer::toString(i)); + ASSERT_TRUE(m1->getCMSMessageID() == m2->getCMSMessageID()); + delete m2; + } + + ASSERT_TRUE(!browserView->hasMoreMessages()) << ("nothing left in the browser"); + ASSERT_TRUE(consumer->receiveNoWait() == NULL) << ("consumer finished"); + + for (std::size_t ix = 0; ix < messages.size(); ++ix) { + cms::Message* msg = messages[ix]; + msg->acknowledge(); + delete msg; + } +} //////////////////////////////////////////////////////////////////////////////// -OpenwireQueueBrowserTest::OpenwireQueueBrowserTest() { +TEST_F(OpenwireQueueBrowserTest, testRepeatedQueueBrowserCreateDestroy) { + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr textMessage(session->createTextMessage("Test")); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + producer->send(textMessage.get()); + session->commit(); + + connection->start(); + + std::unique_ptr browser(session->createBrowser(queue.get())); + + for (int i = 0; i < 200; i++) { + browser.reset(session->createBrowser(queue.get())); + cms::MessageEnumeration* browserView = browser->getEnumeration(); + + if (browserView->hasMoreMessages()) { + std::unique_ptr message(browserView->nextMessage()); + ASSERT_TRUE(message.get() != NULL); + } + + browser.reset(NULL); + } } //////////////////////////////////////////////////////////////////////////////// -OpenwireQueueBrowserTest::~OpenwireQueueBrowserTest() { +TEST_F(OpenwireQueueBrowserTest, testRepeatedQueueBrowserCreateDestroyWithMessageInQueue) { + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr textMessage(session->createTextMessage("Test")); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + for (int i = 0 ; i < 10; ++i) { + producer->send(textMessage.get()); + } + + connection->start(); + + std::unique_ptr browser(session->createBrowser(queue.get())); + + for (int i = 0; i < 200; i++) { + browser.reset(session->createBrowser(queue.get())); + cms::MessageEnumeration* browserView = browser->getEnumeration(); + + if (browserView->hasMoreMessages()) { + std::unique_ptr message(browserView->nextMessage()); + ASSERT_TRUE(message.get() != NULL); + } + + browser.reset(NULL); + } } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireQueueBrowserTest, testReceiveBrowseReceive) { testReceiveBrowseReceive(); } -TEST_F(OpenwireQueueBrowserTest, testBrowseReceive) { testBrowseReceive(); } -TEST_F(OpenwireQueueBrowserTest, testQueueBrowserWith2Consumers) { testQueueBrowserWith2Consumers(); } -TEST_F(OpenwireQueueBrowserTest, testRepeatedQueueBrowserCreateDestroy) { testRepeatedQueueBrowserCreateDestroy(); } -TEST_F(OpenwireQueueBrowserTest, testRepeatedQueueBrowserCreateDestroyWithMessageInQueue) { testRepeatedQueueBrowserCreateDestroyWithMessageInQueue(); } -TEST_F(OpenwireQueueBrowserTest, testBrowsingExpirationIsIgnored) { testBrowsingExpirationIsIgnored(); } +TEST_F(OpenwireQueueBrowserTest, testBrowsingExpirationIsIgnored) { + + const int MESSAGES_TO_SEND = 50; + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + producer->setTimeToLive(1000); + + // Load the Queue with messages set to expire. + for (int i = 1; i <= MESSAGES_TO_SEND; i++) { + std::unique_ptr textMessage(session->createTextMessage("Message: " + Integer::toString(i))); + producer->send(textMessage.get()); + } + + std::unique_ptr browser(session->createBrowser(queue.get())); + cms::MessageEnumeration* enumeration = browser->getEnumeration(); + int browsed = 0; + + Thread::sleep(1000); + + while (enumeration->hasMoreMessages()) { + std::unique_ptr message(enumeration->nextMessage()); + ASSERT_TRUE(message.get() != NULL); + browsed++; + } + + ASSERT_EQ(MESSAGES_TO_SEND, browsed) << ("Should have browsed all"); + + browser->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleRollbackTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleRollbackTest.cpp index 736cb65f3..221b8f70b 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleRollbackTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleRollbackTest.cpp @@ -16,14 +16,20 @@ */ #include +#include +#include +#include + +#include +#include + +#include namespace activemq{ namespace test{ namespace openwire{ class OpenwireSimpleRollbackTest : public SimpleRollbackTest { public: - OpenwireSimpleRollbackTest(); - virtual ~OpenwireSimpleRollbackTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -35,15 +41,78 @@ using namespace cms; using namespace activemq; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf::lang; +using namespace decaf::util; //////////////////////////////////////////////////////////////////////////////// -OpenwireSimpleRollbackTest::OpenwireSimpleRollbackTest() { -} +TEST_F(OpenwireSimpleRollbackTest, testRollbacks) { -//////////////////////////////////////////////////////////////////////////////// -OpenwireSimpleRollbackTest::~OpenwireSimpleRollbackTest() { -} + try { -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireSimpleRollbackTest, testRollbacks) { testRollbacks(); } + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage()); + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + ostringstream lcStream; + lcStream << "SimpleTest - Message #" << i << ends; + txtMessage->setText(lcStream.str()); + producer->send(txtMessage.get()); + } + + session->commit(); + Thread::sleep(50); + + // Wait for the messages to get here + listener.asyncWaitForMessages(IntegrationCommon::defaultMsgCount); + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount); + + session->commit(); + Thread::sleep(50); + + for (unsigned int i = 0; i < 5; ++i) { + ostringstream lcStream; + lcStream << "SimpleTest - Message #" << i << ends; + txtMessage->setText(lcStream.str()); + producer->send(txtMessage.get()); + } + + listener.reset(); + session->rollback(); + Thread::sleep(50); + + listener.reset(); + txtMessage->setText("SimpleTest - Message after Rollback"); + producer->send(txtMessage.get()); + session->commit(); + + // Wait for the messages to get here + listener.asyncWaitForMessages(1); + ASSERT_TRUE(listener.getNumReceived() == 1); + + listener.reset(); + txtMessage->setText("SimpleTest - Message after Rollback"); + producer->send(txtMessage.get()); + session->commit(); + + // Wait for the messages to get here + listener.asyncWaitForMessages(1); + ASSERT_TRUE(listener.getNumReceived() == 1); + session->commit(); + + } catch (std::exception& ex) { + std::cout << ex.what() << std::endl; + throw ex; + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleTest.cpp index 1c75e756d..581b4dc07 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSimpleTest.cpp @@ -21,24 +21,10 @@ namespace activemq{ namespace test{ namespace openwire{ class OpenwireSimpleTest : public SimpleTest { -public: - OpenwireSimpleTest(); - virtual ~OpenwireSimpleTest(); + public: virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } - void testWithZeroConsumerPrefetch(); - void testWithZeroConsumerPrefetchAndNoMessage(); - void testWithZeroConsumerPrefetch2(); - void testWithZeroConsumerPrefetchAndNoMessage2(); - void testWithZeroConsumerPrefetchAndZeroRedelivery(); - void testWithZeroConsumerPrefetchWithInFlightExpiration(); - void testMapMessageSendToQueue(); - void testMapMessageSendToTopic(); - void tesstStreamMessage(); - void testDestroyDestination(); - void testMessageIdSetOnSend(); - void testReceiveWithSessionSyncDispatch(); }; }}} @@ -46,6 +32,7 @@ namespace openwire{ #include #include #include +#include #include #include @@ -64,19 +51,156 @@ using namespace decaf::lang; using namespace decaf::util; //////////////////////////////////////////////////////////////////////////////// -OpenwireSimpleTest::OpenwireSimpleTest() { +// Tests inlined from SimpleTest base class + +TEST_F(OpenwireSimpleTest, testAutoAck) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + std::unique_ptr bytesMessage(session->createBytesMessage()); + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(txtMessage.get()); + } + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(bytesMessage.get()); + } + + // Wait for the messages to get here + listener.asyncWaitForMessages(IntegrationCommon::defaultMsgCount * 2); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount * 2); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testClientAck) { + + cmsProvider->setAckMode(cms::Session::CLIENT_ACKNOWLEDGE); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + std::unique_ptr bytesMessage(session->createBytesMessage()); + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(txtMessage.get()); + } + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(bytesMessage.get()); + } + + // Wait for the messages to get here + listener.asyncWaitForMessages(IntegrationCommon::defaultMsgCount * 2); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount * 2); } //////////////////////////////////////////////////////////////////////////////// -OpenwireSimpleTest::~OpenwireSimpleTest() { +TEST_F(OpenwireSimpleTest, testProducerWithNullDestination) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getNoDestProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + producer->send(cmsProvider->getDestination(), txtMessage.get()); + + // Wait for the messages to get here + listener.asyncWaitForMessages(1); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == 1); } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testWithZeroConsumerPrefetch() { +TEST_F(OpenwireSimpleTest, testProducerSendWithNullMessage) { - cmsProvider->setTopic(false); - cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + ASSERT_THROW(producer->send( NULL, txtMessage.get() ), cms::InvalidDestinationException) << ("Should Throw an InvalidDestinationException"); + + producer = cmsProvider->getNoDestProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + ASSERT_THROW(producer->send( NULL, txtMessage.get() ), cms::UnsupportedOperationException) << ("Should Throw an UnsupportedOperationException"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testProducerSendToNonDefaultDestination) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + std::unique_ptr destination(session->createTemporaryTopic()); + + ASSERT_THROW(producer->send(destination.get(), txtMessage.get()), cms::UnsupportedOperationException) << ("Should Throw an UnsupportedOperationException"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testSyncReceive) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testSyncReceiveClientAck) { + + cmsProvider->setAckMode(cms::Session::CLIENT_ACKNOWLEDGE); cmsProvider->reconnectSession(); // Create CMS Object for Comms @@ -90,17 +214,289 @@ void OpenwireSimpleTest::testWithZeroConsumerPrefetch() { // Send some text messages producer->send(txtMessage.get()); - std::unique_ptr message(consumer->receive(1000)); + std::unique_ptr message(consumer->receive(2000)); ASSERT_TRUE(message.get() != NULL); } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testWithZeroConsumerPrefetch2() { +TEST_F(OpenwireSimpleTest, testMultipleConnections) { + + // Create CMS Object for Comms + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection1(factory->createConnection()); + connection1->start(); + + std::unique_ptr connection2(factory->createConnection()); + connection2->start(); + + ASSERT_TRUE(connection1->getClientID() != connection2->getClientID()); + + std::unique_ptr session1(connection1->createSession()); + std::unique_ptr session2(connection1->createSession()); + + std::unique_ptr topic(session1->createTopic(UUID::randomUUID().toString())); + + std::unique_ptr consumer1(session1->createConsumer(topic.get())); + std::unique_ptr consumer2(session2->createConsumer(topic.get())); + + std::unique_ptr producer(session2->createProducer(topic.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr textMessage(session2->createTextMessage()); + + // Send some text messages + producer->send(textMessage.get()); + + std::unique_ptr message(consumer1->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + message.reset(consumer2->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + // Clean up if we can + consumer1->close(); + consumer2->close(); + producer->close(); + session1->close(); + session2->close(); + + this->cmsProvider->destroyDestination(topic.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testMultipleSessions) { + + // Create CMS Object for Comms + std::unique_ptr session1(cmsProvider->getConnection()->createSession()); + std::unique_ptr session2(cmsProvider->getConnection()->createSession()); + + std::unique_ptr topic(session1->createTopic(UUID::randomUUID().toString())); + + std::unique_ptr consumer1(session1->createConsumer(topic.get())); + std::unique_ptr consumer2(session2->createConsumer(topic.get())); + + std::unique_ptr producer(session2->createProducer(topic.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr textMessage(session2->createTextMessage()); + + // Send some text messages + producer->send(textMessage.get()); + + std::unique_ptr message(consumer1->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + message.reset(consumer2->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + // Clean up if we can + consumer1->close(); + consumer2->close(); + producer->close(); + session1->close(); + session2->close(); + + this->cmsProvider->destroyDestination(topic.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testReceiveAlreadyInQueue) { + + // Create CMS Object for Comms + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection(factory->createConnection()); + + std::unique_ptr session(connection->createSession()); + std::unique_ptr topic(session->createTopic(UUID::randomUUID().toString())); + std::unique_ptr consumer(session->createConsumer(topic.get())); + std::unique_ptr producer(session->createProducer(topic.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr textMessage(session->createTextMessage()); + + // Send some text messages + producer->send(textMessage.get()); + + Thread::sleep(250); + + connection->start(); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + // Clean up if we can + consumer->close(); + producer->close(); + session->close(); + + this->cmsProvider->destroyDestination(topic.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testBytesMessageSendRecv) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr bytesMessage(session->createBytesMessage()); + + bytesMessage->writeBoolean(true); + bytesMessage->writeByte(127); + bytesMessage->writeDouble(123456.789); + bytesMessage->writeInt(65537); + bytesMessage->writeString("TEST-STRING"); + + // Send some text messages + producer->send(bytesMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + ASSERT_THROW(message->setStringProperty("FOO", "BAR"), cms::CMSException) << ("Should throw an ActiveMQExceptio"); + + BytesMessage* bytesMessage2 = dynamic_cast(message.get()); + ASSERT_TRUE(bytesMessage2 != NULL); + ASSERT_THROW(bytesMessage2->writeBoolean(false), cms::CMSException) << ("Should throw an ActiveMQExceptio"); + + ASSERT_TRUE(bytesMessage2->getBodyLength() > 0); + + unsigned char* result = bytesMessage2->getBodyBytes(); + ASSERT_TRUE(result != NULL); + delete[] result; + + bytesMessage2->reset(); + + ASSERT_TRUE(bytesMessage2->readBoolean() == true); + ASSERT_TRUE(bytesMessage2->readByte() == 127); + ASSERT_TRUE(bytesMessage2->readDouble() == 123456.789); + ASSERT_TRUE(bytesMessage2->readInt() == 65537); + ASSERT_TRUE(bytesMessage2->readString() == "TEST-STRING"); +} + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class Listener: public cms::MessageListener { + private: + + bool passed; + bool triggered; + + public: + + Listener() : MessageListener(), passed(false), triggered(false) {} + + virtual ~Listener() {} + + bool isPassed() { + return passed; + } + + bool isTriggered() { + return triggered; + } + + void onMessage(const cms::Message* message) { + try { + triggered = true; + const BytesMessage* bytesMessage = dynamic_cast(message); + + ASSERT_TRUE(bytesMessage != NULL); + ASSERT_TRUE(bytesMessage->getBodyLength() > 0); + + unsigned char* result = bytesMessage->getBodyBytes(); + ASSERT_TRUE(result != NULL); + delete[] result; + + passed = true; + } catch (...) { + passed = false; + } + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testBytesMessageSendRecvAsync) { + + Listener listener; + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr bytesMessage(session->createBytesMessage()); + + bytesMessage->writeBoolean(true); + bytesMessage->writeByte(127); + bytesMessage->writeDouble(123456.789); + bytesMessage->writeInt(65537); + bytesMessage->writeString("TEST-STRING"); + + // Send some text messages + producer->send(bytesMessage.get()); + + int count = 0; + while (!listener.isTriggered() && count++ < 30) { + decaf::lang::Thread::sleep(100); + } + + ASSERT_TRUE(listener.isPassed()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testLibraryInitShutdownInit) { + + { + this->TearDown(); + // Shutdown the ActiveMQ library + ASSERT_NO_THROW(activemq::library::ActiveMQCPP::shutdownLibrary()); + } + { + // Initialize the ActiveMQ library + ASSERT_NO_THROW(activemq::library::ActiveMQCPP::initializeLibrary()); + cmsProvider.reset(new activemq::util::CMSProvider(getBrokerURL())); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testQuickCreateAndDestroy) { + + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection(factory->createConnection()); + std::unique_ptr session(connection->createSession()); + + session.reset( NULL); + connection.reset( NULL); + + connection.reset(factory->createConnection()); + session.reset(connection->createSession()); + connection->start(); + + session.reset( NULL); + connection.reset( NULL); + + for (int i = 0; i < 50; ++i) { + CMSProvider lcmsProvider(this->getBrokerURL()); + lcmsProvider.getSession(); + lcmsProvider.getConsumer(); + lcmsProvider.getProducer(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests defined locally - test logic is inlined directly in the TEST_F macro + +TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetch) { cmsProvider->setTopic(false); - ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); - amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); - amqConnection->getPrefetchPolicy()->setTopicPrefetch(0); + cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); + cmsProvider->reconnectSession(); // Create CMS Object for Comms @@ -119,7 +515,7 @@ void OpenwireSimpleTest::testWithZeroConsumerPrefetch2() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testWithZeroConsumerPrefetchAndNoMessage() { +TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchAndNoMessage) { cmsProvider->setTopic(false); cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); @@ -143,7 +539,31 @@ void OpenwireSimpleTest::testWithZeroConsumerPrefetchAndNoMessage() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testWithZeroConsumerPrefetchAndNoMessage2() { +TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetch2) { + + cmsProvider->setTopic(false); + ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); + amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); + amqConnection->getPrefetchPolicy()->setTopicPrefetch(0); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchAndNoMessage2) { cmsProvider->setTopic(false); ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); @@ -168,7 +588,113 @@ void OpenwireSimpleTest::testWithZeroConsumerPrefetchAndNoMessage2() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testMapMessageSendToQueue() { +TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchAndZeroRedelivery) { + + ActiveMQConnectionFactory factory(getBrokerURL()); + std::unique_ptr connection(factory.createConnection()); + + connection->start(); + + { + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr message(session->createTextMessage("Hello")); + producer->send(message.get()); + producer->close(); + session->close(); + } + + { + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + + session->rollback(); + session->close(); + connection->close(); + } + + connection.reset(factory.createConnection()); + connection->start(); + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + + // Now we test the zero prefetc + zero max redelivery case. + amqConnection->getRedeliveryPolicy()->setMaximumRedeliveries(0); + amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() == NULL); + + session->commit(); + session->close(); + + amqConnection->destroyDestination(queue.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchWithInFlightExpiration) { + + ActiveMQConnectionFactory factory(getBrokerURL()); + std::unique_ptr connection(factory.createConnection()); + + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + amqConnection->getPrefetchPolicy()->setAll(0); + + connection->start(); + + { + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchWithInFlightExpiration")); + + amqConnection->destroyDestination(queue.get()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr expiredMessage(session->createTextMessage("Expired")); + std::unique_ptr validMessage(session->createTextMessage("Valid")); + producer->send(expiredMessage.get(), cms::Message::DEFAULT_DELIVERY_MODE, cms::Message::DEFAULT_MSG_PRIORITY, 2000); + producer->send(validMessage.get()); + session->close(); + } + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchWithInFlightExpiration")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + { + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + TextMessage* received = dynamic_cast(message.get()); + ASSERT_EQ(std::string("Expired"), received->getText()); + } + + session->rollback(); + Thread::sleep(2500); + + { + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + TextMessage* received = dynamic_cast(message.get()); + ASSERT_EQ(std::string("Valid"), received->getText()); + } + + session->commit(); + session->close(); + + amqConnection->destroyDestination(queue.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSimpleTest, testMapMessageSendToQueue) { cmsProvider->setTopic(false); cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); @@ -232,7 +758,7 @@ void OpenwireSimpleTest::testMapMessageSendToQueue() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testMapMessageSendToTopic() { +TEST_F(OpenwireSimpleTest, testMapMessageSendToTopic) { // Create CMS Object for Comms cms::Session* session(cmsProvider->getSession()); @@ -291,7 +817,7 @@ void OpenwireSimpleTest::testMapMessageSendToTopic() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testDestroyDestination() { +TEST_F(OpenwireSimpleTest, testDestroyDestination) { try { @@ -333,7 +859,7 @@ void OpenwireSimpleTest::testDestroyDestination() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::tesstStreamMessage() { +TEST_F(OpenwireSimpleTest, tesstStreamMessage) { // Create CMS Object for Comms cms::Session* session(cmsProvider->getSession()); @@ -393,7 +919,7 @@ void OpenwireSimpleTest::tesstStreamMessage() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testMessageIdSetOnSend() { +TEST_F(OpenwireSimpleTest, testMessageIdSetOnSend) { // Create CMS Object for Comms cms::Session* session(cmsProvider->getSession()); @@ -408,7 +934,7 @@ void OpenwireSimpleTest::testMessageIdSetOnSend() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testReceiveWithSessionSyncDispatch() { +TEST_F(OpenwireSimpleTest, testReceiveWithSessionSyncDispatch) { ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); amqConnection->setAlwaysSessionAsync(false); @@ -429,138 +955,3 @@ void OpenwireSimpleTest::testReceiveWithSessionSyncDispatch() { std::unique_ptr message(consumer->receive(1000)); ASSERT_TRUE(message.get() != NULL); } - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testWithZeroConsumerPrefetchAndZeroRedelivery() { - - ActiveMQConnectionFactory factory(getBrokerURL()); - std::unique_ptr connection(factory.createConnection()); - - connection->start(); - - { - std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); - std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); - std::unique_ptr producer(session->createProducer(queue.get())); - - std::unique_ptr message(session->createTextMessage("Hello")); - producer->send(message.get()); - producer->close(); - session->close(); - } - - { - std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); - std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); - std::unique_ptr consumer(session->createConsumer(queue.get())); - - std::unique_ptr message(consumer->receive(5000)); - ASSERT_TRUE(message.get() != NULL); - - session->rollback(); - session->close(); - connection->close(); - } - - connection.reset(factory.createConnection()); - connection->start(); - ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); - - // Now we test the zero prefetc + zero max redelivery case. - amqConnection->getRedeliveryPolicy()->setMaximumRedeliveries(0); - amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); - - std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); - std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); - std::unique_ptr consumer(session->createConsumer(queue.get())); - - std::unique_ptr message(consumer->receive(5000)); - ASSERT_TRUE(message.get() == NULL); - - session->commit(); - session->close(); - - amqConnection->destroyDestination(queue.get()); -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireSimpleTest::testWithZeroConsumerPrefetchWithInFlightExpiration() { - - ActiveMQConnectionFactory factory(getBrokerURL()); - std::unique_ptr connection(factory.createConnection()); - - ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); - amqConnection->getPrefetchPolicy()->setAll(0); - - connection->start(); - - { - std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); - std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchWithInFlightExpiration")); - - amqConnection->destroyDestination(queue.get()); - - std::unique_ptr producer(session->createProducer(queue.get())); - - std::unique_ptr expiredMessage(session->createTextMessage("Expired")); - std::unique_ptr validMessage(session->createTextMessage("Valid")); - producer->send(expiredMessage.get(), cms::Message::DEFAULT_DELIVERY_MODE, cms::Message::DEFAULT_MSG_PRIORITY, 2000); - producer->send(validMessage.get()); - session->close(); - } - - std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); - std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchWithInFlightExpiration")); - std::unique_ptr consumer(session->createConsumer(queue.get())); - - { - std::unique_ptr message(consumer->receive(5000)); - ASSERT_TRUE(message.get() != NULL); - TextMessage* received = dynamic_cast(message.get()); - ASSERT_EQ(std::string("Expired"), received->getText()); - } - - session->rollback(); - Thread::sleep(2500); - - { - std::unique_ptr message(consumer->receive(5000)); - ASSERT_TRUE(message.get() != NULL); - TextMessage* received = dynamic_cast(message.get()); - ASSERT_EQ(std::string("Valid"), received->getText()); - } - - session->commit(); - session->close(); - - amqConnection->destroyDestination(queue.get()); -} - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireSimpleTest, testAutoAck) { testAutoAck(); } -TEST_F(OpenwireSimpleTest, testClientAck) { testClientAck(); } -TEST_F(OpenwireSimpleTest, testProducerWithNullDestination) { testProducerWithNullDestination(); } -TEST_F(OpenwireSimpleTest, testProducerSendWithNullDestination) { testProducerSendWithNullDestination(); } -TEST_F(OpenwireSimpleTest, testProducerSendToNonDefaultDestination) { testProducerSendToNonDefaultDestination(); } -TEST_F(OpenwireSimpleTest, testSyncReceive) { testSyncReceive(); } -TEST_F(OpenwireSimpleTest, testSyncReceiveClientAck) { testSyncReceiveClientAck(); } -TEST_F(OpenwireSimpleTest, testMultipleConnections) { testMultipleConnections(); } -TEST_F(OpenwireSimpleTest, testMultipleSessions) { testMultipleSessions(); } -TEST_F(OpenwireSimpleTest, testReceiveAlreadyInQueue) { testReceiveAlreadyInQueue(); } -TEST_F(OpenwireSimpleTest, testBytesMessageSendRecv) { testBytesMessageSendRecv(); } -TEST_F(OpenwireSimpleTest, testQuickCreateAndDestroy) { testQuickCreateAndDestroy(); } -TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetch) { testWithZeroConsumerPrefetch(); } -TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchAndNoMessage) { testWithZeroConsumerPrefetchAndNoMessage(); } -TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetch2) { testWithZeroConsumerPrefetch2(); } -TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchAndNoMessage2) { testWithZeroConsumerPrefetchAndNoMessage2(); } -TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchAndZeroRedelivery) { testWithZeroConsumerPrefetchAndZeroRedelivery(); } -TEST_F(OpenwireSimpleTest, testWithZeroConsumerPrefetchWithInFlightExpiration) { testWithZeroConsumerPrefetchWithInFlightExpiration(); } -TEST_F(OpenwireSimpleTest, testMapMessageSendToQueue) { testMapMessageSendToQueue(); } -TEST_F(OpenwireSimpleTest, testMapMessageSendToTopic) { testMapMessageSendToTopic(); } -TEST_F(OpenwireSimpleTest, testDestroyDestination) { testDestroyDestination(); } -TEST_F(OpenwireSimpleTest, tesstStreamMessage) { tesstStreamMessage(); } -TEST_F(OpenwireSimpleTest, testLibraryInitShutdownInit) { testLibraryInitShutdownInit(); } -TEST_F(OpenwireSimpleTest, testBytesMessageSendRecvAsync) { testBytesMessageSendRecvAsync(); } -TEST_F(OpenwireSimpleTest, testMessageIdSetOnSend) { testMessageIdSetOnSend(); } -TEST_F(OpenwireSimpleTest, testReceiveWithSessionSyncDispatch) { testReceiveWithSessionSyncDispatch(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSlowListenerTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSlowListenerTest.cpp index e8f9b28b0..592ada33d 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSlowListenerTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireSlowListenerTest.cpp @@ -16,14 +16,47 @@ */ #include +#include + +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace test{ + + class SlowListener : public cms::MessageListener { + public: + + unsigned int count; + std::set threadIds; + decaf::util::concurrent::Mutex mutex; + + SlowListener() : MessageListener(), count(0), threadIds(), mutex() {} + virtual ~SlowListener() {} + + void onMessage(const cms::Message* message) { + + synchronized( &mutex ) { + count++; + threadIds.insert(decaf::lang::Thread::currentThread()->getId()); + } + + decaf::lang::Thread::sleep(20); + } + }; + +}} namespace activemq{ namespace test{ namespace openwire{ class OpenwireSlowListenerTest : public SlowListenerTest { public: - OpenwireSlowListenerTest(); - virtual ~OpenwireSlowListenerTest(); virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -36,15 +69,55 @@ using namespace activemq; using namespace activemq::test; using namespace activemq::test::openwire; using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf::lang; +using namespace decaf::util; +using namespace decaf::util::concurrent; //////////////////////////////////////////////////////////////////////////////// -OpenwireSlowListenerTest::OpenwireSlowListenerTest() { -} +TEST_F(OpenwireSlowListenerTest, testSlowListener) { -//////////////////////////////////////////////////////////////////////////////// -OpenwireSlowListenerTest::~OpenwireSlowListenerTest() { -} + try { -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireSlowListenerTest, testSlowListener) { testSlowListener(); } + SlowListener listener; + + cms::Session* session = cmsProvider->getSession(); + + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + const unsigned int numConsumers = 5; + cms::MessageConsumer* consumers[numConsumers]; + + // Create several consumers for the same destination. + for (unsigned int i = 0; i < numConsumers; i++) { + consumers[i] = session->createConsumer(cmsProvider->getDestination()); + consumers[i]->setMessageListener(&listener); + } + + std::unique_ptr message(session->createBytesMessage()); + + unsigned int msgCount = 50; + for (unsigned int i = 0; i < msgCount; i++) { + producer->send(message.get()); + } + + // Wait no more than 10 seconds for all the messages to come in. + waitForMessages(msgCount * numConsumers, 10000, &listener); + + synchronized(&listener.mutex) { + // Make sure that the listener was always accessed by the same thread + // and that it received all the messages from all consumers. + ASSERT_EQ(1, (int )listener.threadIds.size()); + ASSERT_EQ((msgCount * numConsumers), listener.count); + } + + for (unsigned int i = 0; i < numConsumers; i++) { + delete consumers[i]; + } + + } catch (ActiveMQException& ex) { + ex.printStackTrace(); + throw ex; + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTempDestinationTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTempDestinationTest.cpp index d3a1fc239..1a0f12a7c 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTempDestinationTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTempDestinationTest.cpp @@ -21,19 +21,7 @@ namespace activemq{ namespace test{ namespace openwire{ class OpenwireTempDestinationTest : public CMSTestFixture { -public: - OpenwireTempDestinationTest() {} - virtual ~OpenwireTempDestinationTest() {} - void testBasics(); - void testTwoConnections(); - void testTempDestOnlyConsumedByLocalConn(); - void testTempQueueHoldsMessagesWithConsumers(); - void testTempQueueHoldsMessagesWithoutConsumers(); - void testTmpQueueWorksUnderLoad(); - void testPublishFailsForClosedConnection(); - void testPublishFailsForDestoryedTempDestination(); - void testDeleteDestinationWithSubscribersFails(); - void testCloseConnectionWithManyTempDests(); + public: virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -199,7 +187,7 @@ namespace openwire { }}} /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testBasics() { +TEST_F(OpenwireTempDestinationTest, testBasics) { try{ @@ -223,7 +211,7 @@ void OpenwireTempDestinationTest::testBasics() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testTwoConnections() { +TEST_F(OpenwireTempDestinationTest, testTwoConnections) { std::string destination = "REQUEST-TOPIC"; @@ -253,7 +241,7 @@ void OpenwireTempDestinationTest::testTwoConnections() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testTempDestOnlyConsumedByLocalConn() { +TEST_F(OpenwireTempDestinationTest, testTempDestOnlyConsumedByLocalConn) { std::unique_ptr factory( new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); @@ -287,7 +275,7 @@ void OpenwireTempDestinationTest::testTempDestOnlyConsumedByLocalConn() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testTempQueueHoldsMessagesWithConsumers() { +TEST_F(OpenwireTempDestinationTest, testTempQueueHoldsMessagesWithConsumers) { std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); std::unique_ptr consumer(cmsProvider->getSession()->createConsumer(queue.get())); @@ -303,7 +291,7 @@ void OpenwireTempDestinationTest::testTempQueueHoldsMessagesWithConsumers() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testTempQueueHoldsMessagesWithoutConsumers() { +TEST_F(OpenwireTempDestinationTest, testTempQueueHoldsMessagesWithoutConsumers) { std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); std::unique_ptr producer(cmsProvider->getSession()->createProducer(queue.get())); @@ -319,7 +307,7 @@ void OpenwireTempDestinationTest::testTempQueueHoldsMessagesWithoutConsumers() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testTmpQueueWorksUnderLoad() { +TEST_F(OpenwireTempDestinationTest, testTmpQueueWorksUnderLoad) { int count = 500; int dataSize = 1024; @@ -354,7 +342,7 @@ void OpenwireTempDestinationTest::testTmpQueueWorksUnderLoad() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testPublishFailsForClosedConnection() { +TEST_F(OpenwireTempDestinationTest, testPublishFailsForClosedConnection) { Pointer factory( new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); @@ -385,7 +373,7 @@ void OpenwireTempDestinationTest::testPublishFailsForClosedConnection() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testPublishFailsForDestoryedTempDestination() { +TEST_F(OpenwireTempDestinationTest, testPublishFailsForDestoryedTempDestination) { Pointer factory( new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); @@ -416,7 +404,7 @@ void OpenwireTempDestinationTest::testPublishFailsForDestoryedTempDestination() } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testDeleteDestinationWithSubscribersFails() { +TEST_F(OpenwireTempDestinationTest, testDeleteDestinationWithSubscribersFails) { std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); std::unique_ptr consumer(cmsProvider->getSession()->createConsumer(queue.get())); @@ -426,7 +414,7 @@ void OpenwireTempDestinationTest::testDeleteDestinationWithSubscribersFails() { } /////////////////////////////////////////////////////////////////////////////// -void OpenwireTempDestinationTest::testCloseConnectionWithManyTempDests() { +TEST_F(OpenwireTempDestinationTest, testCloseConnectionWithManyTempDests) { ArrayList< Pointer > tempQueues; ArrayList< Pointer > producers; @@ -443,16 +431,3 @@ void OpenwireTempDestinationTest::testCloseConnectionWithManyTempDests() { tempQueues.clear(); producers.clear(); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireTempDestinationTest, testBasics) { testBasics(); } -TEST_F(OpenwireTempDestinationTest, testTwoConnections) { testTwoConnections(); } -TEST_F(OpenwireTempDestinationTest, testTempDestOnlyConsumedByLocalConn) { testTempDestOnlyConsumedByLocalConn(); } -TEST_F(OpenwireTempDestinationTest, testTempQueueHoldsMessagesWithConsumers) { testTempQueueHoldsMessagesWithConsumers(); } -TEST_F(OpenwireTempDestinationTest, testTempQueueHoldsMessagesWithoutConsumers) { testTempQueueHoldsMessagesWithoutConsumers(); } -TEST_F(OpenwireTempDestinationTest, testTmpQueueWorksUnderLoad) { testTmpQueueWorksUnderLoad(); } -TEST_F(OpenwireTempDestinationTest, testPublishFailsForClosedConnection) { testPublishFailsForClosedConnection(); } -TEST_F(OpenwireTempDestinationTest, testPublishFailsForDestoryedTempDestination) { testPublishFailsForDestoryedTempDestination(); } -TEST_F(OpenwireTempDestinationTest, testDeleteDestinationWithSubscribersFails) { testDeleteDestinationWithSubscribersFails(); } -TEST_F(OpenwireTempDestinationTest, testCloseConnectionWithManyTempDests) { testCloseConnectionWithManyTempDests(); } diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTransactionTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTransactionTest.cpp index 0ad8089e0..16f48eed4 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTransactionTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireTransactionTest.cpp @@ -17,13 +17,17 @@ #include +#include +#include +#include +#include +#include + namespace activemq{ namespace test{ namespace openwire{ class OpenwireTransactionTest : public TransactionTest { -public: - OpenwireTransactionTest(); - virtual ~OpenwireTransactionTest(); + public: virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } @@ -33,22 +37,239 @@ namespace openwire{ using namespace std; using namespace cms; using namespace activemq; +using namespace activemq::core; using namespace activemq::test; using namespace activemq::test::openwire; +using namespace activemq::util; +using namespace activemq::exceptions; //////////////////////////////////////////////////////////////////////////////// -OpenwireTransactionTest::OpenwireTransactionTest() { +TEST_F(OpenwireTransactionTest, testSendReceiveTransactedBatches) { + + const int batchCount = 10; + const int batchSize = 20; + + // Create CMS Object for Comms + cms::Session* session = cmsProvider->getSession(); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + for (int j = 0; j < batchCount - 8; j++) { + + std::unique_ptr message(session->createTextMessage("Batch Message")); + + for (int i = 0; i < batchSize; i++) { + ASSERT_NO_THROW(producer->send(message.get())) << ("Send should not throw an exception here."); + } + + ASSERT_NO_THROW(session->commit()) << ("Session Commit should not throw an exception here:"); + + for (int i = 0; i < batchSize; i++) { + ASSERT_NO_THROW(message.reset(dynamic_cast(consumer->receive(1000 * 5)))) << ("Receive Shouldn't throw a Message here:"); + + ASSERT_TRUE(message.get() != NULL) << ("Failed to receive all messages in batch"); + ASSERT_TRUE(string("Batch Message") == message->getText()); + } + + ASSERT_NO_THROW(session->commit()) << ("Session Commit should not throw an exception here:"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireTransactionTest, testSendRollback) { + + // Create CMS Object for Comms + cms::Session* session = cmsProvider->getSession(); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr outbound1(session->createTextMessage("First Message")); + std::unique_ptr outbound2(session->createTextMessage("Second Message")); + + // sends a message + producer->send(outbound1.get()); + session->commit(); + + // sends a message that gets rollbacked + std::unique_ptr rollback(session->createTextMessage("I'm going to get rolled back.")); + producer->send(rollback.get()); + session->rollback(); + + // sends a message + producer->send(outbound2.get()); + session->commit(); + + // receives the first message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(1500))); + + // receives the second message + std::unique_ptr inbound2(dynamic_cast(consumer->receive(4000))); + + // validates that the rollbacked was not consumed + session->commit(); + + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + ASSERT_TRUE(outbound2->getText() == inbound2->getText()); } //////////////////////////////////////////////////////////////////////////////// -OpenwireTransactionTest::~OpenwireTransactionTest() { +TEST_F(OpenwireTransactionTest, testSendRollbackCommitRollback) { + + // Create CMS Object for Comms + cms::Session* session = cmsProvider->getSession(); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr outbound1(session->createTextMessage("First Message")); + std::unique_ptr outbound2(session->createTextMessage("Second Message")); + + // sends them and then rolls back. + producer->send(outbound1.get()); + producer->send(outbound2.get()); + session->rollback(); + + // Send one and commit. + producer->send(outbound1.get()); + session->commit(); + + // receives the first message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(1500))); + + ASSERT_TRUE(NULL == consumer->receive( 1500 )); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + + session->rollback(); + + inbound1.reset(dynamic_cast(consumer->receive(1500))); + + ASSERT_TRUE(NULL == consumer->receive( 1500 )); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + + // validates that the rollbacked was not consumed + session->commit(); } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireTransactionTest, testSendReceiveTransactedBatches) { testSendReceiveTransactedBatches(); } -TEST_F(OpenwireTransactionTest, testSendRollback) { testSendRollback(); } -TEST_F(OpenwireTransactionTest, testWithTTLSet) { testWithTTLSet(); } -TEST_F(OpenwireTransactionTest, testSendRollbackCommitRollback) { testSendRollbackCommitRollback(); } -TEST_F(OpenwireTransactionTest, testSessionCommitAfterConsumerClosed) { testSessionCommitAfterConsumerClosed(); } -TEST_F(OpenwireTransactionTest, testSendSessionClose) { testSendSessionClose(); } +TEST_F(OpenwireTransactionTest, testSendSessionClose) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue("testSendSessionClose")); + + // Create the messages used for this test + std::unique_ptr outbound1(session->createTextMessage("First Message")); + std::unique_ptr outbound2(session->createTextMessage("Second Message")); + + Pointer consumer(session->createConsumer(destination.get())); + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + // Send Message #1 + producer->send(outbound1.get()); + session->commit(); + + // Send a Message but roll it back by closing the session that owns the resources + std::unique_ptr rollback(session->createTextMessage("I'm going to get rolled back.")); + producer->send(outbound2.get()); + session->close(); + + session.reset(connection->createSession(Session::SESSION_TRANSACTED)); + destination.reset(session->createQueue("testSendSessionClose")); + consumer.reset(session->createConsumer(destination.get())); + producer.reset(session->createProducer(destination.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + // Send Message #2 + producer->send(outbound2.get()); + session->commit(); + + // receives the first message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(1500))); + + // receives the second message + std::unique_ptr inbound2(dynamic_cast(consumer->receive(4000))); + + // validates that the rolled back was not consumed + session->commit(); + + ASSERT_TRUE(inbound1.get() != NULL) << ("Failed to receive first message"); + ASSERT_TRUE(inbound2.get() != NULL) << ("Failed to receive second message"); + + ASSERT_TRUE(outbound1->getText() == inbound1->getText()) << ("First messages aren't equal"); + ASSERT_TRUE(outbound2->getText() == inbound2->getText()) << ("Second messages aren't equal"); + + session->close(); + amqConnection->destroyDestination(destination.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireTransactionTest, testWithTTLSet) { + + cmsProvider->getProducer()->setDeliveryMode(DeliveryMode::PERSISTENT); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + + std::unique_ptr outbound1(cmsProvider->getSession()->createTextMessage("First Message")); + + const std::size_t NUM_MESSAGES = 50; + + // sends a message + for (std::size_t i = 0; i < NUM_MESSAGES; ++i) { + cmsProvider->getProducer()->send(outbound1.get(), cms::DeliveryMode::PERSISTENT, + cmsProvider->getProducer()->getPriority(), 120 * 1000); + } + + cmsProvider->getSession()->commit(); + + for (std::size_t i = 0; i < NUM_MESSAGES; ++i) { + + // receives the second message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(600000))); + ASSERT_TRUE(inbound1.get() != NULL); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + } + + cmsProvider->getSession()->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireTransactionTest, testSessionCommitAfterConsumerClosed) { + + ActiveMQConnectionFactory factory(getBrokerURL()); + std::unique_ptr connection(factory.createConnection()); + + { + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testSessionCommitAfterConsumerClosed")); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr message(session->createTextMessage("Hello")); + producer->send(message.get()); + producer->close(); + session->close(); + } + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testSessionCommitAfterConsumerClosed")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + connection->start(); + + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + + consumer->close(); + session->commit(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireVirtualTopicTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireVirtualTopicTest.cpp index be464ee93..041b87e58 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireVirtualTopicTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireVirtualTopicTest.cpp @@ -17,34 +17,102 @@ #include +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::util; +using namespace activemq::test; +using namespace activemq::exceptions; + +namespace { + + const string PRODUCER_DESTINATION_NAME = "VirtualTopic.TestDestination"; + const string CONSUMER_A_DESTINATION_NAME = "Consumer.A.VirtualTopic.TestDestination"; + const string CONSUMER_B_DESTINATION_NAME = "Consumer.B.VirtualTopic.TestDestination"; + + void runVirtualTopicSyncTest(activemq::util::CMSProvider* cmsProvider, cms::Session::AcknowledgeMode mode) { + + cmsProvider->setAckMode(mode); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + std::unique_ptr topic(session->createTopic(PRODUCER_DESTINATION_NAME)); + std::unique_ptr queueA(session->createQueue(CONSUMER_A_DESTINATION_NAME)); + std::unique_ptr queueB(session->createQueue(CONSUMER_B_DESTINATION_NAME)); + + std::unique_ptr producer(session->createProducer(topic.get())); + std::unique_ptr consumerA(session->createConsumer(queueA.get())); + std::unique_ptr consumerB(session->createConsumer(queueB.get())); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + for (std::size_t i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(txtMessage.get()); + } + + if (cms::Session::SESSION_TRANSACTED == mode) { + session->commit(); + } + + for (std::size_t i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + + std::unique_ptr messageA(consumerA->receive(2000)); + ASSERT_TRUE(messageA.get() != NULL); + if (cms::Session::CLIENT_ACKNOWLEDGE == mode) { + messageA->acknowledge(); + } + + std::unique_ptr messageB(consumerB->receive(2000)); + ASSERT_TRUE(messageB.get() != NULL); + if (cms::Session::CLIENT_ACKNOWLEDGE == mode) { + messageB->acknowledge(); + } + } + + if (cms::Session::SESSION_TRANSACTED == mode) { + session->commit(); + } + } +} + namespace activemq { namespace test { namespace openwire { + class OpenwireVirtualTopicTest : public VirtualTopicTest { - private: -public: - OpenwireVirtualTopicTest(); - virtual ~OpenwireVirtualTopicTest(); - virtual std::string getBrokerURL() const { + public: + std::string getBrokerURL() const override { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); } }; + }}} -using namespace activemq; -using namespace activemq::test; -using namespace activemq::test::openwire; +using activemq::test::openwire::OpenwireVirtualTopicTest; //////////////////////////////////////////////////////////////////////////////// -OpenwireVirtualTopicTest::OpenwireVirtualTopicTest() { +TEST_F(OpenwireVirtualTopicTest, testVirtualTopicSyncReceiveAutoAck) { + runVirtualTopicSyncTest(cmsProvider.get(), cms::Session::AUTO_ACKNOWLEDGE); } //////////////////////////////////////////////////////////////////////////////// -OpenwireVirtualTopicTest::~OpenwireVirtualTopicTest() { +TEST_F(OpenwireVirtualTopicTest, testVirtualTopicSyncReceiveClinetAck) { + runVirtualTopicSyncTest(cmsProvider.get(), cms::Session::CLIENT_ACKNOWLEDGE); } //////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireVirtualTopicTest, testVirtualTopicSyncReceiveAutoAck) { testVirtualTopicSyncReceiveAutoAck(); } -TEST_F(OpenwireVirtualTopicTest, testVirtualTopicSyncReceiveClinetAck) { testVirtualTopicSyncReceiveClinetAck(); } -TEST_F(OpenwireVirtualTopicTest, testVirtualTopicSyncReceiveTransacted) { testVirtualTopicSyncReceiveTransacted(); } +TEST_F(OpenwireVirtualTopicTest, testVirtualTopicSyncReceiveTransacted) { + runVirtualTopicSyncTest(cmsProvider.get(), cms::Session::SESSION_TRANSACTED); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireXATransactionsTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireXATransactionsTest.cpp index 129c9a959..cb05d0c00 100644 --- a/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireXATransactionsTest.cpp +++ b/activemq-cpp/src/test-integration/activemq/test/openwire/OpenwireXATransactionsTest.cpp @@ -56,7 +56,7 @@ namespace test { namespace openwire { class OpenwireXATransactionsTest : public ::testing::Test { - private: + protected: static const int batchCount; static const int batchSize; @@ -65,8 +65,11 @@ namespace openwire { public: - OpenwireXATransactionsTest(); - virtual ~OpenwireXATransactionsTest(); + OpenwireXATransactionsTest() : txIdGen() { + } + + virtual ~OpenwireXATransactionsTest() { + } virtual std::string getBrokerURL() const { return activemq::util::IntegrationCommon::getInstance().getOpenwireURL(); @@ -75,20 +78,7 @@ namespace openwire { void SetUp() override {} void TearDown() override {} - void testCreateXAConnectionFactory(); - void testCreateXAConnection(); - void testCreateXASession(); - void testGetXAResource(); - void testSendReceiveOutsideTX(); - void testSendReceiveTransactedBatches(); - void testSendRollback(); - void testWithTTLSet(); - void testSendRollbackCommitRollback(); - void testXAResource_Exception1(); - void testXAResource_Exception2(); - void testXAResource_Exception3(); - - private: + protected: cms::Xid* createXid() const; @@ -104,15 +94,33 @@ const int OpenwireXATransactionsTest::batchCount = 10; const int OpenwireXATransactionsTest::batchSize = 20; //////////////////////////////////////////////////////////////////////////////// -OpenwireXATransactionsTest::OpenwireXATransactionsTest() : txIdGen() { -} +cms::Xid* OpenwireXATransactionsTest::createXid() const { -//////////////////////////////////////////////////////////////////////////////// -OpenwireXATransactionsTest::~OpenwireXATransactionsTest() { + std::string branchQualStr = UUID::randomUUID().toString(); + std::string globalTxIdStr = this->txIdGen.generateId(); + + std::vector branchQual( branchQualStr.begin(), branchQualStr.end() ); + std::vector globalTxId( globalTxIdStr.begin(), globalTxIdStr.end() ); + + if( (int)branchQual.size() > Xid::MAXBQUALSIZE ) { + branchQual.resize( Xid::MAXBQUALSIZE ); + } + + if( (int)globalTxId.size() > Xid::MAXGTRIDSIZE ) { + globalTxId.resize( Xid::MAXGTRIDSIZE ); + } + + XATransactionId* id = new XATransactionId(); + + id->setFormatId( 0 ); + id->setBranchQualifier( branchQual ); + id->setGlobalTransactionId( globalTxId ); + + return id; } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testCreateXAConnectionFactory() { +TEST_F(OpenwireXATransactionsTest, testCreateXAConnectionFactory) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -129,7 +137,7 @@ void OpenwireXATransactionsTest::testCreateXAConnectionFactory() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testCreateXAConnection() { +TEST_F(OpenwireXATransactionsTest, testCreateXAConnection) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -149,7 +157,7 @@ void OpenwireXATransactionsTest::testCreateXAConnection() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testCreateXASession() { +TEST_F(OpenwireXATransactionsTest, testCreateXASession) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -172,7 +180,7 @@ void OpenwireXATransactionsTest::testCreateXASession() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testGetXAResource() { +TEST_F(OpenwireXATransactionsTest, testGetXAResource) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -192,7 +200,7 @@ void OpenwireXATransactionsTest::testGetXAResource() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testSendReceiveOutsideTX() { +TEST_F(OpenwireXATransactionsTest, testSendReceiveOutsideTX) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -229,33 +237,7 @@ void OpenwireXATransactionsTest::testSendReceiveOutsideTX() { } //////////////////////////////////////////////////////////////////////////////// -cms::Xid* OpenwireXATransactionsTest::createXid() const { - - std::string branchQualStr = UUID::randomUUID().toString(); - std::string globalTxIdStr = this->txIdGen.generateId(); - - std::vector branchQual( branchQualStr.begin(), branchQualStr.end() ); - std::vector globalTxId( globalTxIdStr.begin(), globalTxIdStr.end() ); - - if( (int)branchQual.size() > Xid::MAXBQUALSIZE ) { - branchQual.resize( Xid::MAXBQUALSIZE ); - } - - if( (int)globalTxId.size() > Xid::MAXGTRIDSIZE ) { - globalTxId.resize( Xid::MAXGTRIDSIZE ); - } - - XATransactionId* id = new XATransactionId(); - - id->setFormatId( 0 ); - id->setBranchQualifier( branchQual ); - id->setGlobalTransactionId( globalTxId ); - - return id; -} - -//////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testSendReceiveTransactedBatches() { +TEST_F(OpenwireXATransactionsTest, testSendReceiveTransactedBatches) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -315,7 +297,7 @@ void OpenwireXATransactionsTest::testSendReceiveTransactedBatches() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testSendRollback() { +TEST_F(OpenwireXATransactionsTest, testSendRollback) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -392,7 +374,7 @@ void OpenwireXATransactionsTest::testSendRollback() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testSendRollbackCommitRollback() { +TEST_F(OpenwireXATransactionsTest, testSendRollbackCommitRollback) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -477,7 +459,7 @@ void OpenwireXATransactionsTest::testSendRollbackCommitRollback() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testWithTTLSet() { +TEST_F(OpenwireXATransactionsTest, testWithTTLSet) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -536,7 +518,7 @@ void OpenwireXATransactionsTest::testWithTTLSet() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testXAResource_Exception1() { +TEST_F(OpenwireXATransactionsTest, testXAResource_Exception1) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -565,7 +547,7 @@ void OpenwireXATransactionsTest::testXAResource_Exception1() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testXAResource_Exception2() { +TEST_F(OpenwireXATransactionsTest, testXAResource_Exception2) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -594,7 +576,7 @@ void OpenwireXATransactionsTest::testXAResource_Exception2() { } //////////////////////////////////////////////////////////////////////////////// -void OpenwireXATransactionsTest::testXAResource_Exception3() { +TEST_F(OpenwireXATransactionsTest, testXAResource_Exception3) { std::unique_ptr factory( XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); @@ -622,19 +604,3 @@ void OpenwireXATransactionsTest::testXAResource_Exception3() { xaResource->forget( ixId.get() ); } - -//////////////////////////////////////////////////////////////////////////////// -// Test registration -TEST_F(OpenwireXATransactionsTest, testCreateXAConnectionFactory) { testCreateXAConnectionFactory(); } -TEST_F(OpenwireXATransactionsTest, testCreateXAConnection) { testCreateXAConnection(); } -TEST_F(OpenwireXATransactionsTest, testCreateXASession) { testCreateXASession(); } -TEST_F(OpenwireXATransactionsTest, testGetXAResource) { testGetXAResource(); } -TEST_F(OpenwireXATransactionsTest, testSendReceiveOutsideTX) { testSendReceiveOutsideTX(); } -TEST_F(OpenwireXATransactionsTest, testSendReceiveTransactedBatches) { testSendReceiveTransactedBatches(); } -TEST_F(OpenwireXATransactionsTest, testSendRollback) { testSendRollback(); } -TEST_F(OpenwireXATransactionsTest, testWithTTLSet) { testWithTTLSet(); } -TEST_F(OpenwireXATransactionsTest, testSendRollbackCommitRollback) { testSendRollbackCommitRollback(); } -TEST_F(OpenwireXATransactionsTest, testXAResource_Exception1) { testXAResource_Exception1(); } -TEST_F(OpenwireXATransactionsTest, testXAResource_Exception2) { testXAResource_Exception2(); } -TEST_F(OpenwireXATransactionsTest, testXAResource_Exception3) { testXAResource_Exception3(); } - diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslAdvisoryTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslAdvisoryTest.cpp new file mode 100644 index 000000000..d47b3ef72 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslAdvisoryTest.cpp @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslAdvisoryTest : public AdvisoryTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +namespace { + + class ConnectionLoadThread : public decaf::lang::Thread { + private: + + cms::ConnectionFactory* factory; + bool noErrors; + std::string errorMessage; + + public: + + ConnectionLoadThread(cms::ConnectionFactory* factory) : + Thread(), factory(factory), noErrors(true), errorMessage() { + } + + virtual ~ConnectionLoadThread() {} + + bool isNoErrors() const { + return this->noErrors; + } + + std::string getErrorMessage() const { + return this->errorMessage; + } + + virtual void run() { + + try { + for (unsigned int i = 0; i < 50; ++i) { + std::unique_ptr connection(factory->createConnection()); + connection->start(); + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + + for (unsigned int j = 0; j < 100; ++j) { + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + } + + decaf::util::concurrent::TimeUnit::MILLISECONDS.sleep(20); + connection->close(); + } + } catch (cms::CMSException& e) { + noErrors = false; + errorMessage = std::string("CMSException: ") + e.what(); + } catch (std::exception& e) { + noErrors = false; + errorMessage = std::string("std::exception: ") + e.what(); + } catch(...) { + noErrors = false; + errorMessage = "Unknown exception"; + } + } + }; + +} + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::lang::exceptions; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::commands; +using namespace activemq::exceptions; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslAdvisoryTest, testConnectionAdvisories) { + + std::unique_ptr factory(ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection(factory->createConnection()); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session(connection->createSession()); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination(session->createTopic("ActiveMQ.Advisory.Connection")); + std::unique_ptr consumer(session->createConsumer(destination.get())); + + connection->start(); + + std::unique_ptr otherConnection(factory->createConnection()); + ASSERT_TRUE(otherConnection.get() != NULL); + otherConnection->start(); + + std::unique_ptr message; + int connectionInfoCount = 0; + + do { + message.reset(consumer->receive(3000)); + + commands::Message* amqMessage = dynamic_cast(message.get()); + if (amqMessage != NULL) { + try { + Pointer connectionInfo = + amqMessage->getDataStructure().dynamicCast(); + + if (connectionInfo != NULL) { + connectionInfoCount++; + } + + } catch (ClassCastException& ex) { + } + } + + } while (message.get() != NULL); + + ASSERT_TRUE(connectionInfoCount >= 2); + + otherConnection->close(); + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslAdvisoryTest, testConcurrentTempDestCreation) { + + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); + + ConnectionLoadThread thread1(factory.get()); + ConnectionLoadThread thread2(factory.get()); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + ASSERT_TRUE(thread1.isNoErrors()) << (std::string("Thread1 error: ") + thread1.getErrorMessage()); + ASSERT_TRUE(thread2.isNoErrors()) << (std::string("Thread2 error: ") + thread2.getErrorMessage()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslAdvisoryTest, testTempDestinationCompositeAdvisoryTopic) { + + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory(getBrokerURL())); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection(factory->createConnection()); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session(connection->createSession()); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr composite( + AdvisorySupport::getTempDestinationCompositeAdvisoryTopic()); + + std::unique_ptr consumer(session->createConsumer(dynamic_cast(composite.get()))); + + connection->start(); + + // Create one of each + std::unique_ptr tempTopic(session->createTemporaryTopic()); + std::unique_ptr tempQueue(session->createTemporaryQueue()); + + // Create a consumer to ensure destination creation based on protocol. + std::unique_ptr tempTopicConsumer(session->createConsumer(tempTopic.get())); + std::unique_ptr tempQueueConsumer(session->createConsumer(tempQueue.get())); + + // Should be an advisory for each + std::unique_ptr advisory1(consumer->receive(2000)); + ASSERT_TRUE(advisory1.get() != NULL); + std::unique_ptr advisory2(consumer->receive(2000)); + ASSERT_TRUE(advisory2.get() != NULL); + + ActiveMQMessage* tempTopicAdvisory = dynamic_cast(advisory1.get()); + ActiveMQMessage* tempQueueAdvisory = dynamic_cast(advisory2.get()); + + // Create one of each + std::unique_ptr topic(session->createTopic(UUID::randomUUID().toString())); + std::unique_ptr queue(session->createQueue(UUID::randomUUID().toString())); + + // Create a producer to ensure destination creation based on protocol. + std::unique_ptr topicProducer(session->createProducer(topic.get())); + std::unique_ptr queueProducer(session->createProducer(queue.get())); + + // Should not be an advisory for each + std::unique_ptr advisory3(consumer->receive(500)); + ASSERT_TRUE(advisory3.get() == NULL); + std::unique_ptr advisory4(consumer->receive(500)); + ASSERT_TRUE(advisory4.get() == NULL); + + connection->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslAsyncSenderTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslAsyncSenderTest.cpp new file mode 100644 index 000000000..a88e3ec75 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslAsyncSenderTest.cpp @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslAsyncSenderTest : public AsyncSenderTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL() + + "&connection.useAsyncSend=true"; + } + }; +}}} + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::core; +using namespace activemq::util; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslAsyncSenderTest, testAsyncSends) { + + try { + + // Create CMS Object for Comms + cms::Session* session( cmsProvider->getSession() ); + + CMSListener listener( session ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener( &listener ); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + std::unique_ptr txtMessage( session->createTextMessage( "TEST MESSAGE" ) ); + std::unique_ptr bytesMessage( session->createBytesMessage() ); + + for( unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i ) { + producer->send( txtMessage.get() ); + } + + for( unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i ) { + producer->send( bytesMessage.get() ); + } + + // Wait for the messages to get here + listener.asyncWaitForMessages( IntegrationCommon::defaultMsgCount * 2 ); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount * 2); + + } catch(...) { + ASSERT_TRUE(false); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslAsyncSenderTest, testOpenwireSslConnector) { + + try{ + + std::unique_ptr connectionFactory( + new ActiveMQConnectionFactory( this->getBrokerURL() ) ); + std::unique_ptr connection( connectionFactory->createConnection() ); + + ActiveMQConnection* amqConnection = + dynamic_cast( connection.get() ); + ASSERT_TRUE(amqConnection != NULL); + + ASSERT_TRUE(amqConnection->isUseAsyncSend()); + ASSERT_TRUE(!amqConnection->isAlwaysSyncSend()); + + connection->start(); + connection->stop(); + + ASSERT_TRUE(true); + } catch(...) { + ASSERT_TRUE(false); + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslClientAckTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslClientAckTest.cpp new file mode 100644 index 000000000..5547f27b9 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslClientAckTest.cpp @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslClientAckTest : public CMSTestFixture { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +#include + +#include + +#include +#include + +#include + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class MyMesageListener: public cms::MessageListener { + private: + + bool dontAck; + + public: + + MyMesageListener( bool dontAck = false ) : MessageListener(), dontAck(dontAck) {} + + virtual ~MyMesageListener() {} + + virtual void onMessage(const Message* message) { + + ASSERT_TRUE(message != NULL); + + if (!dontAck) { + + try { + message->acknowledge(); + } catch (Exception& e) { + e.printStackTrace(); + } + } + } + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslClientAckTest, testAckedMessageAreConsumed) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello")); + producer->send(msg1.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + + // Reset the session-> + session->close(); + session.reset(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslClientAckTest, testLastMessageAcked) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello1")); + std::unique_ptr msg2(session->createTextMessage("Hello2")); + std::unique_ptr msg3(session->createTextMessage("Hello3")); + producer->send(msg1.get()); + producer->send(msg2.get()); + producer->send(msg3.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + + // Reset the session-> + session->close(); + session.reset(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslClientAckTest, testUnAckedMessageAreNotConsumedOnSessionClose) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello")); + producer->send(msg1.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL) << ("Consumer did not get message on first receive()"); + // Don't ack the message. + + // Reset the session-> This should cause the unacknowledged message to be re-delivered. + session->close(); + session.reset(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL) << ("Consumer did not get message on second receive()"); + msg->acknowledge(); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslClientAckTest, testAckedMessageAreConsumedAsync) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + MyMesageListener listener(false); + + std::unique_ptr session(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello")); + producer->send(msg1.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + consumer->setMessageListener(&listener); + + Thread::sleep(5000); + + // Reset the session-> + session->close(); + + session.reset(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslClientAckTest, testUnAckedMessageAreNotConsumedOnSessionCloseAsync) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + // Don't send an ack + MyMesageListener listener(true); + + std::unique_ptr session(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello")); + producer->send(msg1.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + consumer->setMessageListener(&listener); + // Don't ack the message. + + // Reset the session-> This should cause the Unacked message to be redelivered. + session->close(); + + Thread::sleep(5000); + session.reset(connection->createSession(Session::CLIENT_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(2000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + + session->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsConnectionStartStopTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsConnectionStartStopTest.cpp new file mode 100644 index 000000000..38d3ba912 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsConnectionStartStopTest.cpp @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::core; +using namespace activemq::exceptions; + +using namespace decaf; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::lang; + +namespace { + + class CreateSessionRunnable : public Runnable { + private: + + Connection* connection; + Random rand; + CopyOnWriteArrayList* exceptions; + + private: + + CreateSessionRunnable(const CreateSessionRunnable&); + CreateSessionRunnable& operator= (const CreateSessionRunnable&); + + public: + + CreateSessionRunnable(Connection* connection, CopyOnWriteArrayList* exceptions) : + Runnable(), connection(connection), rand(), exceptions(exceptions) { + } + + virtual ~CreateSessionRunnable() {} + + virtual void run() { + try { + TimeUnit::MILLISECONDS.sleep(rand.nextInt(10)); + Pointer(connection->createSession()); + } catch (CMSException& e) { + exceptions->add(e.getMessage()); + } + } + }; + + class StartStopRunnable : public Runnable { + private: + + Connection* connection; + Random rand; + CopyOnWriteArrayList* exceptions; + + private: + + StartStopRunnable(const StartStopRunnable&); + StartStopRunnable& operator= (const StartStopRunnable&); + + public: + + StartStopRunnable(Connection* connection, CopyOnWriteArrayList* exceptions) : + Runnable(), connection(connection), rand(), exceptions(exceptions) { + } + + virtual ~StartStopRunnable() {} + + virtual void run() { + try { + TimeUnit::MILLISECONDS.sleep(rand.nextInt(10)); + connection->start(); + connection->stop(); + } catch (CMSException& e) { + exceptions->add(e.getMessage()); + } + + } + }; + +} + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslCmsConnectionStartStopTest : public CmsConnectionStartStopTest { + public: + std::string getBrokerURL() const override { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; + +}}} + +using activemq::test::openwire_ssl::OpenwireSslCmsConnectionStartStopTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsConnectionStartStopTest, testStoppedConsumerHoldsMessagesTillStarted) { + + Pointer startedSession(startedConnection->createSession()); + Pointer stoppedSession(stoppedConnection->createSession()); + + // Setup the consumers. + Pointer topic(startedSession->createTopic("test")); + Pointer startedConsumer(startedSession->createConsumer(topic.get())); + Pointer stoppedConsumer(stoppedSession->createConsumer(topic.get())); + + // Send the message. + Pointer producer(startedSession->createProducer(topic.get())); + Pointer message(startedSession->createTextMessage("Hello")); + producer->send(message.get()); + + // Test the assertions. + Pointer m(startedConsumer->receive(2000)); + ASSERT_TRUE(m != NULL); + + m.reset(stoppedConsumer->receive(2000)); + ASSERT_TRUE(m == NULL); + + stoppedConnection->start(); + m.reset(stoppedConsumer->receive(5000)); + ASSERT_TRUE(m != NULL); + + startedSession->close(); + stoppedSession->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsConnectionStartStopTest, testMultipleConnectionStops) { + + testStoppedConsumerHoldsMessagesTillStarted(); + stoppedConnection->stop(); + testStoppedConsumerHoldsMessagesTillStarted(); + stoppedConnection->stop(); + testStoppedConsumerHoldsMessagesTillStarted(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsConnectionStartStopTest, testConcurrentSessionCreateWithStart) { + + ThreadPoolExecutor executor(50, Integer::MAX_VALUE, 60LL, TimeUnit::SECONDS, new LinkedBlockingQueue()); + + CopyOnWriteArrayList exceptions; + Random rand; + + for (int i=0; i<2000; i++) { + executor.execute(new CreateSessionRunnable(stoppedConnection.get(), &exceptions)); + executor.execute(new StartStopRunnable(stoppedConnection.get(), &exceptions)); + } + + executor.shutdown(); + ASSERT_TRUE(executor.awaitTermination(45, TimeUnit::SECONDS)) << ("executor terminated"); + ASSERT_TRUE(exceptions.isEmpty()) << ("no exceptions: " + exceptions.toString()); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsSendWithAsyncCallbackTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsSendWithAsyncCallbackTest.cpp new file mode 100644 index 000000000..38bb5673e --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsSendWithAsyncCallbackTest.cpp @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslCmsSendWithAsyncCallbackTest : public CmsSendWithAsyncCallbackTest { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsSendWithAsyncCallbackTest, testAsyncCallbackIsFaster) { + testAsyncCallbackIsFaster(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsTemplateTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsTemplateTest.cpp new file mode 100644 index 000000000..058b75175 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslCmsTemplateTest.cpp @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::util; +using namespace activemq::core; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::lang; + +namespace activemq { +namespace test { + + class TextMessageCreator : public activemq::cmsutil::MessageCreator { + private: + + std::string text; + + public: + + TextMessageCreator(const std::string& text) : + activemq::cmsutil::MessageCreator(), text(text) { + } + + virtual ~TextMessageCreator() { + } + + std::string getText() const { + return text; + } + + virtual cms::Message* createMessage(cms::Session* session) { + + return session->createTextMessage(text); + } + }; + + class Sender: public decaf::lang::Runnable { + private: + + activemq::core::ActiveMQConnectionFactory cf; + activemq::cmsutil::CmsTemplate cmsTemplate; + int count; + + public: + + Sender(const std::string& url, bool pubSub, const std::string& destName, int count) : + decaf::lang::Runnable(), cf(), cmsTemplate(), count(count) { + + cf.setBrokerURI(url); + cmsTemplate.setConnectionFactory(&cf); + cmsTemplate.setPubSubDomain(pubSub); + cmsTemplate.setDefaultDestinationName(destName); + cmsTemplate.setDeliveryPersistent(false); + } + + virtual ~Sender() {} + + virtual void run() { + try { + + // Send a batch of messages. + TextMessageCreator tmc("hello world"); + for (int ix = 0; ix < count; ++ix) { + cmsTemplate.send(&tmc); + } + + } catch (cms::CMSException& ex) { + ex.printStackTrace(); + } + } + }; + + class Receiver: public decaf::lang::Runnable { + private: + + activemq::core::ActiveMQConnectionFactory cf; + activemq::cmsutil::CmsTemplate cmsTemplate; + int count; + int numReceived; + decaf::util::concurrent::CountDownLatch ready; + + public: + + Receiver(const std::string& url, bool pubSub, const std::string& destName, int count) : + decaf::lang::Runnable(), cf(), cmsTemplate(), count(count), numReceived(), ready(1) { + + cf.setBrokerURI(url); + cmsTemplate.setConnectionFactory(&cf); + cmsTemplate.setPubSubDomain(pubSub); + cmsTemplate.setDefaultDestinationName(destName); + cmsTemplate.setDeliveryPersistent(false); + } + + virtual ~Receiver() { + } + + int getNumReceived() const { + return numReceived; + } + + virtual void waitUntilReady() { + ready.await(); + } + + virtual void run() { + + try { + numReceived = 0; + + ready.countDown(); + // Receive a batch of messages. + for (int ix = 0; ix < count; ++ix) { + cms::Message* message = cmsTemplate.receive(); + numReceived++; + delete message; + } + + } catch (cms::CMSException& ex) { + ex.printStackTrace(); + } + } + }; + +}} + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslCmsTemplateTest : public CmsTemplateTest { + public: + std::string getBrokerURL() const override { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; + +}}} + +using activemq::test::openwire_ssl::OpenwireSslCmsTemplateTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsTemplateTest, testBasics) { + + const unsigned int NUM_MESSAGES = IntegrationCommon::defaultMsgCount; + + Receiver receiver(this->getBrokerURL(), false, "testBasics1", NUM_MESSAGES); + Thread rt(&receiver); + rt.start(); + + // Wait for receiver thread to start. + receiver.waitUntilReady(); + + Sender sender(this->getBrokerURL(), false, "testBasics1", NUM_MESSAGES); + Thread st(&sender); + st.start(); + + st.join(); + rt.join(); + + unsigned int numReceived = receiver.getNumReceived(); + ASSERT_TRUE(numReceived == NUM_MESSAGES); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsTemplateTest, testReceiveException) { + + // First, try receiving from a bad url + activemq::core::ActiveMQConnectionFactory cf("tcp://localhost:61666"); + activemq::cmsutil::CmsTemplate cmsTemplate(&cf); + cmsTemplate.setDefaultDestinationName("testReceive1"); + + try { + cmsTemplate.receive(); + FAIL() << ("failed to throw expected exception"); + } catch (CMSException& ex) { + // Expected. + } + + // Now change to a good url and verify that we can reuse the same + // CmsTemplate successfully. + activemq::core::ActiveMQConnectionFactory cf2(this->getBrokerURL()); + cmsTemplate.setConnectionFactory(&cf2); + + // Send 1 message. + Sender sender(this->getBrokerURL(), false, "testReceive1", 1); + Thread st(&sender); + st.start(); + st.join(); + + // Receive the message. + cms::Message* message = cmsTemplate.receive(); + ASSERT_TRUE(message != NULL); + delete message; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslCmsTemplateTest, testSendException) { + + // First, try sending to a bad url. + activemq::core::ActiveMQConnectionFactory cf("tcp://localhost:61666"); + activemq::cmsutil::CmsTemplate cmsTemplate(&cf); + cmsTemplate.setDefaultDestinationName("testSend1"); + try { + TextMessageCreator msgCreator("hello world"); + cmsTemplate.send(&msgCreator); + FAIL() << ("failed to throw expected exception"); + } catch (CMSException& ex) { + // Expected. + } + + // Now change to a good url and verify that we can reuse the same + // CmsTemplate successfully. + activemq::core::ActiveMQConnectionFactory cf2(this->getBrokerURL()); + cmsTemplate.setConnectionFactory(&cf2); + TextMessageCreator msgCreator("hello world"); + cmsTemplate.send(&msgCreator); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslDurableTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslDurableTest.cpp new file mode 100644 index 000000000..46963d64d --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslDurableTest.cpp @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace activemq{ +namespace test{ +namespace openwire_ssl { + class OpenwireSslDurableTest : public DurableTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + virtual std::string getSubscriptionName() const; + }; +}}} + +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::exceptions; +using namespace decaf::util; +using namespace cms; + +//////////////////////////////////////////////////////////////////////////////// +std::string OpenwireSslDurableTest::getSubscriptionName() const { + return UUID::randomUUID().toString(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslDurableTest, testDurableConsumer) { + try { + + // Create CMS Object for Comms + cms::Session* session( cmsProvider->getSession() ); + cmsProvider->setSubscription( this->getSubscriptionName() ); + cmsProvider->setDurable( true ); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + // Send a text message to the consumer while its active + std::unique_ptr txtMessage( session->createTextMessage( "TEST MESSAGE" ) ); + producer->send( txtMessage.get() ); + std::unique_ptr received( consumer->receive( 3000 ) ); + + ASSERT_TRUE(received.get() != NULL); + + cmsProvider->reconnectSession(); + session = cmsProvider->getSession(); + producer = cmsProvider->getProducer(); + + // Send some messages while there is no consumer active. + for( int i = 0; i < MSG_COUNT; ++i ) { + producer->send( txtMessage.get() ); + } + + consumer = cmsProvider->getConsumer(); + + // Send some messages while there is no consumer active. + for( int i = 0; i < MSG_COUNT; ++i ) { + producer->send( txtMessage.get() ); + } + + for( int i = 0; i < MSG_COUNT * 2; i++ ) { + received.reset( consumer->receive( 1000 * 5 ) ); + + ASSERT_TRUE(received.get() != NULL) << ("Failed to receive all messages in batch"); + } + + // Remove the subscription after the consumer is forcibly closed. + cmsProvider->unsubscribe(); + } + catch( ActiveMQException& ex ) { + ASSERT_TRUE(false) << (ex.getStackTraceString()); + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslEnhancedConnectionTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslEnhancedConnectionTest.cpp new file mode 100644 index 000000000..1c74efc2f --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslEnhancedConnectionTest.cpp @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::lang::exceptions; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::commands; +using namespace activemq::exceptions; + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslEnhancedConnectionTest : public ::testing::Test { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; + +}}} + +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class TestDestinationListener : public DestinationListener { + public: + + int queueCount; + int topicCount; + int tempQueueCount; + int tempTopicCount; + + TestDestinationListener() : DestinationListener(), + queueCount(0), + topicCount(0), + tempQueueCount(0), + tempTopicCount(0) { + } + + virtual void onDestinationEvent(cms::DestinationEvent* event) { + + cms::Destination::DestinationType type = event->getDestination()->getDestinationType(); + switch (type) { + case cms::Destination::QUEUE: + if (event->isAddOperation()) { + queueCount++; + } else { + queueCount--; + } + break; + case cms::Destination::TOPIC: + if (event->isAddOperation()) { + topicCount++; + } else { + topicCount--; + } + break; + case cms::Destination::TEMPORARY_QUEUE: + if (event->isAddOperation()) { + tempQueueCount++; + } else { + tempQueueCount--; + } + break; + case cms::Destination::TEMPORARY_TOPIC: + if (event->isAddOperation()) { + tempTopicCount++; + } else { + tempTopicCount--; + } + break; + default: + break; + } + } + + void reset() { + queueCount = 0; + topicCount = 0; + tempQueueCount = 0; + tempTopicCount = 0; + } + }; + +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslEnhancedConnectionTest, testDestinationSource) { + + TestDestinationListener listener; + + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createSession() ); + ASSERT_TRUE(session.get() != NULL); + + ActiveMQConnection* amq = dynamic_cast(connection.get()); + ASSERT_TRUE(amq != NULL); + + cms::EnhancedConnection* enhanced = dynamic_cast(connection.get()); + ASSERT_TRUE(enhanced != NULL); + + std::unique_ptr source(enhanced->getDestinationSource()); + ASSERT_TRUE(source.get() != NULL); + + source->setListener(&listener); + + connection->start(); + source->start(); + + TimeUnit::SECONDS.sleep(2); + + int currTempQueueCount = (int)source->getTemporaryQueues().size(); + int currTempTopicCount = (int)source->getTemporaryTopics().size(); + + std::unique_ptr destination1(session->createTemporaryQueue()); + std::unique_ptr destination2(session->createTemporaryTopic()); + std::unique_ptr destination3(session->createTemporaryQueue()); + std::unique_ptr destination4(session->createTemporaryTopic()); + std::unique_ptr destination5(session->createTemporaryQueue()); + std::unique_ptr destination6(session->createTemporaryTopic()); + + TimeUnit::SECONDS.sleep(2); + + std::vector tempQueues = source->getTemporaryQueues(); + std::vector tempTopics = source->getTemporaryTopics(); + + ASSERT_EQ(currTempQueueCount + 3, (int)tempQueues.size()); + ASSERT_EQ(currTempTopicCount + 3, (int)tempTopics.size()); + + for (int i = 0; i < 3; ++i) { + delete tempQueues[i]; + delete tempTopics[i]; + } + + source->stop(); + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslEnhancedConnectionTest, testDestinationSourceGetters) { + + TestDestinationListener listener; + + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createSession() ); + ASSERT_TRUE(session.get() != NULL); + + ActiveMQConnection* amq = dynamic_cast(connection.get()); + ASSERT_TRUE(amq != NULL); + + cms::EnhancedConnection* enhanced = dynamic_cast(connection.get()); + ASSERT_TRUE(enhanced != NULL); + + std::unique_ptr source(enhanced->getDestinationSource()); + ASSERT_TRUE(source.get() != NULL); + + source->setListener(&listener); + + connection->start(); + source->start(); + + TimeUnit::SECONDS.sleep(2); + + int currentQueueCount = listener.queueCount; + int currentTopicCount = listener.topicCount; + int currentTempQueueCount = listener.tempQueueCount; + int currentTempTopicCount = listener.tempTopicCount; + + std::unique_ptr destination1(session->createTopic(UUID::randomUUID().toString())); + std::unique_ptr consumer1(session->createConsumer(destination1.get())); + std::unique_ptr destination2(session->createQueue(UUID::randomUUID().toString()) ); + std::unique_ptr consumer2(session->createConsumer(destination2.get())); + + consumer1->close(); + consumer2->close(); + + std::unique_ptr destination3( session->createTemporaryQueue() ); + std::unique_ptr destination4( session->createTemporaryTopic() ); + + TimeUnit::SECONDS.sleep(2); + + ASSERT_EQ(currentQueueCount + 1, listener.queueCount) << ("Should be one Queue"); + ASSERT_TRUE(listener.topicCount > currentTopicCount) << ("Should be at least Topic"); + ASSERT_EQ(currentTempQueueCount + 1, listener.tempQueueCount) << ("Should be one temp Queue"); + ASSERT_EQ(currentTempTopicCount + 1, listener.tempTopicCount) << ("Should be one temp Topic"); + + amq->destroyDestination(destination1.get()); + amq->destroyDestination(destination2.get()); + + TimeUnit::SECONDS.sleep(2); + + ASSERT_EQ(currentQueueCount, listener.queueCount) << ("Should be no Queues created by this test"); + + source->stop(); + + std::unique_ptr destination5( session->createTemporaryQueue() ); + std::unique_ptr destination6( session->createTemporaryTopic() ); + + ASSERT_EQ(currentTempQueueCount + 1, listener.tempQueueCount) << ("Temp Queue Counts shouldn't change"); + ASSERT_EQ(currentTempTopicCount + 1, listener.tempTopicCount) << ("Temp Topic Counts shouldn't change"); + + listener.reset(); + source->start(); + + std::unique_ptr destination7( session->createTemporaryQueue() ); + std::unique_ptr destination8( session->createTemporaryTopic() ); + + TimeUnit::SECONDS.sleep(2); + + ASSERT_EQ(currentTempQueueCount + 3, listener.tempQueueCount) << ("Should be three total temp Queues from this test"); + ASSERT_EQ(currentTempTopicCount + 3, listener.tempTopicCount) << ("Should be three total temp Topics from this test"); + + source->stop(); + connection->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslExpirationTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslExpirationTest.cpp new file mode 100644 index 000000000..933d78fdf --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslExpirationTest.cpp @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::util; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + +namespace { + + class Producer: public decaf::lang::Runnable { + private: + + std::unique_ptr cmsProvider; + int numMessages; + long long timeToLive; + bool disableTimeStamps; + + public: + + Producer(const std::string& brokerURL, const std::string& destination, int numMessages, long long timeToLive) : + Runnable(), cmsProvider(), numMessages(numMessages), timeToLive(timeToLive), disableTimeStamps(false) { + + this->cmsProvider.reset(new CMSProvider(brokerURL)); + this->cmsProvider->setDestinationName(destination); + this->cmsProvider->setTopic(false); + } + + virtual ~Producer() { + } + + virtual bool getDisableTimeStamps() const { + return this->disableTimeStamps; + } + + virtual void setDisableTimeStamps(bool value) { + this->disableTimeStamps = value; + } + + virtual void run() { + try { + + cms::Session* session = cmsProvider->getSession(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + producer->setDisableMessageTimeStamp(disableTimeStamps); + + if (!this->disableTimeStamps) { + producer->setTimeToLive(timeToLive); + } + + // Create the Thread Id String + std::ostringstream oss; + oss << Thread::currentThread()->getId(); + string threadIdStr = oss.str(); + + // Create a messages + string text = (string) "Hello world! from thread " + threadIdStr; + + for (int ix = 0; ix < numMessages; ++ix) { + TextMessage* message = session->createTextMessage(text); + producer->send(message); + delete message; + } + + } catch (CMSException& e) { + e.printStackTrace(); + } + } + }; + + class Consumer: public cms::MessageListener, public decaf::lang::Runnable { + private: + + std::unique_ptr cmsProvider; + long initialDelay; + long waitMillis; + int numReceived; + + public: + + Consumer(const std::string& brokerURL, const std::string& destination, long waitMillis) : + Runnable(), cmsProvider(), initialDelay(0), waitMillis(waitMillis), numReceived(0) { + + this->cmsProvider.reset(new CMSProvider(brokerURL)); + this->cmsProvider->setTopic(false); + this->cmsProvider->setDestinationName(destination); + } + + virtual ~Consumer() { + } + + int getNumReceived() const { + return numReceived; + } + + void setInitialDelay(long delay) { + initialDelay = delay; + } + + long getInitialDelay() { + return initialDelay; + } + + virtual void run() { + + try { + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + + if (getInitialDelay() > 0) { + Thread::sleep(getInitialDelay()); + } + + consumer->setMessageListener(this); + + // Sleep while asynchronous messages come in. + Thread::sleep(waitMillis); + + } catch (CMSException& e) { + e.printStackTrace(); + } + } + + virtual void onMessage(const cms::Message* message) { + + try { + const TextMessage* textMessage = dynamic_cast(message); + textMessage->getText(); + numReceived++; + } catch (CMSException& e) { + e.printStackTrace(); + } + } + }; + +} + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslExpirationTest : public ExpirationTest { + public: + std::string getBrokerURL() const override { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; + +}}} + +using activemq::test::openwire_ssl::OpenwireSslExpirationTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslExpirationTest, testExpired) { + + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 1000); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL(), destination, 2000); + consumer.setInitialDelay(1500); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(0, consumer.getNumReceived()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslExpirationTest, testExpiredWithChecksDisabled) { + + { + // Try it once enabled to prove the expiration processing works. + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 1000); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL() + "?connection.consumerExpiryCheckEnabled=true", destination, 2000); + consumer.setInitialDelay(1500); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(0, consumer.getNumReceived()); + } + { + // Now lets try it disabled. + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 1000); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL() + "?connection.consumerExpiryCheckEnabled=false", destination, 2000); + consumer.setInitialDelay(1500); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(2, consumer.getNumReceived()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslExpirationTest, testNotExpired) { + + string destination = UUID::randomUUID().toString(); + Producer producer(this->getBrokerURL(), destination, 2, 2000); + producer.setDisableTimeStamps(true); + Thread producerThread(&producer); + producerThread.start(); + producerThread.join(); + + Consumer consumer(this->getBrokerURL(), destination, 3000); + Thread consumerThread(&consumer); + consumerThread.start(); + consumerThread.join(); + + ASSERT_EQ(2, consumer.getNumReceived()); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslIndividualAckTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslIndividualAckTest.cpp new file mode 100644 index 000000000..9d863c5ac --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslIndividualAckTest.cpp @@ -0,0 +1,324 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslIndividualAckTest : public CMSTestFixture { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +#include + +#include + +#include + +using namespace cms; +using namespace std; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, testAckedMessageAreConsumed) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello")); + producer->send(msg1.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + + // Reset the session. + session->close(); + session.reset(connection->createSession(Session::INDIVIDUAL_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, testLastMessageAcked) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("msg1")); + std::unique_ptr msg2(session->createTextMessage("msg2")); + std::unique_ptr msg3(session->createTextMessage("msg3")); + + producer->send(msg1.get()); + producer->send(msg2.get()); + producer->send(msg3.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + + // Reset the session-> + session->close(); + session.reset(connection->createSession(Session::INDIVIDUAL_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + ASSERT_TRUE(msg1->getText() == dynamic_cast(msg.get())->getText()); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + ASSERT_TRUE(msg2->getText() == dynamic_cast(msg.get())->getText()); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, testUnAckedMessageAreNotConsumedOnSessionClose) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("Hello")); + producer->send(msg1.get()); + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr msg(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + // Don't ack the message. + + // Reset the session-> This should cause the unacknowledged message to be re-delivered. + session->close(); + session.reset(connection->createSession(Session::INDIVIDUAL_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(2000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, testIndividualAcknowledgeMultiMessages_AcknowledgeFirstTest) { + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr msg1(session->createTextMessage("test 1")); + producer->send(msg1.get()); + std::unique_ptr msg2(session->createTextMessage("test 2")); + producer->send(msg2.get()); + + producer->close(); + + std::unique_ptr consumer(session->createConsumer(queue.get())); + + // Read the first message + std::unique_ptr recvMsg1(consumer->receive(2000)); + ASSERT_TRUE(recvMsg1.get() != NULL); + ASSERT_TRUE(msg1->getText() == dynamic_cast(recvMsg1.get())->getText()); + + // Read the second message + std::unique_ptr recvMsg2(consumer->receive(2000)); + ASSERT_TRUE(recvMsg2.get() != NULL); + ASSERT_TRUE(msg2->getText() == dynamic_cast(recvMsg2.get())->getText()); + + // Acknowledge first message + recvMsg1->acknowledge(); + + consumer->close(); + + // Read first message a second time + consumer.reset(session->createConsumer(queue.get())); + std::unique_ptr recvMsg3(consumer->receive(2000)); + ASSERT_TRUE(recvMsg3.get() != NULL); + ASSERT_TRUE(msg2->getText() == dynamic_cast(recvMsg3.get())->getText()); + + // Try to read second message a second time + std::unique_ptr recvMsg4(consumer->receive(2000)); + ASSERT_TRUE(recvMsg4.get() == NULL); + + consumer->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, testManyMessageAckedAfterMessageConsumption) { + int messageCount = 20; + std::unique_ptr msg; + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + for (int i = 0; i < messageCount; i++) { + msg.reset(session->createTextMessage("msg")); + producer->send(msg.get()); + } + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + + for (int i = 0; i < messageCount; i++) { + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() != NULL); + msg->acknowledge(); + } + + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + // Reset the session. + session->close(); + session.reset(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, testManyMessageAckedAfterAllConsumption) { + int messageCount = 20; + std::unique_ptr msg; + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + for (int i = 0; i < messageCount; i++) { + msg.reset(session->createTextMessage("msg")); + producer->send(msg.get()); + } + + // Consume the message... + std::unique_ptr consumer(session->createConsumer(queue.get())); + + std::vector consumedMessages; + + for (int i = 0; i < messageCount; i++) { + Message* message = consumer->receive(1000); + ASSERT_TRUE(message != NULL); + consumedMessages.push_back(message); + } + + for (int i = 0; i < messageCount; i++) { + consumedMessages[i]->acknowledge(); + delete consumedMessages[i]; + } + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + + // Reset the session. + session->close(); + session.reset(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + + // Attempt to Consume the message... + consumer.reset(session->createConsumer(queue.get())); + msg.reset(consumer->receive(1000)); + ASSERT_TRUE(msg.get() == NULL); + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslIndividualAckTest, tesIndividualAcksWithClosedConsumerAndAudit) { + int messageCount = 20; + std::unique_ptr msg; + + std::unique_ptr factory( + ConnectionFactory::createCMSConnectionFactory(std::string("failover:") + getBrokerURL())); + ASSERT_TRUE(factory.get() != NULL); + std::unique_ptr connection(factory->createConnection()); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::INDIVIDUAL_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + for (int i = 0; i < messageCount; i++) { + msg.reset(session->createTextMessage("test message")); + producer->send(msg.get()); + } + + // Consume the messages once but do not ACK them. + std::unique_ptr consumer(session->createConsumer(queue.get())); + + for (int i = 0; i < messageCount; i++) { + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL) << ("First pass consume failed unexpectedly."); + } + + // Consume the messages again, they should all be delivered again. + consumer->close(); + consumer.reset(session->createConsumer(queue.get())); + + for (int i = 0; i < messageCount; i++) { + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL) << ("Second pass consume failed unexpectedly."); + } + + connection->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslJmsMessageGroupsTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslJmsMessageGroupsTest.cpp new file mode 100644 index 000000000..b0bf03321 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslJmsMessageGroupsTest.cpp @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslJmsMessageGroupsTest : public JmsMessageGroupsTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::exceptions; +using namespace cms; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslJmsMessageGroupsTest, testMessageSend) { + + try { + + std::string GROUPID = "TEST-GROUP-ID"; + + // Create CMS Object for Comms + cms::Session* session( cmsProvider->getSession() ); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + std::unique_ptr txtMessage( session->createTextMessage( "TEST MESSAGE" ) ); + txtMessage->setStringProperty( "JMSXGroupID", GROUPID ); + + // Send some text messages + producer->send( txtMessage.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + ASSERT_TRUE(message->getStringProperty( "JMSXGroupID" ) == GROUPID); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslJmsRecoverTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslJmsRecoverTest.cpp new file mode 100644 index 000000000..9321e5bc6 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslJmsRecoverTest.cpp @@ -0,0 +1,333 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::commands; +using namespace activemq::exceptions; + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslJmsRecoverTest : public ::testing::Test { + protected: + + cms::ConnectionFactory* factory = nullptr; + cms::Connection* connection = nullptr; + cms::Destination* destination = nullptr; + + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + + void SetUp() override; + void TearDown() override; + + void doTestSynchRecover(); + void doTestAsynchRecover(); + void doTestAsynchRecoverWithAutoAck(); + + }; + +}}} + +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +void OpenwireSslJmsRecoverTest::SetUp() { + + factory = ConnectionFactory::createCMSConnectionFactory(getBrokerURL()); + ASSERT_TRUE(factory != NULL); + connection = factory->createConnection(); +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenwireSslJmsRecoverTest::TearDown() { + delete factory; + delete connection; + delete destination; +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenwireSslJmsRecoverTest::doTestSynchRecover() { + + std::unique_ptr session(connection->createSession(cms::Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr consumer(session->createConsumer(destination)); + connection->start(); + + std::unique_ptr producer(session->createProducer(destination)); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + producer->send(std::unique_ptr(session->createTextMessage("First")).get()); + producer->send(std::unique_ptr(session->createTextMessage("Second")).get()); + + std::unique_ptr message(dynamic_cast(consumer->receive(2000))); + ASSERT_EQ(string("First"), message->getText()); + ASSERT_TRUE(!message->getCMSRedelivered()); + message->acknowledge(); + + message.reset(dynamic_cast(consumer->receive(2000))); + ASSERT_EQ(string("Second"), message->getText()); + ASSERT_TRUE(!message->getCMSRedelivered()); + + session->recover(); + + message.reset(dynamic_cast(consumer->receive(2000))); + ASSERT_EQ(string("Second"), message->getText()); + ASSERT_TRUE(message->getCMSRedelivered()); + + message->acknowledge(); +} + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class ClientAckMessageListener : public cms::MessageListener { + private: + + cms::Session* session; + std::vector* errorMessages; + CountDownLatch* doneCountDownLatch; + int counter; + + private: + + ClientAckMessageListener(const ClientAckMessageListener&); + ClientAckMessageListener& operator= (const ClientAckMessageListener&); + + public: + + ClientAckMessageListener(cms::Session* session, std::vector* errorMessages, CountDownLatch* doneCountDownLatch) + : session(session), errorMessages(errorMessages), doneCountDownLatch(doneCountDownLatch), counter(0) { + } + + virtual ~ClientAckMessageListener() { + } + + virtual void onMessage(const cms::Message* msg) { + counter++; + try { + const TextMessage* message = dynamic_cast(msg); + switch (counter) { + case 1: + ASSERT_EQ(string("First"), message->getText()); + ASSERT_TRUE(!message->getCMSRedelivered()); + message->acknowledge(); + break; + case 2: + ASSERT_EQ(string("Second"), message->getText()); + ASSERT_TRUE(!message->getCMSRedelivered()); + session->recover(); + break; + case 3: + ASSERT_EQ(string("Second"), message->getText()); + ASSERT_TRUE(message->getCMSRedelivered()); + message->acknowledge(); + doneCountDownLatch->countDown(); + break; + default: + errorMessages->push_back(string("Got too many messages: ") + Long::toString(counter)); + doneCountDownLatch->countDown(); + break; + } + } catch (Exception& e) { + errorMessages->push_back(string("Got exception: ") + e.getMessage()); + doneCountDownLatch->countDown(); + } + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenwireSslJmsRecoverTest::doTestAsynchRecover() { + + std::unique_ptr session(connection->createSession(cms::Session::CLIENT_ACKNOWLEDGE)); + std::vector errorMessages; + CountDownLatch doneCountDownLatch(1); + + std::unique_ptr consumer(session->createConsumer(destination)); + + std::unique_ptr producer(session->createProducer(destination)); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + producer->send(std::unique_ptr(session->createTextMessage("First")).get()); + producer->send(std::unique_ptr(session->createTextMessage("Second")).get()); + + ClientAckMessageListener listener(session.get(), &errorMessages, &doneCountDownLatch); + consumer->setMessageListener(&listener); + + connection->start(); + + if (doneCountDownLatch.await(5, TimeUnit::SECONDS)) { + if (!errorMessages.empty()) { + FAIL() << (errorMessages.front()); + } + } else { + FAIL() << ("Timeout waiting for async message delivery to complete."); + } +} + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class AutoAckMessageListener : public cms::MessageListener { + private: + + cms::Session* session; + std::vector* errorMessages; + CountDownLatch* doneCountDownLatch; + int counter; + + private: + + AutoAckMessageListener(const AutoAckMessageListener&); + AutoAckMessageListener& operator= (const AutoAckMessageListener&); + + public: + + AutoAckMessageListener(cms::Session* session, std::vector* errorMessages, CountDownLatch* doneCountDownLatch) + : session(session), errorMessages(errorMessages), doneCountDownLatch(doneCountDownLatch), counter(0) { + } + + virtual ~AutoAckMessageListener() { + } + + virtual void onMessage(const cms::Message* msg) { + counter++; + try { + const TextMessage* message = dynamic_cast(msg); + switch (counter) { + case 1: + ASSERT_EQ(string("First"), message->getText()); + ASSERT_TRUE(!message->getCMSRedelivered()); + break; + case 2: + ASSERT_EQ(string("Second"), message->getText()); + ASSERT_TRUE(!message->getCMSRedelivered()); + session->recover(); + break; + case 3: + ASSERT_EQ(string("Second"), message->getText()); + ASSERT_TRUE(message->getCMSRedelivered()); + doneCountDownLatch->countDown(); + break; + default: + errorMessages->push_back(string("Got too many messages: ") + Long::toString(counter)); + doneCountDownLatch->countDown(); + break; + } + } catch (Exception& e) { + errorMessages->push_back(string("Got exception: ") + e.getMessage()); + doneCountDownLatch->countDown(); + } + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenwireSslJmsRecoverTest::doTestAsynchRecoverWithAutoAck() { + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::vector errorMessages; + CountDownLatch doneCountDownLatch(1); + + std::unique_ptr consumer(session->createConsumer(destination)); + + std::unique_ptr producer(session->createProducer(destination)); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + producer->send(std::unique_ptr(session->createTextMessage("First")).get()); + producer->send(std::unique_ptr(session->createTextMessage("Second")).get()); + + AutoAckMessageListener listener(session.get(), &errorMessages, &doneCountDownLatch); + consumer->setMessageListener(&listener); + + connection->start(); + + if (doneCountDownLatch.await(5, TimeUnit::SECONDS)) { + if (!errorMessages.empty()) { + FAIL() << (errorMessages.front()); + } + } else { + FAIL() << ("Timeout waiting for async message delivery to complete."); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslJmsRecoverTest, testQueueSynchRecover) { + destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); + doTestSynchRecover(); +} + +TEST_F(OpenwireSslJmsRecoverTest, testQueueAsynchRecover) { + destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecover(); +} + +TEST_F(OpenwireSslJmsRecoverTest, testTopicSynchRecover) { + destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); + doTestSynchRecover(); +} + +TEST_F(OpenwireSslJmsRecoverTest, testTopicAsynchRecover) { + destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecover(); +} + +TEST_F(OpenwireSslJmsRecoverTest, testQueueAsynchRecoverWithAutoAck) { + destination = new ActiveMQQueue(string("Queue-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecoverWithAutoAck(); +} + +TEST_F(OpenwireSslJmsRecoverTest, testTopicAsynchRecoverWithAutoAck) { + destination = new ActiveMQTopic(string("Topic-") + Long::toString(System::currentTimeMillis())); + doTestAsynchRecoverWithAutoAck(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMapMessageTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMapMessageTest.cpp new file mode 100644 index 000000000..493b9110e --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMapMessageTest.cpp @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::commands; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslMapMessageTest : public MapMessageTest { + public: + std::string getBrokerURL() const override { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; + +}}} + +using activemq::test::openwire_ssl::OpenwireSslMapMessageTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMapMessageTest, testEmptyMapSendReceive) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("SomeKey") == false); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMapMessageTest, testMapWithEmptyStringValue) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setString("String1", ""); + mapMessage->setString("String2", "value"); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("String1") == true); + ASSERT_TRUE(recvMapMessage->getString("String1") == ""); + ASSERT_TRUE(recvMapMessage->itemExists("String2") == true); + ASSERT_TRUE(recvMapMessage->itemExists("String3") == false); + ASSERT_TRUE(recvMapMessage->getString("String2") == string("value")); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMapMessageTest, testMapSetEmptyBytesVector) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + std::vector bytes; + + mapMessage->setBytes("BYTES", bytes); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("BYTES") == true); + ASSERT_TRUE(recvMapMessage->getBytes("BYTES").empty() == true); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMapMessageTest, testMapWithSingleCharEntry) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setChar("Char1", 'a'); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("Char1") == true); + ASSERT_TRUE(recvMapMessage->getChar("Char1") == 'a'); + ASSERT_TRUE(recvMapMessage->itemExists("Char2") == false); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMapMessageTest, testMapWithCharAndStringEntry) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setChar("Char1", 'a'); + mapMessage->setString("String1", "string"); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->itemExists("Char1") == true); + ASSERT_TRUE(recvMapMessage->getChar("Char1") == 'a'); + ASSERT_TRUE(recvMapMessage->itemExists("Char2") == false); + ASSERT_TRUE(recvMapMessage->itemExists("String1") == true); + ASSERT_TRUE(recvMapMessage->itemExists("String3") == false); + ASSERT_TRUE(recvMapMessage->getString("String1") == string("string")); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageCompressionTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageCompressionTest.cpp new file mode 100644 index 000000000..2e78b02d9 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageCompressionTest.cpp @@ -0,0 +1,300 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::commands; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + +namespace { + + // The following text should compress well + const string TEXT = std::string() + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. " + "The quick red fox jumped over the lazy brown dog. " + + "The quick red fox jumped over the lazy brown dog. "; + + bool a = true; + unsigned char b = 123; + char c = 'c'; + short d = 0x1234; + int e = 0x12345678; + long long f = 0x1234567812345678LL; + string g = "Hello World!"; + bool h = false; + unsigned char i = 0xFF; + short j = -0x1234; + int k = -0x12345678; + long long l = -0x1234567812345678LL; + float m = 2.1F; + double n = 2.3; +} + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslMessageCompressionTest : public MessageCompressionTest { + public: + std::string getBrokerURL() const override { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL() + + "&connection.useCompression=true"; + } + }; + +}}} + +using activemq::test::openwire_ssl::OpenwireSslMessageCompressionTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageCompressionTest, testTextMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createTextMessage( TEXT ) ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + TextMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a TextMessage"); + + ASSERT_EQ(sent->getText(), recvd->getText()) << ("Received text differs from sent text."); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageCompressionTest, testBytesMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createBytesMessage() ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + sent->writeBoolean( a ); + sent->writeByte( b ); + sent->writeChar( c ); + sent->writeShort( d ); + sent->writeInt( e ); + sent->writeLong( f ); + sent->writeString( g ); + sent->writeBoolean( h ); + sent->writeByte( i ); + sent->writeShort( j ); + sent->writeInt( k ); + sent->writeLong( l ); + sent->writeFloat( m ); + sent->writeDouble( n ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + BytesMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a BytesMessage"); + + ASSERT_EQ(a, recvd->readBoolean()); + ASSERT_EQ(b, recvd->readByte()); + ASSERT_EQ(c, recvd->readChar()); + ASSERT_EQ(d, recvd->readShort()); + ASSERT_EQ(e, recvd->readInt()); + ASSERT_EQ(f, recvd->readLong()); + ASSERT_EQ(g, recvd->readString()); + ASSERT_EQ(h, recvd->readBoolean()); + ASSERT_EQ(i, recvd->readByte()); + ASSERT_EQ(j, recvd->readShort()); + ASSERT_EQ(k, recvd->readInt()); + ASSERT_EQ(l, recvd->readLong()); + ASSERT_EQ(m, recvd->readFloat()); + ASSERT_EQ(n, recvd->readDouble()); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageCompressionTest, testStreamMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createStreamMessage() ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + sent->writeBoolean( a ); + sent->writeByte( b ); + sent->writeChar( c ); + sent->writeShort( d ); + sent->writeInt( e ); + sent->writeLong( f ); + sent->writeString( g ); + sent->writeBoolean( h ); + sent->writeByte( i ); + sent->writeShort( j ); + sent->writeInt( k ); + sent->writeLong( l ); + sent->writeFloat( m ); + sent->writeDouble( n ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + StreamMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a StreamMessage"); + + ASSERT_EQ(a, recvd->readBoolean()); + ASSERT_EQ(b, recvd->readByte()); + ASSERT_EQ(c, recvd->readChar()); + ASSERT_EQ(d, recvd->readShort()); + ASSERT_EQ(e, recvd->readInt()); + ASSERT_EQ(f, recvd->readLong()); + ASSERT_EQ(g, recvd->readString()); + ASSERT_EQ(h, recvd->readBoolean()); + ASSERT_EQ(i, recvd->readByte()); + ASSERT_EQ(j, recvd->readShort()); + ASSERT_EQ(k, recvd->readInt()); + ASSERT_EQ(l, recvd->readLong()); + ASSERT_EQ(m, recvd->readFloat()); + ASSERT_EQ(n, recvd->readDouble()); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageCompressionTest, testMapMessageCompression) { + + ActiveMQConnection* connection = + dynamic_cast( this->cmsProvider->getConnection() ); + + ASSERT_TRUE(connection != NULL); + ASSERT_TRUE(connection->isUseCompression()) << ("Compression not enabled."); + + Session* session = this->cmsProvider->getSession(); + + std::unique_ptr sent( session->createMapMessage() ); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + sent->setBoolean( "a", a ); + sent->setByte( "b", b ); + sent->setChar( "c", c ); + sent->setShort( "d", d ); + sent->setInt( "e", e ); + sent->setLong( "f", f ); + sent->setString( "g", g ); + sent->setBoolean( "h", h ); + sent->setByte( "i", i ); + sent->setShort( "j", j ); + sent->setInt( "k", k ); + sent->setLong( "l", l ); + sent->setFloat( "m", m ); + sent->setDouble( "n", n ); + + // Send some text messages + producer->send( sent.get() ); + + std::unique_ptr message( consumer->receive( 2000 ) ); + ASSERT_TRUE(message.get() != NULL); + + MapMessage* recvd = dynamic_cast( message.get() ); + ASSERT_TRUE(recvd != NULL) << ("Received message was not a MapMessage"); + + ASSERT_EQ(a, recvd->getBoolean( "a" )); + ASSERT_EQ(b, recvd->getByte( "b" )); + ASSERT_EQ(c, recvd->getChar( "c" )); + ASSERT_EQ(d, recvd->getShort( "d" )); + ASSERT_EQ(e, recvd->getInt( "e" )); + ASSERT_EQ(f, recvd->getLong( "f" )); + ASSERT_EQ(g, recvd->getString( "g" )); + ASSERT_EQ(h, recvd->getBoolean( "h" )); + ASSERT_EQ(i, recvd->getByte( "i" )); + ASSERT_EQ(j, recvd->getShort( "j" )); + ASSERT_EQ(k, recvd->getInt( "k" )); + ASSERT_EQ(l, recvd->getLong( "l" )); + ASSERT_EQ(m, recvd->getFloat( "m" )); + ASSERT_EQ(n, recvd->getDouble( "n" )); + + commands::Message* amqMsg = dynamic_cast( message.get() ); + ASSERT_TRUE(amqMsg != NULL) << ("Received message was not an AMQ message type"); + ASSERT_TRUE(amqMsg->isCompressed()) << ("Received message was not compressed."); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageListenerRedeliveryTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageListenerRedeliveryTest.cpp new file mode 100644 index 000000000..80f54f32c --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageListenerRedeliveryTest.cpp @@ -0,0 +1,440 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslMessageListenerRedeliveryTest : public CMSTestFixture { + public: + void SetUp() override {} + void TearDown() override {} + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::lang::exceptions; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::util::concurrent::atomic; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + static std::string DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY = "dlqDeliveryFailureCause"; + + cms::Connection* createConnection(const std::string& brokerUri) { + + ActiveMQConnectionFactory factory(brokerUri); + + factory.getRedeliveryPolicy()->setInitialRedeliveryDelay(0); + factory.getRedeliveryPolicy()->setRedeliveryDelay(1000); + factory.getRedeliveryPolicy()->setMaximumRedeliveries(3); + factory.getRedeliveryPolicy()->setBackOffMultiplier((short) 2); + factory.getRedeliveryPolicy()->setUseExponentialBackOff(true); + + return factory.createConnection(); + } + + class TestMessageListener : public cms::MessageListener { + private: + + int counter; + cms::Session* session; + + public: + + TestMessageListener(cms::Session* session) : counter(0), session(session) {} + + int getCounter() { + return counter; + } + + virtual void onMessage(const cms::Message* message) { + + try { + counter++; + + if (counter <= 4) { + session->rollback(); + } else { + message->acknowledge(); + session->commit(); + } + } catch (CMSException& e) { + } + } + }; + + class ExceptionMessageListener : public cms::MessageListener { + private: + + CountDownLatch doneLatch; + ArrayList received; + std::string testName; + int maxDeliveries; + int count; + + public: + + ExceptionMessageListener(const std::string& testName, int messageCount, int maxDeliveries) : + doneLatch(messageCount), + received(), + testName(testName), + maxDeliveries(maxDeliveries), + count(0) { + } + + int getCount() { + return count; + } + + ArrayList& getReceived() { + return received; + } + + bool await(long long timeout, const TimeUnit& unit) { + return doneLatch.await(timeout, unit); + } + + virtual void onMessage(const cms::Message* message) { + try { + const TextMessage* textMessage = dynamic_cast(message); + received.add(textMessage->getText()); + } catch (cms::CMSException& e) { + e.printStackTrace(); + } + + if (++count < maxDeliveries) { + throw decaf::lang::exceptions::RuntimeException( + __FILE__, __LINE__, testName.append(" forced a redelivery").c_str()); + } + + // new blood + count = 0; + doneLatch.countDown(); + } + }; + + class TrackingMessageListener : public cms::MessageListener { + private: + + CountDownLatch doneLatch; + ArrayList< Pointer > received; + std::string testName; + int count; + + public: + + TrackingMessageListener(const std::string& testName) : + doneLatch(1), + received(), + testName(testName), + count(0) { + } + + TrackingMessageListener(const std::string& testName, int expected) : + doneLatch(expected), + received(), + testName(testName), + count(0) { + } + + int getCount() { + return received.size(); + } + + ArrayList< Pointer >& getReceived() { + return received; + } + + bool await(long long timeout, const TimeUnit& unit) { + return doneLatch.await(timeout, unit); + } + + virtual void onMessage(const cms::Message* message) { + try { + Pointer copy(message->clone()); + received.add(copy); + doneLatch.countDown(); + } catch (cms::CMSException& e) { + e.printStackTrace(); + } + } + }; + + class FailingMessageListener : public cms::MessageListener { + private: + + CountDownLatch doneLatch; + cms::Session* session; + std::string testName; + + public: + + FailingMessageListener(cms::Session* session, const std::string& testName, int expected) : + doneLatch(expected), + session(session), + testName(testName) { + } + + bool await(long long timeout, const TimeUnit& unit) { + return doneLatch.await(timeout, unit); + } + + virtual void onMessage(const cms::Message* message) { + try { + doneLatch.countDown(); + if (session->isTransacted()) { + session->rollback(); + } + } catch (cms::CMSException& e) { + e.printStackTrace(); + } + + throw decaf::lang::exceptions::RuntimeException( + __FILE__, __LINE__, (testName + " forced a redelivery").c_str()); + } + }; + +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageListenerRedeliveryTest, testQueueRollbackConsumerListener) { + + std::unique_ptr connection(createConnection(getBrokerURL())); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testQueueRollbackConsumerListener")); + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr message(session->createTextMessage("Hello")); + + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + amqConnection->destroyDestination(queue.get()); + + producer->setDeliveryMode(cms::DeliveryMode::PERSISTENT); + producer->send(message.get()); + session->commit(); + + std::unique_ptr consumer(session->createConsumer(queue.get())); + + TestMessageListener listener(session.get()); + consumer->setMessageListener(&listener); + + TimeUnit::MILLISECONDS.sleep(500); + + // first try.. should get 2 since there is no delay on the first redelivery. + ASSERT_EQ(2, listener.getCounter()); + + TimeUnit::MILLISECONDS.sleep(1000); + + // 2nd redeliver (redelivery after 1 sec) + ASSERT_EQ(3, listener.getCounter()); + + TimeUnit::MILLISECONDS.sleep(2000); + + // 3rd redeliver (redelivery after 2 seconds) - it should give up after that + ASSERT_EQ(4, listener.getCounter()); + + // create new message + std::unique_ptr secondMessage(session->createTextMessage("Hello 2")); + producer->send(secondMessage.get()); + session->commit(); + + TimeUnit::MILLISECONDS.sleep(500); + + // it should be committed, so no redelivery + ASSERT_EQ(5, listener.getCounter()); + + TimeUnit::MILLISECONDS.sleep(1500); + + // no redelivery, counter should still be 4 + ASSERT_EQ(5, listener.getCounter()); + + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageListenerRedeliveryTest, testQueueSessionListenerExceptionRetry) { + + std::unique_ptr connection(createConnection(getBrokerURL())); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testQueueSessionListenerExceptionRetry")); + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr message1(session->createTextMessage("1")); + std::unique_ptr message2(session->createTextMessage("2")); + + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + amqConnection->destroyDestination(queue.get()); + + producer->setDeliveryMode(cms::DeliveryMode::PERSISTENT); + producer->send(message1.get()); + producer->send(message2.get()); + + std::unique_ptr consumer(session->createConsumer(queue.get())); + + int maxDeliveries = amqConnection->getRedeliveryPolicy()->getMaximumRedeliveries(); + + ExceptionMessageListener listener("testQueueSessionListenerExceptionRetry", 2, maxDeliveries); + consumer->setMessageListener(&listener); + + ASSERT_TRUE(listener.await(30, TimeUnit::SECONDS)) << ("got message before retry expiry"); + + for (int i = 0; i < maxDeliveries; i++) { + ASSERT_EQ(std::string("1"), listener.getReceived().get(i)) << ("got first redelivered: " + Integer::toString(i)); + } + + for (int i = maxDeliveries; i < maxDeliveries * 2; i++) { + ASSERT_EQ(std::string("2"), listener.getReceived().get(i)) << ("got first redelivered: " + Integer::toString(i)); + } + + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageListenerRedeliveryTest, testQueueSessionListenerExceptionDlq) { + + const std::string TEST_NAME = "testQueueSessionListenerExceptionDlq"; + + std::unique_ptr connection(createConnection(getBrokerURL())); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue(TEST_NAME)); + std::unique_ptr dlq(session->createQueue("ActiveMQ.DLQ")); + + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + amqConnection->destroyDestination(queue.get()); + amqConnection->destroyDestination(dlq.get()); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr message(session->createTextMessage("1")); + producer->setDeliveryMode(cms::DeliveryMode::PERSISTENT); + producer->send(message.get()); + + // Track messages going to DLQ + TrackingMessageListener dlqListener(TEST_NAME); + std::unique_ptr dlqConsumer(session->createConsumer(dlq.get())); + dlqConsumer->setMessageListener(&dlqListener); + + // Receive and throw + int maxDeliveries = amqConnection->getRedeliveryPolicy()->getMaximumRedeliveries(); + FailingMessageListener listener(session.get(), TEST_NAME, maxDeliveries); + std::unique_ptr consumer(session->createConsumer(queue.get())); + consumer->setMessageListener(&listener); + + ASSERT_TRUE(listener.await(20, TimeUnit::SECONDS)) << ("got message before retry expiry"); + + // check DLQ + ASSERT_TRUE(dlqListener.await(30, TimeUnit::SECONDS)) << ("got dlq message"); + + // check DLQ message cause is captured + Pointer dlqMessage = dlqListener.getReceived().get(0); + ASSERT_TRUE(dlqMessage != NULL) << ("dlq message captured"); + String cause = dlqMessage->getStringProperty(DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY); + + ASSERT_TRUE(cause.contains("Exception")) << ("cause 'cause' exception is remembered"); + ASSERT_TRUE(cause.contains(TEST_NAME)) << ("is correct exception"); + ASSERT_TRUE(cause.contains("JMSException")) << ("cause exception is remembered"); + ASSERT_TRUE(cause.contains("RedeliveryPolicy")) << ("cause policy is remembered"); + + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageListenerRedeliveryTest, testTransactedQueueSessionListenerExceptionDlq) { + + const std::string TEST_NAME = "testTransactedQueueSessionListenerExceptionDlq"; + + std::unique_ptr connection(createConnection(getBrokerURL())); + connection->start(); + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue(TEST_NAME)); + std::unique_ptr dlq(session->createQueue("ActiveMQ.DLQ")); + + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + amqConnection->destroyDestination(queue.get()); + amqConnection->destroyDestination(dlq.get()); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr message(session->createTextMessage("1")); + producer->setDeliveryMode(cms::DeliveryMode::PERSISTENT); + producer->send(message.get()); + session->commit(); + + // Track messages going to DLQ + TrackingMessageListener dlqListener(TEST_NAME); + std::unique_ptr dlqConsumer(session->createConsumer(dlq.get())); + dlqConsumer->setMessageListener(&dlqListener); + + // Receive and throw + int maxDeliveries = amqConnection->getRedeliveryPolicy()->getMaximumRedeliveries(); + FailingMessageListener listener(session.get(), TEST_NAME, maxDeliveries); + std::unique_ptr consumer(session->createConsumer(queue.get())); + consumer->setMessageListener(&listener); + + ASSERT_TRUE(listener.await(20, TimeUnit::SECONDS)) << ("got message before retry expiry"); + + // check DLQ + ASSERT_TRUE(dlqListener.await(30, TimeUnit::SECONDS)) << ("got dlq message"); + + // check DLQ message cause is captured + Pointer dlqMessage = dlqListener.getReceived().get(0); + ASSERT_TRUE(dlqMessage != NULL) << ("dlq message captured"); + String cause = dlqMessage->getStringProperty(DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY); + + ASSERT_TRUE(cause.contains("Exception")) << ("cause 'cause' exception is remembered"); + ASSERT_TRUE(cause.contains(TEST_NAME)) << ("is correct exception"); + ASSERT_TRUE(cause.contains("JMSException")) << ("cause exception is remembered"); + ASSERT_TRUE(cause.contains("RedeliveryPolicy")) << ("cause policy is remembered"); + + connection->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessagePriorityTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessagePriorityTest.cpp new file mode 100644 index 000000000..b4e3188e0 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessagePriorityTest.cpp @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace { + + class ProducerThread : public decaf::lang::Thread { + private: + + cms::Session* session; + cms::Destination* destination; + int num; + int priority; + + private: + + ProducerThread(const ProducerThread&); + ProducerThread& operator= (const ProducerThread&); + + public: + + ProducerThread(cms::Session* session, cms::Destination* destination, int num, int priority) : + session(session), destination(destination), num(num), priority(priority) { + } + + virtual ~ProducerThread() {} + + virtual void run() { + + decaf::lang::Pointer producer(session->createProducer(destination)); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + producer->setPriority(priority); + + for (int i = 0; i < num; ++i) { + decaf::lang::Pointer message(session->createTextMessage("Test Message")); + producer->send(message.get()); + } + } + }; +} + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslMessagePriorityTest : public MessagePriorityTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace cms; +using namespace decaf::lang; +using namespace decaf::util; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessagePriorityTest, testMessagePrioritySendReceive) { + + const int MSG_COUNT = 25; + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + connectionFactory->setMessagePrioritySupported(true); + + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + connection->start(); + + ProducerThread thread1(session.get(), destination.get(), MSG_COUNT, 9); + ProducerThread thread2(session.get(), destination.get(), MSG_COUNT, 1); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + Thread::sleep(3000); + + for (int i = 0; i < MSG_COUNT * 2; ++i) { + Pointer message(consumer->receive(2000)); + ASSERT_TRUE(message != NULL); + ASSERT_TRUE(message->getCMSPriority() == (i < MSG_COUNT ? 9 : 1)); + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageSelectorTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageSelectorTest.cpp new file mode 100644 index 000000000..0122593fa --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslMessageSelectorTest.cpp @@ -0,0 +1,439 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::test; + +namespace activemq { +namespace test { +namespace openwire_ssl { + /** + * Tests the OpenwireSsl message selector feature. + * Message selectors use SQL92-like syntax to filter messages + * based on message properties and headers. + */ + class OpenwireSslMessageSelectorTest : public CMSTestFixture { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testStringPropertySelector) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer with string selector + std::unique_ptr consumer( + session->createConsumer(queue.get(), "color = 'red'")); + + // Send messages with different property values + std::unique_ptr redMsg(session->createTextMessage("Red message")); + redMsg->setStringProperty("color", "red"); + producer->send(redMsg.get()); + + std::unique_ptr blueMsg(session->createTextMessage("Blue message")); + blueMsg->setStringProperty("color", "blue"); + producer->send(blueMsg.get()); + + std::unique_ptr redMsg2(session->createTextMessage("Red message 2")); + redMsg2->setStringProperty("color", "red"); + producer->send(redMsg2.get()); + + // Consumer should only receive red messages + std::unique_ptr msg1(consumer->receive(1000)); + ASSERT_TRUE(msg1.get() != NULL); + ASSERT_EQ(string("red"), msg1->getStringProperty("color")); + + std::unique_ptr msg2(consumer->receive(1000)); + ASSERT_TRUE(msg2.get() != NULL); + ASSERT_EQ(string("red"), msg2->getStringProperty("color")); + + // No more messages should match + std::unique_ptr msg3(consumer->receive(500)); + ASSERT_TRUE(msg3.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testIntPropertySelector) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer with numeric selector + std::unique_ptr consumer( + session->createConsumer(queue.get(), "quantity > 10")); + + // Send messages with different quantities + for (int i = 5; i <= 20; i += 5) { + std::unique_ptr msg(session->createTextMessage("Quantity: " + std::to_string(i))); + msg->setIntProperty("quantity", i); + producer->send(msg.get()); + } + + // Should receive only messages where quantity > 10 (15 and 20) + std::unique_ptr msg1(consumer->receive(1000)); + ASSERT_TRUE(msg1.get() != NULL); + ASSERT_TRUE(msg1->getIntProperty("quantity") > 10); + + std::unique_ptr msg2(consumer->receive(1000)); + ASSERT_TRUE(msg2.get() != NULL); + ASSERT_TRUE(msg2->getIntProperty("quantity") > 10); + + std::unique_ptr msg3(consumer->receive(500)); + ASSERT_TRUE(msg3.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testBooleanPropertySelector) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer that only accepts urgent messages + std::unique_ptr consumer( + session->createConsumer(queue.get(), "urgent = TRUE")); + + // Send mix of urgent and non-urgent messages + std::unique_ptr urgentMsg(session->createTextMessage("Urgent!")); + urgentMsg->setBooleanProperty("urgent", true); + producer->send(urgentMsg.get()); + + std::unique_ptr normalMsg(session->createTextMessage("Normal")); + normalMsg->setBooleanProperty("urgent", false); + producer->send(normalMsg.get()); + + // Should only receive urgent message + std::unique_ptr msg1(consumer->receive(1000)); + ASSERT_TRUE(msg1.get() != NULL); + ASSERT_TRUE(msg1->getBooleanProperty("urgent") == true); + + std::unique_ptr msg2(consumer->receive(500)); + ASSERT_TRUE(msg2.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testCompoundSelector) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer with compound selector + std::unique_ptr consumer( + session->createConsumer(queue.get(), "color = 'red' AND size > 5")); + + // Send various combinations + std::unique_ptr msg1(session->createTextMessage("Red big")); + msg1->setStringProperty("color", "red"); + msg1->setIntProperty("size", 10); + producer->send(msg1.get()); + + std::unique_ptr msg2(session->createTextMessage("Red small")); + msg2->setStringProperty("color", "red"); + msg2->setIntProperty("size", 3); + producer->send(msg2.get()); + + std::unique_ptr msg3(session->createTextMessage("Blue big")); + msg3->setStringProperty("color", "blue"); + msg3->setIntProperty("size", 10); + producer->send(msg3.get()); + + // Should only receive red AND big message + std::unique_ptr received(consumer->receive(1000)); + ASSERT_TRUE(received.get() != NULL); + ASSERT_EQ(string("red"), received->getStringProperty("color")); + ASSERT_TRUE(received->getIntProperty("size") > 5); + + std::unique_ptr noMore(consumer->receive(500)); + ASSERT_TRUE(noMore.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testSelectorWithLike) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer with LIKE selector + std::unique_ptr consumer( + session->createConsumer(queue.get(), "name LIKE 'test%'")); + + // Send messages + std::unique_ptr msg1(session->createTextMessage("Match 1")); + msg1->setStringProperty("name", "test_message"); + producer->send(msg1.get()); + + std::unique_ptr msg2(session->createTextMessage("Match 2")); + msg2->setStringProperty("name", "testing123"); + producer->send(msg2.get()); + + std::unique_ptr msg3(session->createTextMessage("No match")); + msg3->setStringProperty("name", "other"); + producer->send(msg3.get()); + + // Should receive two messages matching the pattern + std::unique_ptr received1(consumer->receive(1000)); + ASSERT_TRUE(received1.get() != NULL); + + std::unique_ptr received2(consumer->receive(1000)); + ASSERT_TRUE(received2.get() != NULL); + + std::unique_ptr noMore(consumer->receive(500)); + ASSERT_TRUE(noMore.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testSelectorWithIn) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer with IN selector + std::unique_ptr consumer( + session->createConsumer(queue.get(), "status IN ('pending', 'active')")); + + // Send messages with different statuses + std::unique_ptr msg1(session->createTextMessage("Pending")); + msg1->setStringProperty("status", "pending"); + producer->send(msg1.get()); + + std::unique_ptr msg2(session->createTextMessage("Active")); + msg2->setStringProperty("status", "active"); + producer->send(msg2.get()); + + std::unique_ptr msg3(session->createTextMessage("Complete")); + msg3->setStringProperty("status", "complete"); + producer->send(msg3.get()); + + // Should receive pending and active messages + int count = 0; + std::unique_ptr received; + while ((received.reset(consumer->receive(500)), received.get() != NULL)) { + count++; + string status = received->getStringProperty("status"); + ASSERT_TRUE(status == "pending" || status == "active"); + } + ASSERT_EQ(2, count); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testSelectorWithBetween) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer with BETWEEN selector + std::unique_ptr consumer( + session->createConsumer(queue.get(), "price BETWEEN 10 AND 20")); + + // Send messages with different prices + for (int price = 5; price <= 25; price += 5) { + std::unique_ptr msg(session->createTextMessage("Price: " + std::to_string(price))); + msg->setIntProperty("price", price); + producer->send(msg.get()); + } + + // Should receive messages with price 10, 15, 20 + int count = 0; + std::unique_ptr received; + while ((received.reset(consumer->receive(500)), received.get() != NULL)) { + count++; + int price = received->getIntProperty("price"); + ASSERT_TRUE(price >= 10 && price <= 20); + } + ASSERT_EQ(3, count); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testSelectorWithIsNull) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer that selects messages where optional property is null + std::unique_ptr consumer( + session->createConsumer(queue.get(), "optional IS NULL")); + + // Send message without the property + std::unique_ptr msg1(session->createTextMessage("No optional")); + producer->send(msg1.get()); + + // Send message with the property + std::unique_ptr msg2(session->createTextMessage("Has optional")); + msg2->setStringProperty("optional", "value"); + producer->send(msg2.get()); + + // Should only receive message without optional property + std::unique_ptr received(consumer->receive(1000)); + ASSERT_TRUE(received.get() != NULL); + ASSERT_TRUE(!received->propertyExists("optional")); + + std::unique_ptr noMore(consumer->receive(500)); + ASSERT_TRUE(noMore.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testJMSTypeSelector) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer that selects by JMSType + std::unique_ptr consumer( + session->createConsumer(queue.get(), "JMSType = 'order'")); + + // Send messages with different types + std::unique_ptr orderMsg(session->createTextMessage("Order message")); + orderMsg->setCMSType("order"); + producer->send(orderMsg.get()); + + std::unique_ptr invoiceMsg(session->createTextMessage("Invoice message")); + invoiceMsg->setCMSType("invoice"); + producer->send(invoiceMsg.get()); + + // Should only receive order message + std::unique_ptr received(consumer->receive(1000)); + ASSERT_TRUE(received.get() != NULL); + ASSERT_EQ(string("order"), received->getCMSType()); + + std::unique_ptr noMore(consumer->receive(500)); + ASSERT_TRUE(noMore.get() == NULL); + + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslMessageSelectorTest, testJMSPrioritySelector) { + + Connection* connection = this->cmsProvider->getConnection(); + connection->start(); + + std::unique_ptr session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + // Create consumer that selects high priority messages + std::unique_ptr consumer( + session->createConsumer(queue.get(), "JMSPriority >= 7")); + + // Send messages with different priorities + std::unique_ptr highPriority(session->createTextMessage("High priority")); + producer->send(highPriority.get(), DeliveryMode::PERSISTENT, 9, 0); + + std::unique_ptr normalPriority(session->createTextMessage("Normal priority")); + producer->send(normalPriority.get(), DeliveryMode::PERSISTENT, 4, 0); + + std::unique_ptr mediumPriority(session->createTextMessage("Medium priority")); + producer->send(mediumPriority.get(), DeliveryMode::PERSISTENT, 7, 0); + + // Should receive high and medium priority messages (priority >= 7) + int count = 0; + std::unique_ptr received; + while ((received.reset(consumer->receive(500)), received.get() != NULL)) { + count++; + ASSERT_TRUE(received->getCMSPriority() >= 7); + } + ASSERT_EQ(2, count); + + session->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslNonBlockingRedeliveryTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslNonBlockingRedeliveryTest.cpp new file mode 100644 index 000000000..d85338646 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslNonBlockingRedeliveryTest.cpp @@ -0,0 +1,654 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslNonBlockingRedeliveryTest : public CMSTestFixture { + public: + void SetUp() override {} + void TearDown() override {} + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL() + + "?connection.nonBlockingRedelivery=true"; + } + }; +}}} + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::commands; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::util::concurrent::atomic; + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + void sendMessages(const std::string& uri, const std::string destinationName, int count) { + Pointer connectionFactory(new ActiveMQConnectionFactory(uri)); + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createQueue(destinationName)); + Pointer producer(session->createProducer(destination.get())); + for(int i = 0; i < count; ++i) { + Pointer message(session->createTextMessage()); + producer->send(message.get()); + } + connection->close(); + } + + void destroyDestination(const std::string& uri, const std::string destinationName) { + Pointer connectionFactory(new ActiveMQConnectionFactory(uri)); + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createQueue(destinationName)); + Pointer amqCon = connection.dynamicCast(); + amqCon->destroyDestination(destination.get()); + connection->close(); + } + + bool assertTrue(LinkedHashSet< Pointer >& set, int expected) { + for (int i = 0; i <= 60; ++i) { + if (set.size() == expected) { + return true; + } + + Thread::sleep(1000); + } + + return false; + } + + class TestProducer : public Thread { + private: + + std::string destinationName; + std::string brokerUri; + int produceMessages; + + public: + + TestProducer(const std::string& brokerUri, + const std::string& destinationName, + int produceMessages) : Thread(), + destinationName(destinationName), + brokerUri(brokerUri), + produceMessages(produceMessages) { + } + + void run() { + + Pointer connectionFactory; + Pointer connection; + Pointer session; + Pointer destination; + + try { + + connectionFactory.reset(new ActiveMQConnectionFactory(brokerUri)); + connection.reset(connectionFactory->createConnection()); + connection->start(); + session.reset(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + + destination.reset(session->createQueue(destinationName)); + + // Create a MessageProducer from the Session to the Topic or Queue + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + for (int i = 0; i < produceMessages; i++) { + Pointer message(session->createTextMessage()); + message->setLongProperty("TestTime", System::currentTimeMillis()); + try { + producer->send(message.get()); + } catch (Exception& deeperException) { + } + + Thread::sleep(50); + } + } catch (Exception& e) { + } + + try { + if (connection != NULL) { + connection->close(); + } + } catch (Exception& e) { + } + } + }; + + class TestConsumer : public Thread, public MessageListener { + private: + + std::string brokerUri; + std::string destinationName; + CountDownLatch totalMessages; + int expected; + int receivedCount; + bool rolledBack; + bool failed; + LinkedList* messages; + Pointer connectionFactory; + Pointer connection; + Pointer session; + Pointer consumer; + + public: + + TestConsumer(const std::string& brokerUri, + const std::string& destinationName, + LinkedList* messages, + int totalMessages) : Thread(), + brokerUri(brokerUri), + destinationName(destinationName), + totalMessages(totalMessages), + expected(totalMessages), + receivedCount(0), + rolledBack(false), + failed(false), + messages(messages), + connectionFactory(), + connection(), + session(), + consumer() { + } + + bool isFailed() const { + return this->failed; + } + + virtual void run() { + try { + + connectionFactory.reset(new ActiveMQConnectionFactory(brokerUri)); + connection.reset(connectionFactory->createConnection()); + session.reset(connection->createSession(Session::SESSION_TRANSACTED)); + + Pointer amqCon = connection.dynamicCast(); + + RedeliveryPolicy* policy = amqCon->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(1000); + policy->setBackOffMultiplier(-1); + policy->setRedeliveryDelay(1000); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(10); + + Pointer destination(session->createQueue(destinationName)); + consumer.reset(session->createConsumer(destination.get())); + consumer->setMessageListener(this); + + connection->start(); + + if (!totalMessages.await(10, TimeUnit::MINUTES)) { + this->failed = true; + } + + } catch (Exception& e) { + } + try { + if (connection != NULL) { + connection->close(); + } + } catch (Exception& e) { + } + } + + virtual void onMessage(const cms::Message* message) { + receivedCount++; + + try { + + const commands::Message* amqMessage = + dynamic_cast(message); + + if (!rolledBack) { + if (++receivedCount == expected / 2) { + rolledBack = true; + session->rollback(); + } + } else { + Pointer msgId = amqMessage->getMessageId(); + messages->add((int)msgId->getProducerSequenceId()); + session->commit(); + totalMessages.countDown(); + } + + } catch (Exception& ex) { + this->failed = true; + } + } + }; + + class ReceivedListener : public cms::MessageListener { + private: + + LinkedHashSet< Pointer >* received; + + public: + + ReceivedListener(LinkedHashSet< Pointer >* received) : + cms::MessageListener(), received(received) { + } + + virtual ~ReceivedListener() { + } + + virtual void onMessage(const cms::Message* message) { + const commands::Message* amqMessage = + dynamic_cast(message); + + received->add(amqMessage->getMessageId()); + } + + }; + + class SomeRollbacksListener : public cms::MessageListener { + private: + + int count; + Pointer session; + LinkedHashSet< Pointer >* received; + + public: + + SomeRollbacksListener(Pointer session, LinkedHashSet< Pointer >* received) : + cms::MessageListener(), count(0), session(session), received(received) { + } + + virtual ~SomeRollbacksListener() {} + + virtual void onMessage(const cms::Message* message) { + const commands::Message* amqMessage = + dynamic_cast(message); + + if (++count > 10) { + try { + session->rollback(); + count = 0; + } catch (CMSException& e) { + } + } else { + received->add(amqMessage->getMessageId()); + try { + session->commit(); + } catch (CMSException& e) { + } + } + } + + }; + + class RollbacksListener : public cms::MessageListener { + private: + + Pointer session; + + public: + + RollbacksListener(Pointer session) : + cms::MessageListener(), session(session) { + } + + virtual ~RollbacksListener() { + } + + virtual void onMessage(const cms::Message* message) { + try { + session->rollback(); + } catch (CMSException& e) { + } + } + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testConsumerMessagesAreNotOrdered) { + + LinkedList messages; + + const std::string DEST_NAME = "QUEUE.FOO"; + + destroyDestination(getBrokerURL(), DEST_NAME); + + TestProducer producer(getBrokerURL(), DEST_NAME, 100); + TestConsumer consumer(getBrokerURL(), DEST_NAME, &messages, 100); + + producer.start(); + consumer.start(); + + producer.join(); + consumer.join(); + + ASSERT_TRUE(!consumer.isFailed()); + + bool ordered = true; + int lastId = 0; + Pointer > sequenceIds(messages.iterator()); + while (sequenceIds->hasNext()) { + int id = sequenceIds->next(); + if (id != (lastId + 1)) { + ordered = false; + } + + lastId = id; + } + + ASSERT_TRUE(!ordered); + destroyDestination(getBrokerURL(), DEST_NAME); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testMessageDeleiveredWhenNonBlockingEnabled) { + + LinkedHashSet< Pointer > received; + LinkedHashSet< Pointer > beforeRollback; + LinkedHashSet< Pointer > afterRollback; + + const int MSG_COUNT = 100; + const std::string destinationName = "testMessageDeleiveredWhenNonBlockingEnabled"; + + destroyDestination(getBrokerURL(), destinationName); + + Pointer connectionFactory(new ActiveMQConnectionFactory(getBrokerURL())); + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue(destinationName)); + Pointer consumer(session->createConsumer(destination.get())); + + ReceivedListener receivedListener(&received); + consumer->setMessageListener(&receivedListener); + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + + connection->start(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Pre-Rollack received size incorrect"); + + beforeRollback.addAll(received); + received.clear(); + session->rollback(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Post-Rollack received size incorrect"); + + afterRollback.addAll(received); + received.clear(); + + ASSERT_EQ(beforeRollback.size(), afterRollback.size()); + ASSERT_TRUE(beforeRollback.equals(afterRollback)); + session->commit(); + connection->close(); + destroyDestination(getBrokerURL(), destinationName); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testMessageRedeliveriesAreInOrder) { + + LinkedHashSet< Pointer > received; + LinkedHashSet< Pointer > beforeRollback; + LinkedHashSet< Pointer > afterRollback; + + const int MSG_COUNT = 100; + const std::string destinationName = "testMessageDeleiveredWhenNonBlockingEnabled"; + + destroyDestination(getBrokerURL(), destinationName); + + Pointer connectionFactory(new ActiveMQConnectionFactory(getBrokerURL())); + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue(destinationName)); + Pointer consumer(session->createConsumer(destination.get())); + + ReceivedListener receivedListener(&received); + consumer->setMessageListener(&receivedListener); + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + + connection->start(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Pre-Rollack received size incorrect"); + + beforeRollback.addAll(received); + received.clear(); + session->rollback(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Post-Rollack received size incorrect"); + + afterRollback.addAll(received); + received.clear(); + + ASSERT_EQ(beforeRollback.size(), afterRollback.size()); + ASSERT_TRUE(beforeRollback.equals(afterRollback)); + + Pointer< Iterator > > after(afterRollback.iterator()); + Pointer< Iterator > > before(beforeRollback.iterator()); + + while (before->hasNext() && after->hasNext()) { + Pointer original = before->next(); + Pointer rolledBack = after->next(); + + long long originalSeq = original->getProducerSequenceId(); + long long rolledbackSeq = rolledBack->getProducerSequenceId(); + + ASSERT_EQ(originalSeq, rolledbackSeq); + } + + session->commit(); + connection->close(); + destroyDestination(getBrokerURL(), destinationName); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testMessageDeleiveryDoesntStop) { + + LinkedHashSet< Pointer > received; + LinkedHashSet< Pointer > beforeRollback; + LinkedHashSet< Pointer > afterRollback; + + const int MSG_COUNT = 100; + const std::string destinationName = "testMessageDeleiveryDoesntStop"; + + destroyDestination(getBrokerURL(), destinationName); + + Pointer connectionFactory(new ActiveMQConnectionFactory(getBrokerURL())); + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue(destinationName)); + Pointer consumer(session->createConsumer(destination.get())); + + ReceivedListener receivedListener(&received); + consumer->setMessageListener(&receivedListener); + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + + connection->start(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Pre-Rollack received size incorrect"); + + beforeRollback.addAll(received); + received.clear(); + session->rollback(); + + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT * 2)) << ("Post-Rollack received size incorrect"); + + afterRollback.addAll(received); + received.clear(); + + ASSERT_EQ(beforeRollback.size() * 2, afterRollback.size()); + session->commit(); + connection->close(); + + destroyDestination(getBrokerURL(), destinationName); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryIsDelayed) { + + LinkedHashSet< Pointer > received; + + const int MSG_COUNT = 100; + const std::string destinationName = "testNonBlockingMessageDeleiveryIsDelayed"; + + destroyDestination(getBrokerURL(), destinationName); + + Pointer connectionFactory(new ActiveMQConnectionFactory(getBrokerURL())); + connectionFactory->getRedeliveryPolicy()->setInitialRedeliveryDelay(TimeUnit::SECONDS.toMillis(10)); + + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue(destinationName)); + Pointer consumer(session->createConsumer(destination.get())); + + ReceivedListener receivedListener(&received); + consumer->setMessageListener(&receivedListener); + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + + connection->start(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Pre-Rollack received size incorrect"); + + received.clear(); + session->rollback(); + + TimeUnit::SECONDS.sleep(6); + ASSERT_TRUE(received.isEmpty()) << ("Rollback redelivery was not delayed."); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Post-Rollack received size incorrect"); + + session->commit(); + connection->close(); + + destroyDestination(getBrokerURL(), destinationName); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryWithRollbacks) { + + LinkedHashSet< Pointer > received; + + const int MSG_COUNT = 100; + const std::string destinationName = "testNonBlockingMessageDeleiveryWithRollbacks"; + + destroyDestination(getBrokerURL(), destinationName); + + Pointer connectionFactory(new ActiveMQConnectionFactory(getBrokerURL())); + connectionFactory->getRedeliveryPolicy()->setInitialRedeliveryDelay(TimeUnit::SECONDS.toMillis(10)); + + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue(destinationName)); + Pointer consumer(session->createConsumer(destination.get())); + + ReceivedListener receivedListener(&received); + consumer->setMessageListener(&receivedListener); + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + + connection->start(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Pre-Rollack received size incorrect"); + + received.clear(); + + SomeRollbacksListener newListener(session, &received); + consumer->setMessageListener(&newListener); + + session->rollback(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Post-Rollack received size incorrect"); + + session->commit(); + connection->close(); + + destroyDestination(getBrokerURL(), destinationName); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslNonBlockingRedeliveryTest, testNonBlockingMessageDeleiveryWithAllRolledBack) { + + LinkedHashSet< Pointer > received; + LinkedHashSet< Pointer > dlqed; + + const int MSG_COUNT = 100; + const std::string destinationName = "testNonBlockingMessageDeleiveryWithAllRolledBack"; + + destroyDestination(getBrokerURL(), destinationName); + destroyDestination(getBrokerURL(), "ActiveMQ.DLQ"); + + Pointer connectionFactory(new ActiveMQConnectionFactory(getBrokerURL())); + connectionFactory->getRedeliveryPolicy()->setMaximumRedeliveries(5); + connectionFactory->getRedeliveryPolicy()->setInitialRedeliveryDelay(TimeUnit::SECONDS.toMillis(5)); + + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue(destinationName)); + Pointer dlq(session->createQueue("ActiveMQ.DLQ")); + Pointer consumer(session->createConsumer(destination.get())); + Pointer dlqConsumer(session->createConsumer(dlq.get())); + + ReceivedListener dlqReceivedListener(&dlqed); + dlqConsumer->setMessageListener(&dlqReceivedListener); + + ReceivedListener receivedListener(&received); + consumer->setMessageListener(&receivedListener); + + sendMessages(getBrokerURL(), destinationName, MSG_COUNT); + connection->start(); + + ASSERT_TRUE(assertTrue(received, MSG_COUNT)) << ("Pre-Rollack received size incorrect"); + + session->rollback(); + + RollbacksListener rollbackListener(session); + consumer->setMessageListener(&rollbackListener); + + ASSERT_TRUE(assertTrue(dlqed, MSG_COUNT)) << ("Post-Rollack DQL size incorrect"); + + session->commit(); + connection->close(); + + destroyDestination(getBrokerURL(), destinationName); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslOptimizedAckTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslOptimizedAckTest.cpp new file mode 100644 index 000000000..63534e521 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslOptimizedAckTest.cpp @@ -0,0 +1,259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslOptimizedAckTest : public CMSTestFixture { + public: + void SetUp() override {} + void TearDown() override {} + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL() + + "?connection.optimizeAcknowledge=true&cms.prefetchPolicy.all=100"; + } + }; +}}} + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::util::concurrent::atomic; + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class MyMessageListener : public cms::MessageListener { + private: + + AtomicInteger counter; + + public: + + virtual ~MyMessageListener() {} + + virtual void onMessage(const cms::Message* message) { + counter.incrementAndGet(); + } + + int getCounter() { + return counter.get(); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslOptimizedAckTest, testOptimizedAckSettings) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + connectionFactory->setOptimizeAcknowledgeTimeOut(500); + connectionFactory->setOptimizedAckScheduledAckInterval(1000); + + ASSERT_EQ(100, connectionFactory->getPrefetchPolicy()->getQueuePrefetch()); + + Pointer connection(connectionFactory->createConnection()); + connection->start(); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createQueue("TEST.FOO")); + + Pointer consumer(session->createConsumer(destination.get())); + + Pointer amqConsumer = consumer.dynamicCast(); + ASSERT_TRUE(amqConsumer->isOptimizeAcknowledge()); + ASSERT_TRUE(amqConsumer->getOptimizedAckScheduledAckInterval() == 1000); + + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + std::string text = std::string() + "Hello world! From: " + Thread::currentThread()->getName(); + Pointer message; + + message.reset(session->createTextMessage(text)); + producer->send(message.get()); + + Pointer received(consumer->receive(5000)); + ASSERT_TRUE(received != NULL); + + Thread::sleep(1200); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslOptimizedAckTest, testOptimizedAckWithExpiredMsgs) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createQueue("TEST.FOO")); + + Pointer consumer(session->createConsumer(destination.get())); + MyMessageListener listener; + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + std::string text = std::string() + "Hello world! From: " + Thread::currentThread()->getName(); + Pointer message; + + // Produce msgs that will expire quickly + for (int i=0; i<45; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 400); + } + + // Produce msgs that don't expire + for (int i=0; i<60; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 60000); + } + + consumer->setMessageListener(&listener); + Thread::sleep(1000); // let the batch of 45 expire. + connection->start(); + + int cycle = 0; + while (cycle++ < 20) { + if (listener.getCounter() == 60) { + break; + } + Thread::sleep(1000); + } + + ASSERT_TRUE(listener.getCounter() == 60) << ("Should have received 60 messages."); + + producer->close(); + consumer->close(); + session->close(); + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslOptimizedAckTest, testOptimizedAckWithExpiredMsgsSync) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + connection->start(); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createQueue("TEST.FOO")); + + Pointer consumer(session->createConsumer(destination.get())); + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + std::string text = std::string() + "Hello world! From: " + Thread::currentThread()->getName(); + Pointer message; + + // Produce msgs that will expire quickly + for (int i=0; i<45; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 10); + } + + // Produce msgs that don't expire + for (int i=0; i<60; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 30000); + } + + Thread::sleep(200); + + for (int counter = 1; counter <= 60; ++counter) { + Pointer message(consumer->receive(2000)); + ASSERT_TRUE(message != NULL); + } + + producer->close(); + consumer->close(); + session->close(); + connection->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslOptimizedAckTest, testOptimizedAckWithExpiredMsgsSync2) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + connection->start(); + Pointer session(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(session->createQueue("TEST.FOO")); + + Pointer consumer(session->createConsumer(destination.get())); + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + std::string text = std::string() + "Hello world! From: " + Thread::currentThread()->getName(); + Pointer message; + + // Produce msgs that don't expire + for (int i=0; i<56; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 30000); + } + // Produce msgs that will expire quickly + for (int i=0; i<44; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 10); + } + // Produce some moremsgs that don't expire + for (int i=0; i<4; i++) { + message.reset(session->createTextMessage(text)); + producer->send(message.get(), 1, 1, 30000); + } + + Thread::sleep(200); + + for (int counter = 1; counter <= 60; ++counter) { + Pointer message(consumer->receive(2000)); + ASSERT_TRUE(message != NULL); + } + + producer->close(); + consumer->close(); + session->close(); + connection->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslQueueBrowserTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslQueueBrowserTest.cpp new file mode 100644 index 000000000..a64d8d6e7 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslQueueBrowserTest.cpp @@ -0,0 +1,321 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslQueueBrowserTest : public QueueBrowserTest { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::core; +using namespace decaf; +using namespace decaf::lang; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslQueueBrowserTest, testReceiveBrowseReceive) { + + cms::Session* session(cmsProvider->getSession()); + + std::unique_ptr queue(session->createQueue("testReceiveBrowseReceive")); + + std::unique_ptr consumer(session->createConsumer(queue.get())); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr message1(session->createTextMessage("First Message")); + std::unique_ptr message2(session->createTextMessage("Second Message")); + std::unique_ptr message3(session->createTextMessage("Third Message")); + + // lets consume any outstanding messages from previous test runs + cms::Message* message; + while ((message = consumer->receive(1000)) != NULL) { + delete message; + } + + producer->send(message1.get()); + producer->send(message2.get()); + producer->send(message3.get()); + + // Get the first. + std::unique_ptr inbound(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message1->getText(), inbound->getText()); + consumer->close(); + + std::unique_ptr browser(session->createBrowser(queue.get())); + cms::MessageEnumeration* enumeration = browser->getEnumeration(); + + // browse the second + ASSERT_TRUE(enumeration->hasMoreMessages()) << ("should have received the second message"); + inbound.reset(dynamic_cast(enumeration->nextMessage())); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message2->getText(), inbound->getText()); + + // browse the third. + ASSERT_TRUE(enumeration->hasMoreMessages()) << ("should have received the third message"); + inbound.reset(dynamic_cast(enumeration->nextMessage())); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message3->getText(), inbound->getText()); + + // There should be no more. + bool tooMany = false; + while (enumeration->hasMoreMessages()) { + tooMany = true; + } + ASSERT_TRUE(!tooMany) << ("Should not have browsed any more messages"); + browser->close(); + + // Re-open the consumer + consumer.reset(session->createConsumer(queue.get())); + // Receive the second. + inbound.reset(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message2->getText(), inbound->getText()); + // Receive the third. + inbound.reset(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message3->getText(), inbound->getText()); + + consumer->close(); + browser->close(); + producer->close(); + cmsProvider->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslQueueBrowserTest, testBrowseReceive) { + + std::unique_ptr inbound; + + cms::Session* session(cmsProvider->getSession()); + std::unique_ptr queue(session->createQueue("testBrowseReceive")); + std::unique_ptr message1(session->createTextMessage("First Message")); + std::unique_ptr producer(session->createProducer(queue.get())); + + producer->send(message1.get()); + + // create browser first + std::unique_ptr browser(session->createBrowser(queue.get())); + cms::MessageEnumeration* enumeration = browser->getEnumeration(); + + // create consumer + std::unique_ptr consumer(session->createConsumer(queue.get())); + + // browse the first message + ASSERT_TRUE(enumeration->hasMoreMessages()) << ("should have received the first message"); + inbound.reset(dynamic_cast(enumeration->nextMessage())); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message1->getText(), inbound->getText()); + + // Receive the first message. + inbound.reset(dynamic_cast(consumer->receive(1000))); + ASSERT_TRUE(inbound.get() != NULL); + ASSERT_EQ(message1->getText(), inbound->getText()); + + consumer->close(); + browser->close(); + producer->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslQueueBrowserTest, testQueueBrowserWith2Consumers) { + + const int numMessages = 100; + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + connection->setAlwaysSyncSend(false); + + std::unique_ptr session(connection->createSession(cms::Session::CLIENT_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testQueueBrowserWith2Consumers")); + + std::unique_ptr queuePrefetch10( + session->createQueue("testQueueBrowserWith2Consumers?consumer.prefetchSize=10")); + std::unique_ptr queuePrefetch1( + session->createQueue("testQueueBrowserWith2Consumers?consumer.prefetchSize=1")); + + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection2(dynamic_cast(factory->createConnection())); + connection2->start(); + + std::unique_ptr session2(connection2->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr consumer(session->createConsumer(queuePrefetch10.get())); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + for (int i = 0; i < numMessages; i++) { + std::unique_ptr message( + session->createTextMessage(std::string("Message: ") + Integer::toString(i))); + producer->send(message.get()); + } + + std::unique_ptr browser(session2->createBrowser(queuePrefetch1.get())); + cms::MessageEnumeration* browserView = browser->getEnumeration(); + + std::vector messages; + for (int i = 0; i < numMessages; i++) { + cms::Message* m1 = consumer->receive(5000); + ASSERT_TRUE(m1 != NULL) << (std::string("m1 is null for index: ") + Integer::toString(i)); + messages.push_back(m1); + } + + for (int i = 0; i < numMessages && browserView->hasMoreMessages(); i++) { + cms::Message* m1 = messages[i]; + cms::Message* m2 = browserView->nextMessage(); + ASSERT_TRUE(m2 != NULL) << (std::string("m2 is null for index: ") + Integer::toString(i)); + ASSERT_TRUE(m1->getCMSMessageID() == m2->getCMSMessageID()); + delete m2; + } + + ASSERT_TRUE(!browserView->hasMoreMessages()) << ("nothing left in the browser"); + ASSERT_TRUE(consumer->receiveNoWait() == NULL) << ("consumer finished"); + + for (std::size_t ix = 0; ix < messages.size(); ++ix) { + cms::Message* msg = messages[ix]; + msg->acknowledge(); + delete msg; + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslQueueBrowserTest, testRepeatedQueueBrowserCreateDestroy) { + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr textMessage(session->createTextMessage("Test")); + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + producer->send(textMessage.get()); + session->commit(); + + connection->start(); + + std::unique_ptr browser(session->createBrowser(queue.get())); + + for (int i = 0; i < 200; i++) { + browser.reset(session->createBrowser(queue.get())); + cms::MessageEnumeration* browserView = browser->getEnumeration(); + + if (browserView->hasMoreMessages()) { + std::unique_ptr message(browserView->nextMessage()); + ASSERT_TRUE(message.get() != NULL); + } + + browser.reset(NULL); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslQueueBrowserTest, testRepeatedQueueBrowserCreateDestroyWithMessageInQueue) { + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + + std::unique_ptr producer(session->createProducer(queue.get())); + std::unique_ptr textMessage(session->createTextMessage("Test")); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + for (int i = 0 ; i < 10; ++i) { + producer->send(textMessage.get()); + } + + connection->start(); + + std::unique_ptr browser(session->createBrowser(queue.get())); + + for (int i = 0; i < 200; i++) { + browser.reset(session->createBrowser(queue.get())); + cms::MessageEnumeration* browserView = browser->getEnumeration(); + + if (browserView->hasMoreMessages()) { + std::unique_ptr message(browserView->nextMessage()); + ASSERT_TRUE(message.get() != NULL); + } + + browser.reset(NULL); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslQueueBrowserTest, testBrowsingExpirationIsIgnored) { + + const int MESSAGES_TO_SEND = 50; + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + ASSERT_TRUE(connection != NULL); + + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createTemporaryQueue()); + std::unique_ptr producer(session->createProducer(queue.get())); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + producer->setTimeToLive(1000); + + // Load the Queue with messages set to expire. + for (int i = 1; i <= MESSAGES_TO_SEND; i++) { + std::unique_ptr textMessage(session->createTextMessage("Message: " + Integer::toString(i))); + producer->send(textMessage.get()); + } + + std::unique_ptr browser(session->createBrowser(queue.get())); + cms::MessageEnumeration* enumeration = browser->getEnumeration(); + int browsed = 0; + + Thread::sleep(1000); + + while (enumeration->hasMoreMessages()) { + std::unique_ptr message(enumeration->nextMessage()); + ASSERT_TRUE(message.get() != NULL); + browsed++; + } + + ASSERT_EQ(MESSAGES_TO_SEND, browsed) << ("Should have browsed all"); + + browser->close(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslRedeliveryPolicyTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslRedeliveryPolicyTest.cpp new file mode 100644 index 000000000..017364831 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslRedeliveryPolicyTest.cpp @@ -0,0 +1,787 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace activemq { +namespace test { +namespace openwire_ssl { + class OpenwireSslRedeliveryPolicyTest : public CMSTestFixture { + public: + void SetUp() override {} + void TearDown() override {} + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace cms; +using namespace activemq; +using namespace activemq::commands; +using namespace activemq::core; +using namespace activemq::core::policies; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::util::concurrent::atomic; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testGetNext) { + + DefaultRedeliveryPolicy policy; + policy.setInitialRedeliveryDelay(0); + policy.setRedeliveryDelay(500); + policy.setBackOffMultiplier((short) 2); + policy.setUseExponentialBackOff(true); + + long long delay = policy.getNextRedeliveryDelay(0); + ASSERT_EQ(500LL, delay) << ("Incorrect delay for cycle 1"); + delay = policy.getNextRedeliveryDelay(delay); + ASSERT_EQ(500L*2LL, delay) << ("Incorrect delay for cycle 2"); + delay = policy.getNextRedeliveryDelay(delay); + ASSERT_EQ(500L*4LL, delay) << ("Incorrect delay for cycle 3"); + + policy.setUseExponentialBackOff(false); + delay = policy.getNextRedeliveryDelay(delay); + ASSERT_EQ(500LL, delay) << ("Incorrect delay for cycle 4"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testGetNextWithInitialDelay) { + + DefaultRedeliveryPolicy policy; + policy.setInitialRedeliveryDelay(500); + + long long delay = policy.getNextRedeliveryDelay(500); + ASSERT_EQ(1000LL, delay) << ("Incorrect delay for cycle 1"); + delay = policy.getNextRedeliveryDelay(delay); + ASSERT_EQ(1000LL, delay) << ("Incorrect delay for cycle 2"); + delay = policy.getNextRedeliveryDelay(delay); + ASSERT_EQ(1000LL, delay) << ("Incorrect delay for cycle 3"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testExponentialRedeliveryPolicyDelaysDeliveryOnRollback) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(0); + policy->setRedeliveryDelay(500); + policy->setBackOffMultiplier((short) 2); + policy->setUseExponentialBackOff(true); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(1000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // No delay on first rollback.. + received.reset(consumer->receive(250)); + ASSERT_TRUE(received != NULL); + session->rollback(); + + // Show subsequent re-delivery delay is incrementing. + received.reset(consumer->receive(250)); + ASSERT_TRUE(received == NULL); + + received.reset(consumer->receive(750)); + ASSERT_TRUE(received != NULL); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // Show re-delivery delay is incrementing exponentially + received.reset(consumer->receive(100)); + ASSERT_TRUE(received == NULL); + received.reset(consumer->receive(500)); + ASSERT_TRUE(received == NULL); + received.reset(consumer->receive(800)); + ASSERT_TRUE(received != NULL); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testNornalRedeliveryPolicyDelaysDeliveryOnRollback) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(0); + policy->setRedeliveryDelay(500); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(1000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // No delay on first rollback.. + received.reset(consumer->receive(250)); + ASSERT_TRUE(received != NULL); + session->rollback(); + + // Show subsequent re-delivery delay is incrementing. + received.reset(consumer->receive(100)); + ASSERT_TRUE(received == NULL); + received.reset(consumer->receive(700)); + ASSERT_TRUE(received != NULL); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // The message gets redelivered after 500 ms every time since + // we are not using exponential backoff. + received.reset(consumer->receive(100)); + ASSERT_TRUE(received == NULL); + received.reset(consumer->receive(700)); + ASSERT_TRUE(received != NULL); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testDLQHandling) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(100); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(2); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + Pointer dlq(session->createQueue("ActiveMQ.DLQ")); + amqConnection->destroyDestination(dlq.get()); + Pointer dlqConsumer(session->createConsumer(dlq.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(1000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get second delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(2000)); + ASSERT_TRUE(received != NULL) << ("Failed to get third delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // The last rollback should cause the 1st message to get sent to the DLQ + received.reset(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get first delivery of msg 2"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); + + // We should be able to get the message off the DLQ now. + received.reset(dlqConsumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get DLQ'd message"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->commit(); + + if (textMessage->propertyExists("dlqDeliveryFailureCause")) { + std::string cause = textMessage->getStringProperty("dlqDeliveryFailureCause"); + ASSERT_TRUE(cause.find("RedeliveryPolicy") != std::string::npos) << ("cause exception has no policy ref"); + } + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testInfiniteMaximumNumberOfRedeliveries) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(100); + policy->setUseExponentialBackOff(false); + // let's set the maximum redeliveries to no maximum (ie. infinite) + policy->setMaximumRedeliveries(-1); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(1000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // we should be able to get the 1st message redelivered until a session.commit is called + received.reset(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get second delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(2000)); + ASSERT_TRUE(received != NULL) << ("Failed to get third delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(2000)); + ASSERT_TRUE(received != NULL) << ("Failed to get fourth delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(2000)); + ASSERT_TRUE(received != NULL) << ("Failed to get fifth delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(2000)); + ASSERT_TRUE(received != NULL) << ("Failed to get sixth delivery"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->commit(); + + received.reset(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get message two"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testMaximumRedeliveryDelay) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(10); + policy->setUseExponentialBackOff(true); + policy->setMaximumRedeliveries(-1); + policy->setRedeliveryDelay(50); + policy->setMaximumRedeliveryDelay(1000); + policy->setBackOffMultiplier((short) 2); + policy->setUseExponentialBackOff(true); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received; + + for(int i = 0; i < 10; ++i) { + // we should be able to get the 1st message redelivered until a session.commit is called + received.reset(consumer->receive(2000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get message"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + } + + received.reset(consumer->receive(2000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get message one last time"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->commit(); + + received.reset(consumer->receive(2000)); + ASSERT_TRUE(received != NULL) << ("Failed to get message two"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); + + long long result = policy->getNextRedeliveryDelay(Integer::MAX_VALUE); + ASSERT_EQ(1000LL, result) << ("Max delay should be 1 second."); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testZeroMaximumNumberOfRedeliveries) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(100); + policy->setUseExponentialBackOff(false); + // let's set the maximum redeliveries to 0 + policy->setMaximumRedeliveries(0); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(1000)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // the 1st message should not be redelivered since maximumRedeliveries is set to 0 + received.reset(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get message two"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testRepeatedRedeliveryReceiveNoCommit) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + connection->start(); + Pointer dlqSession(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(dlqSession->createQueue("testRepeatedRedeliveryReceiveNoCommit")); + Pointer dlq(dlqSession->createQueue("ActiveMQ.DLQ")); + amqConnection->destroyDestination(destination.get()); + amqConnection->destroyDestination(dlq.get()); + Pointer producer(dlqSession->createProducer(destination.get())); + Pointer consumer(dlqSession->createConsumer(dlq.get())); + + Pointer message1(dlqSession->createTextMessage("1st")); + producer->send(message1.get()); + + const int MAX_REDELIVERIES = 4; + + for (int i = 0; i <= MAX_REDELIVERIES + 1; i++) { + Pointer loopConnection(connectionFactory->createConnection()); + Pointer amqConnection = loopConnection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(0); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(MAX_REDELIVERIES); + + loopConnection->start(); + Pointer session(loopConnection->createSession(Session::SESSION_TRANSACTED)); + Pointer consumer(session->createConsumer(destination.get())); + + Pointer received(consumer->receive(1000)); + + if (i <= MAX_REDELIVERIES) { + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + ASSERT_EQ(i, textMessage->getRedeliveryCounter()); + } else { + ASSERT_TRUE(received == NULL) << ("null on exceeding redelivery count"); + } + + loopConnection->close(); + } + + // We should be able to get the message off the DLQ now. + Pointer received(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get from DLQ"); + Pointer textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + + if (textMessage->propertyExists("dlqDeliveryFailureCause")) { + std::string cause = textMessage->getStringProperty("dlqDeliveryFailureCause"); + ASSERT_TRUE(cause.find("RedeliveryPolicy") != std::string::npos) << ("cause exception has no policy ref"); + } else { + FAIL() << ("Message did not have a rollback cause"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class AsyncListener : public cms::MessageListener { + private: + + AtomicInteger* receivedCount; + CountDownLatch* done; + + public: + + AsyncListener(AtomicInteger* receivedCount, CountDownLatch* done) { + this->receivedCount = receivedCount; + this->done = done; + } + + virtual void onMessage(const cms::Message* message) { + try { + const ActiveMQTextMessage* textMessage = dynamic_cast(message); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + ASSERT_EQ(receivedCount->get(), textMessage->getRedeliveryCounter()); + receivedCount->incrementAndGet(); + done->countDown(); + } catch (Exception& ignored) { + ignored.printStackTrace(); + } + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testRepeatedRedeliveryOnMessageNoCommit) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + connection->start(); + Pointer dlqSession(connection->createSession(Session::AUTO_ACKNOWLEDGE)); + Pointer destination(dlqSession->createQueue("testRepeatedRedeliveryOnMessageNoCommit")); + Pointer dlq(dlqSession->createQueue("ActiveMQ.DLQ")); + amqConnection->destroyDestination(destination.get()); + amqConnection->destroyDestination(dlq.get()); + Pointer producer(dlqSession->createProducer(destination.get())); + Pointer consumer(dlqSession->createConsumer(dlq.get())); + + // Send the messages + Pointer message1(dlqSession->createTextMessage("1st")); + producer->send(message1.get()); + + const int MAX_REDELIVERIES = 4; + AtomicInteger receivedCount(0); + + for (int i = 0; i <= MAX_REDELIVERIES + 1; i++) { + + Pointer loopConnection(connectionFactory->createConnection()); + Pointer amqConnection = loopConnection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(0); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(MAX_REDELIVERIES); + + loopConnection->start(); + Pointer session(loopConnection->createSession(Session::SESSION_TRANSACTED)); + Pointer consumer(session->createConsumer(destination.get())); + + CountDownLatch done(1); + + AsyncListener listener(&receivedCount, &done); + consumer->setMessageListener(&listener); + + if (i <= MAX_REDELIVERIES) { + ASSERT_TRUE(done.await(5, TimeUnit::SECONDS)) << ("listener didn't get a message"); + } else { + // final redlivery gets poisoned before dispatch + ASSERT_TRUE(!done.await(2, TimeUnit::SECONDS)) << ("listener got unexpected message"); + } + + loopConnection->close(); + } + + // We should be able to get the message off the DLQ now. + Pointer received(consumer->receive(1000)); + ASSERT_TRUE(received != NULL) << ("Failed to get from DLQ"); + Pointer textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + + if (textMessage->propertyExists("dlqDeliveryFailureCause")) { + std::string cause = textMessage->getStringProperty("dlqDeliveryFailureCause"); + ASSERT_TRUE(cause.find("RedeliveryPolicy") != std::string::npos) << ("cause exception has no policy ref"); + } else { + FAIL() << ("Message did not have a rollback cause"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testInitialRedeliveryDelayZero) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(0); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(1); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(100)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + // Both should be able for consumption. + received.reset(consumer->receive(100)); + textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get message one again"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + + received.reset(consumer->receive(100)); + ASSERT_TRUE(received != NULL) << ("Failed to get message two"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testInitialRedeliveryDelayOne) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(1000); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(1); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(100)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(100)); + ASSERT_TRUE(received == NULL); + + received.reset(consumer->receive(2000)); + textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get message one again"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + + received.reset(consumer->receive(100)); + ASSERT_TRUE(received != NULL) << ("Failed to get message two"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslRedeliveryPolicyTest, testRedeliveryDelayOne) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + // Receive a message with the JMS API + RedeliveryPolicy* policy = amqConnection->getRedeliveryPolicy(); + policy->setInitialRedeliveryDelay(0); + policy->setRedeliveryDelay(1000); + policy->setUseExponentialBackOff(false); + policy->setMaximumRedeliveries(2); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createTemporaryQueue()); + Pointer producer(session->createProducer(destination.get())); + Pointer consumer(session->createConsumer(destination.get())); + + // Send the messages + Pointer message1(session->createTextMessage("1st")); + Pointer message2(session->createTextMessage("2nd")); + + producer->send(message1.get()); + producer->send(message2.get()); + session->commit(); + + Pointer received(consumer->receive(100)); + Pointer textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get first delivery"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(100)); + textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("first redelivery was not immediate."); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + session->rollback(); + + received.reset(consumer->receive(100)); + ASSERT_TRUE(received == NULL) << ("seconds redelivery should be delayed."); + + received.reset(consumer->receive(2000)); + textMessage = received.dynamicCast(); + ASSERT_TRUE(textMessage != NULL) << ("Failed to get message one again"); + ASSERT_EQ(std::string("1st"), textMessage->getText()); + + received.reset(consumer->receive(100)); + ASSERT_TRUE(received != NULL) << ("Failed to get message two"); + textMessage = received.dynamicCast(); + ASSERT_EQ(std::string("2nd"), textMessage->getText()); + session->commit(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSimpleRollbackTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSimpleRollbackTest.cpp new file mode 100644 index 000000000..07bf365ca --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSimpleRollbackTest.cpp @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +#include + +namespace activemq{ +namespace test{ +namespace openwire_ssl { + class OpenwireSslSimpleRollbackTest : public SimpleRollbackTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf::lang; +using namespace decaf::util; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleRollbackTest, testRollbacks) { + + try { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage()); + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + ostringstream lcStream; + lcStream << "SimpleTest - Message #" << i << ends; + txtMessage->setText(lcStream.str()); + producer->send(txtMessage.get()); + } + + session->commit(); + Thread::sleep(50); + + // Wait for the messages to get here + listener.asyncWaitForMessages(IntegrationCommon::defaultMsgCount); + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount); + + session->commit(); + Thread::sleep(50); + + for (unsigned int i = 0; i < 5; ++i) { + ostringstream lcStream; + lcStream << "SimpleTest - Message #" << i << ends; + txtMessage->setText(lcStream.str()); + producer->send(txtMessage.get()); + } + + listener.reset(); + session->rollback(); + Thread::sleep(50); + + listener.reset(); + txtMessage->setText("SimpleTest - Message after Rollback"); + producer->send(txtMessage.get()); + session->commit(); + + // Wait for the messages to get here + listener.asyncWaitForMessages(1); + ASSERT_TRUE(listener.getNumReceived() == 1); + + listener.reset(); + txtMessage->setText("SimpleTest - Message after Rollback"); + producer->send(txtMessage.get()); + session->commit(); + + // Wait for the messages to get here + listener.asyncWaitForMessages(1); + ASSERT_TRUE(listener.getNumReceived() == 1); + session->commit(); + + } catch (std::exception& ex) { + std::cout << ex.what() << std::endl; + throw ex; + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSimpleTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSimpleTest.cpp new file mode 100644 index 000000000..89e6ea084 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSimpleTest.cpp @@ -0,0 +1,957 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace activemq{ +namespace test{ +namespace openwire_ssl { + class OpenwireSslSimpleTest : public SimpleTest { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; + +//////////////////////////////////////////////////////////////////////////////// +// Tests inlined from SimpleTest base class + +TEST_F(OpenwireSslSimpleTest, testAutoAck) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + std::unique_ptr bytesMessage(session->createBytesMessage()); + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(txtMessage.get()); + } + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(bytesMessage.get()); + } + + // Wait for the messages to get here + listener.asyncWaitForMessages(IntegrationCommon::defaultMsgCount * 2); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount * 2); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testClientAck) { + + cmsProvider->setAckMode(cms::Session::CLIENT_ACKNOWLEDGE); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + std::unique_ptr bytesMessage(session->createBytesMessage()); + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(txtMessage.get()); + } + + for (unsigned int i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(bytesMessage.get()); + } + + // Wait for the messages to get here + listener.asyncWaitForMessages(IntegrationCommon::defaultMsgCount * 2); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == IntegrationCommon::defaultMsgCount * 2); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testProducerWithNullDestination) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getNoDestProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + producer->send(cmsProvider->getDestination(), txtMessage.get()); + + // Wait for the messages to get here + listener.asyncWaitForMessages(1); + + unsigned int numReceived = listener.getNumReceived(); + ASSERT_TRUE(numReceived == 1); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testProducerSendWithNullMessage) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + ASSERT_THROW(producer->send( NULL, txtMessage.get() ), cms::InvalidDestinationException) << ("Should Throw an InvalidDestinationException"); + + producer = cmsProvider->getNoDestProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + ASSERT_THROW(producer->send( NULL, txtMessage.get() ), cms::UnsupportedOperationException) << ("Should Throw an UnsupportedOperationException"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testProducerSendToNonDefaultDestination) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + CMSListener listener(session); + + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + std::unique_ptr destination(session->createTemporaryTopic()); + + ASSERT_THROW(producer->send(destination.get(), txtMessage.get()), cms::UnsupportedOperationException) << ("Should Throw an UnsupportedOperationException"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testSyncReceive) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testSyncReceiveClientAck) { + + cmsProvider->setAckMode(cms::Session::CLIENT_ACKNOWLEDGE); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testMultipleConnections) { + + // Create CMS Object for Comms + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection1(factory->createConnection()); + connection1->start(); + + std::unique_ptr connection2(factory->createConnection()); + connection2->start(); + + ASSERT_TRUE(connection1->getClientID() != connection2->getClientID()); + + std::unique_ptr session1(connection1->createSession()); + std::unique_ptr session2(connection1->createSession()); + + std::unique_ptr topic(session1->createTopic(UUID::randomUUID().toString())); + + std::unique_ptr consumer1(session1->createConsumer(topic.get())); + std::unique_ptr consumer2(session2->createConsumer(topic.get())); + + std::unique_ptr producer(session2->createProducer(topic.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr textMessage(session2->createTextMessage()); + + // Send some text messages + producer->send(textMessage.get()); + + std::unique_ptr message(consumer1->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + message.reset(consumer2->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + // Clean up if we can + consumer1->close(); + consumer2->close(); + producer->close(); + session1->close(); + session2->close(); + + this->cmsProvider->destroyDestination(topic.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testMultipleSessions) { + + // Create CMS Object for Comms + std::unique_ptr session1(cmsProvider->getConnection()->createSession()); + std::unique_ptr session2(cmsProvider->getConnection()->createSession()); + + std::unique_ptr topic(session1->createTopic(UUID::randomUUID().toString())); + + std::unique_ptr consumer1(session1->createConsumer(topic.get())); + std::unique_ptr consumer2(session2->createConsumer(topic.get())); + + std::unique_ptr producer(session2->createProducer(topic.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr textMessage(session2->createTextMessage()); + + // Send some text messages + producer->send(textMessage.get()); + + std::unique_ptr message(consumer1->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + message.reset(consumer2->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + // Clean up if we can + consumer1->close(); + consumer2->close(); + producer->close(); + session1->close(); + session2->close(); + + this->cmsProvider->destroyDestination(topic.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testReceiveAlreadyInQueue) { + + // Create CMS Object for Comms + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection(factory->createConnection()); + + std::unique_ptr session(connection->createSession()); + std::unique_ptr topic(session->createTopic(UUID::randomUUID().toString())); + std::unique_ptr consumer(session->createConsumer(topic.get())); + std::unique_ptr producer(session->createProducer(topic.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr textMessage(session->createTextMessage()); + + // Send some text messages + producer->send(textMessage.get()); + + Thread::sleep(250); + + connection->start(); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + // Clean up if we can + consumer->close(); + producer->close(); + session->close(); + + this->cmsProvider->destroyDestination(topic.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testBytesMessageSendRecv) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr bytesMessage(session->createBytesMessage()); + + bytesMessage->writeBoolean(true); + bytesMessage->writeByte(127); + bytesMessage->writeDouble(123456.789); + bytesMessage->writeInt(65537); + bytesMessage->writeString("TEST-STRING"); + + // Send some text messages + producer->send(bytesMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + ASSERT_THROW(message->setStringProperty("FOO", "BAR"), cms::CMSException) << ("Should throw an ActiveMQExceptio"); + + BytesMessage* bytesMessage2 = dynamic_cast(message.get()); + ASSERT_TRUE(bytesMessage2 != NULL); + ASSERT_THROW(bytesMessage2->writeBoolean(false), cms::CMSException) << ("Should throw an ActiveMQExceptio"); + + ASSERT_TRUE(bytesMessage2->getBodyLength() > 0); + + unsigned char* result = bytesMessage2->getBodyBytes(); + ASSERT_TRUE(result != NULL); + delete[] result; + + bytesMessage2->reset(); + + ASSERT_TRUE(bytesMessage2->readBoolean() == true); + ASSERT_TRUE(bytesMessage2->readByte() == 127); + ASSERT_TRUE(bytesMessage2->readDouble() == 123456.789); + ASSERT_TRUE(bytesMessage2->readInt() == 65537); + ASSERT_TRUE(bytesMessage2->readString() == "TEST-STRING"); +} + +//////////////////////////////////////////////////////////////////////////////// +namespace { + + class Listener: public cms::MessageListener { + private: + + bool passed; + bool triggered; + + public: + + Listener() : MessageListener(), passed(false), triggered(false) {} + + virtual ~Listener() {} + + bool isPassed() { + return passed; + } + + bool isTriggered() { + return triggered; + } + + void onMessage(const cms::Message* message) { + try { + triggered = true; + const BytesMessage* bytesMessage = dynamic_cast(message); + + ASSERT_TRUE(bytesMessage != NULL); + ASSERT_TRUE(bytesMessage->getBodyLength() > 0); + + unsigned char* result = bytesMessage->getBodyBytes(); + ASSERT_TRUE(result != NULL); + delete[] result; + + passed = true; + } catch (...) { + passed = false; + } + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testBytesMessageSendRecvAsync) { + + Listener listener; + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + consumer->setMessageListener(&listener); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr bytesMessage(session->createBytesMessage()); + + bytesMessage->writeBoolean(true); + bytesMessage->writeByte(127); + bytesMessage->writeDouble(123456.789); + bytesMessage->writeInt(65537); + bytesMessage->writeString("TEST-STRING"); + + // Send some text messages + producer->send(bytesMessage.get()); + + int count = 0; + while (!listener.isTriggered() && count++ < 30) { + decaf::lang::Thread::sleep(100); + } + + ASSERT_TRUE(listener.isPassed()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testLibraryInitShutdownInit) { + + { + this->TearDown(); + // Shutdown the ActiveMQ library + ASSERT_NO_THROW(activemq::library::ActiveMQCPP::shutdownLibrary()); + } + { + // Initialize the ActiveMQ library + ASSERT_NO_THROW(activemq::library::ActiveMQCPP::initializeLibrary()); + cmsProvider.reset(new activemq::util::CMSProvider(getBrokerURL())); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testQuickCreateAndDestroy) { + + std::unique_ptr factory(new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + std::unique_ptr connection(factory->createConnection()); + std::unique_ptr session(connection->createSession()); + + session.reset( NULL); + connection.reset( NULL); + + connection.reset(factory->createConnection()); + session.reset(connection->createSession()); + connection->start(); + + session.reset( NULL); + connection.reset( NULL); + + for (int i = 0; i < 50; ++i) { + CMSProvider lcmsProvider(this->getBrokerURL()); + lcmsProvider.getSession(); + lcmsProvider.getConsumer(); + lcmsProvider.getProducer(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests defined locally - test logic is inlined directly in the TEST_F macro + +TEST_F(OpenwireSslSimpleTest, testWithZeroConsumerPrefetch) { + + cmsProvider->setTopic(false); + cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); + + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testWithZeroConsumerPrefetchAndNoMessage) { + + cmsProvider->setTopic(false); + cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); + + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + + // Should be no message and no exceptions + std::unique_ptr message(consumer->receiveNoWait()); + ASSERT_TRUE(message.get() == NULL); + + // Should be no message and no exceptions + message.reset(consumer->receive(1000)); + ASSERT_TRUE(message.get() == NULL); + + consumer->close(); + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testWithZeroConsumerPrefetch2) { + + cmsProvider->setTopic(false); + ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); + amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); + amqConnection->getPrefetchPolicy()->setTopicPrefetch(0); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testWithZeroConsumerPrefetchAndNoMessage2) { + + cmsProvider->setTopic(false); + ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); + amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); + amqConnection->getPrefetchPolicy()->setTopicPrefetch(0); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + + // Should be no message and no exceptions + std::unique_ptr message(consumer->receiveNoWait()); + ASSERT_TRUE(message.get() == NULL); + + // Should be no message and no exceptions + message.reset(consumer->receive(1000)); + ASSERT_TRUE(message.get() == NULL); + + consumer->close(); + session->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testWithZeroConsumerPrefetchAndZeroRedelivery) { + + ActiveMQConnectionFactory factory(getBrokerURL()); + std::unique_ptr connection(factory.createConnection()); + + connection->start(); + + { + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr message(session->createTextMessage("Hello")); + producer->send(message.get()); + producer->close(); + session->close(); + } + + { + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + + session->rollback(); + session->close(); + connection->close(); + } + + connection.reset(factory.createConnection()); + connection->start(); + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + + // Now we test the zero prefetc + zero max redelivery case. + amqConnection->getRedeliveryPolicy()->setMaximumRedeliveries(0); + amqConnection->getPrefetchPolicy()->setQueuePrefetch(0); + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchAndZeroRedelivery")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() == NULL); + + session->commit(); + session->close(); + + amqConnection->destroyDestination(queue.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testWithZeroConsumerPrefetchWithInFlightExpiration) { + + ActiveMQConnectionFactory factory(getBrokerURL()); + std::unique_ptr connection(factory.createConnection()); + + ActiveMQConnection* amqConnection = dynamic_cast(connection.get()); + amqConnection->getPrefetchPolicy()->setAll(0); + + connection->start(); + + { + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchWithInFlightExpiration")); + + amqConnection->destroyDestination(queue.get()); + + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr expiredMessage(session->createTextMessage("Expired")); + std::unique_ptr validMessage(session->createTextMessage("Valid")); + producer->send(expiredMessage.get(), cms::Message::DEFAULT_DELIVERY_MODE, cms::Message::DEFAULT_MSG_PRIORITY, 2000); + producer->send(validMessage.get()); + session->close(); + } + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testWithZeroConsumerPrefetchWithInFlightExpiration")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + { + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + TextMessage* received = dynamic_cast(message.get()); + ASSERT_EQ(std::string("Expired"), received->getText()); + } + + session->rollback(); + Thread::sleep(2500); + + { + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + TextMessage* received = dynamic_cast(message.get()); + ASSERT_EQ(std::string("Valid"), received->getText()); + } + + session->commit(); + session->close(); + + amqConnection->destroyDestination(queue.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testMapMessageSendToQueue) { + + cmsProvider->setTopic(false); + cmsProvider->setDestinationName(UUID::randomUUID().toString() + "?consumer.prefetchSize=0"); + + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + unsigned char byteValue = 'A'; + char charValue = 'B'; + bool booleanValue = true; + short shortValue = 2048; + int intValue = 655369; + long long longValue = 0xFFFFFFFF00000000ULL; + float floatValue = 45.6545f; + double doubleValue = 654564.654654; + std::string stringValue = "The test string"; + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setString("stringKey", stringValue); + mapMessage->setBoolean("boolKey", booleanValue); + mapMessage->setByte("byteKey", byteValue); + mapMessage->setChar("charKey", charValue); + mapMessage->setShort("shortKey", shortValue); + mapMessage->setInt("intKey", intValue); + mapMessage->setLong("longKey", longValue); + mapMessage->setFloat("floatKey", floatValue); + mapMessage->setDouble("doubleKey", doubleValue); + + std::vector bytes; + bytes.push_back(65); + bytes.push_back(66); + bytes.push_back(67); + bytes.push_back(68); + bytes.push_back(69); + mapMessage->setBytes("bytesKey", bytes); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->getString("stringKey") == stringValue); + ASSERT_TRUE(recvMapMessage->getBoolean("boolKey") == booleanValue); + ASSERT_TRUE(recvMapMessage->getByte("byteKey") == byteValue); + ASSERT_TRUE(recvMapMessage->getChar("charKey") == charValue); + ASSERT_TRUE(recvMapMessage->getShort("shortKey") == shortValue); + ASSERT_TRUE(recvMapMessage->getInt("intKey") == intValue); + ASSERT_TRUE(recvMapMessage->getLong("longKey") == longValue); + ASSERT_TRUE(recvMapMessage->getFloat("floatKey") == floatValue); + ASSERT_TRUE(recvMapMessage->getDouble("doubleKey") == doubleValue); + ASSERT_TRUE(recvMapMessage->getBytes("bytesKey") == bytes); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testMapMessageSendToTopic) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + unsigned char byteValue = 'A'; + char charValue = 'B'; + bool booleanValue = true; + short shortValue = 2048; + int intValue = 655369; + long long longValue = 0xFFFFFFFF00000000ULL; + float floatValue = 45.6545f; + double doubleValue = 654564.654654; + std::string stringValue = "The test string"; + + std::unique_ptr mapMessage(session->createMapMessage()); + + mapMessage->setString("stringKey", stringValue); + mapMessage->setBoolean("boolKey", booleanValue); + mapMessage->setByte("byteKey", byteValue); + mapMessage->setChar("charKey", charValue); + mapMessage->setShort("shortKey", shortValue); + mapMessage->setInt("intKey", intValue); + mapMessage->setLong("longKey", longValue); + mapMessage->setFloat("floatKey", floatValue); + mapMessage->setDouble("doubleKey", doubleValue); + + std::vector bytes; + bytes.push_back(65); + bytes.push_back(66); + bytes.push_back(67); + bytes.push_back(68); + bytes.push_back(69); + mapMessage->setBytes("bytesKey", bytes); + + // Send some text messages + producer->send(mapMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::MapMessage* recvMapMessage = dynamic_cast(message.get()); + ASSERT_TRUE(recvMapMessage != NULL); + ASSERT_TRUE(recvMapMessage->getString("stringKey") == stringValue); + ASSERT_TRUE(recvMapMessage->getBoolean("boolKey") == booleanValue); + ASSERT_TRUE(recvMapMessage->getByte("byteKey") == byteValue); + ASSERT_TRUE(recvMapMessage->getChar("charKey") == charValue); + ASSERT_TRUE(recvMapMessage->getShort("shortKey") == shortValue); + ASSERT_TRUE(recvMapMessage->getInt("intKey") == intValue); + ASSERT_TRUE(recvMapMessage->getLong("longKey") == longValue); + ASSERT_TRUE(recvMapMessage->getFloat("floatKey") == floatValue); + ASSERT_TRUE(recvMapMessage->getDouble("doubleKey") == doubleValue); + ASSERT_TRUE(recvMapMessage->getBytes("bytesKey") == bytes); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testDestroyDestination) { + + try { + + cmsProvider->setDestinationName("testDestroyDestination"); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL); + + ActiveMQConnection* connection = dynamic_cast(cmsProvider->getConnection()); + + ASSERT_TRUE(connection != NULL); + + try { + connection->destroyDestination(cmsProvider->getDestination()); + ASSERT_TRUE(false) << ("Destination Should be in use."); + } catch (ActiveMQException& ex) { + } + + cmsProvider->reconnectSession(); + + connection->destroyDestination(cmsProvider->getDestination()); + + } catch (ActiveMQException& ex) { + ex.printStackTrace(); + ASSERT_TRUE(false) << ("CAUGHT EXCEPTION"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, tesstStreamMessage) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + unsigned char byteValue = 'A'; + char charValue = 'B'; + bool booleanValue = true; + short shortValue = 2048; + int intValue = 655369; + long long longValue = 0xFFFFFFFF00000000ULL; + float floatValue = 45.6545f; + double doubleValue = 654564.654654; + std::string stringValue = "The test string"; + + std::unique_ptr streamMessage(session->createStreamMessage()); + + streamMessage->writeString(stringValue); + streamMessage->writeBoolean(booleanValue); + streamMessage->writeByte(byteValue); + streamMessage->writeChar(charValue); + streamMessage->writeShort(shortValue); + streamMessage->writeInt(intValue); + streamMessage->writeLong(longValue); + streamMessage->writeFloat(floatValue); + streamMessage->writeDouble(doubleValue); + + std::vector bytes; + std::vector readBytes(100); + bytes.push_back(65); + bytes.push_back(66); + bytes.push_back(67); + bytes.push_back(68); + bytes.push_back(69); + streamMessage->writeBytes(bytes); + + // Send some text messages + producer->send(streamMessage.get()); + + std::unique_ptr message(consumer->receive(2000)); + ASSERT_TRUE(message.get() != NULL); + + cms::StreamMessage* rcvStreamMessage = dynamic_cast(message.get()); + ASSERT_TRUE(rcvStreamMessage != NULL); + ASSERT_TRUE(rcvStreamMessage->readString() == stringValue); + ASSERT_TRUE(rcvStreamMessage->readBoolean() == booleanValue); + ASSERT_TRUE(rcvStreamMessage->readByte() == byteValue); + ASSERT_TRUE(rcvStreamMessage->readChar() == charValue); + ASSERT_TRUE(rcvStreamMessage->readShort() == shortValue); + ASSERT_TRUE(rcvStreamMessage->readInt() == intValue); + ASSERT_TRUE(rcvStreamMessage->readLong() == longValue); + ASSERT_TRUE(rcvStreamMessage->readFloat() == floatValue); + ASSERT_TRUE(rcvStreamMessage->readDouble() == doubleValue); + ASSERT_TRUE(rcvStreamMessage->readBytes(readBytes) == (int )bytes.size()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testMessageIdSetOnSend) { + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr message(session->createMessage()); + producer->send(message.get()); + + ASSERT_TRUE(message->getCMSMessageID() != ""); + ASSERT_TRUE(message->getCMSDestination() != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSimpleTest, testReceiveWithSessionSyncDispatch) { + + ActiveMQConnection* amqConnection = dynamic_cast(cmsProvider->getConnection()); + amqConnection->setAlwaysSessionAsync(false); + + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + // Send some text messages + producer->send(txtMessage.get()); + + std::unique_ptr message(consumer->receive(1000)); + ASSERT_TRUE(message.get() != NULL); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSlowListenerTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSlowListenerTest.cpp new file mode 100644 index 000000000..7198b73ed --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslSlowListenerTest.cpp @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace test{ + + class SlowListener : public cms::MessageListener { + public: + + unsigned int count; + std::set threadIds; + decaf::util::concurrent::Mutex mutex; + + SlowListener() : MessageListener(), count(0), threadIds(), mutex() {} + virtual ~SlowListener() {} + + void onMessage(const cms::Message* message) { + + synchronized( &mutex ) { + count++; + threadIds.insert(decaf::lang::Thread::currentThread()->getId()); + } + + decaf::lang::Thread::sleep(20); + } + }; + +}} + +namespace activemq{ +namespace test{ +namespace openwire_ssl { + class OpenwireSslSlowListenerTest : public SlowListenerTest { +public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf::lang; +using namespace decaf::util; +using namespace decaf::util::concurrent; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslSlowListenerTest, testSlowListener) { + + try { + + SlowListener listener; + + cms::Session* session = cmsProvider->getSession(); + + cms::MessageProducer* producer = cmsProvider->getProducer(); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + const unsigned int numConsumers = 5; + cms::MessageConsumer* consumers[numConsumers]; + + // Create several consumers for the same destination. + for (unsigned int i = 0; i < numConsumers; i++) { + consumers[i] = session->createConsumer(cmsProvider->getDestination()); + consumers[i]->setMessageListener(&listener); + } + + std::unique_ptr message(session->createBytesMessage()); + + unsigned int msgCount = 50; + for (unsigned int i = 0; i < msgCount; i++) { + producer->send(message.get()); + } + + // Wait no more than 10 seconds for all the messages to come in. + waitForMessages(msgCount * numConsumers, 10000, &listener); + + synchronized(&listener.mutex) { + // Make sure that the listener was always accessed by the same thread + // and that it received all the messages from all consumers. + ASSERT_EQ(1, (int )listener.threadIds.size()); + ASSERT_EQ((msgCount * numConsumers), listener.count); + } + + for (unsigned int i = 0; i < numConsumers; i++) { + delete consumers[i]; + } + + } catch (ActiveMQException& ex) { + ex.printStackTrace(); + throw ex; + } +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslTempDestinationTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslTempDestinationTest.cpp new file mode 100644 index 000000000..0ecee69be --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslTempDestinationTest.cpp @@ -0,0 +1,433 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace activemq{ +namespace test{ +namespace openwire_ssl { + class OpenwireSslTempDestinationTest : public CMSTestFixture { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace decaf; +using namespace decaf::util; +using namespace decaf::util::concurrent; +using namespace decaf::lang; + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class Requester : public cms::MessageListener, + public decaf::lang::Runnable { + private: + + std::unique_ptr cmsProvider; + std::unique_ptr tempTopicConsumer; + + unsigned int numReceived; + unsigned int messageCount; + + decaf::util::concurrent::CountDownLatch ready; + decaf::util::concurrent::CountDownLatch responses; + + public: + + Requester(const std::string& url, const std::string& destination, unsigned int messageCount ) : + cmsProvider(), tempTopicConsumer(), numReceived(0), messageCount(messageCount), ready(1), responses(messageCount) { + + this->cmsProvider.reset( new CMSProvider( url ) ); + this->cmsProvider->setDestinationName( destination ); + + this->cmsProvider->getProducer()->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); + + this->tempTopicConsumer.reset( + cmsProvider->getSession()->createConsumer( + cmsProvider->getTempDestination() ) ); + this->tempTopicConsumer->setMessageListener( this ); + } + + virtual ~Requester() {} + + virtual unsigned int getNumReceived() const { + return this->numReceived; + } + + virtual void waitUnitReady() { + this->ready.await(); + } + + virtual void awaitAllResponses() { + this->responses.await( 2000 * this->messageCount ); + } + + virtual void run() { + + try { + + std::unique_ptr message( + this->cmsProvider->getSession()->createTextMessage() ); + message->setCMSReplyTo( this->cmsProvider->getTempDestination() ); + + this->ready.countDown(); + + for( unsigned int i = 0; i < messageCount; ++i ) { + this->cmsProvider->getProducer()->send( message.get() ); + } + + } catch( CMSException& e ) { + e.printStackTrace(); + } + } + + virtual void onMessage( const cms::Message* message ) { + + try { + + this->numReceived++; + this->responses.countDown(); + + } catch( CMSException& e ) { + e.printStackTrace(); + } + } + }; + + class Responder : public cms::MessageListener { + private: + + std::unique_ptr cmsProvider; + + unsigned int numReceived; + unsigned int messageCount; + + decaf::util::concurrent::CountDownLatch requests; + + public: + + Responder(const std::string& url, const std::string& destination, unsigned int messageCount) : + cmsProvider(), numReceived(0), messageCount(messageCount), requests(messageCount) { + + this->cmsProvider.reset(new CMSProvider(url)); + this->cmsProvider->setDestinationName(destination); + this->cmsProvider->getNoDestProducer()->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + this->cmsProvider->getConsumer()->setMessageListener(this); + } + + virtual ~Responder() {} + + virtual unsigned int getNumReceived() const { + return this->numReceived; + } + + virtual void awaitAllRequests() { + this->requests.await( 2000 * this->messageCount ); + } + + virtual void onMessage( const cms::Message* message ) { + + try { + + if( message->getCMSReplyTo() != NULL ) { + + std::unique_ptr response( + cmsProvider->getSession()->createMessage() ); + + // Send it back to the replyTo Destination + this->cmsProvider->getNoDestProducer()->send( + message->getCMSReplyTo(), response.get() ); + + this->requests.countDown(); + } + + this->numReceived++; + + } catch( CMSException& e ) { + e.printStackTrace(); + } + } + }; + +}}} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testBasics) { + + try{ + + std::unique_ptr tempConsumer( + cmsProvider->getSession()->createConsumer( + cmsProvider->getTempDestination() ) ); + + std::unique_ptr message( + cmsProvider->getSession()->createTextMessage() ); + + // Fire a message to the temporary topic + cmsProvider->getNoDestProducer()->send( + cmsProvider->getTempDestination(), message.get() ); + + std::unique_ptr received( tempConsumer->receive( 3000 ) ); + + ASSERT_TRUE(received.get() != NULL); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testTwoConnections) { + + std::string destination = "REQUEST-TOPIC"; + + std::unique_ptr requester( + new Requester( cmsProvider->getBrokerURL(), destination, 10 ) ); + std::unique_ptr responder( + new Responder( cmsProvider->getBrokerURL(), destination, 10 ) ); + + // Launch the Consumers in new Threads. + Thread requestorThread( requester.get() ); + requestorThread.start(); + + // Responder should get all its requests first + responder->awaitAllRequests(); + + // Now the Requester should get all its responses. + requester->awaitAllResponses(); + + // Check that the responder received all the required requests + ASSERT_TRUE(responder->getNumReceived() == 10); + + // Check that the requester received all the required responses + ASSERT_TRUE(requester->getNumReceived() == 10); + + // Shutdown the Requester. + requestorThread.join(); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testTempDestOnlyConsumedByLocalConn) { + + std::unique_ptr factory( + new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + factory->setAlwaysSyncSend(true); + + std::unique_ptr tempConnection(factory->createConnection()); + tempConnection->start(); + std::unique_ptr tempSession(tempConnection->createSession()); + std::unique_ptr queue(tempSession->createTemporaryQueue()); + std::unique_ptr producer(tempSession->createProducer(queue.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr message(tempSession->createTextMessage("First")); + producer->send(message.get()); + + // temp destination should not be consume when using another connection + std::unique_ptr otherConnection(factory->createConnection()); + std::unique_ptr otherSession(otherConnection->createSession()); + std::unique_ptr otherQueue(otherSession->createTemporaryQueue()); + std::unique_ptr consumer(otherSession->createConsumer(otherQueue.get())); + std::unique_ptr msg(consumer->receive(3000)); + ASSERT_TRUE(msg.get() == NULL); + + // should throw InvalidDestinationException when consuming a temp + // destination from another connection + ASSERT_THROW(otherSession->createConsumer(queue.get()), InvalidDestinationException) << ("Should throw a CMS InvalidDestinationException"); + + // should be able to consume temp destination from the same connection + consumer.reset(tempSession->createConsumer(queue.get())); + msg.reset(consumer->receive(3000)); + ASSERT_TRUE(msg.get() != NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testTempQueueHoldsMessagesWithConsumers) { + + std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); + std::unique_ptr consumer(cmsProvider->getSession()->createConsumer(queue.get())); + std::unique_ptr producer(cmsProvider->getSession()->createProducer(queue.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr message(cmsProvider->getSession()->createTextMessage("Hello")); + producer->send(message.get()); + + std::unique_ptr message2(consumer->receive(3000)); + ASSERT_TRUE(message2.get() != NULL); + ASSERT_TRUE(dynamic_cast(message2.get()) != NULL) << ("Expected message to be a TextMessage"); + ASSERT_TRUE(dynamic_cast(message2.get())->getText() == message->getText()) << (std::string("Expected message to be a '") + message->getText() + "'"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testTempQueueHoldsMessagesWithoutConsumers) { + + std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); + std::unique_ptr producer(cmsProvider->getSession()->createProducer(queue.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr message(cmsProvider->getSession()->createTextMessage("Hello")); + producer->send(message.get()); + + std::unique_ptr consumer(cmsProvider->getSession()->createConsumer(queue.get())); + std::unique_ptr message2(consumer->receive(3000)); + ASSERT_TRUE(message2.get() != NULL); + ASSERT_TRUE(dynamic_cast(message2.get()) != NULL) << ("Expected message to be a TextMessage"); + ASSERT_TRUE(dynamic_cast(message2.get())->getText() == message->getText()) << (std::string("Expected message to be a '") + message->getText() + "'"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testTmpQueueWorksUnderLoad) { + + int count = 500; + int dataSize = 1024; + + ArrayList > list(count); + std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); + std::unique_ptr producer(cmsProvider->getSession()->createProducer(queue.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + unsigned char data[1024]; + for (int i = 0; i < dataSize; ++i) { + data[i] = 255; + } + + for (int i = 0; i < count; i++) { + Pointer message(cmsProvider->getSession()->createBytesMessage()); + message->writeBytes(data, 0, dataSize); + message->setIntProperty("c", i); + producer->send(message.get()); + list.add(message); + } + + std::unique_ptr consumer(cmsProvider->getSession()->createConsumer(queue.get())); + for (int i = 0; i < count; i++) { + Pointer message2(consumer->receive(2000)); + ASSERT_TRUE(message2 != NULL); + ASSERT_EQ(i, message2->getIntProperty("c")); + ASSERT_TRUE(dynamic_cast(message2.get()) != NULL) << ("Expected message to be a BytesMessage"); + } + + list.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testPublishFailsForClosedConnection) { + + Pointer factory( + new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + factory->setAlwaysSyncSend(true); + + std::unique_ptr tempConnection(factory->createConnection()); + tempConnection->start(); + + std::unique_ptr tempSession(tempConnection->createSession()); + std::unique_ptr queue(tempSession->createTemporaryQueue()); + + Thread::sleep(2000); + + // This message delivery should work since the temp connection is still open. + std::unique_ptr producer(cmsProvider->getSession()->createProducer(queue.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr message(cmsProvider->getSession()->createTextMessage("First")); + producer->send(message.get()); + Thread::sleep(2000); + + // Closing the connection should destroy the temp queue that was created. + tempConnection->close(); + Thread::sleep(5000); + + message.reset(cmsProvider->getSession()->createTextMessage("Hello")); + + ASSERT_THROW(producer->send(message.get()), cms::InvalidDestinationException) << ("Should throw a InvalidDestinationException since temp destination should not exist anymore."); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testPublishFailsForDestoryedTempDestination) { + + Pointer factory( + new ActiveMQConnectionFactory(cmsProvider->getBrokerURL())); + factory->setAlwaysSyncSend(true); + + std::unique_ptr tempConnection(factory->createConnection()); + tempConnection->start(); + + std::unique_ptr tempSession(tempConnection->createSession()); + std::unique_ptr queue(tempSession->createTemporaryQueue()); + + Thread::sleep(2000); + + // This message delivery should work since the temp connection is still open. + std::unique_ptr producer(cmsProvider->getSession()->createProducer(queue.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + std::unique_ptr message(cmsProvider->getSession()->createTextMessage("First")); + producer->send(message.get()); + Thread::sleep(2000); + + // deleting the Queue will cause sends to fail + queue->destroy(); + Thread::sleep(5000); // Wait a little bit to let the delete take effect. + + message.reset(cmsProvider->getSession()->createTextMessage("Hello")); + + ASSERT_THROW(producer->send(message.get()), InvalidDestinationException) << ("Should throw a InvalidDestinationException since temp destination should not exist anymore."); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testDeleteDestinationWithSubscribersFails) { + + std::unique_ptr queue(cmsProvider->getSession()->createTemporaryQueue()); + std::unique_ptr consumer(cmsProvider->getSession()->createConsumer(queue.get())); + + // This message delivery should NOT work since the temp connection is now closed. + ASSERT_THROW(queue->destroy(), CMSException) << ("Should fail with CMSException as Subscribers are active"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTempDestinationTest, testCloseConnectionWithManyTempDests) { + + ArrayList< Pointer > tempQueues; + ArrayList< Pointer > producers; + + for (int i = 0; i < 25; ++i) { + Pointer tempQueue(cmsProvider->getSession()->createTemporaryQueue()); + tempQueues.add(tempQueue); + Pointer producer(cmsProvider->getSession()->createProducer(tempQueue.get())); + producers.add(producer); + } + + cmsProvider->getConnection()->close(); + + tempQueues.clear(); + producers.clear(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslTransactionTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslTransactionTest.cpp new file mode 100644 index 000000000..dd91eca4d --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslTransactionTest.cpp @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +namespace activemq{ +namespace test{ +namespace openwire_ssl { + class OpenwireSslTransactionTest : public TransactionTest { + public: + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; +}}} + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; +using namespace activemq::util; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTransactionTest, testSendReceiveTransactedBatches) { + + const int batchCount = 10; + const int batchSize = 20; + + // Create CMS Object for Comms + cms::Session* session = cmsProvider->getSession(); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + for (int j = 0; j < batchCount - 8; j++) { + + std::unique_ptr message(session->createTextMessage("Batch Message")); + + for (int i = 0; i < batchSize; i++) { + ASSERT_NO_THROW(producer->send(message.get())) << ("Send should not throw an exception here."); + } + + ASSERT_NO_THROW(session->commit()) << ("Session Commit should not throw an exception here:"); + + for (int i = 0; i < batchSize; i++) { + ASSERT_NO_THROW(message.reset(dynamic_cast(consumer->receive(1000 * 5)))) << ("Receive Shouldn't throw a Message here:"); + + ASSERT_TRUE(message.get() != NULL) << ("Failed to receive all messages in batch"); + ASSERT_TRUE(string("Batch Message") == message->getText()); + } + + ASSERT_NO_THROW(session->commit()) << ("Session Commit should not throw an exception here:"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTransactionTest, testSendRollback) { + + // Create CMS Object for Comms + cms::Session* session = cmsProvider->getSession(); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr outbound1(session->createTextMessage("First Message")); + std::unique_ptr outbound2(session->createTextMessage("Second Message")); + + // sends a message + producer->send(outbound1.get()); + session->commit(); + + // sends a message that gets rollbacked + std::unique_ptr rollback(session->createTextMessage("I'm going to get rolled back.")); + producer->send(rollback.get()); + session->rollback(); + + // sends a message + producer->send(outbound2.get()); + session->commit(); + + // receives the first message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(1500))); + + // receives the second message + std::unique_ptr inbound2(dynamic_cast(consumer->receive(4000))); + + // validates that the rollbacked was not consumed + session->commit(); + + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + ASSERT_TRUE(outbound2->getText() == inbound2->getText()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTransactionTest, testSendRollbackCommitRollback) { + + // Create CMS Object for Comms + cms::Session* session = cmsProvider->getSession(); + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + cms::MessageProducer* producer = cmsProvider->getProducer(); + + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + std::unique_ptr outbound1(session->createTextMessage("First Message")); + std::unique_ptr outbound2(session->createTextMessage("Second Message")); + + // sends them and then rolls back. + producer->send(outbound1.get()); + producer->send(outbound2.get()); + session->rollback(); + + // Send one and commit. + producer->send(outbound1.get()); + session->commit(); + + // receives the first message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(1500))); + + ASSERT_TRUE(NULL == consumer->receive( 1500 )); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + + session->rollback(); + + inbound1.reset(dynamic_cast(consumer->receive(1500))); + + ASSERT_TRUE(NULL == consumer->receive( 1500 )); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + + // validates that the rollbacked was not consumed + session->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTransactionTest, testSendSessionClose) { + + Pointer connectionFactory( + new ActiveMQConnectionFactory(getBrokerURL())); + + Pointer connection(connectionFactory->createConnection()); + Pointer amqConnection = connection.dynamicCast(); + + connection->start(); + Pointer session(connection->createSession(Session::SESSION_TRANSACTED)); + Pointer destination(session->createQueue("testSendSessionClose")); + + // Create the messages used for this test + std::unique_ptr outbound1(session->createTextMessage("First Message")); + std::unique_ptr outbound2(session->createTextMessage("Second Message")); + + Pointer consumer(session->createConsumer(destination.get())); + Pointer producer(session->createProducer(destination.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + // Send Message #1 + producer->send(outbound1.get()); + session->commit(); + + // Send a Message but roll it back by closing the session that owns the resources + std::unique_ptr rollback(session->createTextMessage("I'm going to get rolled back.")); + producer->send(outbound2.get()); + session->close(); + + session.reset(connection->createSession(Session::SESSION_TRANSACTED)); + destination.reset(session->createQueue("testSendSessionClose")); + consumer.reset(session->createConsumer(destination.get())); + producer.reset(session->createProducer(destination.get())); + producer->setDeliveryMode(DeliveryMode::NON_PERSISTENT); + + // Send Message #2 + producer->send(outbound2.get()); + session->commit(); + + // receives the first message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(1500))); + + // receives the second message + std::unique_ptr inbound2(dynamic_cast(consumer->receive(4000))); + + // validates that the rolled back was not consumed + session->commit(); + + ASSERT_TRUE(inbound1.get() != NULL) << ("Failed to receive first message"); + ASSERT_TRUE(inbound2.get() != NULL) << ("Failed to receive second message"); + + ASSERT_TRUE(outbound1->getText() == inbound1->getText()) << ("First messages aren't equal"); + ASSERT_TRUE(outbound2->getText() == inbound2->getText()) << ("Second messages aren't equal"); + + session->close(); + amqConnection->destroyDestination(destination.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTransactionTest, testWithTTLSet) { + + cmsProvider->getProducer()->setDeliveryMode(DeliveryMode::PERSISTENT); + + cms::MessageConsumer* consumer = cmsProvider->getConsumer(); + + std::unique_ptr outbound1(cmsProvider->getSession()->createTextMessage("First Message")); + + const std::size_t NUM_MESSAGES = 50; + + // sends a message + for (std::size_t i = 0; i < NUM_MESSAGES; ++i) { + cmsProvider->getProducer()->send(outbound1.get(), cms::DeliveryMode::PERSISTENT, + cmsProvider->getProducer()->getPriority(), 120 * 1000); + } + + cmsProvider->getSession()->commit(); + + for (std::size_t i = 0; i < NUM_MESSAGES; ++i) { + + // receives the second message + std::unique_ptr inbound1(dynamic_cast(consumer->receive(600000))); + ASSERT_TRUE(inbound1.get() != NULL); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + } + + cmsProvider->getSession()->commit(); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslTransactionTest, testSessionCommitAfterConsumerClosed) { + + ActiveMQConnectionFactory factory(getBrokerURL()); + std::unique_ptr connection(factory.createConnection()); + + { + std::unique_ptr session(connection->createSession(cms::Session::AUTO_ACKNOWLEDGE)); + std::unique_ptr queue(session->createQueue("testSessionCommitAfterConsumerClosed")); + std::unique_ptr producer(session->createProducer(queue.get())); + + std::unique_ptr message(session->createTextMessage("Hello")); + producer->send(message.get()); + producer->close(); + session->close(); + } + + std::unique_ptr session(connection->createSession(cms::Session::SESSION_TRANSACTED)); + std::unique_ptr queue(session->createQueue("testSessionCommitAfterConsumerClosed")); + std::unique_ptr consumer(session->createConsumer(queue.get())); + + connection->start(); + + std::unique_ptr message(consumer->receive(5000)); + ASSERT_TRUE(message.get() != NULL); + + consumer->close(); + session->commit(); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslVirtualTopicTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslVirtualTopicTest.cpp new file mode 100644 index 000000000..0523b03d4 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslVirtualTopicTest.cpp @@ -0,0 +1,118 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::util; +using namespace activemq::test; +using namespace activemq::exceptions; + +namespace { + + const string PRODUCER_DESTINATION_NAME = "VirtualTopic.TestDestination"; + const string CONSUMER_A_DESTINATION_NAME = "Consumer.A.VirtualTopic.TestDestination"; + const string CONSUMER_B_DESTINATION_NAME = "Consumer.B.VirtualTopic.TestDestination"; + + void runVirtualTopicSyncTest(activemq::util::CMSProvider* cmsProvider, cms::Session::AcknowledgeMode mode) { + + cmsProvider->setAckMode(mode); + cmsProvider->reconnectSession(); + + // Create CMS Object for Comms + cms::Session* session(cmsProvider->getSession()); + + std::unique_ptr topic(session->createTopic(PRODUCER_DESTINATION_NAME)); + std::unique_ptr queueA(session->createQueue(CONSUMER_A_DESTINATION_NAME)); + std::unique_ptr queueB(session->createQueue(CONSUMER_B_DESTINATION_NAME)); + + std::unique_ptr producer(session->createProducer(topic.get())); + std::unique_ptr consumerA(session->createConsumer(queueA.get())); + std::unique_ptr consumerB(session->createConsumer(queueB.get())); + + producer->setDeliveryMode(cms::DeliveryMode::NON_PERSISTENT); + + std::unique_ptr txtMessage(session->createTextMessage("TEST MESSAGE")); + + for (std::size_t i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + producer->send(txtMessage.get()); + } + + if (cms::Session::SESSION_TRANSACTED == mode) { + session->commit(); + } + + for (std::size_t i = 0; i < IntegrationCommon::defaultMsgCount; ++i) { + + std::unique_ptr messageA(consumerA->receive(2000)); + ASSERT_TRUE(messageA.get() != NULL); + if (cms::Session::CLIENT_ACKNOWLEDGE == mode) { + messageA->acknowledge(); + } + + std::unique_ptr messageB(consumerB->receive(2000)); + ASSERT_TRUE(messageB.get() != NULL); + if (cms::Session::CLIENT_ACKNOWLEDGE == mode) { + messageB->acknowledge(); + } + } + + if (cms::Session::SESSION_TRANSACTED == mode) { + session->commit(); + } + } +} + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslVirtualTopicTest : public VirtualTopicTest { + public: + std::string getBrokerURL() const override { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + }; + +}}} + +using activemq::test::openwire_ssl::OpenwireSslVirtualTopicTest; + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslVirtualTopicTest, testVirtualTopicSyncReceiveAutoAck) { + runVirtualTopicSyncTest(cmsProvider.get(), cms::Session::AUTO_ACKNOWLEDGE); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslVirtualTopicTest, testVirtualTopicSyncReceiveClinetAck) { + runVirtualTopicSyncTest(cmsProvider.get(), cms::Session::CLIENT_ACKNOWLEDGE); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslVirtualTopicTest, testVirtualTopicSyncReceiveTransacted) { + runVirtualTopicSyncTest(cmsProvider.get(), cms::Session::SESSION_TRANSACTED); +} diff --git a/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslXATransactionsTest.cpp b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslXATransactionsTest.cpp new file mode 100644 index 000000000..68d40e456 --- /dev/null +++ b/activemq-cpp/src/test-integration/activemq/test/openwire_ssl/OpenwireSslXATransactionsTest.cpp @@ -0,0 +1,606 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace cms; +using namespace std; +using namespace decaf; +using namespace decaf::lang; +using namespace decaf::util; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::commands; +using namespace activemq::exceptions; + +namespace activemq { +namespace test { +namespace openwire_ssl { + + class OpenwireSslXATransactionsTest : public ::testing::Test { + protected: + + static const int batchCount; + static const int batchSize; + + util::IdGenerator txIdGen; + + public: + + OpenwireSslXATransactionsTest() : txIdGen() { + } + + virtual ~OpenwireSslXATransactionsTest() { + } + + virtual std::string getBrokerURL() const { + return activemq::util::IntegrationCommon::getInstance().getSslOpenwireURL(); + } + + void SetUp() override {} + void TearDown() override {} + + protected: + + cms::Xid* createXid() const; + + }; + +}}} + +using namespace activemq::test; +using namespace activemq::test::openwire_ssl; + +//////////////////////////////////////////////////////////////////////////////// +const int OpenwireSslXATransactionsTest::batchCount = 10; +const int OpenwireSslXATransactionsTest::batchSize = 20; + +//////////////////////////////////////////////////////////////////////////////// +cms::Xid* OpenwireSslXATransactionsTest::createXid() const { + + std::string branchQualStr = UUID::randomUUID().toString(); + std::string globalTxIdStr = this->txIdGen.generateId(); + + std::vector branchQual( branchQualStr.begin(), branchQualStr.end() ); + std::vector globalTxId( globalTxIdStr.begin(), globalTxIdStr.end() ); + + if( (int)branchQual.size() > Xid::MAXBQUALSIZE ) { + branchQual.resize( Xid::MAXBQUALSIZE ); + } + + if( (int)globalTxId.size() > Xid::MAXGTRIDSIZE ) { + globalTxId.resize( Xid::MAXGTRIDSIZE ); + } + + XATransactionId* id = new XATransactionId(); + + id->setFormatId( 0 ); + id->setBranchQualifier( branchQual ); + id->setGlobalTransactionId( globalTxId ); + + return id; +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testCreateXAConnectionFactory) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + ConnectionFactory* cmsFactory = dynamic_cast( factory.get() ); + ASSERT_TRUE(cmsFactory != NULL); + + ActiveMQConnectionFactory* amqFactory = dynamic_cast( factory.get() ); + ASSERT_TRUE(amqFactory != NULL); + + ActiveMQXAConnectionFactory* amqXAFactory = dynamic_cast( factory.get() ); + ASSERT_TRUE(amqXAFactory != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testCreateXAConnection) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + Connection* cmsConnection = dynamic_cast( connection.get() ); + ASSERT_TRUE(cmsConnection != NULL); + + ActiveMQConnection* amqConnection = dynamic_cast( connection.get() ); + ASSERT_TRUE(amqConnection != NULL); + + ActiveMQXAConnection* amqXAConnection = dynamic_cast( connection.get() ); + ASSERT_TRUE(amqXAConnection != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testCreateXASession) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + Session* cmsSession = dynamic_cast( session.get() ); + ASSERT_TRUE(cmsSession != NULL); + + ActiveMQSession* amqSession = dynamic_cast( session.get() ); + ASSERT_TRUE(amqSession != NULL); + + ActiveMQXASession* amqXASession = dynamic_cast( session.get() ); + ASSERT_TRUE(amqXASession != NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testGetXAResource) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + ASSERT_TRUE(xaResource->isSameRM( xaResource )); + ASSERT_NO_THROW(xaResource->setTransactionTimeout( 10000 )); + ASSERT_TRUE(xaResource->getTransactionTimeout() == 0); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testSendReceiveOutsideTX) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + ActiveMQXASession* amqXASession = dynamic_cast( session.get() ); + ASSERT_TRUE(amqXASession != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + std::unique_ptr consumer( session->createConsumer( destination.get() ) ); + + ASSERT_TRUE(amqXASession->isAutoAcknowledge() == true); + ASSERT_TRUE(amqXASession->isTransacted() == false); + + connection->start(); + + for( int i = 0; i < 50; ++i ) { + std::unique_ptr message( session->createTextMessage( "TEST" ) ); + producer->send( message.get() ); + } + + for( int i = 0; i < 50; ++i ) { + std::unique_ptr message( consumer->receive( 3000 ) ); + ASSERT_TRUE(message.get() != NULL); + ASSERT_TRUE(dynamic_cast( message.get() ) != NULL); + } +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testSendReceiveTransactedBatches) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + std::unique_ptr consumer( session->createConsumer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + connection->start(); + + for( int j = 0; j < batchCount; j++ ) { + + std::unique_ptr txIdSend( this->createXid() ); + xaResource->start( txIdSend.get(), 0 ); + + std::unique_ptr message( session->createTextMessage( "Batch Message" ) ); + + for( int i = 0; i < batchSize; i++ ) { + ASSERT_NO_THROW(producer->send( message.get() )) << ("Send should not throw an exception here."); + } + + ASSERT_NO_THROW(xaResource->end( txIdSend.get(), XAResource::TMSUCCESS )) << ("Should not have thrown an Exception for xaResource->end"); + ASSERT_NO_THROW(xaResource->prepare( txIdSend.get() )) << ("Should not have thrown an Exception for xaResource->prepare"); + ASSERT_NO_THROW(xaResource->commit( txIdSend.get(), false )) << ("Should not have thrown an Exception for xaResource->commit"); + + std::unique_ptr txIdRecv( this->createXid() ); + xaResource->start( txIdRecv.get(), 0 ); + + for( int i = 0; i < batchSize; i++ ) { + + ASSERT_NO_THROW(message.reset( dynamic_cast( consumer->receive( 1000 * 5 ) ) )) << ("Receive Shouldn't throw a Message here:"); + + ASSERT_TRUE(message.get() != NULL) << ("Failed to receive all messages in batch"); + ASSERT_TRUE(string("Batch Message") == message->getText()); + } + + ASSERT_NO_THROW(xaResource->end( txIdRecv.get(), XAResource::TMSUCCESS )) << ("Should not have thrown an Exception for xaResource->end"); + ASSERT_NO_THROW(xaResource->prepare( txIdRecv.get() )) << ("Should not have thrown an Exception for xaResource->prepare"); + ASSERT_NO_THROW(xaResource->commit( txIdRecv.get(), false )) << ("Should not have thrown an Exception for xaResource->commit"); + } + + std::unique_ptr message; + + ASSERT_NO_THROW(message.reset( consumer->receive( 2000 ) )) << ("Receive Shouldn't throw a Message here:"); + + ASSERT_TRUE(message.get() == NULL) << ("Unexpected Message Received after XA Batches all processed"); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testSendRollback) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + std::unique_ptr consumer( session->createConsumer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + connection->start(); + + std::unique_ptr outbound1( session->createTextMessage( "First Message" ) ); + std::unique_ptr outbound2( session->createTextMessage( "Second Message" ) ); + + // start a new XA Transaction + std::unique_ptr ixId( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // sends a message + producer->send( outbound1.get() ); + + // commit the sent message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->prepare( ixId.get() ); + xaResource->commit( ixId.get(), false ); + + // start a new XA Transaction + ixId.reset( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // sends a message that gets rollbacked + std::unique_ptr rollback( + session->createTextMessage( "I'm going to get rolled back." ) ); + producer->send( rollback.get() ); + + // Roll back the sent message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->rollback( ixId.get() ); + + // start a new XA Transaction + ixId.reset( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // sends a message + producer->send( outbound2.get() ); + + // commit the sent message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->prepare( ixId.get() ); + xaResource->commit( ixId.get(), false ); + + // receives the first message + std::unique_ptr inbound1( + dynamic_cast( consumer->receive( 1500 ) ) ); + + // receives the second message + std::unique_ptr inbound2( + dynamic_cast( consumer->receive( 4000 ) ) ); + + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + ASSERT_TRUE(outbound2->getText() == inbound2->getText()); + + // Checks to make sure there's no other messages on the Destination. + ASSERT_TRUE(consumer->receive( 3000 ) == NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testSendRollbackCommitRollback) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + connection->start(); + + std::unique_ptr outbound1( session->createTextMessage( "First Message" ) ); + std::unique_ptr outbound2( session->createTextMessage( "Second Message" ) ); + + // start a new XA Transaction + std::unique_ptr ixId( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // sends them and then rolls back. + producer->send( outbound1.get() ); + producer->send( outbound2.get() ); + + // Roll back the sent message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->rollback( ixId.get() ); + + // start a new XA Transaction + ixId.reset( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // Send one and commit. + producer->send( outbound1.get() ); + + // commit the sent message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->prepare( ixId.get() ); + xaResource->commit( ixId.get(), false ); + + // start a new XA Transaction + ixId.reset( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + std::unique_ptr consumer( session->createConsumer( destination.get() ) ); + + // receives the first message + std::unique_ptr inbound1( + dynamic_cast( consumer->receive( 1500 ) ) ); + + ASSERT_TRUE(NULL == consumer->receive( 1500 )); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + + // Roll back the sent message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->rollback( ixId.get() ); + + consumer->close(); + + // start a new XA Transaction + ixId.reset( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + consumer.reset( session->createConsumer( destination.get() ) ); + + inbound1.reset( + dynamic_cast( consumer->receive( 1500 ) ) ); + + ASSERT_TRUE(NULL == consumer->receive( 1500 )); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + + // commit the received message + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->prepare( ixId.get() ); + xaResource->commit( ixId.get(), false ); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testWithTTLSet) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + connection->start(); + + std::unique_ptr outbound1( session->createTextMessage( "First Message" ) ); + + const std::size_t NUM_MESSAGES = 50; + + // start a new XA Transaction + std::unique_ptr ixId( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // sends a message + for( std::size_t i = 0; i < NUM_MESSAGES; ++i ) { + producer->send( outbound1.get(), cms::DeliveryMode::PERSISTENT, 4, 120*1000 ); + } + + // commit the sent messages + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->prepare( ixId.get() ); + xaResource->commit( ixId.get(), false ); + + // start a new XA Transaction + ixId.reset( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + std::unique_ptr consumer( session->createConsumer( destination.get() ) ); + + for( std::size_t i = 0; i < NUM_MESSAGES; ++i ) { + + std::unique_ptr inbound1( + dynamic_cast( consumer->receive( 600000 ) ) ); + ASSERT_TRUE(inbound1.get() != NULL); + ASSERT_TRUE(outbound1->getText() == inbound1->getText()); + } + + // commit the received messages + xaResource->end( ixId.get(), XAResource::TMSUCCESS ); + xaResource->prepare( ixId.get() ); + xaResource->commit( ixId.get(), false ); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testXAResource_Exception1) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + // start a new XA Transaction + std::unique_ptr ixId( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // prepare the sent messages without an end call. + ASSERT_THROW(xaResource->prepare( ixId.get() ), XAException) << ("Prepare Should have thrown an XAException"); + + xaResource->forget( ixId.get() ); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testXAResource_Exception2) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + // start a new XA Transaction + std::unique_ptr ixId( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // commit the sent messages without an end call. + ASSERT_THROW(xaResource->commit( ixId.get(), true ), XAException) << ("Commit Should have thrown an XAException"); + + xaResource->forget( ixId.get() ); +} + +//////////////////////////////////////////////////////////////////////////////// +TEST_F(OpenwireSslXATransactionsTest, testXAResource_Exception3) { + + std::unique_ptr factory( + XAConnectionFactory::createCMSXAConnectionFactory( getBrokerURL() ) ); + ASSERT_TRUE(factory.get() != NULL); + + std::unique_ptr connection( factory->createXAConnection() ); + ASSERT_TRUE(connection.get() != NULL); + + std::unique_ptr session( connection->createXASession() ); + ASSERT_TRUE(session.get() != NULL); + + std::unique_ptr destination( session->createTemporaryQueue() ); + std::unique_ptr producer( session->createProducer( destination.get() ) ); + + XAResource* xaResource = session->getXAResource(); + ASSERT_TRUE(xaResource != NULL); + + // start a new XA Transaction + std::unique_ptr ixId( this->createXid() ); + std::unique_ptr ixIdOther( this->createXid() ); + xaResource->start( ixId.get(), 0 ); + + // rollback the sent messages without an end call. + ASSERT_THROW(xaResource->end( ixIdOther.get(), XAResource::TMSUSPEND ), XAException) << ("end Should have thrown an XAException"); + + xaResource->forget( ixId.get() ); +} diff --git a/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.cpp b/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.cpp index b06566d7d..112995ff0 100644 --- a/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.cpp +++ b/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.cpp @@ -16,9 +16,11 @@ */ #include "IntegrationCommon.h" +#include using namespace activemq; using namespace util; +using namespace decaf::lang; //////////////////////////////////////////////////////////////////////////////// const int IntegrationCommon::defaultDelay = 1000; @@ -30,6 +32,7 @@ IntegrationCommon::IntegrationCommon() : urlCommon() , stompURL() , openwireURL() + , sslOpenwireURL() , openwireURL1() , openwireURL2() , openwireURL3() @@ -39,6 +42,11 @@ IntegrationCommon::IntegrationCommon() this->stompURL = this->urlCommon + "61613?wireFormat=stomp"; this->openwireURL = this->urlCommon + "61616?transport.trace=false"; + // SSL URL for SSL integration testing + // Requires: docker compose --profile ssl up (certificates generated automatically) + // Peer verification uses ca.pem injected via SSL_CERT_FILE by CTest (see CMakeLists.txt) + this->sslOpenwireURL = "ssl://localhost:61617?transport.trace=false"; + // Multi-broker URLs for failover testing // These require: docker compose --profile failover up this->openwireURL1 = this->urlCommon + "61617?transport.trace=false"; diff --git a/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.h b/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.h index 988ae262e..969bbf45b 100644 --- a/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.h +++ b/activemq-cpp/src/test-integration/activemq/util/IntegrationCommon.h @@ -36,6 +36,17 @@ namespace util{ return this->openwireURL; } + // SSL URL for SSL integration testing + // Requires: docker compose --profile ssl up + + /** + * Returns the SSL OpenWire URL (port 61617). + * Requires: docker compose --profile ssl up (certificates generated automatically) + */ + virtual std::string getSslOpenwireURL() const { + return this->sslOpenwireURL; + } + // Failover URLs for multi-broker testing // Requires: docker compose --profile failover up @@ -85,6 +96,7 @@ namespace util{ std::string urlCommon; std::string stompURL; std::string openwireURL; + std::string sslOpenwireURL; // Multi-broker URLs std::string openwireURL1; diff --git a/activemq-cpp/src/test/CMakeLists.txt b/activemq-cpp/src/test/CMakeLists.txt index 145396012..562e159ec 100644 --- a/activemq-cpp/src/test/CMakeLists.txt +++ b/activemq-cpp/src/test/CMakeLists.txt @@ -78,9 +78,13 @@ add_unit_test_module( LABELS activemq threads ) -# ─── Module 6: activemq-transport (14 tests) ───────────────────────────────── +# ─── Module 6: activemq-transport (13 tests + 1 SSL) ───────────────────────── # MockBrokerService.cpp is needed by FailoverTransportTest and # SingleBrokerReconnectTest but contains no TEST_F macros. +set(_transport_ssl_sources) +if(AMQCPP_USE_SSL) + list(APPEND _transport_ssl_sources activemq/transport/tcp/SslTransportTest.cpp) +endif() add_unit_test_module( NAME neoactivemq-unit-activemq-transport SOURCES @@ -97,7 +101,7 @@ add_unit_test_module( activemq/transport/mock/MockTransportFactoryTest.cpp activemq/transport/tcp/TcpTransportTest.cpp activemq/transport/tcp/SingleBrokerReconnectTest.cpp - activemq/transport/tcp/SslTransportTest.cpp + ${_transport_ssl_sources} EXTRA_SOURCES activemq/mock/MockBrokerService.cpp LABELS activemq transport @@ -195,14 +199,20 @@ add_unit_test_module( LABELS activemq wireformat ) -# ─── Module 9: decaf-internal (16 tests) ───────────────────────────────────── +# ─── Module 9: decaf-internal (14 tests + 2 SSL) ───────────────────────────── +set(_decaf_internal_ssl_sources) +if(AMQCPP_USE_SSL) + list(APPEND _decaf_internal_ssl_sources + decaf/internal/net/ssl/DefaultSSLSocketFactoryTest.cpp + decaf/internal/net/ssl/openssl/OpenSSLSocketTest.cpp + ) +endif() add_unit_test_module( NAME neoactivemq-unit-decaf-internal SOURCES decaf/internal/net/URIEncoderDecoderTest.cpp decaf/internal/net/URIHelperTest.cpp - decaf/internal/net/ssl/DefaultSSLSocketFactoryTest.cpp - decaf/internal/net/ssl/openssl/OpenSSLSocketTest.cpp + ${_decaf_internal_ssl_sources} decaf/internal/nio/BufferFactoryTest.cpp decaf/internal/nio/ByteArrayBufferTest.cpp decaf/internal/nio/CharArrayBufferTest.cpp @@ -268,7 +278,11 @@ add_unit_test_module( LABELS decaf lang ) -# ─── Module 12: decaf-net (12 tests, includes ssl) ─────────────────────────── +# ─── Module 12: decaf-net (11 tests + 1 SSL) ───────────────────────────────── +set(_decaf_net_ssl_sources) +if(AMQCPP_USE_SSL) + list(APPEND _decaf_net_ssl_sources decaf/net/ssl/SSLSocketFactoryTest.cpp) +endif() add_unit_test_module( NAME neoactivemq-unit-decaf-net SOURCES @@ -283,7 +297,7 @@ add_unit_test_module( decaf/net/URLDecoderTest.cpp decaf/net/URLEncoderTest.cpp decaf/net/URLTest.cpp - decaf/net/ssl/SSLSocketFactoryTest.cpp + ${_decaf_net_ssl_sources} LABELS decaf net ) diff --git a/activemq-cpp/src/test/decaf/net/ssl/SSLSocketFactoryTest.cpp b/activemq-cpp/src/test/decaf/net/ssl/SSLSocketFactoryTest.cpp index 73611cfa2..c9e90ebd4 100644 --- a/activemq-cpp/src/test/decaf/net/ssl/SSLSocketFactoryTest.cpp +++ b/activemq-cpp/src/test/decaf/net/ssl/SSLSocketFactoryTest.cpp @@ -49,7 +49,7 @@ TEST_F(SSLSocketFactoryTest, testGetDefault) { ASSERT_TRUE(factory != NULL); -#ifdef HAVE_OPENSSL +#ifdef AMQCPP_USE_SSL std::unique_ptr sock( factory->createSocket() ); ASSERT_TRUE(sock.get() != NULL); diff --git a/activemq-cpp/src/test/main.cpp b/activemq-cpp/src/test/main.cpp index 2bc509f91..5ba541714 100644 --- a/activemq-cpp/src/test/main.cpp +++ b/activemq-cpp/src/test/main.cpp @@ -31,6 +31,29 @@ #include #include +#ifdef _WIN32 +#include +static LONG WINAPI flightRecorderExceptionFilter(EXCEPTION_POINTERS* /*exInfo*/) { + std::cerr << std::endl << "=== CRASH DETECTED - Flight Recorder Dump (last " + << activemq::util::AMQLogger::flightRecorderSize() << " entries) ===" << std::endl; + activemq::util::AMQLogger::dumpFlightRecorder(std::cerr); + std::cerr << "=== End Flight Recorder Dump ===" << std::endl; + std::cerr.flush(); + return EXCEPTION_CONTINUE_SEARCH; +} +#else +#include +static void flightRecorderSignalHandler(int sig) { + std::cerr << std::endl << "=== SIGNAL " << sig << " - Flight Recorder Dump (last " + << activemq::util::AMQLogger::flightRecorderSize() << " entries) ===" << std::endl; + activemq::util::AMQLogger::dumpFlightRecorder(std::cerr); + std::cerr << "=== End Flight Recorder Dump ===" << std::endl; + std::cerr.flush(); + signal(sig, SIG_DFL); + raise(sig); +} +#endif + int main( int argc, char **argv ) { activemq::library::ActiveMQCPP::initializeLibrary(); @@ -39,6 +62,21 @@ int main( int argc, char **argv ) { activemq::util::AMQLogger::initializeFlightRecorder(0.005); // Enable DEBUG level logging globally to record entries to flight recorder activemq::util::AMQLogger::setLevel(activemq::util::AMQLogLevel::DEBUG); + + // Install crash handler to dump Flight Recorder on segfault / access violation +#ifdef _WIN32 + SetUnhandledExceptionFilter(flightRecorderExceptionFilter); +#else + struct sigaction sa = {}; + sa.sa_handler = flightRecorderSignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; + sigaction(SIGSEGV, &sa, nullptr); + sigaction(SIGABRT, &sa, nullptr); + sigaction(SIGFPE, &sa, nullptr); + sigaction(SIGBUS, &sa, nullptr); + sigaction(SIGILL, &sa, nullptr); +#endif // Enable record-only mode: skip formatting overhead, only record to flight recorder // Logs will be formatted and printed only on failure/timeout (lazy formatting) activemq::util::AMQLogger::setRecordOnlyMode(true); diff --git a/cmake/neoactivemq-cpp-config.cmake b/cmake/neoactivemq-cpp-config.cmake index b68d4e26a..2cccf7069 100644 --- a/cmake/neoactivemq-cpp-config.cmake +++ b/cmake/neoactivemq-cpp-config.cmake @@ -12,7 +12,7 @@ find_dependency(asio CONFIG REQUIRED) find_dependency(ZLIB REQUIRED) # Optional: OpenSSL support -if(@AMQCPP_HAVE_OPENSSL@) +if(@AMQCPP_USE_SSL@) find_dependency(OpenSSL) endif() diff --git a/cmake/presets/base.json b/cmake/presets/base.json index f260369f1..26431a502 100644 --- a/cmake/presets/base.json +++ b/cmake/presets/base.json @@ -37,6 +37,7 @@ "hidden": true, "cacheVariables": { "AMQCPP_BUILD_TESTS": true, + "AMQCPP_USE_SSL": true, "VCPKG_MANIFEST_FEATURES": "tests;ssl" } }, diff --git a/docker-compose.yml b/docker-compose.yml index 90f39272e..21b3f3959 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,5 @@ services: # Default single broker for basic integration tests - # Configure with: ACTIVEMQ_IMAGE=rmohr/activemq:5.15.9 docker compose up activemq: image: ${ACTIVEMQ_IMAGE:-apache/activemq-classic:5.18.7} container_name: activemq-test @@ -18,6 +17,53 @@ services: start_period: 30s restart: unless-stopped + # ============================================================================ + # SSL INTEGRATION TEST BROKER + # Use with: docker compose --profile ssl up + # + # Certificates are generated automatically by the ssl-cert-generator service. + # + # Exposes: + # - Port 61617: SSL OpenWire (ssl://) + # - Port 8165: Web Console (remapped to avoid conflict with default broker) + # ============================================================================ + + # Generate SSL certificates for ActiveMQ SSL testing + ssl-cert-generator: + image: alpine/openssl:latest + container_name: activemq-ssl-cert-generator + profiles: ["ssl"] + volumes: + - ./docker/ssl/generate-certs-entrypoint.sh:/generate-certs.sh:ro + - ./docker/ssl/certs:/certs + entrypoint: ["/bin/sh"] + command: ["/generate-certs.sh"] + restart: "no" + + # Broker with SSL transport connector for SSL integration testing + activemq-ssl: + image: ${ACTIVEMQ_IMAGE:-apache/activemq-classic:5.18.7} + container_name: activemq-ssl-test + profiles: ["ssl"] + depends_on: + ssl-cert-generator: + condition: service_completed_successfully + ports: + - "127.0.0.1:61617:61617" # SSL OpenWire (localhost only) + - "127.0.0.1:8165:8161" # Web Console (localhost only, remapped to avoid conflict with default broker) + volumes: + - ./docker/ssl/activemq-ssl.xml:/opt/apache-activemq/conf/activemq.xml:ro + - ./docker/ssl/certs:/opt/apache-activemq/conf/certs:ro + environment: + - ACTIVEMQ_OPTS=-Xms512m -Xmx2g -Djetty.host=0.0.0.0 + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:8161/admin || curl -sf http://localhost:8161/"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + restart: unless-stopped + # ============================================================================ # FAILOVER AND MULTI-CONNECTION TEST BROKERS # Use with: docker compose --profile failover up @@ -25,10 +71,9 @@ services: # Supported ActiveMQ versions (set ACTIVEMQ_IMAGE env var): # - apache/activemq-classic:5.18.7 (default) # - apache/activemq-classic:6.2.0 - # - rmohr/activemq:5.15.9 # # Example: - # ACTIVEMQ_IMAGE=rmohr/activemq:5.15.9 docker compose --profile failover up -d + # ACTIVEMQ_IMAGE=apache/activemq-classic:6.2.0 docker compose --profile failover up -d # ============================================================================ # Broker 1 for failover testing (primary) diff --git a/docker/ssl/QUICKSTART.md b/docker/ssl/QUICKSTART.md new file mode 100644 index 000000000..b1c742a5e --- /dev/null +++ b/docker/ssl/QUICKSTART.md @@ -0,0 +1,26 @@ +# SSL Testing Quick Start + +## Start ActiveMQ with SSL + +```bash +docker compose --profile ssl up +``` + +Certificates are generated automatically. The SSL broker will be available at `ssl://localhost:61617` + +## Run SSL Tests + +```bash +cmake --build --preset x86-windows-debug-test +ctest --preset x86-windows-debug-test -R SSL +``` + +## Stop the Broker + +```bash +docker compose --profile ssl down +``` + +## Need More Info? + +See [README.md](README.md) for detailed documentation, troubleshooting, and advanced usage. diff --git a/docker/ssl/README.md b/docker/ssl/README.md new file mode 100644 index 000000000..a1c6ef61c --- /dev/null +++ b/docker/ssl/README.md @@ -0,0 +1,176 @@ +# SSL Certificate Generation for ActiveMQ Testing + +This directory contains scripts to generate self-signed SSL certificates for ActiveMQ integration testing. + +## Quick Start + +Start the SSL broker with automatic certificate generation: + +```bash +docker compose --profile ssl up +``` + +That's it! The `ssl-cert-generator` service will: +1. Check if valid certificates already exist +2. Generate new certificates if needed (or if they're expiring soon) +3. Start ActiveMQ SSL broker on `ssl://localhost:61617` + +**Prerequisites:** +- Docker Desktop (Windows/macOS) or Docker Engine (Linux) +- Docker Compose V2 + +**Tip:** See [QUICKSTART.md](QUICKSTART.md) for just the essential commands. + +## Generated Files + +The scripts create the following files in the `certs/` directory: + +| File | Description | Used By | +|------|-------------|---------| +| `ca.pem` | CA certificate (PEM format) | C++ client for trust verification | +| `ca-key.pem` | CA private key | Certificate signing (not used in tests) | +| `broker.p12` | Broker keystore (PKCS12) | ActiveMQ broker (server certificate) | +| `broker-truststore.p12` | Broker truststore (PKCS12) | ActiveMQ broker (CA trust) | +| `broker-cert.pem` | Broker certificate (PEM) | Intermediate file | +| `broker-key.pem` | Broker private key (PEM) | Intermediate file | + +**Password:** All keystores use the password `password` + +**Validity:** All certificates are valid for 3650 days (10 years) + +**Subject Alternative Names:** The broker certificate includes: +- DNS: `localhost` +- IP: `127.0.0.1` + +## How It Works + +When you run `docker compose --profile ssl up`: + +1. The `ssl-cert-generator` service starts first using the `alpine/openssl` image +2. It runs [generate-certs-entrypoint.sh](generate-certs-entrypoint.sh) inside the container +3. The script checks if valid certificates exist (valid for at least 30 more days) +4. If certificates are missing or expiring, it generates new ones +5. Once complete, the service exits successfully +6. The `activemq-ssl` service starts only after certificate generation succeeds +7. ActiveMQ loads the certificates from the shared `certs/` volume + +## Common Operations + +### Viewing Logs + +```bash +# View certificate generator logs +docker compose --profile ssl logs ssl-cert-generator + +# View ActiveMQ SSL broker logs +docker compose --profile ssl logs activemq-ssl + +# Follow logs in real-time +docker compose --profile ssl logs -f +``` + +### Stopping the Broker + +```bash +# Stop and remove containers +docker compose --profile ssl down + +# Stop but keep containers +docker compose --profile ssl stop +``` + +### Regenerating Certificates + +Certificates are valid for 10 years and automatically regenerate when: +- They don't exist +- They're expiring within 30 days + +To force regeneration: + +```bash +# Stop the broker +docker compose --profile ssl down + +# Delete certificates +rm -rf docker/ssl/certs/*.pem docker/ssl/certs/*.p12 +# On Windows PowerShell: Remove-Item docker/ssl/certs/*.pem,docker/ssl/certs/*.p12 + +# Restart (will regenerate automatically) +docker compose --profile ssl up +``` + +### Running Tests + +The SSL integration tests provide comprehensive validation of the SSL/TLS transport layer (29 tests total, mirroring all OpenWire test functionality). + +```bash +# Build tests +cmake --build --preset x86-windows-debug-test + +# Run all SSL integration tests (29 tests) +SSL_CERT_FILE=docker/ssl/certs/ca.pem ctest --preset x86-windows-debug-test -L integration-openwire-ssl --output-on-failure + +# Or run all integration tests (OpenWire + OpenWire SSL + STOMP) +ctest --preset x86-windows-debug-test -L integration --output-on-failure +``` + +**SSL Test Coverage:** +- ✅ All acknowledgment modes (client, individual, optimized) +- ✅ Advisory messages +- ✅ Async sending and callbacks +- ✅ Message selectors and groups +- ✅ Durable subscriptions +- ✅ Transactions (local and XA) +- ✅ Redelivery policies and session recovery +- ✅ Temporary destinations +- ✅ Message compression and priority +- ✅ Queue browsing and virtual topics +- ✅ Slow consumers and expiration +- ✅ Enhanced connection features + +## Troubleshooting + +### "No such file or directory" error +Make sure you're running from the project root directory (where `docker-compose.yml` is located). + +### Docker not found +Install Docker Desktop and ensure it's running: +```bash +docker --version +docker ps +``` + +### Certificate validation failures in tests +1. Check certificates were generated: `ls docker/ssl/certs/` +2. Verify broker is running: `docker compose --profile ssl ps` +3. Check broker logs: `docker compose --profile ssl logs activemq-ssl` +4. Verify certificate expiry: `openssl x509 -in docker/ssl/certs/ca.pem -noout -dates` + +### Port already in use (61617) +Another service is using port 61617. Change the port mapping in `docker-compose.yml`: +```yaml +ports: + - "127.0.0.1:61627:61617" # Map to different local port +``` + +## Integration with Tests + +The generated certificates are used by: + +1. **ActiveMQ Broker** (via `activemq-ssl.xml`): + - Keystore: `broker.p12` + - Truststore: `broker-truststore.p12` + +2. **C++ Client Tests** (via SSL tests): + - Trust certificate: `ca.pem` + +## Security Notes + +⚠️ **These certificates are for testing only!** + +- Self-signed certificates should never be used in production +- The keystore password is hardcoded and publicly known +- Certificates are checked into source control +- No certificate revocation mechanism exists + +For production use, obtain certificates from a trusted Certificate Authority (CA). diff --git a/docker/ssl/activemq-ssl.xml b/docker/ssl/activemq-ssl.xml new file mode 100644 index 000000000..5e6495940 --- /dev/null +++ b/docker/ssl/activemq-ssl.xml @@ -0,0 +1,82 @@ + + + + + + file:${activemq.conf}/credentials.properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docker/ssl/generate-certs-entrypoint.sh b/docker/ssl/generate-certs-entrypoint.sh new file mode 100644 index 000000000..9680ccced --- /dev/null +++ b/docker/ssl/generate-certs-entrypoint.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# Certificate generation script that runs inside alpine/openssl Docker container. +# This script is executed automatically by docker-compose when using the ssl profile. +# +# Creates: +# - ca.pem / ca-key.pem : CA certificate and key (PEM) +# - broker.p12 : Broker keystore (PKCS12, for ActiveMQ) +# - broker-truststore.p12 : Broker truststore containing CA cert (PKCS12) +# +# All certificates are valid for 3650 days (10 years). +# The password for all keystores is "password". + +set -e + +CERT_DIR="/certs" +PASSWORD="password" + +# Check if certificates already exist and are valid +if [ -f "${CERT_DIR}/ca.pem" ] && \ + [ -f "${CERT_DIR}/broker.p12" ] && \ + [ -f "${CERT_DIR}/broker-truststore.p12" ]; then + + # Check if CA certificate is still valid for at least 30 days + if openssl x509 -in "${CERT_DIR}/ca.pem" -noout -checkend 2592000 2>/dev/null; then + echo "=== Valid SSL certificates already exist, skipping generation ===" + ls -lah "${CERT_DIR}/" + exit 0 + else + echo "=== Existing certificates expired or expiring soon, regenerating ===" + fi +else + echo "=== Certificates not found, generating new ones ===" +fi + +# Clean up old/invalid certs +rm -rf "${CERT_DIR}"/* + +echo "=== Generating CA key and certificate ===" +openssl req -x509 -newkey rsa:2048 -nodes \ + -keyout "${CERT_DIR}/ca-key.pem" \ + -out "${CERT_DIR}/ca.pem" \ + -days 3650 \ + -subj "/CN=ActiveMQ Test CA/O=Test/C=US" + +echo "=== Generating broker key and certificate signing request ===" +openssl req -newkey rsa:2048 -nodes \ + -keyout "${CERT_DIR}/broker-key.pem" \ + -out "${CERT_DIR}/broker.csr" \ + -subj "/CN=localhost/O=Test/C=US" + +echo "=== Signing broker certificate with CA ===" +echo "subjectAltName=DNS:localhost,IP:127.0.0.1" > "${CERT_DIR}/broker-ext.cnf" +openssl x509 -req \ + -in "${CERT_DIR}/broker.csr" \ + -CA "${CERT_DIR}/ca.pem" \ + -CAkey "${CERT_DIR}/ca-key.pem" \ + -CAcreateserial \ + -out "${CERT_DIR}/broker-cert.pem" \ + -days 3650 \ + -extfile "${CERT_DIR}/broker-ext.cnf" + +echo "=== Creating broker PKCS12 keystore ===" +openssl pkcs12 -export \ + -in "${CERT_DIR}/broker-cert.pem" \ + -inkey "${CERT_DIR}/broker-key.pem" \ + -certfile "${CERT_DIR}/ca.pem" \ + -out "${CERT_DIR}/broker.p12" \ + -name broker \ + -password "pass:${PASSWORD}" + +echo "=== Creating broker truststore (PKCS12 with CA cert) ===" +openssl pkcs12 -export \ + -nokeys \ + -in "${CERT_DIR}/ca.pem" \ + -out "${CERT_DIR}/broker-truststore.p12" \ + -name ca \ + -password "pass:${PASSWORD}" + +echo "=== Cleaning up intermediate files ===" +rm -f "${CERT_DIR}/broker.csr" \ + "${CERT_DIR}/ca.srl" \ + "${CERT_DIR}/broker-ext.cnf" + +echo "=== Certificate generation complete ===" +echo "Generated files:" +ls -lah "${CERT_DIR}/" +echo "" +echo "CA certificate: ${CERT_DIR}/ca.pem" +echo "Broker keystore: ${CERT_DIR}/broker.p12" +echo "Broker truststore: ${CERT_DIR}/broker-truststore.p12" +echo "" +echo "All keystores use password: ${PASSWORD}"