├── .3rd-party ├── DEPENDENCIES └── README.md ├── .circleci ├── config.yml └── run-qa.sh ├── .github └── workflows │ ├── first-interaction.yml │ └── stale.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── check-dependencies.sh ├── eclipse_codeformatter.xml ├── hawkbit-device-simulator ├── .gitignore ├── README.md ├── docker │ ├── 0.2.0-SNAPSHOT │ │ └── Dockerfile │ └── 0.3.0-SNAPSHOT │ │ └── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── eclipse │ │ │ └── hawkbit │ │ │ └── simulator │ │ │ ├── AbstractSimulatedDevice.java │ │ │ ├── DDISimulatedDevice.java │ │ │ ├── DMFSimulatedDevice.java │ │ │ ├── DeviceSimulator.java │ │ │ ├── DeviceSimulatorRepository.java │ │ │ ├── DeviceSimulatorUpdater.java │ │ │ ├── NextPollTimeController.java │ │ │ ├── SimulatedDeviceFactory.java │ │ │ ├── SimulationController.java │ │ │ ├── SimulationProperties.java │ │ │ ├── SimulatorStartup.java │ │ │ ├── UpdateStatus.java │ │ │ ├── amqp │ │ │ ├── AmqpConfiguration.java │ │ │ ├── AmqpProperties.java │ │ │ ├── DmfReceiverService.java │ │ │ ├── DmfSenderService.java │ │ │ ├── MessageService.java │ │ │ └── SimulatedUpdate.java │ │ │ └── http │ │ │ ├── BasicAuthProperties.java │ │ │ ├── BasicAuthSecurityConfiguration.java │ │ │ ├── GatewayTokenInterceptor.java │ │ │ └── NoAuthSecurityConfiguration.java │ └── resources │ │ ├── application.properties │ │ └── logback-spring.xml │ └── test │ └── java │ └── org │ └── eclipse │ └── hawkbit │ └── simulator │ ├── AllowAllWebSecurityTest.java │ ├── AuthenticatedWebSecurityTest.java │ └── DdiWebSecurityTest.java ├── hawkbit-example-core-feign-client ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── eclipse │ └── hawkbit │ └── feign │ └── core │ └── client │ ├── ApplicationJsonRequestHeaderInterceptor.java │ ├── FeignClientConfiguration.java │ └── IgnoreMultipleConsumersProducersSpringMvcContract.java ├── hawkbit-example-ddi-feign-client ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── eclipse │ └── hawkbit │ └── ddi │ └── client │ └── resource │ └── RootControllerResourceClient.java ├── hawkbit-example-mgmt-feign-client ├── .gitignore ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── eclipse │ └── hawkbit │ └── mgmt │ └── client │ └── resource │ ├── MgmtDistributionSetClientResource.java │ ├── MgmtDistributionSetTagClientResource.java │ ├── MgmtDistributionSetTypeClientResource.java │ ├── MgmtDownloadArtifactClientResource.java │ ├── MgmtDownloadClientResource.java │ ├── MgmtRolloutClientResource.java │ ├── MgmtSoftwareModuleClientResource.java │ ├── MgmtSoftwareModuleTypeClientResource.java │ ├── MgmtSystemManagementClientResource.java │ ├── MgmtTargetClientResource.java │ ├── MgmtTargetFilterQueryClientResource.java │ ├── MgmtTargetTagClientResource.java │ ├── MgmtTenantManagementClientResource.java │ └── builder │ ├── DistributionSetBuilder.java │ ├── DistributionSetTypeBuilder.java │ ├── RolloutBuilder.java │ ├── SoftwareModuleAssigmentBuilder.java │ ├── SoftwareModuleBuilder.java │ ├── SoftwareModuleTypeBuilder.java │ ├── TagBuilder.java │ ├── TargetBuilder.java │ └── TargetFilterQueryBuilder.java ├── hawkbit-example-mgmt-simulator ├── .gitignore ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── eclipse │ │ └── hawkbit │ │ └── mgmt │ │ └── client │ │ ├── Application.java │ │ ├── ClientConfigurationProperties.java │ │ └── scenarios │ │ ├── ConfigurableScenario.java │ │ └── upload │ │ ├── ArtifactFile.java │ │ └── FeignMultipartEncoder.java │ └── resources │ ├── application.properties │ └── logback-spring.xml ├── hawkbit_logo.png ├── licenses ├── LICENSE_HEADER_TEMPLATE.txt ├── LICENSE_HEADER_TEMPLATE_BOSCH.txt ├── LICENSE_HEADER_TEMPLATE_BOSCH_18.txt ├── LICENSE_HEADER_TEMPLATE_BOSCH_19.txt ├── LICENSE_HEADER_TEMPLATE_BOSCH_20.txt ├── LICENSE_HEADER_TEMPLATE_BOSCH_23.txt ├── LICENSE_HEADER_TEMPLATE_MICROSOFT_18.txt ├── LICENSE_HEADER_TEMPLATE_SIEMENS.txt └── LICENSE_HEADER_TEMPLATE_SIEMENS_18.txt └── pom.xml /.3rd-party/README.md: -------------------------------------------------------------------------------- 1 | # Third-Party Dependencies 2 | 3 | This folder provides listings of all 3rd-party dependencies incl. their licenses. There is a dedicated subfolder for each release (and milestone) holding the release-specific information. 4 | 5 | The files are generated using the [check-dependencies.sh](https://github.com/eclipse/hawkbit-examples/tree/master/check-dependencies.sh) script. The script makes use of the [Eclipse Dash License Tool](https://github.com/eclipse/dash-licenses) which identifies and vets the licenses of the project content. 6 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018 Bosch Software Innovations GmbH and others 3 | # 4 | # This program and the accompanying materials are made 5 | # available under the terms of the Eclipse Public License 2.0 6 | # which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | 11 | version: 2 12 | jobs: 13 | build: 14 | 15 | working_directory: ~/hawkBit-examples 16 | 17 | docker: 18 | - image: cimg/openjdk:17.0.11 19 | auth: 20 | username: $DOCKERHUB_USER 21 | password: $DOCKERHUB_ACCESSTOKEN 22 | environment: 23 | _JAVA_OPTIONS: "-Xms512m -Xmx1024m" 24 | 25 | steps: 26 | 27 | - checkout 28 | 29 | - restore_cache: 30 | key: circleci-hawkbit-examples-{{ checksum "pom.xml" }} 31 | 32 | - run: 33 | name: Install dependencies without testing 34 | command: mvn install --batch-mode -DskipTests 35 | 36 | - run: 37 | name: Test incl. license check 38 | command: './.circleci/run-qa.sh' 39 | 40 | - run: 41 | name: Record test results 42 | command: mkdir -p testresults/jUnit && find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} testresults/jUnit/ \; 43 | 44 | - store_test_results: 45 | path: testresults 46 | 47 | - store_artifacts: 48 | path: pom.xml 49 | 50 | - save_cache: 51 | paths: 52 | - ~/.m2 53 | key: circleci-hawkbit-examples-{{ checksum "pom.xml" }} 54 | 55 | notify: 56 | webhooks: 57 | # Gitter hook 58 | - url: https://webhooks.gitter.im/e/a20a6bc2bda5a8a77d39 59 | -------------------------------------------------------------------------------- /.circleci/run-qa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2015 Bosch Software Innovations GmbH and others 4 | # 5 | # This program and the accompanying materials are made 6 | # available under the terms of the Eclipse Public License 2.0 7 | # which is available at https://www.eclipse.org/legal/epl-2.0/ 8 | # 9 | # SPDX-License-Identifier: EPL-2.0 10 | # 11 | 12 | #echo "$CIRCLE_PULL_REQUEST pull request" 13 | 14 | # Run SonarQube only for master branch 15 | if [ $CIRCLE_BRANCH = master ] ; then 16 | mvn verify license:check org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.branch.name=eclipse-master -Dsonar.login=$SONAR_ACCESS_TOKEN --batch-mode 17 | else 18 | mvn verify license:check --batch-mode 19 | fi 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/first-interaction.yml: -------------------------------------------------------------------------------- 1 | name: First User Interaction 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened] 6 | 7 | jobs: 8 | greeting: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/first-interaction@v1 12 | with: 13 | repo-token: ${{ secrets.PAT_SECRET }} 14 | pr-message: |- 15 | Thanks @${{ github.actor }} for taking the time to contribute to hawkBit! We really appreciate this. Make yourself comfortable while I'm looking for a committer to help you with your contribution. 16 | Please make sure you read the [contribution guide](https://github.com/eclipse/hawkbit/blob/master/CONTRIBUTING.md) and signed the Eclipse Contributor Agreement (ECA). -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark & close stale issues 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/stale@v3 13 | with: 14 | repo-token: ${{ secrets.PAT_SECRET }} 15 | days-before-stale: -1 16 | days-before-close: 15 17 | stale-issue-label: 'awaiting' 18 | close-issue-message: |- 19 | There has been no response from the original author so I closed this issue. 20 | Please reach out if you have or find the answers we need so that we can investigate further. 21 | only-labels: 'awaiting' 22 | skip-stale-issue-message: 'true' 23 | skip-stale-pr-message: 'true' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories # 2 | build 3 | bin 4 | tmp 5 | .settings 6 | .classpath 7 | target 8 | .allure 9 | .vscode 10 | .springbeans 11 | .metadata 12 | .project 13 | 14 | # Windows image file caches 15 | Thumbs.db 16 | 17 | # Folder config file 18 | Desktop.ini 19 | 20 | # OSX 21 | .DS_Store 22 | .svn 23 | 24 | # Thumbnails 25 | ._* 26 | 27 | # Files that might appear on external disk 28 | .Spotlight-V100 29 | .Trashes 30 | 31 | # Compiled filed 32 | *.class 33 | 34 | # Package Files # 35 | *.jar 36 | *.war 37 | *.ear 38 | *.db 39 | 40 | # Sonar 41 | .sonar_lock 42 | 43 | # VSCode 44 | classpath-data.json 45 | 46 | # Created by spring-boot-configuration-processor 47 | .factorypath 48 | 49 | # Eclipse IDE 50 | *.pydevproject 51 | 52 | # Maven 53 | maven.properties 54 | .flattened-pom.xml 55 | 56 | # Test Files 57 | *.tmp 58 | 59 | # Intellij IDE 60 | *.iml 61 | .idea 62 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hawkbit-dev@eclipse.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to eclipse hawkBit 2 | 3 | :+1: First off, thanks for taking the time to contribute! We really appreciate this. :+1: 4 | 5 | Please read this if you intend to contribute to the project. 6 | 7 | ## Conventions 8 | 9 | ### Code Style 10 | 11 | * Java files: 12 | * we follow the standard eclipse IDE (built in) code formatter with the following changes: 13 | * Tab policy: spaces only: 4 14 | * We recommend using at least Eclipse [Mars](https://www.eclipse.org/mars/) IDE release. It seems that the Java code formatter line break handling has been changed between [Luna](https://www.eclipse.org/luna/) and Mars. 15 | * XML files: 16 | * we follow the standard eclipse IDE XML formatter with the following changes: 17 | * Indent using spaces only: 3 18 | * SCSS files: 19 | * we follow the standard [scss-lint](https://github.com/brigade/scss-lint/) rules with the following exception: 20 | * disabled rules: ImportantRule, PropertySortOrder 21 | * Sonarqube: 22 | * Our rule set can be found [here](https://sonarcloud.io/organizations/bosch-iot-rollouts/rules) 23 | 24 | ### Utility library usage 25 | 26 | hawkBit has currently [Apache commons lang](https://commons.apache.org/proper/commons-lang/) on the classpath in several of its modules. However, we see introducing too many utility libraries problematic as we force these as transitive dependencies on hawkBit users. We in fact are looking into reducing them in future not adding new ones. 27 | 28 | So we kindly ask contributors: 29 | 30 | * not introduce extra utility library dependencies 31 | * keep them out of the core modules (e.g. hawkbit-core, hawkbit-rest-core, hawkbit-http-security) to avoid that all modules have them as transitive dependency 32 | * use utility functions in general based in the following priority: 33 | * use utility functions from JDK if feasible 34 | * use Spring utility classes if feasible 35 | * use [Apache commons lang](https://commons.apache.org/proper/commons-lang/) if feasible 36 | 37 | ### Test documentation 38 | 39 | Please documented the test cases that you contribute by means of [Allure](http://allure.qatools.ru) annotations and proper test method naming. 40 | 41 | All test classes are documented with [Allure's](https://github.com/allure-framework/allure-core/wiki/Features-and-Stories) **@Features** and **@Stories** annotations in the following format: 42 | ``` 43 | @Features("TEST_TYPE - HAWKBIT_COMPONENT") 44 | @Stories("Test class description") 45 | ``` 46 | 47 | Test types are: 48 | * Unit Tests - for single units tests with a mocked environment 49 | * Component Tests - for complete components including lower layers, e.g. Spring MVC test on rest API including repository and database. 50 | * Integration Tests - including clients, e.g. Selenium UI tests with various browsers. 51 | * System Tests - on target environments, e.g. Cloud Foundry. 52 | 53 | Examples for hawkBit components: 54 | * Management API 55 | * Direct Device Integration API 56 | * Device Management Federation API 57 | * Management UI 58 | * Repository 59 | * Security 60 | 61 | ``` 62 | @Features("Component Tests - Management API") 63 | @Stories("Distribution Set Type Resource") 64 | ``` 65 | 66 | In addition all test method's name describes in **camel case** what the test is all about and has a long description aith Allures **@Description** annotation. 67 | 68 | ## Legal considerations for your contribution 69 | 70 | The following steps are necessary to comply with the Eclipse Foundation's IP policy. 71 | 72 | Please also read [this](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git) 73 | 74 | In order for any contributions to be accepted you MUST do the following things. 75 | 76 | * Sign the [Eclipse Foundation Contributor License Agreement](http://www.eclipse.org/legal/CLA.php). 77 | To sign the Eclipse CLA you need to: 78 | 79 | * Obtain an Eclipse Foundation userid. Anyone who currently uses Eclipse Bugzilla or Gerrit systems already has one of those. 80 | If you don’t, you need to [register](https://dev.eclipse.org/site_login/createaccount.php). 81 | 82 | * Login into the [projects portal](https://projects.eclipse.org/), select “My Account”, and then the “Contributor License Agreement” tab. 83 | 84 | * Add your github username in your Eclipse Foundation account settings. Log in it to Eclipse and go to account settings. 85 | 86 | * "Sign-off" your commits 87 | 88 | Every commit you make in your patch or pull request MUST be "signed off". 89 | 90 | You do this by adding the `-s` flag when you make the commit(s), e.g. 91 | 92 | git commit -s -m "Shave the yak some more" 93 | 94 | ### License Header 95 | 96 | Please make sure newly created files contain a proper license header like this: 97 | 98 | ``` 99 | /** 100 | * Copyright (c) and others. 101 | * 102 | * All rights reserved. This program and the accompanying materials 103 | * are made available under the terms of the Eclipse Public License v1.0 104 | * which accompanies this distribution, and is available at 105 | * https://www.eclipse.org/legal/epl-v10.html 106 | */ 107 | ``` 108 | 109 | ## Making your changes 110 | 111 | * Fork the repository on GitHub 112 | * Create a new branch for your changes 113 | * Make your changes 114 | * Make sure you include tests 115 | * Make sure the tests pass after your changes 116 | * Commit your changes into that branch 117 | * Use descriptive and meaningful commit messages 118 | * If you have a lot of commits squash them into a single commit 119 | * Make sure you use the `-s` flag when committing as explained above. 120 | * Push your changes to your branch in your forked repository 121 | 122 | ## Submitting the changes 123 | 124 | Submit a pull request via the normal GitHub UI (desktop or web). 125 | 126 | ## After submitting 127 | 128 | * Do not use your branch for any other development, otherwise further changes that you make will be visible in the PR. 129 | 130 | # Further information 131 | 132 | * [Eclipse Project Page](http://projects.eclipse.org/projects/iot.hawkbit) 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Eclipse hawkBit™ - Examples collection 4 | 5 | Example projects that show how [hawkBit](https://github.com/eclipse/hawkbit) can be customized or hawkBit compatible APIs leveraged. 6 | 7 | [![Circle CI](https://circleci.com/gh/eclipse/hawkbit-examples.svg?style=shield)](https://circleci.com/gh/eclipse/hawkbit-examples) 8 | 9 | ## API client examples 10 | 11 | - `hawkbit-example-ddi-feign-client` : Example client based on the feign project for hawkBit's [Direct Device Integration API](https://www.eclipse.org/hawkbit/apis/ddi_api). 12 | - `hawkbit-example-mgmt-feign-client` : Example client based on the feign project for hawkBit's [Management API](https://www.eclipse.org/hawkbit/apis/management_api) 13 | 14 | ## Simulators 15 | 16 | - `hawkbit-device-simulator` : Simulates device software updates, leveraging the hawkBit device integration options. 17 | - `hawkbit-example-mgmt-simulator` : Example client simulation for the _hawkBit_ Management API based on Spring Boot and the hawkbit-example-mgmt-feign-client. 18 | 19 | 20 | ## Customization examples 21 | 22 | - `hawkbit-custom-theme-example` : Example for a customized theme for [Management UI](https://www.eclipse.org/hawkbit/ui). 23 | -------------------------------------------------------------------------------- /check-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2023 Bosch.IO GmbH and others 4 | # 5 | # This program and the accompanying materials are made 6 | # available under the terms of the Eclipse Public License 2.0 7 | # which is available at https://www.eclipse.org/legal/epl-2.0/ 8 | # 9 | # SPDX-License-Identifier: EPL-2.0 10 | # 11 | 12 | DASH_SUMMARY=".3rd-party/DEPENDENCIES" 13 | DASH_REVIEW_SUMMARY=".3rd-party/DEPENDENCIES_REVIEW" 14 | 15 | if [ -z "$1" ] 16 | then 17 | DASH_IP_LAB= 18 | else 19 | DASH_IP_LAB="-Ddash.review.summary=${DASH_REVIEW_SUMMARY} -Ddash.iplab.token=$1" 20 | fi 21 | 22 | mvn clean install -DskipTests -Ddash.skip=false \ 23 | -Ddash.summary=${DASH_SUMMARY} ${DASH_IP_LAB} -------------------------------------------------------------------------------- /hawkbit-device-simulator/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target/ 3 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/README.md: -------------------------------------------------------------------------------- 1 | # hawkBit Device Simulator 2 | 3 | The device simulator handles software update commands from the update server. It is designed to be used very conveniently, 4 | for example, from within a browser. Hence, all the endpoints use the GET verb. 5 | 6 | ## Run on your own workstation 7 | ``` 8 | java -jar examples/hawkbit-device-simulator/target/hawkbit-device-simulator-*-SNAPSHOT.jar 9 | ``` 10 | Or: 11 | ``` 12 | run org.eclipse.hawkbit.simulator.DeviceSimulator 13 | ``` 14 | 15 | ## hawkBit APIs 16 | 17 | The simulator supports `DDI` as well as the `DMF` integration APIs. 18 | 19 | In case there is no AMQP message broker (like rabbitMQ) running, you can disable the AMQP support for the device simulator, so the simulator is not trying to connect to an amqp message broker. 20 | 21 | Configuration property `hawkbit.device.simulator.amqp.enabled=false` 22 | 23 | ## Usage 24 | 25 | ### REST API 26 | The device simulator exposes an REST-API which can be used to trigger device creation. 27 | 28 | Optional parameters: 29 | * name : name prefix simulated devices (default: "dmfSimulated"), followed by counter 30 | * amount : number of simulated devices (default: 20, capped at: 4000) 31 | * tenant : in a multi-tenant ready hawkBit installation (default: "DEFAULT") 32 | * api : the API which should be used for the simulated device either `dmf` or `ddi` (default: "dmf") 33 | * endpoint : URL which defines the hawkbit DDI base endpoint (default: "http://localhost:8080") 34 | * polldelay : number in seconds of the delay when DDI simulated devices should poll the endpoint (default: "30") 35 | * gatewaytoken : an hawkbit gateway token to be used in case hawkbit does not allow anonymous access for DDI devices (default: "") 36 | 37 | 38 | Example: for 20 simulated devices by DMF API (default) 39 | ``` 40 | http://localhost:8083/start 41 | ``` 42 | 43 | Example: for 10 simulated devices that start with the name prefix "activeSim": 44 | ``` 45 | http://localhost:8083/start?amount=10&name=activeSim 46 | ``` 47 | 48 | Example: for 5 simulated devices that start with the name prefix "ddi" using the Direct Device Integration API (http) authenticated by given gateway token, a pool interval of 10 seconds and a custom port for the DDI service.: 49 | ``` 50 | http://localhost:8083/start?amount=5&name=ddi&api=ddi&gatewaytoken=d5F2mmlARiMuMOquRmLlxW4xZFHy4mEV&polldelay=10&endpoint=http://localhost:8085 51 | ``` 52 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/docker/0.2.0-SNAPSHOT/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u171-jre-alpine 2 | 3 | MAINTAINER Kai Zimmermann 4 | 5 | ENV HAWKBIT_SIM_VERSION=0.2.0-SNAPSHOT \ 6 | HAWKBIT_SIM_HOME=/opt/hawkbit-simulator 7 | 8 | # Http port 9 | EXPOSE 8083 10 | 11 | RUN set -x \ 12 | && mkdir -p $HAWKBIT_SIM_HOME \ 13 | && cd $HAWKBIT_SIM_HOME \ 14 | && apk add --no-cache libressl wget \ 15 | && wget -O hawkbit-simluator.jar --no-verbose "http://repo.eclipse.org/service/local/artifact/maven/redirect?r=hawkbit-snapshots&g=org.eclipse.hawkbit&a=hawkbit-device-simulator&v=0.2.0-SNAPSHOT" 16 | 17 | WORKDIR $HAWKBIT_SIM_HOME 18 | ENTRYPOINT ["java","-jar","hawkbit-simluator.jar"] 19 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/docker/0.3.0-SNAPSHOT/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u171-jre-alpine 2 | 3 | ENV HAWKBIT_SIM_VERSION=0.3.0-SNAPSHOT \ 4 | HAWKBIT_SIM_HOME=/opt/hawkbit-simulator 5 | 6 | # Http port 7 | EXPOSE 8083 8 | 9 | RUN set -x \ 10 | && mkdir -p $HAWKBIT_SIM_HOME \ 11 | && cd $HAWKBIT_SIM_HOME \ 12 | && apk add --no-cache libressl wget \ 13 | && wget -O hawkbit-simluator.jar --no-verbose "http://repo.eclipse.org/service/local/artifact/maven/redirect?r=hawkbit-snapshots&g=org.eclipse.hawkbit&a=hawkbit-device-simulator&v=${HAWKBIT_SIM_VERSION}" 14 | 15 | WORKDIR $HAWKBIT_SIM_HOME 16 | ENTRYPOINT ["java","-jar","hawkbit-simluator.jar"] -------------------------------------------------------------------------------- /hawkbit-device-simulator/pom.xml: -------------------------------------------------------------------------------- 1 | 12 | 15 | 4.0.0 16 | 17 | org.eclipse.hawkbit 18 | ${revision} 19 | hawkbit-examples-parent 20 | 21 | 22 | hawkbit-device-simulator 23 | hawkBit :: Examples :: Device Simulator 24 | Device Management Federation API based simulator 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-maven-plugin 31 | 32 | 33 | 34 | repackage 35 | 36 | 37 | ${baseDir} 38 | org.eclipse.hawkbit.simulator.DeviceSimulator 39 | JAR 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | src/main/resources 48 | 49 | 50 | cf 51 | true 52 | ${project.build.directory} 53 | 54 | manifest.yml 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.eclipse.hawkbit 63 | hawkbit-dmf-api 64 | 65 | 66 | org.eclipse.hawkbit 67 | hawkbit-example-ddi-feign-client 68 | ${project.version} 69 | 70 | 71 | org.springframework.amqp 72 | spring-rabbit 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-web 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-starter-jetty 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-starter-logging 85 | 86 | 87 | org.springframework.security 88 | spring-security-web 89 | 90 | 91 | org.springframework.security 92 | spring-security-config 93 | 94 | 95 | org.springframework.boot 96 | spring-boot-starter 97 | 98 | 99 | org.springframework.boot 100 | spring-boot-starter-actuator 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-autoconfigure 105 | 106 | 107 | org.springframework.cloud 108 | spring-cloud-context 109 | 110 | 111 | org.springframework.cloud 112 | spring-cloud-starter-openfeign 113 | 114 | 115 | io.github.openfeign 116 | feign-jackson 117 | 118 | 119 | org.apache.httpcomponents 120 | httpclient 121 | 4.5.14 122 | 123 | 124 | 125 | org.springframework.boot 126 | spring-boot-starter-test 127 | test 128 | 129 | 130 | org.springframework.security 131 | spring-security-test 132 | test 133 | 134 | 135 | io.qameta.allure 136 | allure-junit5 137 | test 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | /** 13 | * The bean of a simulated device which can be stored in the 14 | * {@link DeviceSimulatorRepository} or shown in the UI. 15 | * 16 | */ 17 | public abstract class AbstractSimulatedDevice { 18 | 19 | private String id; 20 | private String tenant; 21 | private UpdateStatus updateStatus; 22 | private Protocol protocol = Protocol.DMF_AMQP; 23 | private String targetSecurityToken; 24 | private int pollDelaySec; 25 | private int nextPollCounterSec; 26 | 27 | /** 28 | * Enum definition of the protocol to be used for the simulated device. 29 | * 30 | */ 31 | public enum Protocol { 32 | /** 33 | * Device Management Federation API via AMQP, push mechanism. 34 | */ 35 | DMF_AMQP, 36 | /** 37 | * Direct Device Interface via HTTP, poll mechanism. 38 | */ 39 | DDI_HTTP 40 | } 41 | 42 | /** 43 | * empty constructor. 44 | */ 45 | AbstractSimulatedDevice() { 46 | 47 | } 48 | 49 | /** 50 | * Creates a new simulated device. 51 | * 52 | * @param id 53 | * the ID of the simulated device 54 | * @param tenant 55 | * the tenant of the simulated device 56 | * @param pollDelaySec 57 | */ 58 | AbstractSimulatedDevice(final String id, final String tenant, final Protocol protocol, final int pollDelaySec) { 59 | this.id = id; 60 | this.tenant = tenant; 61 | this.protocol = protocol; 62 | this.pollDelaySec = pollDelaySec; 63 | } 64 | 65 | /** 66 | * Can be called by a scheduler to trigger a device polling, like in real 67 | * scenarios devices are frequently asking for updates etc. 68 | */ 69 | public abstract void poll(); 70 | 71 | public int getPollDelaySec() { 72 | return pollDelaySec; 73 | } 74 | 75 | public void setPollDelaySec(final int pollDelaySec) { 76 | this.pollDelaySec = pollDelaySec; 77 | } 78 | 79 | public abstract void updateAttribute(final String mode, final String key, final String value); 80 | 81 | /** 82 | * Method to clean-up resource e.g. when the simulated device has been 83 | * removed from the repository. 84 | */ 85 | public void clean() { 86 | this.updateStatus = null; 87 | } 88 | 89 | public String getId() { 90 | return id; 91 | } 92 | 93 | public String getTenant() { 94 | return tenant; 95 | } 96 | 97 | public void setId(final String id) { 98 | this.id = id; 99 | } 100 | 101 | public void setTenant(final String tenant) { 102 | this.tenant = tenant; 103 | } 104 | 105 | public UpdateStatus getUpdateStatus() { 106 | return updateStatus; 107 | } 108 | 109 | public void setUpdateStatus(final UpdateStatus updateStatus) { 110 | this.updateStatus = updateStatus; 111 | } 112 | 113 | public Protocol getProtocol() { 114 | return protocol; 115 | } 116 | 117 | public int getNextPollCounterSec() { 118 | return nextPollCounterSec; 119 | } 120 | 121 | public void setNextPollCounterSec(final int nextPollDelayInSec) { 122 | this.nextPollCounterSec = nextPollDelayInSec; 123 | } 124 | 125 | public String getTargetSecurityToken() { 126 | return targetSecurityToken; 127 | } 128 | 129 | public void setTargetSecurityToken(final String targetSecurityToken) { 130 | this.targetSecurityToken = targetSecurityToken; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.util.AbstractMap; 13 | import java.util.Collections; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Map.Entry; 18 | import java.util.Optional; 19 | import java.util.stream.Collectors; 20 | 21 | import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback; 22 | import org.eclipse.hawkbit.ddi.json.model.DdiArtifact; 23 | import org.eclipse.hawkbit.ddi.json.model.DdiChunk; 24 | import org.eclipse.hawkbit.ddi.json.model.DdiConfigData; 25 | import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationFeedback; 26 | import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase; 27 | import org.eclipse.hawkbit.ddi.json.model.DdiDeployment; 28 | import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.HandlingType; 29 | import org.eclipse.hawkbit.ddi.json.model.DdiDeploymentBase; 30 | import org.eclipse.hawkbit.ddi.json.model.DdiResult; 31 | import org.eclipse.hawkbit.ddi.json.model.DdiResult.FinalResult; 32 | import org.eclipse.hawkbit.ddi.json.model.DdiStatus; 33 | import org.eclipse.hawkbit.ddi.json.model.DdiStatus.ExecutionStatus; 34 | import org.eclipse.hawkbit.ddi.json.model.DdiUpdateMode; 35 | import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi; 36 | import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; 37 | import org.eclipse.hawkbit.dmf.json.model.DmfArtifact; 38 | import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash; 39 | import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule; 40 | import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback; 41 | import org.slf4j.Logger; 42 | import org.slf4j.LoggerFactory; 43 | import org.springframework.hateoas.Link; 44 | import org.springframework.http.HttpStatus; 45 | import org.springframework.http.ResponseEntity; 46 | 47 | /** 48 | * A simulated device using the DDI API of the hawkBit update server. 49 | * 50 | */ 51 | public class DDISimulatedDevice extends AbstractSimulatedDevice { 52 | 53 | private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class); 54 | private static final String LOG_PREFIX = "[{}:{}] "; 55 | 56 | private final DdiRootControllerRestApi controllerResource; 57 | 58 | private final DeviceSimulatorUpdater deviceUpdater; 59 | 60 | private final String gatewayToken; 61 | 62 | private static final String DEPLOYMENT_BASE_LINK = "deploymentBase"; 63 | 64 | private static final String CONFIRMATION_BASE_LINK = "confirmationBase"; 65 | 66 | private volatile boolean removed; 67 | private volatile Long currentActionId; 68 | 69 | /** 70 | * @param id 71 | * the ID of the device 72 | * @param tenant 73 | * the tenant of the simulated device 74 | * @param pollDelaySec 75 | * the delay of the poll interval in sec 76 | * @param controllerResource 77 | * the http controller resource 78 | * @param deviceUpdater 79 | * the service to update devices 80 | * @param gatewayToken 81 | * to authenticate at DDI and for download as well 82 | */ 83 | public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec, 84 | final DdiRootControllerRestApi controllerResource, final DeviceSimulatorUpdater deviceUpdater, 85 | final String gatewayToken) { 86 | super(id, tenant, Protocol.DDI_HTTP, pollDelaySec); 87 | this.controllerResource = controllerResource; 88 | this.deviceUpdater = deviceUpdater; 89 | this.gatewayToken = gatewayToken; 90 | } 91 | 92 | @Override 93 | public void clean() { 94 | super.clean(); 95 | removed = true; 96 | } 97 | 98 | /** 99 | * Polls the base URL for the DDI API interface. 100 | */ 101 | @Override 102 | public void poll() { 103 | if (!removed) { 104 | final Optional confirmationBaseLink = getRequiredLink(CONFIRMATION_BASE_LINK); 105 | if (confirmationBaseLink.isPresent()) { 106 | sendConfirmationFeedback(getActionId(confirmationBaseLink.get())); 107 | } else { 108 | getRequiredLink(DEPLOYMENT_BASE_LINK).flatMap(this::getActionWithDeployment).ifPresent(actionWithDeployment -> { 109 | final Long actionId = actionWithDeployment.getKey(); 110 | final DdiDeployment deployment = actionWithDeployment.getValue().getDeployment(); 111 | final HandlingType updateType = deployment.getUpdate(); 112 | final List modules = deployment.getChunks(); 113 | 114 | currentActionId = actionId; 115 | startDdiUpdate(actionId, updateType, modules); 116 | }); 117 | } 118 | } 119 | } 120 | 121 | private Optional getRequiredLink(final String nameOfTheLink) { 122 | LOGGER.trace(LOG_PREFIX + "Polling ...", getTenant(), getId()); 123 | ResponseEntity poll = null; 124 | try { 125 | poll = controllerResource.getControllerBase(getTenant(), getId()); 126 | } catch (final RuntimeException ex) { 127 | LOGGER.error(LOG_PREFIX + "Failed base poll", getTenant(), getId(), ex); 128 | return Optional.empty(); 129 | } 130 | 131 | if (HttpStatus.OK != poll.getStatusCode()) { 132 | LOGGER.error(LOG_PREFIX + "Failed base poll {}", getTenant(), getId(), poll.getStatusCode()); 133 | return Optional.empty(); 134 | } 135 | 136 | final DdiControllerBase pollBody = poll.getBody(); 137 | final Optional link = pollBody != null ? pollBody.getLink(nameOfTheLink) : Optional.empty(); 138 | link.ifPresentOrElse( 139 | l -> LOGGER.debug(LOG_PREFIX + "Polling finished. Has link: {}", getTenant(), getId(), l), 140 | () -> LOGGER.trace(LOG_PREFIX + "Polling finished. No link", getTenant(), getId())); 141 | return link; 142 | } 143 | 144 | private Optional> getActionWithDeployment(final Link deploymentBaseLink) { 145 | final long actionId = getActionId(deploymentBaseLink); 146 | if (currentActionId == null || currentActionId == actionId) { 147 | final ResponseEntity action = controllerResource 148 | .getControllerDeploymentBaseAction(getTenant(), getId(), actionId, -1, null); 149 | 150 | if (HttpStatus.OK != action.getStatusCode()) { 151 | return Optional.empty(); 152 | } 153 | 154 | if (action.getBody() != null) { 155 | return Optional.of(new AbstractMap.SimpleEntry<>(actionId, action.getBody())); 156 | } 157 | } 158 | 159 | return Optional.empty(); 160 | } 161 | 162 | @Override 163 | public void updateAttribute(final String mode, final String key, final String value) { 164 | 165 | final DdiUpdateMode updateMode; 166 | switch (mode.toLowerCase()) { 167 | case "replace": 168 | updateMode = DdiUpdateMode.REPLACE; 169 | break; 170 | case "remove": 171 | updateMode = DdiUpdateMode.REMOVE; 172 | break; 173 | case "merge": 174 | default: 175 | updateMode = DdiUpdateMode.MERGE; 176 | break; 177 | } 178 | 179 | final DdiConfigData configData = new DdiConfigData(Collections.singletonMap(key, value), updateMode); 180 | 181 | controllerResource.putConfigData(configData, super.getTenant(), super.getId()); 182 | } 183 | 184 | private static DmfSoftwareModule convertChunk(final DdiChunk ddi) { 185 | final DmfSoftwareModule converted = new DmfSoftwareModule(); 186 | converted.setModuleVersion(ddi.getVersion()); 187 | converted.setArtifacts( 188 | ddi.getArtifacts().stream().map(DDISimulatedDevice::convertArtifact).collect(Collectors.toList())); 189 | 190 | return converted; 191 | } 192 | 193 | private static DmfArtifact convertArtifact(final DdiArtifact ddi) { 194 | final DmfArtifact converted = new DmfArtifact(); 195 | converted.setSize(ddi.getSize()); 196 | converted.setFilename(ddi.getFilename()); 197 | converted.setHashes(new DmfArtifactHash(ddi.getHashes().getSha1(), (ddi.getHashes().getMd5()))); 198 | 199 | final Map urls = new HashMap<>(); 200 | ddi.getLink("download").ifPresent(link -> urls.put("HTTPS", link.getHref())); 201 | ddi.getLink("download-http").ifPresent(link -> urls.put("HTTP", link.getHref())); 202 | converted.setUrls(urls); 203 | 204 | return converted; 205 | } 206 | 207 | private void startDdiUpdate(final long actionId, final HandlingType updateType, final List modules) { 208 | 209 | deviceUpdater.startUpdate(getTenant(), getId(), 210 | modules.stream().map(DDISimulatedDevice::convertChunk).collect(Collectors.toList()), null, gatewayToken, 211 | sendFeedback(actionId), 212 | HandlingType.SKIP == updateType ? EventTopic.DOWNLOAD : EventTopic.DOWNLOAD_AND_INSTALL); 213 | } 214 | 215 | private UpdaterCallback sendFeedback(final long actionId) { 216 | return device -> { 217 | final DdiActionFeedback feedback = calculateFeedback(device); 218 | controllerResource.postDeploymentBaseActionFeedback(feedback, getTenant(), getId(), actionId); 219 | currentActionId = null; 220 | }; 221 | } 222 | 223 | private void sendConfirmationFeedback(final long actionId) { 224 | final DdiConfirmationFeedback ddiConfirmationFeedback = new DdiConfirmationFeedback( 225 | DdiConfirmationFeedback.Confirmation.CONFIRMED, 0, Collections.singletonList( 226 | "the confirmation status for the device is" + DdiConfirmationFeedback.Confirmation.CONFIRMED)); 227 | controllerResource.postConfirmationActionFeedback(ddiConfirmationFeedback, getTenant(), getId(), actionId); 228 | } 229 | 230 | private DdiActionFeedback calculateFeedback(final AbstractSimulatedDevice device) { 231 | DdiActionFeedback feedback; 232 | 233 | switch (device.getUpdateStatus().getResponseStatus()) { 234 | case SUCCESSFUL: 235 | feedback = new DdiActionFeedback(null, new DdiStatus(ExecutionStatus.CLOSED, 236 | new DdiResult(FinalResult.SUCCESS, null), 200, device.getUpdateStatus().getStatusMessages())); 237 | break; 238 | case ERROR: 239 | feedback = new DdiActionFeedback(null, new DdiStatus(ExecutionStatus.CLOSED, 240 | new DdiResult(FinalResult.FAILURE, null), null, device.getUpdateStatus().getStatusMessages())); 241 | break; 242 | case DOWNLOADING: 243 | feedback = new DdiActionFeedback(null, new DdiStatus(ExecutionStatus.DOWNLOAD, 244 | new DdiResult(FinalResult.NONE, null), null, device.getUpdateStatus().getStatusMessages())); 245 | break; 246 | case DOWNLOADED: 247 | feedback = new DdiActionFeedback(null, new DdiStatus(ExecutionStatus.DOWNLOADED, 248 | new DdiResult(FinalResult.NONE, null), null, device.getUpdateStatus().getStatusMessages())); 249 | break; 250 | case RUNNING: 251 | feedback = new DdiActionFeedback(null, new DdiStatus(ExecutionStatus.PROCEEDING, 252 | new DdiResult(FinalResult.NONE, null), null, device.getUpdateStatus().getStatusMessages())); 253 | break; 254 | default: 255 | throw new IllegalStateException("simulated device has an unknown response status + " 256 | + device.getUpdateStatus().getResponseStatus()); 257 | } 258 | return feedback; 259 | } 260 | 261 | private long getActionId(final Link link) { 262 | final String href = link.getHref(); 263 | return Long.parseLong(href.substring(href.lastIndexOf('/') + 1, href.indexOf('?'))); 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode; 13 | import org.eclipse.hawkbit.simulator.amqp.DmfSenderService; 14 | 15 | /** 16 | * A simulated device using the DMF API of the hawkBit update server. 17 | */ 18 | public class DMFSimulatedDevice extends AbstractSimulatedDevice { 19 | private final DmfSenderService spSenderService; 20 | 21 | /** 22 | * @param id 23 | * the ID of the device 24 | * @param tenant 25 | * the tenant of the simulated device 26 | */ 27 | public DMFSimulatedDevice(final String id, final String tenant, final DmfSenderService spSenderService, 28 | final int pollDelaySec) { 29 | super(id, tenant, Protocol.DMF_AMQP, pollDelaySec); 30 | this.spSenderService = spSenderService; 31 | } 32 | 33 | @Override 34 | public void poll() { 35 | spSenderService.createOrUpdateThing(super.getTenant(), super.getId()); 36 | } 37 | 38 | @Override 39 | public void updateAttribute(final String mode, final String key, final String value) { 40 | 41 | final DmfUpdateMode updateMode; 42 | 43 | switch (mode.toLowerCase()) { 44 | 45 | case "replace" : 46 | updateMode = DmfUpdateMode.REPLACE; 47 | break; 48 | case "remove" : 49 | updateMode = DmfUpdateMode.REMOVE; 50 | break; 51 | case "merge" : 52 | default : 53 | updateMode = DmfUpdateMode.MERGE; 54 | break; 55 | } 56 | 57 | spSenderService.updateAttributesOfThing(super.getTenant(), super.getId(), updateMode, key, value); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | 15 | import org.springframework.boot.SpringApplication; 16 | import org.springframework.boot.autoconfigure.SpringBootApplication; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.scheduling.TaskScheduler; 19 | import org.springframework.scheduling.annotation.EnableScheduling; 20 | import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; 21 | 22 | /** 23 | * The main-method to start the Spring-Boot application. 24 | * 25 | */ 26 | @SpringBootApplication 27 | @EnableScheduling 28 | public class DeviceSimulator { 29 | 30 | public DeviceSimulator() { 31 | // utility class 32 | } 33 | 34 | /** 35 | * @return central ScheduledExecutorService 36 | */ 37 | @Bean 38 | ScheduledExecutorService threadPool() { 39 | return Executors.newScheduledThreadPool(4); 40 | } 41 | 42 | @Bean 43 | TaskScheduler taskScheduler() { 44 | return new ConcurrentTaskScheduler(threadPool()); 45 | } 46 | 47 | /** 48 | * Start the Spring Boot Application. 49 | * 50 | * @param args 51 | * the args 52 | */ 53 | // Exception squid:S2095 - Spring boot standard behavior 54 | @SuppressWarnings({ "squid:S2095" }) 55 | public static void main(final String[] args) { 56 | SpringApplication.run(DeviceSimulator.class, args); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorRepository.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.util.Collection; 13 | import java.util.HashSet; 14 | import java.util.Map; 15 | import java.util.Set; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | 18 | import org.springframework.stereotype.Service; 19 | 20 | /** 21 | * An in-memory simulated device repository to hold the simulated device in 22 | * memory and be able to retrieve them again. 23 | * 24 | */ 25 | @Service 26 | public class DeviceSimulatorRepository { 27 | 28 | private final Map devices = new ConcurrentHashMap<>(); 29 | 30 | private final Set tenants = new HashSet<>(); 31 | 32 | /** 33 | * Adds a simulated device to the repository. 34 | * 35 | * @param simulatedDevice 36 | * the device to add 37 | * @return the device which has been added to the repository 38 | */ 39 | public AbstractSimulatedDevice add(final AbstractSimulatedDevice simulatedDevice) { 40 | devices.put(new DeviceKey(simulatedDevice.getTenant().toLowerCase(), simulatedDevice.getId()), simulatedDevice); 41 | tenants.add(simulatedDevice.getTenant().toLowerCase()); 42 | return simulatedDevice; 43 | } 44 | 45 | /** 46 | * @return all simulated devices 47 | */ 48 | public Collection getAll() { 49 | return devices.values(); 50 | } 51 | 52 | /** 53 | * Retrieves a single simulated devices or {@code null} if device does not 54 | * exists. 55 | * 56 | * @param tenant 57 | * the tenant of the simulated device 58 | * @param id 59 | * the ID of the device 60 | * @return a simulated device from the repository or {@code null} if device 61 | * does not exixts. 62 | */ 63 | public AbstractSimulatedDevice get(final String tenant, final String id) { 64 | return devices.get(new DeviceKey(tenant.toLowerCase(), id)); 65 | } 66 | 67 | /** 68 | * Removes a device from the simulation. 69 | * 70 | * @param tenant 71 | * the tenant of the simulated device 72 | * @param id 73 | * the ID of the device 74 | * @return the simulated device or null if it was not in the 75 | * repository 76 | */ 77 | public AbstractSimulatedDevice remove(final String tenant, final String id) { 78 | return devices.remove(new DeviceKey(tenant.toLowerCase(), id)); 79 | } 80 | 81 | public Set getTenants() { 82 | return tenants; 83 | } 84 | 85 | /** 86 | * Clears all stored devices. 87 | */ 88 | public void clear() { 89 | devices.values().forEach(AbstractSimulatedDevice::clean); 90 | devices.clear(); 91 | tenants.clear(); 92 | } 93 | 94 | private static final class DeviceKey { 95 | private final String tenant; 96 | private final String id; 97 | 98 | private DeviceKey(final String tenant, final String id) { 99 | this.tenant = tenant; 100 | this.id = id; 101 | } 102 | 103 | @Override 104 | public int hashCode() { 105 | final int prime = 31; 106 | int result = 1; 107 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 108 | result = prime * result + ((tenant == null) ? 0 : tenant.hashCode()); 109 | return result; 110 | } 111 | 112 | @Override 113 | public boolean equals(final Object obj) { 114 | 115 | if (this == obj) { 116 | return true; 117 | } 118 | if (obj == null) { 119 | return false; 120 | } 121 | if (getClass() != obj.getClass()) { 122 | return false; 123 | } 124 | final DeviceKey other = (DeviceKey) obj; 125 | if (id == null) { 126 | if (other.id != null) { 127 | return false; 128 | } 129 | } else if (!id.equals(other.id)) { 130 | return false; 131 | } 132 | if (tenant == null) { 133 | if (other.tenant != null) { 134 | return false; 135 | } 136 | } else if (!tenant.equals(other.tenant)) { 137 | return false; 138 | } 139 | return true; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.util.Collection; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.ScheduledExecutorService; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.stereotype.Component; 22 | 23 | /** 24 | * Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every 25 | * second. 26 | */ 27 | @Component 28 | public class NextPollTimeController { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(NextPollTimeController.class); 31 | 32 | private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); 33 | private static final ExecutorService pollService = Executors.newFixedThreadPool(1); 34 | 35 | @Autowired 36 | private DeviceSimulatorRepository repository; 37 | 38 | /** 39 | * Constructor which schedules the poll trigger runnable every second. 40 | */ 41 | public NextPollTimeController() { 42 | executorService.scheduleWithFixedDelay(new NextPollUpdaterRunnable(), 1, 1, TimeUnit.SECONDS); 43 | } 44 | 45 | private class NextPollUpdaterRunnable implements Runnable { 46 | @Override 47 | public void run() { 48 | final Collection devices = repository.getAll(); 49 | 50 | devices.forEach(device -> { 51 | int nextCounter = device.getNextPollCounterSec() - 1; 52 | if (nextCounter < 0) { 53 | try { 54 | pollService.submit(() -> device.poll()); 55 | } catch (final IllegalStateException e) { 56 | LOGGER.trace("Device could not be polled", e); 57 | } 58 | nextCounter = device.getPollDelaySec(); 59 | } 60 | 61 | device.setNextPollCounterSec(nextCounter); 62 | }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.net.URL; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | import org.eclipse.hawkbit.ddi.client.resource.RootControllerResourceClient; 17 | import org.eclipse.hawkbit.feign.core.client.IgnoreMultipleConsumersProducersSpringMvcContract; 18 | import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; 19 | import org.eclipse.hawkbit.simulator.amqp.DmfSenderService; 20 | import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; 23 | import org.springframework.hateoas.mediatype.hal.Jackson2HalModule; 24 | import org.springframework.stereotype.Service; 25 | 26 | import com.fasterxml.jackson.databind.DeserializationFeature; 27 | import com.fasterxml.jackson.databind.ObjectMapper; 28 | 29 | import feign.Feign; 30 | import feign.Logger.Level; 31 | import feign.jackson.JacksonDecoder; 32 | import feign.jackson.JacksonEncoder; 33 | import feign.slf4j.Slf4jLogger; 34 | 35 | /** 36 | * The simulated device factory to create either {@link DMFSimulatedDevice} or 37 | * {@link DDISimulatedDevice}. 38 | */ 39 | @Service 40 | public class SimulatedDeviceFactory { 41 | @Autowired 42 | private DeviceSimulatorUpdater deviceUpdater; 43 | 44 | @Autowired(required = false) 45 | private DmfSenderService spSenderService; 46 | 47 | @Autowired 48 | private ScheduledExecutorService threadPool; 49 | 50 | /** 51 | * Creating a simulated device. 52 | * 53 | * @param id 54 | * the ID of the simulated device 55 | * @param tenant 56 | * the tenant of the simulated device 57 | * @param protocol 58 | * the protocol which should be used be the simulated device 59 | * @param pollDelaySec 60 | * the poll delay time in seconds which should be used for 61 | * {@link DDISimulatedDevice}s and {@link DMFSimulatedDevice} 62 | * @param baseEndpoint 63 | * the http base endpoint which should be used for 64 | * {@link DDISimulatedDevice}s 65 | * @param gatewayToken 66 | * the gatewayToken to be used to authenticate 67 | * {@link DDISimulatedDevice}s at the endpoint 68 | * @return the created simulated device 69 | */ 70 | public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol, 71 | final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) { 72 | return createSimulatedDevice(id, tenant, protocol, pollDelaySec, baseEndpoint, gatewayToken, false); 73 | } 74 | 75 | private AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol, 76 | final int pollDelaySec, final URL baseEndpoint, final String gatewayToken, final boolean pollImmediatly) { 77 | switch (protocol) { 78 | case DMF_AMQP: 79 | return createDmfDevice(id, tenant, pollDelaySec, pollImmediatly); 80 | case DDI_HTTP: 81 | return createDdiDevice(id, tenant, pollDelaySec, baseEndpoint, gatewayToken); 82 | default: 83 | throw new IllegalArgumentException("Protocol " + protocol + " unknown"); 84 | } 85 | } 86 | 87 | private AbstractSimulatedDevice createDdiDevice(final String id, final String tenant, final int pollDelaySec, 88 | final URL baseEndpoint, final String gatewayToken) { 89 | 90 | final ObjectMapper mapper = new ObjectMapper() 91 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 92 | .registerModule(new Jackson2HalModule()); 93 | 94 | final RootControllerResourceClient controllerResource = Feign.builder() 95 | .requestInterceptor(new GatewayTokenInterceptor(gatewayToken)) 96 | .contract(new IgnoreMultipleConsumersProducersSpringMvcContract()).logLevel(Level.HEADERS) 97 | .decoder(new ResponseEntityDecoder(new JacksonDecoder(mapper))).encoder(new JacksonEncoder()) 98 | .logger(new Slf4jLogger()).decode404() 99 | .target(RootControllerResourceClient.class, baseEndpoint.toString()); 100 | 101 | return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, deviceUpdater, gatewayToken); 102 | } 103 | 104 | private AbstractSimulatedDevice createDmfDevice(final String id, final String tenant, final int pollDelaySec, 105 | final boolean pollImmediatly) { 106 | final AbstractSimulatedDevice device = new DMFSimulatedDevice(id, tenant, spSenderService, pollDelaySec); 107 | device.setNextPollCounterSec(pollDelaySec); 108 | if (pollImmediatly) { 109 | spSenderService.createOrUpdateThing(tenant, id); 110 | } 111 | 112 | threadPool.schedule(() -> spSenderService.updateAttributesOfThing(tenant, id), 2_000, TimeUnit.MILLISECONDS); 113 | 114 | return device; 115 | } 116 | 117 | /** 118 | * Creating a simulated device and send an immediate DMF poll to update 119 | * server. 120 | * 121 | * @param id 122 | * the ID of the simulated device 123 | * @param tenant 124 | * the tenant of the simulated device 125 | * @param protocol 126 | * the protocol which should be used be the simulated device 127 | * @param pollDelaySec 128 | * the poll delay time in seconds which should be used for 129 | * {@link DDISimulatedDevice}s and {@link DMFSimulatedDevice} 130 | * @param baseEndpoint 131 | * the http base endpoint which should be used for 132 | * {@link DDISimulatedDevice}s 133 | * @param gatewayToken 134 | * the gatewayToken to be used to authenticate 135 | * {@link DDISimulatedDevice}s at the endpoint 136 | * @return the created simulated device 137 | */ 138 | public AbstractSimulatedDevice createSimulatedDeviceWithImmediatePoll(final String id, final String tenant, 139 | final Protocol protocol, final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) { 140 | return createSimulatedDevice(id, tenant, protocol, pollDelaySec, baseEndpoint, gatewayToken, true); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.net.MalformedURLException; 13 | import java.net.URL; 14 | import java.util.Collections; 15 | import java.util.Optional; 16 | 17 | import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; 18 | import org.eclipse.hawkbit.simulator.amqp.AmqpProperties; 19 | import org.eclipse.hawkbit.simulator.amqp.DmfSenderService; 20 | import org.eclipse.hawkbit.simulator.amqp.SimulatedUpdate; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.http.ResponseEntity; 23 | import org.springframework.web.bind.annotation.GetMapping; 24 | import org.springframework.web.bind.annotation.RequestParam; 25 | import org.springframework.web.bind.annotation.RestController; 26 | 27 | /** 28 | * REST endpoint for controlling the device simulator. 29 | */ 30 | @RestController 31 | public class SimulationController { 32 | 33 | private final DeviceSimulatorRepository repository; 34 | 35 | private final SimulatedDeviceFactory deviceFactory; 36 | 37 | private final AmqpProperties amqpProperties; 38 | 39 | private final SimulationProperties simulationProperties; 40 | 41 | private Optional spSenderService = Optional.empty(); 42 | 43 | @Autowired 44 | public SimulationController(final DeviceSimulatorRepository repository, final SimulatedDeviceFactory deviceFactory, 45 | final AmqpProperties amqpProperties, final SimulationProperties simulationProperties) { 46 | this.repository = repository; 47 | this.deviceFactory = deviceFactory; 48 | this.amqpProperties = amqpProperties; 49 | this.simulationProperties = simulationProperties; 50 | } 51 | 52 | /** 53 | * Simple endpoint indicating that simulator is running. 54 | */ 55 | @GetMapping("/") 56 | public ResponseEntity status() { 57 | return ResponseEntity.ok("Simulator running"); 58 | } 59 | 60 | /** 61 | * The start resource to start a device creation. 62 | * 63 | * @param name 64 | * the name prefix of the generated device naming 65 | * @param amount 66 | * the amount of devices to be created 67 | * @param tenant 68 | * the tenant to create the device to 69 | * @param api 70 | * the api-protocol to be used either {@code dmf} or {@code ddi} 71 | * @param endpoint 72 | * the URL endpoint to be used of the hawkbit-update-server for 73 | * DDI devices 74 | * @param pollDelay 75 | * number of delay in seconds to delay polling of DDI devices 76 | * @param gatewayToken 77 | * the hawkbit-update-server gatewaytoken in case authentication 78 | * is enforced in hawkbit 79 | * @return a response string that devices has been created 80 | * @throws MalformedURLException 81 | */ 82 | @GetMapping("/start") 83 | public ResponseEntity start(@RequestParam(value = "name", defaultValue = "simulated") final String name, 84 | @RequestParam(value = "amount", defaultValue = "20") final int amount, 85 | @RequestParam(value = "tenant", required = false) final String tenant, 86 | @RequestParam(value = "api", defaultValue = "dmf") final String api, 87 | @RequestParam(value = "endpoint", defaultValue = "http://localhost:8080") final String endpoint, 88 | @RequestParam(value = "polldelay", defaultValue = "30") final int pollDelay, 89 | @RequestParam(value = "gatewaytoken", defaultValue = "") final String gatewayToken) 90 | throws MalformedURLException { 91 | 92 | final Protocol protocol; 93 | switch (api.toLowerCase()) { 94 | case "dmf": 95 | protocol = Protocol.DMF_AMQP; 96 | break; 97 | case "ddi": 98 | protocol = Protocol.DDI_HTTP; 99 | break; 100 | default: 101 | return ResponseEntity.badRequest().body("query param api only allows value of 'dmf' or 'ddi'"); 102 | } 103 | 104 | if (protocol == Protocol.DMF_AMQP && isDmfDisabled()) { 105 | return createAmqpDisabledResponse(); 106 | } 107 | 108 | for (int i = 0; i < amount; i++) { 109 | final String deviceId = name + i; 110 | repository.add(deviceFactory.createSimulatedDeviceWithImmediatePoll(deviceId, 111 | (tenant != null ? tenant : simulationProperties.getDefaultTenant()), protocol, pollDelay, 112 | new URL(endpoint), gatewayToken)); 113 | } 114 | 115 | return ResponseEntity.ok("Updated " + amount + " " + protocol + " connected targets!"); 116 | } 117 | 118 | private ResponseEntity createAmqpDisabledResponse() { 119 | return ResponseEntity.badRequest().body( 120 | "The AMQP interface has been disabled, to use DMF protocol you need to enable the AMQP interface via '" 121 | + AmqpProperties.CONFIGURATION_PREFIX + ".enabled=true'"); 122 | } 123 | 124 | /** 125 | * Update an attribute of a device. 126 | * 127 | * NOTE: This represents not the expected client behaviour for DDI, since a 128 | * DDI client shall only update its attributes if requested by hawkBit. 129 | * 130 | * @param tenant 131 | * The tenant the device belongs to 132 | * @param controllerId 133 | * The controller id of the device that should be updated. 134 | * @param mode 135 | * Update mode ('merge', 'replace', or 'remove') 136 | * @param key 137 | * Key of the attribute to be updated 138 | * @param value 139 | * Value of the attribute 140 | * @return HTTP OK (200) if the update has been triggered. 141 | */ 142 | @GetMapping("/attributes") 143 | public ResponseEntity update(@RequestParam(value = "tenant", required = false) final String tenant, 144 | @RequestParam(value = "controllerid") final String controllerId, 145 | @RequestParam(value = "mode", defaultValue = "merge") final String mode, 146 | @RequestParam(value = "key") final String key, 147 | @RequestParam(value = "value", required = false) final String value) { 148 | 149 | final AbstractSimulatedDevice simulatedDevice = repository 150 | .get((tenant != null ? tenant : simulationProperties.getDefaultTenant()), controllerId); 151 | 152 | if (simulatedDevice == null) { 153 | return ResponseEntity.notFound().build(); 154 | } 155 | 156 | simulatedDevice.updateAttribute(mode, key, value); 157 | 158 | return ResponseEntity.ok("Update triggered"); 159 | } 160 | 161 | /** 162 | * Remove a simulated device 163 | * 164 | * @param tenant 165 | * The tenant the device belongs to 166 | * @param controllerId 167 | * The controller id of the device that should be removed. 168 | * @return HTTP OK (200) if the device was removed, or HTTP NO FOUND (404) 169 | * if not found. 170 | */ 171 | @GetMapping("/remove") 172 | public ResponseEntity remove(@RequestParam(value = "tenant", required = false) final String tenant, 173 | @RequestParam(value = "controllerid") final String controllerId) { 174 | 175 | final AbstractSimulatedDevice controller = repository 176 | .remove((tenant != null ? tenant : simulationProperties.getDefaultTenant()), controllerId); 177 | 178 | if (controller == null) { 179 | return ResponseEntity.notFound().build(); 180 | } 181 | 182 | return ResponseEntity.ok("Deleted"); 183 | } 184 | 185 | /** 186 | * Reset the device simulator by removing all simulated devices 187 | * 188 | * @return A response string that the simulator has been reset 189 | */ 190 | @GetMapping("/reset") 191 | public ResponseEntity reset() { 192 | 193 | repository.clear(); 194 | 195 | return ResponseEntity.ok("All simulated devices have been removed."); 196 | } 197 | 198 | /** 199 | * Report action as FINISHED Sends an UpdateActionStatus event with value 200 | * FINISHED 201 | * 202 | * @return A response string that the action_finished event was sent 203 | */ 204 | @GetMapping("/finishAction") 205 | public ResponseEntity finishAction(@RequestParam(value = "actionId") final long actionId, 206 | @RequestParam(value = "tenant", required = false) final String tenant, 207 | @RequestParam(value = "controllerId") final String controllerId) { 208 | 209 | if (!spSenderService.isPresent()) { 210 | return createAmqpDisabledResponse(); 211 | } 212 | 213 | final String theTenant = tenant != null && !tenant.isEmpty() ? tenant : simulationProperties.getDefaultTenant(); 214 | final AbstractSimulatedDevice device = repository.get(theTenant, controllerId); 215 | 216 | if (device == null) { 217 | return ResponseEntity.notFound().build(); 218 | } 219 | 220 | spSenderService.get().finishUpdateProcess(new SimulatedUpdate(device.getTenant(), device.getId(), actionId), 221 | Collections.singletonList("Simulation Finished.")); 222 | 223 | return ResponseEntity.ok(String.format("Action with id: [%d] reported as FINISHED", actionId)); 224 | } 225 | 226 | @Autowired(required = false) 227 | public void setSpSenderService(final DmfSenderService spSenderService) { 228 | this.spSenderService = Optional.of(spSenderService); 229 | } 230 | 231 | private boolean isDmfDisabled() { 232 | return !amqpProperties.isEnabled(); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import java.util.Optional; 16 | import java.util.Random; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import jakarta.validation.constraints.NotEmpty; 20 | 21 | import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; 22 | import org.springframework.boot.context.properties.ConfigurationProperties; 23 | import org.springframework.stereotype.Component; 24 | 25 | /** 26 | * General simulator service properties. 27 | * 28 | */ 29 | @Component 30 | @ConfigurationProperties("hawkbit.device.simulator") 31 | // Exception for squid:S2245 : not security relevant random number generation 32 | @SuppressWarnings("squid:S2245") 33 | public class SimulationProperties { 34 | 35 | private static final Random RANDOM = new Random(); 36 | 37 | private String defaultTenant = "DEFAULT"; 38 | 39 | /** 40 | * Set to true if the target or gateway auth token should be 41 | * added to the download request as an authorization header. Activated by 42 | * default but may be worth to disable if not needed. 43 | */ 44 | private boolean downloadAuthenticationEnabled = true; 45 | 46 | /** 47 | * List of tenants where the simulator should auto start simulations after 48 | * startup. 49 | */ 50 | private final List autostarts = new ArrayList<>(); 51 | 52 | private final List attributes = new ArrayList<>(); 53 | 54 | public String getDefaultTenant() { 55 | return defaultTenant; 56 | } 57 | 58 | public void setDefaultTenant(final String defaultTenant) { 59 | this.defaultTenant = defaultTenant; 60 | } 61 | 62 | public boolean isDownloadAuthenticationEnabled() { 63 | return downloadAuthenticationEnabled; 64 | } 65 | 66 | public void setDownloadAuthenticationEnabled(final boolean downloadAuthenticationEnabled) { 67 | this.downloadAuthenticationEnabled = downloadAuthenticationEnabled; 68 | } 69 | 70 | public List getAttributes() { 71 | return attributes; 72 | } 73 | 74 | public List getAutostarts() { 75 | return this.autostarts; 76 | } 77 | 78 | /** 79 | * Properties for target attributes set as part of simulation. 80 | * 81 | */ 82 | public static class Attribute { 83 | private String key; 84 | private String value; 85 | private String random; 86 | 87 | public String getKey() { 88 | return key; 89 | } 90 | 91 | public String getValue() { 92 | return Optional.ofNullable(value).orElseGet(this::getRandomElement); 93 | } 94 | 95 | public void setKey(final String key) { 96 | this.key = key; 97 | } 98 | 99 | public void setValue(final String value) { 100 | this.value = value; 101 | } 102 | 103 | public void setRandom(final String random) { 104 | this.random = random; 105 | } 106 | 107 | public String getRandom() { 108 | return random; 109 | } 110 | 111 | private String getRandomElement() { 112 | final List options = Arrays.stream(random.split(",")) 113 | .map(String::trim) 114 | .filter(s -> !s.isEmpty()).toList(); 115 | return options.get(RANDOM.nextInt(options.size())); 116 | } 117 | } 118 | 119 | /** 120 | * Auto start configuration for simulation setups that the simulator begins 121 | * after startup. 122 | * 123 | */ 124 | public static class Autostart { 125 | /** 126 | * Name prefix of simulated devices, followed by counter, e.g. 127 | * simulated0, simulated1, simulated2.... 128 | */ 129 | private String name = "simulated"; 130 | 131 | /** 132 | * Amount of simulated devices. 133 | */ 134 | private int amount = 20; 135 | 136 | /** 137 | * Tenant name for the simulation. 138 | */ 139 | @NotEmpty 140 | private String tenant; 141 | 142 | /** 143 | * API for simulation. 144 | */ 145 | private Protocol api = Protocol.DMF_AMQP; 146 | 147 | /** 148 | * Endpoint in case of DDI API based simulation. 149 | */ 150 | private String endpoint = "http://localhost:8080"; 151 | 152 | /** 153 | * Poll time in {@link TimeUnit#SECONDS} for simulated devices. 154 | */ 155 | private int pollDelay = (int) TimeUnit.MINUTES.toSeconds(30); 156 | 157 | /** 158 | * Optional gateway token for DDI API based simulation. 159 | */ 160 | private String gatewayToken = ""; 161 | 162 | public String getName() { 163 | return name; 164 | } 165 | 166 | public void setName(final String name) { 167 | this.name = name; 168 | } 169 | 170 | public int getAmount() { 171 | return amount; 172 | } 173 | 174 | public void setAmount(final int amount) { 175 | this.amount = amount; 176 | } 177 | 178 | public String getTenant() { 179 | return tenant; 180 | } 181 | 182 | public void setTenant(final String tenant) { 183 | this.tenant = tenant; 184 | } 185 | 186 | public Protocol getApi() { 187 | return api; 188 | } 189 | 190 | public void setApi(final Protocol api) { 191 | this.api = api; 192 | } 193 | 194 | public String getEndpoint() { 195 | return endpoint; 196 | } 197 | 198 | public void setEndpoint(final String endpoint) { 199 | this.endpoint = endpoint; 200 | } 201 | 202 | public int getPollDelay() { 203 | return pollDelay; 204 | } 205 | 206 | public void setPollDelay(final int pollDelay) { 207 | this.pollDelay = pollDelay; 208 | } 209 | 210 | public String getGatewayToken() { 211 | return gatewayToken; 212 | } 213 | 214 | public void setGatewayToken(final String gatewayToken) { 215 | this.gatewayToken = gatewayToken; 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatorStartup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.net.MalformedURLException; 13 | import java.net.URL; 14 | 15 | import org.eclipse.hawkbit.simulator.amqp.AmqpProperties; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.boot.context.event.ApplicationReadyEvent; 21 | import org.springframework.context.ApplicationListener; 22 | import org.springframework.stereotype.Component; 23 | 24 | /** 25 | * Execution of operations after startup. Set up of simulations. 26 | * 27 | */ 28 | @Component 29 | @ConditionalOnProperty(prefix = "hawkbit.device.simulator", name = "autostart", matchIfMissing = true) 30 | public class SimulatorStartup implements ApplicationListener { 31 | private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorStartup.class); 32 | 33 | @Autowired 34 | private SimulationProperties simulationProperties; 35 | 36 | @Autowired 37 | private DeviceSimulatorRepository repository; 38 | 39 | @Autowired 40 | private SimulatedDeviceFactory deviceFactory; 41 | 42 | @Autowired 43 | private AmqpProperties amqpProperties; 44 | 45 | @Override 46 | public void onApplicationEvent(final ApplicationReadyEvent event) { 47 | LOGGER.debug("{} autostarts will be executed", simulationProperties.getAutostarts().size()); 48 | 49 | simulationProperties.getAutostarts().forEach(autostart -> { 50 | LOGGER.debug("Autostart runs for tenant {} and API {}", autostart.getTenant(), autostart.getApi()); 51 | for (int i = 0; i < autostart.getAmount(); i++) { 52 | final String deviceId = autostart.getName() + i; 53 | try { 54 | if (amqpProperties.isEnabled()) { 55 | repository.add(deviceFactory.createSimulatedDeviceWithImmediatePoll(deviceId, 56 | autostart.getTenant(), autostart.getApi(), autostart.getPollDelay(), 57 | new URL(autostart.getEndpoint()), autostart.getGatewayToken())); 58 | } 59 | 60 | } catch (final MalformedURLException e) { 61 | LOGGER.error("Creation of simulated device at startup failed.", e); 62 | } 63 | } 64 | }); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/UpdateStatus.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * Update status of the simulated update. 17 | * 18 | */ 19 | public class UpdateStatus { 20 | private ResponseStatus responseStatus; 21 | private List statusMessages; 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param responseStatus 27 | * of the update 28 | */ 29 | public UpdateStatus(final ResponseStatus responseStatus) { 30 | this.responseStatus = responseStatus; 31 | } 32 | 33 | /** 34 | * Constructor including status message. 35 | * 36 | * @param responseStatus 37 | * of the update 38 | * @param message 39 | * of the update status 40 | */ 41 | public UpdateStatus(final ResponseStatus responseStatus, final String message) { 42 | this(responseStatus); 43 | statusMessages = new ArrayList<>(); 44 | statusMessages.add(message); 45 | } 46 | 47 | /** 48 | * Constructor including status messages. 49 | * 50 | * @param responseStatus 51 | * of the update 52 | * @param statusMessages 53 | * list of status messages 54 | */ 55 | public UpdateStatus(final ResponseStatus responseStatus, final List statusMessages) { 56 | this(responseStatus); 57 | this.statusMessages = statusMessages; 58 | } 59 | 60 | public ResponseStatus getResponseStatus() { 61 | return responseStatus; 62 | } 63 | 64 | public void setResponseStatus(final ResponseStatus responseStatus) { 65 | this.responseStatus = responseStatus; 66 | } 67 | 68 | public List getStatusMessages() { 69 | if (statusMessages == null) { 70 | statusMessages = new ArrayList<>(); 71 | } 72 | 73 | return statusMessages; 74 | } 75 | 76 | /** 77 | * The status to response to the hawkBit update server if an simulated 78 | * update process should be respond with successful or failure update. 79 | */ 80 | public enum ResponseStatus { 81 | /** 82 | * Update has been successful and response the successful update. 83 | */ 84 | SUCCESSFUL, 85 | 86 | /** 87 | * Update has been not successful and response the error update. 88 | */ 89 | ERROR, 90 | 91 | /** 92 | * Update is running (intermediate status). 93 | */ 94 | RUNNING, 95 | 96 | /** 97 | * Device starts to download. 98 | */ 99 | DOWNLOADING, 100 | 101 | /** 102 | * Device is finished with downloading. 103 | */ 104 | DOWNLOADED, 105 | 106 | /** 107 | * Device has been approved for the installation. 108 | */ 109 | CONFIRMED; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.amqp; 11 | 12 | import java.time.Duration; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository; 17 | import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater; 18 | import org.eclipse.hawkbit.simulator.SimulationProperties; 19 | import org.springframework.amqp.core.Binding; 20 | import org.springframework.amqp.core.BindingBuilder; 21 | import org.springframework.amqp.core.FanoutExchange; 22 | import org.springframework.amqp.core.Queue; 23 | import org.springframework.amqp.core.QueueBuilder; 24 | import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; 25 | import org.springframework.amqp.rabbit.connection.ConnectionFactory; 26 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 27 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 29 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 30 | import org.springframework.context.annotation.Bean; 31 | import org.springframework.context.annotation.Configuration; 32 | 33 | /** 34 | * The spring AMQP configuration to use a AMQP for communication with SP update 35 | * server. 36 | */ 37 | @Configuration 38 | @EnableConfigurationProperties(AmqpProperties.class) 39 | @ConditionalOnProperty(prefix = AmqpProperties.CONFIGURATION_PREFIX, name = "enabled") 40 | public class AmqpConfiguration { 41 | 42 | @Bean 43 | DmfReceiverService dmfReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties, 44 | final DmfSenderService spSenderService, final DeviceSimulatorUpdater deviceUpdater, 45 | final DeviceSimulatorRepository repository) { 46 | return new DmfReceiverService(rabbitTemplate, amqpProperties, spSenderService, deviceUpdater, repository); 47 | } 48 | 49 | @Bean 50 | DmfSenderService dmfSenderService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties, 51 | final SimulationProperties simulationProperties) { 52 | return new DmfSenderService(rabbitTemplate, amqpProperties, simulationProperties); 53 | } 54 | 55 | @Bean 56 | public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) { 57 | final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); 58 | 59 | // It is necessary to define rabbitTemplate as a Bean and set 60 | // Jackson2JsonMessageConverter explicitly here in order to convert only 61 | // OUTCOMING messages to json. In case of INCOMING messages, 62 | // Jackson2JsonMessageConverter can not handle messages with NULL 63 | // payload (e.g. REQUEST_ATTRIBUTES_UPDATE), so the 64 | // SimpleMessageConverter is used instead per default. 65 | rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); 66 | 67 | return rabbitTemplate; 68 | } 69 | 70 | /** 71 | * Creates the receiver queue from update server for receiving message from 72 | * update server. 73 | * 74 | * @return the queue 75 | */ 76 | @Bean 77 | Queue receiverConnectorQueueFromHawkBit(final AmqpProperties amqpProperties) { 78 | return QueueBuilder.nonDurable(amqpProperties.getReceiverConnectorQueueFromSp()).autoDelete() 79 | .withArguments(getTTLMaxArgs()).build(); 80 | } 81 | 82 | private static Map getTTLMaxArgs() { 83 | final Map args = new HashMap<>(2); 84 | args.put("x-message-ttl", Duration.ofDays(1).toMillis()); 85 | args.put("x-max-length", 100_000); 86 | return args; 87 | } 88 | 89 | /** 90 | * Creates the receiver exchange for sending messages to update server. 91 | * 92 | * @return the exchange 93 | */ 94 | @Bean 95 | FanoutExchange exchangeQueueToConnector(final AmqpProperties amqpProperties) { 96 | return new FanoutExchange(amqpProperties.getSenderForSpExchange(), false, true); 97 | } 98 | 99 | /** 100 | * Create the Binding 101 | * {@link AmqpConfiguration#receiverConnectorQueueFromHawkBit()} to 102 | * {@link AmqpConfiguration#exchangeQueueToConnector()}. 103 | * 104 | * @return the binding and create the queue and exchange 105 | */ 106 | @Bean 107 | Binding bindReceiverQueueToSpExchange(final AmqpProperties amqpProperties) { 108 | return BindingBuilder.bind(receiverConnectorQueueFromHawkBit(amqpProperties)) 109 | .to(exchangeQueueToConnector(amqpProperties)); 110 | } 111 | 112 | @Configuration 113 | @ConditionalOnProperty(prefix = AmqpProperties.CONFIGURATION_PREFIX, name = "customVhost") 114 | protected static class CachingConnectionFactoryInitializer { 115 | 116 | CachingConnectionFactoryInitializer(final CachingConnectionFactory connectionFactory, 117 | final AmqpProperties amqpProperties) { 118 | connectionFactory.setVirtualHost(amqpProperties.getCustomVhost()); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.amqp; 11 | 12 | import org.springframework.boot.context.properties.ConfigurationProperties; 13 | import org.springframework.stereotype.Component; 14 | 15 | /** 16 | * Bean which holds the necessary properties for configuring the AMQP 17 | * connection. 18 | * 19 | */ 20 | @Component 21 | @ConfigurationProperties("hawkbit.device.simulator.amqp") 22 | public class AmqpProperties { 23 | 24 | /** 25 | * The prefix for this configuration. 26 | */ 27 | public static final String CONFIGURATION_PREFIX = "hawkbit.device.simulator.amqp"; 28 | 29 | /** 30 | * The property string of ~.amqp.enabled 31 | */ 32 | public static final String CONFIGURATION_ENABLED_PROPERTY = CONFIGURATION_PREFIX + ".enabled"; 33 | 34 | /** 35 | * Indicates if the AMQP interface is enabled for the device simulator. 36 | */ 37 | private boolean enabled; 38 | 39 | /** 40 | * Set to true for the simulator run DMF health check. 41 | */ 42 | private boolean checkDmfHealth = false; 43 | 44 | /** 45 | * Queue for receiving DMF messages from update server. 46 | */ 47 | private String receiverConnectorQueueFromSp = "simulator_receiver"; 48 | 49 | /** 50 | * Exchange for sending DMF messages to update server. 51 | */ 52 | private String senderForSpExchange = "simulator.replyTo"; 53 | 54 | /** 55 | * Message time to live (ttl) for the deadletter queue. Default ttl is 1 56 | * hour. 57 | */ 58 | private int deadLetterTtl = 60_000; 59 | 60 | private String customVhost; 61 | 62 | public boolean isCheckDmfHealth() { 63 | return checkDmfHealth; 64 | } 65 | 66 | public void setCheckDmfHealth(final boolean checkDmfHealth) { 67 | this.checkDmfHealth = checkDmfHealth; 68 | } 69 | 70 | public String getReceiverConnectorQueueFromSp() { 71 | return receiverConnectorQueueFromSp; 72 | } 73 | 74 | public void setReceiverConnectorQueueFromSp(final String receiverConnectorQueueFromSp) { 75 | this.receiverConnectorQueueFromSp = receiverConnectorQueueFromSp; 76 | } 77 | 78 | public String getSenderForSpExchange() { 79 | return senderForSpExchange; 80 | } 81 | 82 | public void setSenderForSpExchange(final String senderForSpExchange) { 83 | this.senderForSpExchange = senderForSpExchange; 84 | } 85 | 86 | public int getDeadLetterTtl() { 87 | return deadLetterTtl; 88 | } 89 | 90 | public void setDeadLetterTtl(final int deadLetterTtl) { 91 | this.deadLetterTtl = deadLetterTtl; 92 | } 93 | 94 | public boolean isEnabled() { 95 | return enabled; 96 | } 97 | 98 | public void setEnabled(final boolean enabled) { 99 | this.enabled = enabled; 100 | } 101 | 102 | public String getCustomVhost() { 103 | return customVhost; 104 | } 105 | 106 | public void setCustomVhost(final String customVhost) { 107 | this.customVhost = customVhost; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/MessageService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.amqp; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import org.springframework.amqp.core.Message; 15 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 16 | import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; 17 | 18 | /** 19 | * Abstract class for sender and receiver service. 20 | * 21 | * 22 | * 23 | */ 24 | public class MessageService { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(MessageService.class); 27 | 28 | protected final RabbitTemplate rabbitTemplate; 29 | 30 | protected final AmqpProperties amqpProperties; 31 | 32 | /** 33 | * Constructor. 34 | * 35 | * @param rabbitTemplate 36 | * the rabbit template 37 | * @param amqpProperties 38 | * the amqp properties 39 | */ 40 | public MessageService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties) { 41 | this.rabbitTemplate = rabbitTemplate; 42 | this.amqpProperties = amqpProperties; 43 | } 44 | 45 | /** 46 | * Method to call when error emerges. 47 | * 48 | * @param message 49 | * the message that triggered the error 50 | * @param error 51 | * the error 52 | */ 53 | public void logAndThrowMessageError(final Message message, final String error) { 54 | LOGGER.error("Error \"{}\" reported by message {}", error, message.getMessageProperties().getMessageId()); 55 | throw new IllegalArgumentException(error); 56 | } 57 | 58 | /** 59 | * Convert a message body to a given class and set the message header 60 | * AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME for Jackson converter. 61 | * 62 | * @param message 63 | * which body will converted 64 | * @param clazz 65 | * the body class 66 | * @return the converted body 67 | */ 68 | @SuppressWarnings("unchecked") 69 | public T convertMessage(final Message message, final Class clazz) { 70 | message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, 71 | clazz.getTypeName()); 72 | return (T) rabbitTemplate.getMessageConverter().fromMessage(message); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SimulatedUpdate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.amqp; 11 | 12 | import java.io.Serializable; 13 | import java.time.LocalDateTime; 14 | 15 | /** 16 | * Object for holding attributes for a simulated update for the device 17 | * simulator. 18 | */ 19 | public class SimulatedUpdate implements Serializable { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | private final String tenant; 24 | private final String thingId; 25 | private final Long actionId; 26 | private transient LocalDateTime startCacheTime; 27 | 28 | /** 29 | * @param tenant 30 | * the tenant for this thing and for this simulated update 31 | * @param thingId 32 | * the thing id that this simulated update correlates to 33 | * @param actionId 34 | * the id of the action related to this simulated update 35 | */ 36 | public SimulatedUpdate(final String tenant, final String thingId, final Long actionId) { 37 | this.tenant = tenant; 38 | this.thingId = thingId; 39 | this.actionId = actionId; 40 | this.startCacheTime = LocalDateTime.now(); 41 | } 42 | 43 | public String getTenant() { 44 | return tenant; 45 | } 46 | 47 | public String getThingId() { 48 | return thingId; 49 | } 50 | 51 | public Long getActionId() { 52 | return actionId; 53 | } 54 | 55 | public LocalDateTime getStartCacheTime() { 56 | return startCacheTime; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/BasicAuthProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.http; 11 | 12 | import org.springframework.boot.context.properties.ConfigurationProperties; 13 | 14 | /** 15 | * Basic authentication properties. 16 | * 17 | */ 18 | @ConfigurationProperties(prefix = BasicAuthProperties.CONFIGURATION_PREFIX) 19 | public class BasicAuthProperties { 20 | 21 | /** 22 | * The prefix for this configuration. 23 | */ 24 | public static final String CONFIGURATION_PREFIX = "hawkbit.device.simulator.auth"; 25 | 26 | /** 27 | * The property string of ~.auth.enabled 28 | */ 29 | public static final String CONFIGURATION_ENABLED_PROPERTY = CONFIGURATION_PREFIX + ".enabled"; 30 | 31 | /** 32 | * Indicates if basic auth is enabled for the device simulator. 33 | */ 34 | private boolean enabled; 35 | 36 | /** 37 | * The set username for basic auth 38 | */ 39 | private String user; 40 | 41 | /** 42 | * The set password for basic auth 43 | */ 44 | private String password; 45 | 46 | 47 | public boolean isEnabled() { 48 | return enabled; 49 | } 50 | 51 | public void setEnabled(final boolean enabled) { 52 | this.enabled = enabled; 53 | } 54 | 55 | public String getUser() { 56 | return user; 57 | } 58 | 59 | public void setUser(String user) { 60 | this.user = user; 61 | } 62 | 63 | public String getPassword() { 64 | return password; 65 | } 66 | 67 | public void setPassword(String password) { 68 | this.password = password; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/BasicAuthSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch.IO GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.http; 11 | 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 13 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.security.config.Customizer; 18 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 19 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 20 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 21 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; 22 | import org.springframework.security.crypto.factory.PasswordEncoderFactories; 23 | import org.springframework.security.crypto.password.PasswordEncoder; 24 | import org.springframework.security.web.SecurityFilterChain; 25 | 26 | /** 27 | * Spring security configuration to grant access for the configured in-memory user. 28 | */ 29 | @Configuration 30 | @EnableWebSecurity 31 | @EnableConfigurationProperties({BasicAuthProperties.class}) 32 | @ConditionalOnProperty(name = BasicAuthProperties.CONFIGURATION_ENABLED_PROPERTY, havingValue = "true") 33 | public class BasicAuthSecurityConfiguration { 34 | 35 | private final BasicAuthProperties basicAuthProperties; 36 | 37 | protected BasicAuthSecurityConfiguration(final BasicAuthProperties basicAuthProperties) { 38 | this.basicAuthProperties = basicAuthProperties; 39 | } 40 | 41 | @Bean 42 | protected SecurityFilterChain filterChainBasicAuth(final HttpSecurity http) throws Exception { 43 | http 44 | .getSharedObject(AuthenticationManagerBuilder.class) 45 | .inMemoryAuthentication() 46 | .withUser(basicAuthProperties.getUser()) 47 | .password(passwordEncoder().encode(basicAuthProperties.getPassword())) 48 | .roles("ADMIN"); 49 | 50 | return http 51 | .csrf(AbstractHttpConfigurer::disable) 52 | .authorizeHttpRequests(amrmRegistry -> amrmRegistry 53 | .requestMatchers("/").permitAll() 54 | .anyRequest().authenticated()) 55 | .httpBasic(Customizer.withDefaults()) 56 | .exceptionHandling(configurer -> configurer.authenticationEntryPoint((request, response, e) -> { 57 | response.addHeader("WWW-Authenticate", "Basic realm=\"Device Simulator\""); 58 | response.setStatus(HttpStatus.UNAUTHORIZED.value()); 59 | response.getWriter().write(HttpStatus.UNAUTHORIZED.getReasonPhrase()); 60 | })) 61 | .build(); 62 | } 63 | 64 | @Bean 65 | protected PasswordEncoder passwordEncoder() { 66 | return PasswordEncoderFactories.createDelegatingPasswordEncoder(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/GatewayTokenInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.http; 11 | 12 | import feign.RequestInterceptor; 13 | import feign.RequestTemplate; 14 | 15 | /** 16 | * A feign interceptor to apply the gateway-token header to each http-request. 17 | * 18 | */ 19 | public class GatewayTokenInterceptor implements RequestInterceptor { 20 | 21 | private final String gatewayToken; 22 | 23 | /** 24 | * @param gatewayToken 25 | * the gatwway token to be used in the http-header 26 | */ 27 | public GatewayTokenInterceptor(final String gatewayToken) { 28 | this.gatewayToken = gatewayToken; 29 | } 30 | 31 | @Override 32 | public void apply(final RequestTemplate template) { 33 | template.header("Authorization", "GatewayToken " + gatewayToken); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/NoAuthSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator.http; 11 | 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 16 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 17 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; 18 | import org.springframework.security.web.SecurityFilterChain; 19 | 20 | /** 21 | * Spring security configuration. 22 | * Depending on the ~.auth.enabled property access is granted for all requests. 23 | */ 24 | @Configuration 25 | @EnableWebSecurity 26 | @ConditionalOnProperty(name = BasicAuthProperties.CONFIGURATION_ENABLED_PROPERTY, havingValue = "false", matchIfMissing = true) 27 | public class NoAuthSecurityConfiguration { 28 | 29 | /** 30 | * Spring security configuration to grant access for all incoming requests. 31 | * Default configuration, if ~.auth.enabled is false or not set 32 | */ 33 | @Bean 34 | protected SecurityFilterChain filterChainNoAuth(final HttpSecurity httpSec) throws Exception { 35 | return httpSec 36 | .authorizeRequests(amrmRegistry -> amrmRegistry.requestMatchers("/**").permitAll()) 37 | .csrf(AbstractHttpConfigurer::disable) 38 | .build(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | # 4 | # This program and the accompanying materials are made 5 | # available under the terms of the Eclipse Public License 2.0 6 | # which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | 11 | ## Configuration for DMF communication 12 | 13 | 14 | hawkbit.device.simulator.amqp.enabled=true 15 | hawkbit.device.simulator.amqp.receiverConnectorQueueFromSp=simulator_receiver 16 | hawkbit.device.simulator.amqp.deadLetterQueue=simulator_deadletter 17 | hawkbit.device.simulator.amqp.deadLetterExchange=simulator.deadletter 18 | hawkbit.device.simulator.amqp.senderForSpExchange=simulator.replyTo 19 | 20 | hawkbit.device.simulator.default-tenant=DEFAULT 21 | 22 | ## Configuration for simulations 23 | hawkbit.device.simulator.autostarts.[0].tenant=${hawkbit.device.simulator.default-tenant} 24 | 25 | hawkbit.device.simulator.attributes[0].key=isoCode 26 | hawkbit.device.simulator.attributes[0].random=DE,US,AU,FR,DK,CA 27 | hawkbit.device.simulator.attributes[1].key=hwRevision 28 | hawkbit.device.simulator.attributes[1].value=1.1 29 | hawkbit.device.simulator.attributes[2].key=serial 30 | hawkbit.device.simulator.attributes[2].value=${random.value} 31 | 32 | management.endpoints.enabled-by-default=false 33 | management.endpoint.health.enabled=true 34 | 35 | ## Configuration for local RabbitMQ integration 36 | spring.rabbitmq.username=guest 37 | spring.rabbitmq.password=guest 38 | spring.rabbitmq.virtualHost=/ 39 | spring.rabbitmq.host=localhost 40 | spring.rabbitmq.port=5672 41 | spring.rabbitmq.dynamic=true 42 | 43 | ## Configuration for basic auth 44 | hawkbit.device.simulator.auth.enabled=false 45 | hawkbit.device.simulator.auth.user=username 46 | hawkbit.device.simulator.auth.password=password 47 | 48 | server.port=8083 49 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/test/java/org/eclipse/hawkbit/simulator/AllowAllWebSecurityTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 14 | 15 | import io.qameta.allure.Description; 16 | import io.qameta.allure.Feature; 17 | import io.qameta.allure.Story; 18 | import org.eclipse.hawkbit.simulator.http.BasicAuthProperties; 19 | import org.junit.jupiter.api.Test; 20 | import org.springframework.test.context.TestPropertySource; 21 | 22 | @Feature("Component Tests - Hawkbit Device Simulator") 23 | @Story("Web Security Test, Allow All Requests") 24 | @TestPropertySource(properties = {BasicAuthProperties.CONFIGURATION_ENABLED_PROPERTY + " = false"}) 25 | public class AllowAllWebSecurityTest extends DdiWebSecurityTest { 26 | 27 | @Test 28 | @Description("Verifies status when accessing base url - results in 200") 29 | public void shouldGetOkForBaseUrl() throws Exception { 30 | mockMvc.perform(get(SIMULATOR_BASE_URL)).andExpect(status().isOk()); 31 | } 32 | 33 | @Test 34 | @Description("Verifies status when creating simulated devices - results in 200") 35 | public void shouldGetOkForStart() throws Exception { 36 | mockMvc.perform(get(SIMULATOR_BASE_URL_START)).andExpect(status().isOk()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/test/java/org/eclipse/hawkbit/simulator/AuthenticatedWebSecurityTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 14 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; 15 | 16 | import io.qameta.allure.Description; 17 | import io.qameta.allure.Feature; 18 | import io.qameta.allure.Story; 19 | import org.eclipse.hawkbit.simulator.http.BasicAuthProperties; 20 | import org.junit.jupiter.api.Test; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.test.context.TestPropertySource; 23 | 24 | @Feature("Component Tests - Hawkbit Device Simulator") 25 | @Story("Web Security Test, Basic Authentication") 26 | @TestPropertySource(properties = {BasicAuthProperties.CONFIGURATION_ENABLED_PROPERTY + " = true"}) 27 | public class AuthenticatedWebSecurityTest extends DdiWebSecurityTest { 28 | 29 | @Autowired 30 | private BasicAuthProperties basicAuthProperties; 31 | 32 | @Test 33 | @Description("Verifies status when accessing base url - results in 200") 34 | public void shouldGetOkForBaseUrl() throws Exception { 35 | mockMvc.perform(get(SIMULATOR_BASE_URL)).andExpect(status().isOk()); 36 | } 37 | 38 | @Test 39 | @Description("Verifies status when creating simulated devices - results in 401") 40 | public void shouldGetUnauthorizedForStart() throws Exception { 41 | mockMvc.perform(get(SIMULATOR_BASE_URL_START)).andExpect(status().isUnauthorized()); 42 | } 43 | 44 | @Test 45 | @Description("Verifies status when creating simulated devices as authorized user - results in 200") 46 | public void shouldGetOkForAuthenticatedUser() throws Exception { 47 | mockMvc.perform(get(SIMULATOR_BASE_URL_START) 48 | .with(httpBasic(basicAuthProperties.getUser(), basicAuthProperties.getPassword()))) 49 | .andExpect(status().isOk()); 50 | } 51 | 52 | @Test 53 | @Description("Verifies status when creating simulated devices with wrong credentials - results in 401") 54 | public void shouldGetUnauthorizedForNotExistingUser() throws Exception { 55 | mockMvc.perform(get(SIMULATOR_BASE_URL_START) 56 | .with(httpBasic(basicAuthProperties.getUser() + "random", basicAuthProperties.getPassword()))) 57 | .andExpect(status().isUnauthorized()); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /hawkbit-device-simulator/src/test/java/org/eclipse/hawkbit/simulator/DdiWebSecurityTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.simulator; 11 | 12 | import org.eclipse.hawkbit.simulator.amqp.AmqpProperties; 13 | import org.junit.jupiter.api.extension.ExtendWith; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | import org.springframework.test.context.TestPropertySource; 18 | import org.springframework.test.context.junit.jupiter.SpringExtension; 19 | import org.springframework.test.web.servlet.MockMvc; 20 | 21 | @SpringBootTest 22 | @AutoConfigureMockMvc 23 | @TestPropertySource(properties = {AmqpProperties.CONFIGURATION_ENABLED_PROPERTY + " = false"}) 24 | public abstract class DdiWebSecurityTest { 25 | 26 | @Autowired 27 | protected MockMvc mockMvc; 28 | 29 | static final String SIMULATOR_BASE_URL = "/"; 30 | static final String SIMULATOR_BASE_URL_START = SIMULATOR_BASE_URL + "start?amount=5&api=ddi"; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /hawkbit-example-core-feign-client/README.md: -------------------------------------------------------------------------------- 1 | # Eclipse.IoT hawkBit - Example Core Feign Client 2 | 3 | This modules contains core beans that are needed to create a [Feign Client](https://github.com/Netflix/feign) with hwakBit. 4 | 5 | # Compile 6 | 7 | #### Build hawkbit-example-core-feign-client 8 | 9 | ``` 10 | $ cd hawkbit/examples/hawkbit-example-core-feign-client 11 | $ mvn clean install 12 | ``` 13 | 14 | -------------------------------------------------------------------------------- /hawkbit-example-core-feign-client/pom.xml: -------------------------------------------------------------------------------- 1 | 12 | 14 | 4.0.0 15 | 16 | org.eclipse.hawkbit 17 | hawkbit-examples-parent 18 | ${revision} 19 | 20 | 21 | hawkbit-example-core-feign-client 22 | hawkBit :: Examples :: Feign Core Client 23 | 24 | 25 | 26 | org.slf4j 27 | slf4j-api 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-openfeign 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /hawkbit-example-core-feign-client/src/main/java/org/eclipse/hawkbit/feign/core/client/ApplicationJsonRequestHeaderInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.feign.core.client; 11 | 12 | import org.springframework.http.MediaType; 13 | 14 | import feign.RequestInterceptor; 15 | import feign.RequestTemplate; 16 | 17 | 18 | /** 19 | * An feign request interceptor to set the defined {@code Accept} and 20 | * {@code Content-Type} headers for each request to {@code application/json}. 21 | */ 22 | public class ApplicationJsonRequestHeaderInterceptor implements RequestInterceptor { 23 | 24 | @Override 25 | public void apply(final RequestTemplate template) { 26 | template.header("Accept", MediaType.APPLICATION_JSON_VALUE); 27 | template.header("Content-Type", MediaType.APPLICATION_JSON_VALUE); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /hawkbit-example-core-feign-client/src/main/java/org/eclipse/hawkbit/feign/core/client/FeignClientConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.feign.core.client; 11 | 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 13 | import org.springframework.cloud.openfeign.FeignClientsConfiguration; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Import; 17 | 18 | import feign.Contract; 19 | import feign.Feign; 20 | 21 | /** 22 | * Spring annotated java configuration class which defines necessary beans for 23 | * configure the feign-client. 24 | */ 25 | @Configuration 26 | @ConditionalOnClass(Feign.class) 27 | @Import(FeignClientsConfiguration.class) 28 | public class FeignClientConfiguration { 29 | 30 | @Bean 31 | public ApplicationJsonRequestHeaderInterceptor jsonHeaderInterceptor() { 32 | return new ApplicationJsonRequestHeaderInterceptor(); 33 | } 34 | 35 | @Bean 36 | public Contract feignContract() { 37 | return new IgnoreMultipleConsumersProducersSpringMvcContract(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hawkbit-example-core-feign-client/src/main/java/org/eclipse/hawkbit/feign/core/client/IgnoreMultipleConsumersProducersSpringMvcContract.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.feign.core.client; 11 | 12 | import java.lang.annotation.Annotation; 13 | import java.lang.reflect.Method; 14 | import java.util.LinkedHashMap; 15 | 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.cloud.openfeign.support.SpringMvcContract; 19 | 20 | import feign.MethodMetadata; 21 | import feign.Param; 22 | 23 | /** 24 | * Own implementation of the {@link SpringMvcContract} which catches the 25 | * {@link IllegalStateException} which occurs due multiple produces and consumes 26 | * values in the request-mapping 27 | * annoation.https://github.com/spring-cloud/spring-cloud-netflix/issues/808 28 | */ 29 | public class IgnoreMultipleConsumersProducersSpringMvcContract extends SpringMvcContract { 30 | 31 | private static final Logger LOGGER = LoggerFactory 32 | .getLogger(IgnoreMultipleConsumersProducersSpringMvcContract.class); 33 | 34 | @Override 35 | protected void processAnnotationOnMethod(final MethodMetadata data, final Annotation methodAnnotation, 36 | final Method method) { 37 | try { 38 | super.processAnnotationOnMethod(data, methodAnnotation, method); 39 | } catch (final IllegalStateException e) { 40 | // ignore illegalstateexception here because it's thrown because of 41 | // multiple consumers and produces, see 42 | // https://github.com/spring-cloud/spring-cloud-netflix/issues/808 43 | LOGGER.trace(e.getMessage(), e); 44 | 45 | // This line from super is mandatory to avoid that access to the 46 | // expander causes a nullpointer. 47 | data.indexToExpander(new LinkedHashMap()); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /hawkbit-example-ddi-feign-client/README.md: -------------------------------------------------------------------------------- 1 | # Eclipse.IoT hawkBit - Example DDI Feign Client API 2 | 3 | This modules contains the declarative client binding resources of the DDI API. 4 | For more information see 'Feign Inheritance Support' at [Spring Cloud] (http://projects.spring.io/spring-cloud/spring-cloud.html). 5 | Powered by [Feign](https://github.com/Netflix/feign). 6 | 7 | # Compile 8 | 9 | #### Build hawkbit-example-ddi-feign-client 10 | 11 | ``` 12 | $ cd hawkbit/examples/hawkbit-example-ddi-feign-client 13 | $ mvn clean install 14 | ``` 15 | -------------------------------------------------------------------------------- /hawkbit-example-ddi-feign-client/pom.xml: -------------------------------------------------------------------------------- 1 | 12 | 14 | 4.0.0 15 | 16 | org.eclipse.hawkbit 17 | hawkbit-examples-parent 18 | ${revision} 19 | 20 | hawkbit-example-ddi-feign-client 21 | 22 | hawkBit :: Examples :: DDI Feign Client 23 | 24 | 25 | 26 | org.eclipse.hawkbit 27 | hawkbit-example-core-feign-client 28 | ${project.version} 29 | 30 | 31 | org.eclipse.hawkbit 32 | hawkbit-ddi-api 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-starter-openfeign 37 | 38 | 39 | org.apache.tomcat.embed 40 | tomcat-embed-el 41 | 42 | 43 | org.hibernate.validator 44 | hibernate-validator 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /hawkbit-example-ddi-feign-client/src/main/java/org/eclipse/hawkbit/ddi/client/resource/RootControllerResourceClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.ddi.client.resource; 11 | 12 | import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the Rootcontroller resource of the DDI API. 17 | */ 18 | @FeignClient(name = "RootControllerClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface RootControllerResourceClient extends DdiRootControllerRestApi { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /bin/ 3 | /.apt_generated/ 4 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/README.md: -------------------------------------------------------------------------------- 1 | # Eclipse.IoT hawkBit - Example Management Feign Client API 2 | 3 | This modules contains the declarative client binding resources of the Management API. 4 | For more information see 'Feign Inheritance Support' at [Spring Cloud] (http://projects.spring.io/spring-cloud/spring-cloud.html). 5 | Powered by [Feign](https://github.com/Netflix/feign). 6 | 7 | # Compile 8 | 9 | #### Build hawkbit-example-mgmt-feign-client 10 | 11 | ``` 12 | $ cd hawkbit/examples/hawkbit-example-mgmt-feign-client 13 | $ mvn clean install 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/pom.xml: -------------------------------------------------------------------------------- 1 | 12 | 14 | 4.0.0 15 | 16 | org.eclipse.hawkbit 17 | hawkbit-examples-parent 18 | ${revision} 19 | 20 | jar 21 | hawkbit-example-mgmt-feign-client 22 | hawkBit :: Examples :: Management client API 23 | 24 | 25 | 26 | org.eclipse.hawkbit 27 | hawkbit-example-core-feign-client 28 | ${project.version} 29 | 30 | 31 | org.eclipse.hawkbit 32 | hawkbit-mgmt-api 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-starter-openfeign 37 | 38 | 39 | org.apache.tomcat.embed 40 | tomcat-embed-el 41 | 42 | 43 | org.hibernate.validator 44 | hibernate-validator 45 | 46 | 47 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtDistributionSetClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the DistributionSet resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtDistributionSetClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtDistributionSetClientResource extends MgmtDistributionSetRestApi { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtDistributionSetTagClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTagRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the DistributionSetTag resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtDistributionSetTagClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtDistributionSetTagClientResource extends MgmtDistributionSetTagRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtDistributionSetTypeClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTypeRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the DistributionSetType resource of the management API. 17 | * 18 | */ 19 | @FeignClient(name = "MgmtDistributionSetTypeClient", url = "${hawkbit.url:localhost:8080}") 20 | public interface MgmtDistributionSetTypeClientResource extends MgmtDistributionSetTypeRestApi { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtDownloadArtifactClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * A feign-client interface declaration which allows to build a feign-client 17 | * stub. 18 | */ 19 | @FeignClient(name = "MgmtDownloadArtifactClient", url = "${hawkbit.url:localhost:8080}") 20 | public interface MgmtDownloadArtifactClientResource extends MgmtDownloadArtifactRestApi { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtDownloadClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * 17 | */ 18 | @FeignClient(name = "MgmtDownloadClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtDownloadClientResource extends MgmtDownloadRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtRolloutClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the Rollout resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtRolloutClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtRolloutClientResource extends MgmtRolloutRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtSoftwareModuleClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; 13 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; 14 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi; 15 | import org.springframework.cloud.openfeign.FeignClient; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.web.bind.annotation.PathVariable; 18 | import org.springframework.web.bind.annotation.PostMapping; 19 | import org.springframework.web.bind.annotation.RequestParam; 20 | import org.springframework.web.multipart.MultipartFile; 21 | 22 | import feign.Param; 23 | 24 | /** 25 | * Client binding for the SoftwareModule resource of the management API. 26 | */ 27 | @FeignClient(name = "MgmtSoftwareModuleClient", url = "${hawkbit.url:localhost:8080}") 28 | public interface MgmtSoftwareModuleClientResource extends MgmtSoftwareModuleRestApi { 29 | 30 | @Override 31 | @PostMapping(value = MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts") 32 | ResponseEntity uploadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, 33 | @Param("file") final MultipartFile file, 34 | @RequestParam(value = "filename", required = false) final String optionalFileName, 35 | @RequestParam(value = "md5sum", required = false) final String md5Sum, 36 | @RequestParam(value = "sha1sum", required = false) final String sha1Sum, 37 | @RequestParam(value = "sha256sum", required = false) final String sha256sum); 38 | } 39 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtSoftwareModuleTypeClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the SoftwareModuleType resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtSoftwareModuleTypeClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtSoftwareModuleTypeClientResource extends MgmtSoftwareModuleTypeRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtSystemManagementClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtSystemManagementRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the {@link MgmtSystemManagementRestApi}. 17 | * 18 | */ 19 | @FeignClient(name = "MgmtSystemManagementClient", url = "${hawkbit.url:localhost:8080}") 20 | public interface MgmtSystemManagementClientResource extends MgmtSystemManagementRestApi { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtTargetClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the Target resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtTargetClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtTargetClientResource extends MgmtTargetRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtTargetFilterQueryClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the Target filter query resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtTargetFilterQueryClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtTargetFilterQueryClientResource extends MgmtTargetFilterQueryRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtTargetTagClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTagRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the TargetTag resource of the management API. 17 | */ 18 | @FeignClient(name = "MgmtTargetTagClient", url = "${hawkbit.url:localhost:8080}") 19 | public interface MgmtTargetTagClientResource extends MgmtTargetTagRestApi { 20 | } 21 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/MgmtTenantManagementClientResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource; 11 | 12 | import org.eclipse.hawkbit.mgmt.rest.api.MgmtTenantManagementRestApi; 13 | import org.springframework.cloud.openfeign.FeignClient; 14 | 15 | /** 16 | * Client binding for the {@link MgmtTenantManagementRestApi}. 17 | * 18 | */ 19 | @FeignClient(name = "MgmtTenantManagementClient", url = "${hawkbit.url:localhost:8080}") 20 | public interface MgmtTenantManagementClientResource extends MgmtTenantManagementRestApi { 21 | } 22 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPost; 17 | import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment; 18 | 19 | /** 20 | * Builder pattern for building {@link MgmtDistributionSetRequestBodyPost}. 21 | */ 22 | // Exception squid:S1701 - builder pattern 23 | @SuppressWarnings({ "squid:S1701" }) 24 | public class DistributionSetBuilder { 25 | 26 | private String name; 27 | private String version; 28 | private String type; 29 | private String description; 30 | private Boolean requiredMigrationStep; 31 | private final List modules = new ArrayList<>(); 32 | 33 | /** 34 | * @param name 35 | * the name of the distribution set 36 | * @return the builder itself 37 | */ 38 | public DistributionSetBuilder name(final String name) { 39 | this.name = name; 40 | return this; 41 | } 42 | 43 | public DistributionSetBuilder moduleByID(final Long id) { 44 | final MgmtSoftwareModuleAssigment softwareModuleAssigmentRest = new MgmtSoftwareModuleAssigment(); 45 | softwareModuleAssigmentRest.setId(id); 46 | modules.add(softwareModuleAssigmentRest); 47 | return this; 48 | } 49 | 50 | /** 51 | * @param version 52 | * the version of the distribution set 53 | * @return the builder itself 54 | */ 55 | public DistributionSetBuilder version(final String version) { 56 | this.version = version; 57 | return this; 58 | } 59 | 60 | /** 61 | * @param type 62 | * the distribution set type name for this distribution set 63 | * @return the builder itself 64 | */ 65 | public DistributionSetBuilder type(final String type) { 66 | this.type = type; 67 | return this; 68 | } 69 | 70 | /** 71 | * @param description 72 | * the description 73 | * @return the builder itself 74 | */ 75 | public DistributionSetBuilder description(final String description) { 76 | this.description = description; 77 | return this; 78 | } 79 | 80 | /** 81 | * @param requiredMigrationStep 82 | * the boolean for required migration step 83 | * @return the builder itself 84 | */ 85 | public DistributionSetBuilder requiredMigrationStep(final Boolean requiredMigrationStep) { 86 | this.requiredMigrationStep = requiredMigrationStep; 87 | return this; 88 | } 89 | 90 | /** 91 | * Builds a list with a single entry of 92 | * {@link MgmtDistributionSetRequestBodyPost} which can directly be used to 93 | * post on the RESTful-API. 94 | * 95 | * @return a single entry list of {@link MgmtDistributionSetRequestBodyPost} 96 | */ 97 | public List build() { 98 | return Arrays.asList(doBuild("")); 99 | } 100 | 101 | /** 102 | * Builds a list of multiple {@link MgmtDistributionSetRequestBodyPost} to 103 | * create multiple distribution sets at once. An increasing number will be 104 | * used for version of the distribution set. The name and type will remain 105 | * the same. 106 | * 107 | * @param count 108 | * the amount of distribution sets body which should be created 109 | * @return a list of {@link MgmtDistributionSetRequestBodyPost} 110 | */ 111 | public List buildAsList(final int count) { 112 | return buildAsList(0, count); 113 | } 114 | 115 | /** 116 | * Builds a list of multiple {@link MgmtDistributionSetRequestBodyPost} to 117 | * create multiple distribution sets at once. An increasing number will be 118 | * used for version of the distribution set starting from given offset. The 119 | * name and type will remain the same. 120 | * 121 | * @param count 122 | * the amount of distribution sets body which should be created 123 | * @param offset 124 | * for for index start 125 | * @return a list of {@link MgmtDistributionSetRequestBodyPost} 126 | */ 127 | public List buildAsList(final int offset, final int count) { 128 | final List bodyList = new ArrayList<>(); 129 | for (int index = offset; index < count + offset; index++) { 130 | bodyList.add(doBuild(String.valueOf(index))); 131 | } 132 | 133 | return bodyList; 134 | } 135 | 136 | private MgmtDistributionSetRequestBodyPost doBuild(final String suffix) { 137 | final MgmtDistributionSetRequestBodyPost body = new MgmtDistributionSetRequestBodyPost(); 138 | body.setName(name); 139 | body.setVersion(version + suffix); 140 | body.setType(type); 141 | body.setDescription(description); 142 | body.setModules(modules); 143 | body.setRequiredMigrationStep(requiredMigrationStep); 144 | return body; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetTypeBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost; 17 | import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeAssigment; 18 | 19 | /** 20 | * 21 | * Builder pattern for building {@link MgmtDistributionSetTypeRequestBodyPost}. 22 | * 23 | */ 24 | // Exception squid:S1701 - builder pattern 25 | @SuppressWarnings({ "squid:S1701" }) 26 | public class DistributionSetTypeBuilder { 27 | 28 | private String key; 29 | private String name; 30 | private String description; 31 | private final List mandatorymodules = new ArrayList<>(); 32 | private final List optionalmodules = new ArrayList<>(); 33 | 34 | /** 35 | * @param key 36 | * the key of the distribution set type 37 | * @return the builder itself 38 | */ 39 | public DistributionSetTypeBuilder key(final String key) { 40 | this.key = key; 41 | return this; 42 | } 43 | 44 | /** 45 | * @param name 46 | * the name of the distribution set type 47 | * @return the builder itself 48 | */ 49 | public DistributionSetTypeBuilder name(final String name) { 50 | this.name = name; 51 | return this; 52 | } 53 | 54 | /** 55 | * @param description 56 | * the description 57 | * @return the builder itself 58 | */ 59 | public DistributionSetTypeBuilder description(final String description) { 60 | this.description = description; 61 | return this; 62 | } 63 | 64 | /** 65 | * @param softwareModuleTypeIds 66 | * the IDs of the software module types which should be mandatory 67 | * for the distribution set type 68 | * @return the builder itself 69 | */ 70 | public DistributionSetTypeBuilder mandatorymodules(final Long... softwareModuleTypeIds) { 71 | for (final Long id : softwareModuleTypeIds) { 72 | final MgmtSoftwareModuleTypeAssigment softwareModuleTypeAssigmentRest = new MgmtSoftwareModuleTypeAssigment(); 73 | softwareModuleTypeAssigmentRest.setId(id); 74 | this.mandatorymodules.add(softwareModuleTypeAssigmentRest); 75 | } 76 | return this; 77 | } 78 | 79 | /** 80 | * 81 | * @param softwareModuleTypeIds 82 | * the IDs of the software module types which should be optional 83 | * for the distribution set type 84 | * @return the builder itself 85 | */ 86 | public DistributionSetTypeBuilder optionalmodules(final Long... softwareModuleTypeIds) { 87 | for (final Long id : softwareModuleTypeIds) { 88 | final MgmtSoftwareModuleTypeAssigment softwareModuleTypeAssigmentRest = new MgmtSoftwareModuleTypeAssigment(); 89 | softwareModuleTypeAssigmentRest.setId(id); 90 | this.optionalmodules.add(softwareModuleTypeAssigmentRest); 91 | } 92 | return this; 93 | } 94 | 95 | /** 96 | * Builds a list with a single entry of 97 | * {@link MgmtDistributionSetTypeRequestBodyPost} which can directly be used 98 | * in the RESTful-API. 99 | * 100 | * @return a single entry list of 101 | * {@link MgmtDistributionSetTypeRequestBodyPost} 102 | */ 103 | public List build() { 104 | return Arrays.asList(doBuild("")); 105 | } 106 | 107 | /** 108 | * Builds a list of multiple {@link MgmtDistributionSetTypeRequestBodyPost} 109 | * to create multiple distribution set types at once. An increasing number 110 | * will be added to the name and key of the distribution set type. The 111 | * optional and mandatory software module types will remain the same. 112 | * 113 | * @param count 114 | * the amount of distribution sets type body which should be 115 | * created 116 | * @return a list of {@link MgmtDistributionSetTypeRequestBodyPost} 117 | */ 118 | public List buildAsList(final int count) { 119 | final List bodyList = new ArrayList<>(); 120 | for (int index = 0; index < count; index++) { 121 | bodyList.add(doBuild(String.valueOf(index))); 122 | } 123 | return bodyList; 124 | 125 | } 126 | 127 | private MgmtDistributionSetTypeRequestBodyPost doBuild(final String suffix) { 128 | final MgmtDistributionSetTypeRequestBodyPost body = new MgmtDistributionSetTypeRequestBodyPost(); 129 | body.setKey(key + suffix); 130 | body.setName(name + suffix); 131 | body.setDescription(description); 132 | body.setMandatorymodules(mandatorymodules); 133 | body.setOptionalmodules(optionalmodules); 134 | return body; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/RolloutBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.List; 13 | 14 | import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutCondition; 15 | import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutCondition.Condition; 16 | import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPost; 17 | import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup; 18 | 19 | /** 20 | * 21 | * Builder pattern for building {@link MgmtRolloutRestRequestBodyPost}. 22 | * 23 | */ 24 | // Exception squid:S1701 - builder pattern 25 | @SuppressWarnings({ "squid:S1701" }) 26 | public class RolloutBuilder { 27 | 28 | private String name; 29 | private int groupSize; 30 | private String targetFilterQuery; 31 | private long distributionSetId; 32 | private String successThreshold; 33 | private String errorThreshold; 34 | private String description; 35 | private List semiAutomaticGroups; 36 | 37 | /** 38 | * @param name 39 | * the name of the rollout 40 | * @return the builder itself 41 | */ 42 | public RolloutBuilder name(final String name) { 43 | this.name = name; 44 | return this; 45 | } 46 | 47 | /** 48 | * @param semiAutomaticGroups 49 | * as alternative to full automatic, i.e. {@link #groupSize(int)} 50 | * @return the builder itself 51 | */ 52 | public RolloutBuilder semiAutomaticGroups(final List semiAutomaticGroups) { 53 | this.semiAutomaticGroups = semiAutomaticGroups; 54 | return this; 55 | } 56 | 57 | /** 58 | * @param groupSize 59 | * the amount of groups the rollout should be split into 60 | * @return the builder itself 61 | */ 62 | public RolloutBuilder groupSize(final int groupSize) { 63 | this.groupSize = groupSize; 64 | return this; 65 | } 66 | 67 | /** 68 | * @param targetFilterQuery 69 | * the FIQL query language to filter targets to contain in the 70 | * rollout 71 | * @return the builder itself 72 | */ 73 | public RolloutBuilder targetFilterQuery(final String targetFilterQuery) { 74 | this.targetFilterQuery = targetFilterQuery; 75 | return this; 76 | } 77 | 78 | /** 79 | * @param description 80 | * the description 81 | * @return the builder itself 82 | */ 83 | public RolloutBuilder description(final String description) { 84 | this.description = description; 85 | return this; 86 | } 87 | 88 | /** 89 | * @param distributionSetId 90 | * the ID of the distribution set to assign to the target in the 91 | * rollout 92 | * @return the builder itself 93 | */ 94 | public RolloutBuilder distributionSetId(final long distributionSetId) { 95 | this.distributionSetId = distributionSetId; 96 | return this; 97 | } 98 | 99 | /** 100 | * @param successThreshold 101 | * the threshold to be used to indicate if a deployment group is 102 | * successful, to trigger the success action 103 | * @return the builder itself 104 | */ 105 | public RolloutBuilder successThreshold(final String successThreshold) { 106 | this.successThreshold = successThreshold; 107 | return this; 108 | } 109 | 110 | /** 111 | * @param errorThreshold 112 | * the threshold to be used to indicate if a deployment group is 113 | * failing, to trigger the error action 114 | * @return the builder itself 115 | */ 116 | public RolloutBuilder errorThreshold(final String errorThreshold) { 117 | this.errorThreshold = errorThreshold; 118 | return this; 119 | } 120 | 121 | /** 122 | * Builds the rollout rest body to creating a rollout. 123 | * 124 | * @return the rest request body for creating a rollout 125 | */ 126 | public MgmtRolloutRestRequestBodyPost build() { 127 | return doBuild(); 128 | } 129 | 130 | private MgmtRolloutRestRequestBodyPost doBuild() { 131 | final MgmtRolloutRestRequestBodyPost body = new MgmtRolloutRestRequestBodyPost(); 132 | body.setName(name); 133 | body.setAmountGroups(groupSize); 134 | body.setTargetFilterQuery(targetFilterQuery); 135 | body.setDistributionSetId(distributionSetId); 136 | body.setDescription(description); 137 | body.setSuccessCondition(new MgmtRolloutCondition(Condition.THRESHOLD, successThreshold)); 138 | body.setErrorCondition(new MgmtRolloutCondition(Condition.THRESHOLD, errorThreshold)); 139 | body.setGroups(semiAutomaticGroups); 140 | return body; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleAssigmentBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment; 16 | 17 | /** 18 | * 19 | * Builder pattern for building {@link MgmtSoftwareModuleAssigment}. 20 | * 21 | */ 22 | // Exception squid:S1701 - builder pattern 23 | @SuppressWarnings({ "squid:S1701" }) 24 | public class SoftwareModuleAssigmentBuilder { 25 | 26 | private final List ids; 27 | 28 | public SoftwareModuleAssigmentBuilder() { 29 | ids = new ArrayList<>(); 30 | } 31 | 32 | /** 33 | * @param id 34 | * the id of the software module 35 | * @return the builder itself 36 | */ 37 | public SoftwareModuleAssigmentBuilder id(final Long id) { 38 | ids.add(id); 39 | return this; 40 | } 41 | 42 | /** 43 | * Builds a list with a single entry of {@link MgmtSoftwareModuleAssigment} 44 | * which can directly be used in the RESTful-API. 45 | * 46 | * @return a single entry list of {@link MgmtSoftwareModuleAssigment} 47 | */ 48 | public List build() { 49 | final List softwareModuleAssigmentRestList = new ArrayList<>(); 50 | for (final Long id : ids) { 51 | final MgmtSoftwareModuleAssigment softwareModuleAssigmentRest = new MgmtSoftwareModuleAssigment(); 52 | softwareModuleAssigmentRest.setId(id); 53 | softwareModuleAssigmentRestList.add(softwareModuleAssigmentRest); 54 | } 55 | return softwareModuleAssigmentRestList; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost; 17 | import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPost; 18 | 19 | /** 20 | * 21 | * Builder pattern for building {@link MgmtSoftwareModuleRequestBodyPost}. 22 | * 23 | */ 24 | // Exception squid:S1701 - builder pattern 25 | @SuppressWarnings({ "squid:S1701" }) 26 | public class SoftwareModuleBuilder { 27 | 28 | private String name; 29 | private String version; 30 | private String type; 31 | private String vendor; 32 | private String description; 33 | 34 | /** 35 | * @param name 36 | * the name of the software module 37 | * @return the builder itself 38 | */ 39 | public SoftwareModuleBuilder name(final String name) { 40 | this.name = name; 41 | return this; 42 | } 43 | 44 | /** 45 | * @param version 46 | * the version of the software module 47 | * @return the builder itsefl 48 | */ 49 | public SoftwareModuleBuilder version(final String version) { 50 | this.version = version; 51 | return this; 52 | } 53 | 54 | /** 55 | * @param type 56 | * the key of the software module type to be used for this 57 | * software module 58 | * @return the builder itself 59 | */ 60 | public SoftwareModuleBuilder type(final String type) { 61 | this.type = type; 62 | return this; 63 | } 64 | 65 | /** 66 | * @param vendor 67 | * the vendor 68 | * @return the builder itself 69 | */ 70 | public SoftwareModuleBuilder vendor(final String vendor) { 71 | this.vendor = vendor; 72 | return this; 73 | } 74 | 75 | /** 76 | * @param description 77 | * the description 78 | * @return the builder itself 79 | */ 80 | public SoftwareModuleBuilder description(final String description) { 81 | this.description = description; 82 | return this; 83 | } 84 | 85 | /** 86 | * Builds a list with a single entry of 87 | * {@link MgmtSoftwareModuleRequestBodyPost} which can directly be used in 88 | * the RESTful-API. 89 | * 90 | * @return a single entry list of {@link MgmtSoftwareModuleRequestBodyPost} 91 | */ 92 | public List build() { 93 | return Arrays.asList(doBuild("")); 94 | } 95 | 96 | /** 97 | * Builds a list of multiple {@link MgmtSoftwareModuleRequestBodyPost} to 98 | * create multiple software module at once. An increasing number will be 99 | * added to the version of the software module. The name and type will 100 | * remain the same. 101 | * 102 | * @param count 103 | * the amount of software module body which should be created 104 | * @return a list of {@link MgmtDistributionSetTypeRequestBodyPost} 105 | */ 106 | public List buildAsList(final int count) { 107 | final List bodyList = new ArrayList<>(); 108 | for (int index = 0; index < count; index++) { 109 | bodyList.add(doBuild(String.valueOf(index))); 110 | } 111 | 112 | return bodyList; 113 | } 114 | 115 | private MgmtSoftwareModuleRequestBodyPost doBuild(final String suffix) { 116 | final MgmtSoftwareModuleRequestBodyPost body = new MgmtSoftwareModuleRequestBodyPost(); 117 | body.setName(name); 118 | body.setVersion(version + suffix); 119 | body.setType(type); 120 | body.setVendor(vendor); 121 | body.setDescription(description); 122 | return body; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPost; 17 | import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost; 18 | 19 | /** 20 | * 21 | * Builder pattern for building {@link MgmtSoftwareModuleRequestBodyPost}. 22 | * 23 | */ 24 | // Exception squid:S1701 - builder pattern 25 | @SuppressWarnings({ "squid:S1701" }) 26 | public class SoftwareModuleTypeBuilder { 27 | 28 | private String key; 29 | private String name; 30 | private String description; 31 | private int maxAssignments; 32 | 33 | /** 34 | * @param key 35 | * the key of the software module type 36 | * @return the builder itself 37 | */ 38 | public SoftwareModuleTypeBuilder key(final String key) { 39 | this.key = key; 40 | return this; 41 | } 42 | 43 | /** 44 | * @param name 45 | * the name of the software module type 46 | * @return the builder itself 47 | */ 48 | public SoftwareModuleTypeBuilder name(final String name) { 49 | this.name = name; 50 | return this; 51 | } 52 | 53 | /** 54 | * @param description 55 | * of the module 56 | * @return the builder itself 57 | */ 58 | public SoftwareModuleTypeBuilder description(final String description) { 59 | this.description = description; 60 | return this; 61 | } 62 | 63 | /** 64 | * @param maxAssignments 65 | * of a module of that type to the same distribution set 66 | * @return the builder itself 67 | */ 68 | public SoftwareModuleTypeBuilder maxAssignments(final int maxAssignments) { 69 | this.maxAssignments = maxAssignments; 70 | return this; 71 | } 72 | 73 | /** 74 | * Builds a list with a single entry of 75 | * {@link MgmtSoftwareModuleTypeRequestBodyPost} which can directly be used 76 | * in the RESTful-API. 77 | * 78 | * @return a single entry list of 79 | * {@link MgmtSoftwareModuleTypeRequestBodyPost} 80 | */ 81 | public List build() { 82 | return Arrays.asList(doBuild("")); 83 | } 84 | 85 | /** 86 | * Builds a list of multiple {@link MgmtSoftwareModuleTypeRequestBodyPost} 87 | * to create multiple software module types at once. An increasing number 88 | * will be added to the name and key of the software module type. 89 | * 90 | * @param count 91 | * the amount of software module type bodies which should be 92 | * created 93 | * @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost} 94 | */ 95 | public List buildAsList(final int count) { 96 | final List bodyList = new ArrayList<>(); 97 | for (int index = 0; index < count; index++) { 98 | bodyList.add(doBuild(String.valueOf(index))); 99 | } 100 | return bodyList; 101 | } 102 | 103 | private MgmtSoftwareModuleTypeRequestBodyPost doBuild(final String suffix) { 104 | final MgmtSoftwareModuleTypeRequestBodyPost body = new MgmtSoftwareModuleTypeRequestBodyPost(); 105 | body.setKey(key + suffix); 106 | body.setName(name + suffix); 107 | body.setDescription(description); 108 | body.setMaxAssignments(maxAssignments); 109 | return body; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TagBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut; 17 | 18 | /** 19 | * Builder pattern for building {@link MgmtTagRequestBodyPut}. 20 | * 21 | */ 22 | // Exception squid:S1701 - builder pattern 23 | @SuppressWarnings({ "squid:S1701" }) 24 | public class TagBuilder { 25 | 26 | private String name; 27 | private String description; 28 | private String color; 29 | 30 | /** 31 | * @param name 32 | * the name of the tag 33 | * @return the builder itself 34 | */ 35 | public TagBuilder name(final String name) { 36 | this.name = name; 37 | return this; 38 | } 39 | 40 | /** 41 | * @param description 42 | * the description of the tag 43 | * @return the builder itself 44 | */ 45 | public TagBuilder description(final String description) { 46 | this.description = description; 47 | return this; 48 | } 49 | 50 | /** 51 | * @param color 52 | * the colour of the tag 53 | * @return the builder itself 54 | */ 55 | public TagBuilder color(final String color) { 56 | this.color = color; 57 | return this; 58 | } 59 | 60 | /** 61 | * Builds a list with a single entry of {@link MgmtTagRequestBodyPut} which 62 | * can directly be used in the RESTful-API. 63 | * 64 | * @return a single entry list of {@link MgmtTagRequestBodyPut} 65 | */ 66 | public List build() { 67 | return Arrays.asList(doBuild(name)); 68 | } 69 | 70 | /** 71 | * Builds a list of multiple {@link MgmtTagRequestBodyPut} to create 72 | * multiple tags at once. An increasing number will be added to the name of 73 | * the tag. The color and description will remain the same. 74 | * 75 | * @param count 76 | * the amount of distribution sets body which should be created 77 | * @return a list of {@link MgmtTagRequestBodyPut} 78 | */ 79 | public List buildAsList(final int count) { 80 | final List bodyList = new ArrayList<>(); 81 | for (int index = 0; index < count; index++) { 82 | bodyList.add(doBuild(name + index)); 83 | } 84 | 85 | return bodyList; 86 | } 87 | 88 | private MgmtTagRequestBodyPut doBuild(final String prefixName) { 89 | final MgmtTagRequestBodyPut body = new MgmtTagRequestBodyPut(); 90 | body.setName(prefixName); 91 | body.setDescription(description); 92 | body.setColour(color); 93 | return body; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TargetBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost; 17 | import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody; 18 | 19 | /** 20 | * 21 | * Builder pattern for building {@link MgmtTargetRequestBody}. 22 | * 23 | */ 24 | // Exception squid:S1701 - builder pattern 25 | @SuppressWarnings({ "squid:S1701" }) 26 | public class TargetBuilder { 27 | 28 | private String controllerId; 29 | private String name; 30 | private String description; 31 | private String address; 32 | private Boolean requestAttributes; 33 | 34 | /** 35 | * @param controllerId 36 | * the ID of the controller/target 37 | * @return the builder itself 38 | */ 39 | public TargetBuilder controllerId(final String controllerId) { 40 | this.controllerId = controllerId; 41 | return this; 42 | } 43 | 44 | /** 45 | * @param name 46 | * the name of the target 47 | * @return the builder itself 48 | */ 49 | public TargetBuilder name(final String name) { 50 | this.name = name; 51 | return this; 52 | } 53 | 54 | /** 55 | * @param address 56 | * the address of the target 57 | * @return the builder itself 58 | */ 59 | public TargetBuilder address(final String address) { 60 | this.address = address; 61 | return this; 62 | } 63 | 64 | /** 65 | * @param description 66 | * the description of the target 67 | * @return the builder itself 68 | */ 69 | public TargetBuilder description(final String description) { 70 | this.description = description; 71 | return this; 72 | } 73 | 74 | /** 75 | * @param requestAttributes 76 | * flag to request new attributes from target 77 | * @return the builder itself 78 | */ 79 | public TargetBuilder requestAttributes(final Boolean requestAttributes) { 80 | this.requestAttributes = requestAttributes; 81 | return this; 82 | } 83 | 84 | /** 85 | * Builds a list with a single entry of {@link MgmtTargetRequestBody} which 86 | * can directly be used in the RESTful-API. 87 | * 88 | * @return a single entry list of {@link MgmtTargetRequestBody} 89 | */ 90 | public List build() { 91 | return Arrays.asList(doBuild("")); 92 | } 93 | 94 | /** 95 | * Builds a list of multiple {@link MgmtTargetRequestBody} to create 96 | * multiple targets at once. An increasing number will be added to the 97 | * controllerId and name of the target. The description will remain. 98 | * 99 | * @param count 100 | * the amount of target bodies which should be created 101 | * @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost} 102 | */ 103 | public List buildAsList(final int count) { 104 | 105 | return buildAsList(0, count); 106 | } 107 | 108 | /** 109 | * Builds a list of multiple {@link MgmtTargetRequestBody} to create 110 | * multiple targets at once. An increasing number will be added to the 111 | * controllerId and name of the target starting from the provided offset. 112 | * The description will remain. 113 | * 114 | * @param count 115 | * the amount of target bodies which should be created 116 | * @param offset 117 | * for for index start 118 | * @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost} 119 | */ 120 | public List buildAsList(final int offset, final int count) { 121 | final List bodyList = new ArrayList<>(); 122 | for (int index = offset; index < count + offset; index++) { 123 | bodyList.add(doBuild(String.format("%06d", index))); 124 | } 125 | return bodyList; 126 | } 127 | 128 | private MgmtTargetRequestBody doBuild(final String suffix) { 129 | final MgmtTargetRequestBody body = new MgmtTargetRequestBody(); 130 | body.setControllerId(controllerId + suffix); 131 | if (name == null) { 132 | name = controllerId; 133 | } 134 | body.setName(name + suffix); 135 | body.setDescription(description); 136 | body.setAddress(address); 137 | body.setRequestAttributes(requestAttributes); 138 | return body; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TargetFilterQueryBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.resource.builder; 11 | 12 | import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; 13 | 14 | /** 15 | * Builder pattern for building {@link MgmtTargetFilterQueryRequestBody}. 16 | */ 17 | // Exception squid:S1701 - builder pattern 18 | @SuppressWarnings({ "squid:S1701" }) 19 | public class TargetFilterQueryBuilder { 20 | 21 | private String name; 22 | private String query; 23 | 24 | /** 25 | * @param name 26 | * the name of the filter 27 | * @return the builder itself 28 | */ 29 | public TargetFilterQueryBuilder name(final String name) { 30 | this.name = name; 31 | return this; 32 | } 33 | 34 | /** 35 | * @param query 36 | * the query filter 37 | * @return the builder itself 38 | */ 39 | public TargetFilterQueryBuilder query(final String query) { 40 | this.query = query; 41 | return this; 42 | } 43 | 44 | /** 45 | * Builds a single entry of {@link MgmtTargetFilterQueryRequestBody} which 46 | * can directly be used to post on the RESTful-API. 47 | * 48 | * @return a single entry of {@link MgmtTargetFilterQueryRequestBody} 49 | */ 50 | public MgmtTargetFilterQueryRequestBody build() { 51 | final MgmtTargetFilterQueryRequestBody body = new MgmtTargetFilterQueryRequestBody(); 52 | body.setName(name); 53 | body.setQuery(query); 54 | return body; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /bin/ 3 | /.apt_generated/ 4 | /.springBeans 5 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/README.md: -------------------------------------------------------------------------------- 1 | # Eclipse.IoT hawkBit - Example Management Feign Client 2 | 3 | Example Spring Boot client that shows how to efficiently use the hawkBit Example Management Feign Client and the hawkBit Management API. 4 | 5 | Powered by [Feign](https://github.com/Netflix/feign). 6 | 7 | ## How to run the example client 8 | 9 | Run getting started example 10 | 11 | 12 | 13 | $ java -jar hawkbit-example-mgmt-simulator-#version#-exec.jar 14 | 15 | 16 | Run create and start rollout example 17 | 18 | 19 | $ java -jar hawkbit-example-mgmt-simulator-#version#-exec.jar --createrollout 20 | 21 | 22 | ## This example shows 23 | 24 | In getting started example: 25 | * creating software modules type 26 | * creating distribution set type 27 | * creating distribution sets 28 | * creating software modules 29 | * assigning software modules to distribution sets 30 | 31 | In create rollout example: 32 | * creating software modules type 33 | * creating distribution set type 34 | * creating distribution sets 35 | * creating software modules 36 | * assigning software modules to distribution sets 37 | * creating a rollout 38 | * starting a rollout 39 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/pom.xml: -------------------------------------------------------------------------------- 1 | 12 | 14 | 4.0.0 15 | 16 | org.eclipse.hawkbit 17 | hawkbit-examples-parent 18 | ${revision} 19 | 20 | jar 21 | hawkbit-example-mgmt-simulator 22 | hawkBit :: Examples :: Management feign client simulator 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-maven-plugin 29 | 30 | org.eclipse.hawkbit.mgmt.client.Application 31 | exec 32 | JAR 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.eclipse.hawkbit 41 | hawkbit-example-mgmt-feign-client 42 | ${project.version} 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-logging 51 | 52 | 53 | org.springframework.cloud 54 | spring-cloud-context 55 | 56 | 57 | org.springframework.cloud 58 | spring-cloud-starter-openfeign 59 | 60 | 61 | io.github.openfeign 62 | feign-jackson 63 | 64 | 65 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client; 11 | 12 | import org.eclipse.hawkbit.feign.core.client.FeignClientConfiguration; 13 | import org.eclipse.hawkbit.feign.core.client.IgnoreMultipleConsumersProducersSpringMvcContract; 14 | import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetClientResource; 15 | import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetTagClientResource; 16 | import org.eclipse.hawkbit.mgmt.client.resource.MgmtRolloutClientResource; 17 | import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleClientResource; 18 | import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetClientResource; 19 | import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetTagClientResource; 20 | import org.eclipse.hawkbit.mgmt.client.scenarios.ConfigurableScenario; 21 | import org.eclipse.hawkbit.mgmt.client.scenarios.upload.FeignMultipartEncoder; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.boot.Banner.Mode; 24 | import org.springframework.boot.CommandLineRunner; 25 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 26 | import org.springframework.boot.autoconfigure.SpringBootApplication; 27 | import org.springframework.boot.builder.SpringApplicationBuilder; 28 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 29 | import org.springframework.cloud.openfeign.EnableFeignClients; 30 | import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; 31 | import org.springframework.context.annotation.Bean; 32 | import org.springframework.context.annotation.Import; 33 | import org.springframework.hateoas.mediatype.hal.Jackson2HalModule; 34 | 35 | import com.fasterxml.jackson.databind.DeserializationFeature; 36 | import com.fasterxml.jackson.databind.ObjectMapper; 37 | 38 | import feign.Feign; 39 | import feign.Logger; 40 | import feign.auth.BasicAuthRequestInterceptor; 41 | import feign.jackson.JacksonDecoder; 42 | import feign.slf4j.Slf4jLogger; 43 | 44 | @SpringBootApplication 45 | @EnableFeignClients("org.eclipse.hawkbit.mgmt.client.resource") 46 | @EnableConfigurationProperties(ClientConfigurationProperties.class) 47 | @AutoConfigureAfter(FeignClientConfiguration.class) 48 | @Import(FeignClientConfiguration.class) 49 | public class Application implements CommandLineRunner { 50 | 51 | @Autowired 52 | private ConfigurableScenario configuredScenario; 53 | 54 | public static void main(final String[] args) { 55 | new SpringApplicationBuilder().bannerMode(Mode.OFF).sources(Application.class).run(args); 56 | } 57 | 58 | @Override 59 | public void run(final String... args) throws Exception { 60 | if (containsArg("--createrollout", args)) { 61 | // run the create and start rollout example 62 | configuredScenario.runWithRollout(); 63 | } else { 64 | // run the configured scenario from properties 65 | configuredScenario.run(); 66 | } 67 | } 68 | 69 | @Bean 70 | public BasicAuthRequestInterceptor basicAuthRequestInterceptor(final ClientConfigurationProperties configuration) { 71 | return new BasicAuthRequestInterceptor(configuration.getUsername(), configuration.getPassword()); 72 | } 73 | 74 | @Bean 75 | public ConfigurableScenario configurableScenario(final MgmtDistributionSetClientResource distributionSetResource, 76 | final MgmtSoftwareModuleClientResource softwareModuleResource, 77 | final MgmtTargetClientResource targetResource, final MgmtRolloutClientResource rolloutResource, 78 | final MgmtTargetTagClientResource targetTagResource, 79 | final MgmtDistributionSetTagClientResource distributionSetTagResource, 80 | final ClientConfigurationProperties clientConfigurationProperties) { 81 | return new ConfigurableScenario(distributionSetResource, softwareModuleResource, 82 | uploadSoftwareModule(clientConfigurationProperties), targetResource, rolloutResource, targetTagResource, 83 | distributionSetTagResource, clientConfigurationProperties); 84 | } 85 | 86 | private static MgmtSoftwareModuleClientResource uploadSoftwareModule( 87 | final ClientConfigurationProperties configuration) { 88 | final ObjectMapper mapper = new ObjectMapper() 89 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 90 | .registerModule(new Jackson2HalModule()); 91 | return Feign.builder().contract(new IgnoreMultipleConsumersProducersSpringMvcContract()).decode404() 92 | .requestInterceptor( 93 | new BasicAuthRequestInterceptor(configuration.getUsername(), configuration.getPassword())) 94 | .logger(new Slf4jLogger()).encoder(new FeignMultipartEncoder()) 95 | .decoder(new ResponseEntityDecoder(new JacksonDecoder(mapper))) 96 | .target(MgmtSoftwareModuleClientResource.class, configuration.getUrl()); 97 | } 98 | 99 | @Bean 100 | public Logger.Level feignLoggerLevel() { 101 | return Logger.Level.FULL; 102 | } 103 | 104 | private static boolean containsArg(final String containsArg, final String... args) { 105 | for (final String arg : args) { 106 | if (arg.equalsIgnoreCase(containsArg)) { 107 | return true; 108 | } 109 | } 110 | return false; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.springframework.boot.context.properties.ConfigurationProperties; 17 | 18 | /** 19 | * Configuration bean which holds the configuration of the client e.g. the base 20 | * URL of the hawkbit-server and the credentials to use the RESTful Management 21 | * API. 22 | */ 23 | @ConfigurationProperties(prefix = "hawkbit") 24 | public class ClientConfigurationProperties { 25 | 26 | /** 27 | * Update server URI. 28 | */ 29 | private String url = "localhost:8080"; 30 | 31 | /** 32 | * Update server user name. 33 | */ 34 | private String username = "admin"; 35 | 36 | /** 37 | * Update server password. 38 | */ 39 | @SuppressWarnings("squid:S2068") 40 | // this password is only used for examples 41 | private String password = "admin"; 42 | 43 | private final List scenarios = new ArrayList<>(); 44 | 45 | /** 46 | * Simulation {@link Scenario}. 47 | * 48 | */ 49 | public static class Scenario { 50 | private boolean cleanRepository; 51 | private boolean waitTillRolloutComplete = true; 52 | private int targets = 100; 53 | private int distributionSets = 10; 54 | private int appModulesPerDistributionSet = 2; 55 | private String dsName = "Package"; 56 | private String smSwName = "Application"; 57 | private String smFwName = "Firmware"; 58 | private String targetName = "Device"; 59 | private int artifactsPerSM = 1; 60 | private String targetAddress = "amqp:/simulator.replyTo"; 61 | private boolean runRollouts = true; 62 | private boolean runSemiAutomaticRollouts = true; 63 | private short rolloutSuccessThreshold = 80; 64 | private int rolloutDeploymentGroups = 4; 65 | private List deviceGroups = Arrays.asList("EU", "AM", "APAC"); 66 | 67 | /** 68 | * Targets tags per page. 69 | */ 70 | private int targetTags; 71 | 72 | /** 73 | * Distribution Set tags per set 74 | */ 75 | private int dsTags = 5; 76 | 77 | /** 78 | * Artifact size. Values can use the suffixed "MB" or "KB" to indicate a 79 | * Megabyte or Kilobyte size. 80 | */ 81 | private String artifactSize = "1MB"; 82 | 83 | public boolean isRunSemiAutomaticRollouts() { 84 | return runSemiAutomaticRollouts; 85 | } 86 | 87 | public void setRunSemiAutomaticRollouts(final boolean runSemiAutomaticRollouts) { 88 | this.runSemiAutomaticRollouts = runSemiAutomaticRollouts; 89 | } 90 | 91 | public List getDeviceGroups() { 92 | return deviceGroups; 93 | } 94 | 95 | public void setDeviceGroups(final List deviceGroups) { 96 | this.deviceGroups = deviceGroups; 97 | } 98 | 99 | public boolean isCleanRepository() { 100 | return cleanRepository; 101 | } 102 | 103 | public void setCleanRepository(final boolean cleanRepository) { 104 | this.cleanRepository = cleanRepository; 105 | } 106 | 107 | public boolean isWaitTillRolloutComplete() { 108 | return waitTillRolloutComplete; 109 | } 110 | 111 | public void setWaitTillRolloutComplete(final boolean waitTillRolloutComplete) { 112 | this.waitTillRolloutComplete = waitTillRolloutComplete; 113 | } 114 | 115 | public int getRolloutDeploymentGroups() { 116 | return rolloutDeploymentGroups; 117 | } 118 | 119 | public void setRolloutDeploymentGroups(final int rolloutDeploymentGroups) { 120 | this.rolloutDeploymentGroups = rolloutDeploymentGroups; 121 | } 122 | 123 | public boolean isRunRollouts() { 124 | return runRollouts; 125 | } 126 | 127 | public void setRunRollouts(final boolean runRollouts) { 128 | this.runRollouts = runRollouts; 129 | } 130 | 131 | public String getTargetAddress() { 132 | return targetAddress; 133 | } 134 | 135 | public void setTargetAddress(final String targetAddress) { 136 | this.targetAddress = targetAddress; 137 | } 138 | 139 | public int getArtifactsPerSM() { 140 | return artifactsPerSM; 141 | } 142 | 143 | public void setArtifactsPerSM(final int artifactsPerSM) { 144 | this.artifactsPerSM = artifactsPerSM; 145 | } 146 | 147 | public String getArtifactSize() { 148 | return artifactSize; 149 | } 150 | 151 | public void setArtifactSize(final String artifactSize) { 152 | this.artifactSize = artifactSize; 153 | } 154 | 155 | public String getTargetName() { 156 | return targetName; 157 | } 158 | 159 | public void setTargetName(final String targetName) { 160 | this.targetName = targetName; 161 | } 162 | 163 | public String getDsName() { 164 | return dsName; 165 | } 166 | 167 | public void setDsName(final String dsName) { 168 | this.dsName = dsName; 169 | } 170 | 171 | public String getSmSwName() { 172 | return smSwName; 173 | } 174 | 175 | public void setSmSwName(final String smSwName) { 176 | this.smSwName = smSwName; 177 | } 178 | 179 | public String getSmFwName() { 180 | return smFwName; 181 | } 182 | 183 | public void setSmFwName(final String smFwName) { 184 | this.smFwName = smFwName; 185 | } 186 | 187 | public int getTargets() { 188 | return targets; 189 | } 190 | 191 | public int getDistributionSets() { 192 | return distributionSets; 193 | } 194 | 195 | public int getAppModulesPerDistributionSet() { 196 | return appModulesPerDistributionSet; 197 | } 198 | 199 | public void setTargets(final int targets) { 200 | this.targets = targets; 201 | } 202 | 203 | public void setDistributionSets(final int distributionSets) { 204 | this.distributionSets = distributionSets; 205 | } 206 | 207 | public void setAppModulesPerDistributionSet(final int appModulesPerDistributionSet) { 208 | this.appModulesPerDistributionSet = appModulesPerDistributionSet; 209 | } 210 | 211 | public void setTargetTags(final int targetTags) { 212 | this.targetTags = targetTags; 213 | } 214 | 215 | public int getTargetTags() { 216 | return targetTags; 217 | } 218 | 219 | public int getDsTags() { 220 | return dsTags; 221 | } 222 | 223 | public void setDsTags(final int dsTags) { 224 | this.dsTags = dsTags; 225 | } 226 | 227 | public short getRolloutSuccessThreshold() { 228 | return rolloutSuccessThreshold; 229 | } 230 | 231 | public void setRolloutSuccessThreshold(final short rolloutSuccessThreshold) { 232 | this.rolloutSuccessThreshold = rolloutSuccessThreshold; 233 | } 234 | 235 | } 236 | 237 | public List getScenarios() { 238 | return scenarios; 239 | } 240 | 241 | public void addScenarios(final Scenario scenario) { 242 | scenarios.add(scenario); 243 | } 244 | 245 | public String getUrl() { 246 | return url; 247 | } 248 | 249 | public void setUrl(final String url) { 250 | this.url = url; 251 | } 252 | 253 | public String getUsername() { 254 | return username; 255 | } 256 | 257 | public void setUsername(final String username) { 258 | this.username = username; 259 | } 260 | 261 | public String getPassword() { 262 | return password; 263 | } 264 | 265 | public void setPassword(final String password) { 266 | this.password = password; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/upload/ArtifactFile.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.scenarios.upload; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.util.Optional; 17 | 18 | import org.springframework.util.Assert; 19 | import org.springframework.util.FileCopyUtils; 20 | import org.springframework.web.multipart.MultipartFile; 21 | 22 | /** 23 | * Implementation for {@link MultipartFile} for hawkBit artifact upload. 24 | * 25 | */ 26 | public class ArtifactFile implements MultipartFile { 27 | 28 | private final String name; 29 | 30 | private final String originalFilename; 31 | 32 | private final String contentType; 33 | 34 | private final byte[] content; 35 | 36 | /** 37 | * Create a new ArtifactFile with the given content. 38 | * 39 | * @param name 40 | * the name of the file 41 | * @param content 42 | * the content of the file 43 | */ 44 | public ArtifactFile(final String name, final byte[] content) { 45 | this(name, "", null, content); 46 | } 47 | 48 | /** 49 | * Create a new ArtifactFile with the given content. 50 | * 51 | * @param name 52 | * of the file 53 | * @param originalFilename 54 | * the original filename (as on the client's machine) 55 | * @param contentType 56 | * the content type 57 | * @param content 58 | * of the file 59 | */ 60 | public ArtifactFile(final String name, final String originalFilename, final String contentType, 61 | final byte[] content) { 62 | Assert.hasLength(name, "Name must not be null"); 63 | this.name = name; 64 | this.originalFilename = Optional.ofNullable(originalFilename).orElse(""); 65 | this.contentType = contentType; 66 | this.content = Optional.ofNullable(content).orElse(new byte[0]); 67 | } 68 | 69 | @Override 70 | public String getName() { 71 | return this.name; 72 | } 73 | 74 | @Override 75 | public String getOriginalFilename() { 76 | return this.originalFilename; 77 | } 78 | 79 | @Override 80 | public String getContentType() { 81 | return this.contentType; 82 | } 83 | 84 | @Override 85 | public boolean isEmpty() { 86 | return this.content.length == 0; 87 | } 88 | 89 | @Override 90 | public long getSize() { 91 | return this.content.length; 92 | } 93 | 94 | @Override 95 | public byte[] getBytes() throws IOException { 96 | return this.content; 97 | } 98 | 99 | @Override 100 | public InputStream getInputStream() throws IOException { 101 | return new ByteArrayInputStream(this.content); 102 | } 103 | 104 | @Override 105 | public void transferTo(final File dest) throws IOException { 106 | FileCopyUtils.copy(this.content, dest); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/upload/FeignMultipartEncoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | * 4 | * This program and the accompanying materials are made 5 | * available under the terms of the Eclipse Public License 2.0 6 | * which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | * 8 | * SPDX-License-Identifier: EPL-2.0 9 | */ 10 | package org.eclipse.hawkbit.mgmt.client.scenarios.upload; 11 | 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.OutputStream; 16 | import java.lang.reflect.Type; 17 | import java.nio.charset.Charset; 18 | import java.util.List; 19 | import java.util.Map.Entry; 20 | 21 | import org.springframework.core.io.InputStreamResource; 22 | import org.springframework.http.HttpHeaders; 23 | import org.springframework.http.HttpOutputMessage; 24 | import org.springframework.http.MediaType; 25 | import org.springframework.http.converter.HttpMessageConverter; 26 | import org.springframework.util.LinkedMultiValueMap; 27 | import org.springframework.util.MultiValueMap; 28 | import org.springframework.web.client.RestTemplate; 29 | import org.springframework.web.multipart.MultipartFile; 30 | 31 | import feign.RequestTemplate; 32 | import feign.codec.EncodeException; 33 | import feign.codec.Encoder; 34 | 35 | /** 36 | * A feign encoder implementation which handles {@link MultipartFile} body. 37 | */ 38 | public class FeignMultipartEncoder implements Encoder { 39 | 40 | private final List> converters = new RestTemplate().getMessageConverters(); 41 | private final HttpHeaders multipartHeaders = new HttpHeaders(); 42 | private final HttpHeaders jsonHeaders = new HttpHeaders(); 43 | 44 | private static final Charset UTF_8 = Charset.forName("UTF-8"); 45 | 46 | public FeignMultipartEncoder() { 47 | multipartHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); 48 | jsonHeaders.setContentType(MediaType.APPLICATION_JSON); 49 | } 50 | 51 | @Override 52 | public void encode(final Object object, final Type bodyType, final RequestTemplate template) { 53 | encodeMultipartFormRequest(object, template); 54 | } 55 | 56 | private void encodeMultipartFormRequest(final Object value, final RequestTemplate template) { 57 | if (value == null) { 58 | throw new EncodeException("Cannot encode request with null value."); 59 | } 60 | if (!isMultipartFile(value)) { 61 | throw new EncodeException("Only multipart can be handled by this encoder"); 62 | } 63 | encodeRequest(encodeMultipartFile((MultipartFile) value), multipartHeaders, template); 64 | } 65 | 66 | @SuppressWarnings("unchecked") 67 | private void encodeRequest(final Object value, final HttpHeaders requestHeaders, final RequestTemplate template) { 68 | final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 69 | final HttpOutputMessage dummyRequest = new HttpOutputMessageImpl(outputStream, requestHeaders); 70 | try { 71 | final Class requestType = value.getClass(); 72 | final MediaType requestContentType = requestHeaders.getContentType(); 73 | for (final HttpMessageConverter messageConverter : converters) { 74 | if (messageConverter.canWrite(requestType, requestContentType)) { 75 | ((HttpMessageConverter) messageConverter).write(value, requestContentType, dummyRequest); 76 | break; 77 | } 78 | } 79 | } catch (final IOException ex) { 80 | throw new EncodeException("Cannot encode request.", ex); 81 | } 82 | final HttpHeaders headers = dummyRequest.getHeaders(); 83 | for (final Entry> entry : headers.entrySet()) { 84 | template.header(entry.getKey(), entry.getValue()); 85 | } 86 | /* 87 | * we should use a template output stream... this will cause issues if 88 | * files are too big, since the whole request will be in memory. 89 | */ 90 | template.body(outputStream.toByteArray(), UTF_8); 91 | } 92 | 93 | private MultiValueMap encodeMultipartFile(final MultipartFile file) { 94 | try { 95 | final MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); 96 | multiValueMap.add("file", new MultipartFileResource(file.getName(), file.getSize(), file.getInputStream())); 97 | return multiValueMap; 98 | } catch (final IOException ex) { 99 | throw new EncodeException("Cannot encode request.", ex); 100 | } 101 | } 102 | 103 | private static boolean isMultipartFile(final Object object) { 104 | return object instanceof MultipartFile; 105 | } 106 | 107 | private static final class HttpOutputMessageImpl implements HttpOutputMessage { 108 | 109 | private final OutputStream body; 110 | private final HttpHeaders headers; 111 | 112 | private HttpOutputMessageImpl(final OutputStream body, final HttpHeaders headers) { 113 | this.body = body; 114 | this.headers = headers; 115 | } 116 | 117 | @Override 118 | public OutputStream getBody() throws IOException { 119 | return body; 120 | } 121 | 122 | @Override 123 | public HttpHeaders getHeaders() { 124 | return headers; 125 | } 126 | 127 | } 128 | 129 | /** 130 | * Dummy resource class. Wraps file content and its original name. 131 | */ 132 | static class MultipartFileResource extends InputStreamResource { 133 | 134 | private final String filename; 135 | private final long size; 136 | 137 | public MultipartFileResource(final String filename, final long size, final InputStream inputStream) { 138 | super(inputStream); 139 | this.size = size; 140 | this.filename = filename; 141 | } 142 | 143 | @Override 144 | public String getFilename() { 145 | return this.filename; 146 | } 147 | 148 | @Override 149 | public long contentLength() throws IOException { 150 | return size; 151 | } 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Bosch Software Innovations GmbH and others 3 | # 4 | # This program and the accompanying materials are made 5 | # available under the terms of the Eclipse Public License 2.0 6 | # which is available at https://www.eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | 11 | hawkbit.url=http://localhost:8080 12 | hawkbit.username=admin 13 | hawkbit.password=admin 14 | 15 | spring.main.banner-mode=off 16 | 17 | feign.hystrix.enabled=false 18 | 19 | hawkbit.scenarios.[0].cleanRepository=false 20 | hawkbit.scenarios.[0].targets=0 21 | hawkbit.scenarios.[0].ds-name=gettingstarted-example 22 | hawkbit.scenarios.[0].distribution-sets=3 23 | hawkbit.scenarios.[0].sm-fw-name=gettingstarted-example 24 | hawkbit.scenarios.[0].sm-sw-name=gettingstarted-example 25 | hawkbit.scenarios.[0].runRollouts=false 26 | hawkbit.scenarios.[0].runSemiAutomaticRollouts=false 27 | hawkbit.scenarios.[0].artifactsPerSM=0 28 | -------------------------------------------------------------------------------- /hawkbit-example-mgmt-simulator/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /hawkbit_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eclipse-hawkbit/hawkbit-examples/1c240079b10de114b71cf011ef3f6f7161fbc579/hawkbit_logo.png -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Contributors to the Eclipse Foundation 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_BOSCH.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Bosch Software Innovations GmbH and others 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_BOSCH_18.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Bosch Software Innovations GmbH and others 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_BOSCH_19.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Bosch Software Innovations GmbH and others 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_BOSCH_20.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Bosch.IO GmbH and others 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_BOSCH_23.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Bosch.IO GmbH and others 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_18.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Microsoft and others 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_SIEMENS.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Siemens AG, 2017 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /licenses/LICENSE_HEADER_TEMPLATE_SIEMENS_18.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Siemens AG, 2018 2 | 3 | This program and the accompanying materials are made 4 | available under the terms of the Eclipse Public License 2.0 5 | which is available at https://www.eclipse.org/legal/epl-2.0/ 6 | 7 | SPDX-License-Identifier: EPL-2.0 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 12 | 14 | 4.0.0 15 | 16 | 17 | org.eclipse.hawkbit 18 | hawkbit-parent 19 | 0-SNAPSHOT 20 | 21 | 22 | hawkbit-examples-parent 23 | ${revision} 24 | pom 25 | hawkBit :: Examples 26 | 27 | 28 | 29 | EPL-2.0 30 | https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt 31 | Eclipse Public License - Version 2.0 32 | 33 | 34 | 35 | 36 | 37 | eclipserel 38 | hawkBit Repository - Release 39 | https://repo.eclipse.org/content/repositories/hawkbit-releases 40 | 41 | 42 | eclipsesnap 43 | hawkBit Repository - Snapshots 44 | https://repo.eclipse.org/content/repositories/hawkbit-snapshots 45 | 46 | 47 | 48 | 49 | 50 | dash-licenses 51 | https://repo.eclipse.org/content/repositories/dash-licenses 52 | 53 | 54 | 55 | 56 | hawkbit-device-simulator 57 | hawkbit-example-core-feign-client 58 | hawkbit-example-ddi-feign-client 59 | hawkbit-example-mgmt-feign-client 60 | hawkbit-example-mgmt-simulator 61 | 62 | 63 | 64 | 0-SNAPSHOT 65 | 0-SNAPSHOT 66 | 67 | 68 | scm:git:git@github.com:eclipse/hawkbit-examples.git 69 | scm:git:https://github.com/eclipse/hawkbit-examples.git 70 | https://github.com/eclipse/hawkbit-examples.git 71 | 72 | 73 | 74 | https://circleci.com/gh/eclipse/hawkbit-examples 75 | 76 | 77 | 78 | 79 | ${release.scm.connection} 80 | ${release.scm.developerConnection} 81 | ${release.scm.url} 82 | 83 | 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-compiler-plugin 89 | 90 | -Xlint:all 91 | true 92 | true 93 | 94 | 95 | 96 | com.mycila 97 | license-maven-plugin 98 | 99 |
licenses/LICENSE_HEADER_TEMPLATE.txt
100 | 101 | licenses/LICENSE_HEADER_TEMPLATE_BOSCH_23.txt 102 | licenses/LICENSE_HEADER_TEMPLATE_BOSCH_20.txt 103 | licenses/LICENSE_HEADER_TEMPLATE_SIEMENS.txt 104 | licenses/LICENSE_HEADER_TEMPLATE_SIEMENS_18.txt 105 | licenses/LICENSE_HEADER_TEMPLATE_BOSCH.txt 106 | licenses/LICENSE_HEADER_TEMPLATE_BOSCH_18.txt 107 | licenses/LICENSE_HEADER_TEMPLATE_BOSCH_19.txt 108 | licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_18.txt 109 | 110 | 111 | .azure-pipelines/* 112 | .devcontainer/* 113 | **/banner.txt 114 | **/helm/** 115 | **/README 116 | .3rd-party/** 117 | .github/** 118 | **/.git* 119 | .git* 120 | **/*.sql 121 | eclipse_codeformatter.xml 122 | .sonar 123 | **/docker/** 124 | **/.sonar/** 125 | docs/content/** 126 | docs/layouts/** 127 | docs/static/** 128 | docs/*.toml 129 | **/spring.factories 130 | **/LICENSE* 131 | 132 | 133 | JAVADOC_STYLE 134 | 135 |
136 |
137 | 138 | org.apache.maven.plugins 139 | maven-enforcer-plugin 140 | 1.4.1 141 | 142 | 143 | 145 | enforce-no-snapshots 146 | 147 | enforce 148 | 149 | 150 | ${snapshotDependencyAllowed} 151 | 152 | 153 | No Snapshots Allowed! 154 | 155 | 156 | No Snapshots Allowed! 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | org.codehaus.mojo 165 | flatten-maven-plugin 166 | 167 | ossrh 168 | true 169 | 170 | 171 | 172 | flatten 173 | process-resources 174 | 175 | flatten 176 | 177 | 178 | 179 | flatten.clean 180 | clean 181 | 182 | clean 183 | 184 | 185 | 186 | 187 | 188 | org.codehaus.mojo 189 | versions-maven-plugin 190 | 191 | 192 | maven-scm-plugin 193 | ${maven.scm.plugin.version} 194 | 195 | ${project.version} 196 | 197 | 198 | 199 | org.jacoco 200 | jacoco-maven-plugin 201 | 202 | 203 | prepare-ut-agent 204 | process-test-classes 205 | 206 | prepare-agent 207 | 208 | 209 | ${jacoco.reportPath} 210 | jacoco.agent.ut.arg 211 | true 212 | 213 | 214 | 215 | prepare-it-agent 216 | pre-integration-test 217 | 218 | prepare-agent 219 | 220 | 221 | ${jacoco.itReportPath} 222 | jacoco.agent.it.arg 223 | true 224 | 225 | 226 | 227 | 228 | 229 | org.apache.maven.plugins 230 | maven-source-plugin 231 | 232 | 233 | attach-sources 234 | 235 | jar 236 | 237 | 238 | 239 | 240 | 241 | org.eclipse.dash 242 | license-tool-plugin 243 | 1.0.2 244 | 245 | 246 | license-check 247 | 248 | license-check 249 | 250 | 251 | 252 | 253 |
254 | 255 | 256 | 257 | 258 | org.codehaus.mojo 259 | flatten-maven-plugin 260 | 1.5.0 261 | 262 | 263 | 264 |
265 | 266 | 267 | 268 | 269 | org.eclipse.hawkbit 270 | hawkbit-boot-starter 271 | ${hawkbit.version} 272 | 273 | 274 | org.eclipse.hawkbit 275 | hawkbit-dmf-api 276 | ${hawkbit.version} 277 | 278 | 279 | org.eclipse.hawkbit 280 | hawkbit-ddi-api 281 | ${hawkbit.version} 282 | 283 | 284 | org.eclipse.hawkbit 285 | hawkbit-mgmt-api 286 | ${hawkbit.version} 287 | 288 | 289 | 290 |
291 | --------------------------------------------------------------------------------