Initial check in accordance with Parallel IP

This commit is contained in:
Kai Zimmermann
2016-01-21 13:18:55 +01:00
parent c5e4f1139f
commit 7497ab61ed
763 changed files with 114508 additions and 0 deletions

105
3rd-dependencies/compile.txt Executable file
View File

@@ -0,0 +1,105 @@
aopalliance:aopalliance:jar:1.0:compile
com.fasterxml.jackson.core:jackson-annotations:jar:2.5.0:compile
com.fasterxml.jackson.core:jackson-core:jar:2.5.0:compile
com.fasterxml.jackson.core:jackson-databind:jar:2.5.0:compile
com.fasterxml:classmate:jar:1.3.0:compile
com.google.guava:guava:jar:18.0:compile
com.h2database:h2:jar:1.4.186:compile
com.rabbitmq:amqp-client:jar:3.5.5:compile
com.vaadin.external.atmosphere:atmosphere-runtime:jar:2.2.7.vaadin1:compile
com.vaadin.external.flute:flute:jar:1.3.0.gg2:compile
com.vaadin.external.google:guava:jar:16.0.1.vaadin1:compile
com.vaadin.external.slf4j:vaadin-slf4j-jdk14:jar:1.6.1:compile
com.vaadin.external.streamhtmlparser:streamhtmlparser-jsilver:jar:0.0.10.vaadin1:compile
com.vaadin:vaadin-push:jar:7.5.7:compile
com.vaadin:vaadin-sass-compiler:jar:0.9.12:compile
com.vaadin:vaadin-server:jar:7.5.7:compile
com.vaadin:vaadin-shared:jar:7.5.7:compile
com.vaadin:vaadin-spring-boot:jar:1.0.0:compile
com.vaadin:vaadin-spring:jar:1.0.0:compile
com.vaadin:vaadin-themes:jar:7.5.7:compile
commons-io:commons-io:jar:2.4:compile
cz.jirutka.rsql:rsql-parser:jar:2.0.0:compile
io.springfox:springfox-core:jar:2.0.3:compile
io.springfox:springfox-schema:jar:2.0.3:compile
io.springfox:springfox-spi:jar:2.0.3:compile
io.springfox:springfox-spring-web:jar:2.0.3:compile
io.springfox:springfox-swagger-common:jar:2.0.3:compile
io.springfox:springfox-swagger2:jar:2.0.3:compile
io.swagger:swagger-annotations:jar:1.5.0:compile
io.swagger:swagger-models:jar:1.5.0:compile
javax.servlet:javax.servlet-api:jar:3.1.0:compile
javax.transaction:javax.transaction-api:jar:1.2:compile
javax.validation:validation-api:jar:1.1.0.Final:compile
joda-time:joda-time:jar:2.5:compile
net._01001111:jlorem:jar:1.1:compile
org.apache.commons:commons-lang3:jar:3.4:compile
org.apache.commons:commons-pool2:jar:2.2:compile
org.apache.logging.log4j:log4j-api:jar:2.1:compile
org.apache.logging.log4j:log4j-core:jar:2.1:compile
org.apache.logging.log4j:log4j-slf4j-impl:jar:2.1:compile
org.apache.tomcat:tomcat-jdbc:jar:8.0.28:compile
org.apache.tomcat:tomcat-juli:jar:8.0.28:compile
org.aspectj:aspectjrt:jar:1.8.5:compile
org.aspectj:aspectjweaver:jar:1.8.5:compile
org.eclipse.persistence:javax.persistence:jar:2.1.0:compile
org.eclipse.persistence:org.eclipse.persistence.antlr:jar:2.6.0:compile
org.eclipse.persistence:org.eclipse.persistence.asm:jar:2.6.0:compile
org.eclipse.persistence:org.eclipse.persistence.core:jar:2.6.0:compile
org.eclipse.persistence:org.eclipse.persistence.jpa.jpql:jar:2.6.0:compile
org.eclipse.persistence:org.eclipse.persistence.jpa:jar:2.6.0:compile
org.flywaydb:flyway-core:jar:3.1:compile
org.glassfish:javax.json:jar:1.0.4:compile
org.hibernate:hibernate-validator:jar:5.2.2.Final:compile
org.jboss.logging:jboss-logging:jar:3.2.1.Final:compile
org.jsoup:jsoup:jar:1.8.1:compile
org.mapstruct:mapstruct:jar:1.0.0.Beta4:compile
org.mongodb:mongo-java-driver:jar:3.0.2:compile
org.objenesis:objenesis:jar:2.1:compile
org.slf4j:jcl-over-slf4j:jar:1.7.12:compile
org.slf4j:jul-to-slf4j:jar:1.7.12:compile
org.slf4j:slf4j-api:jar:1.7.7:compile
org.springframework.amqp:spring-amqp:jar:1.4.6.RELEASE:compile
org.springframework.amqp:spring-rabbit:jar:1.4.6.RELEASE:compile
org.springframework.boot:spring-boot-autoconfigure:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot-starter-aop:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot-starter-data-jpa:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot-starter-jdbc:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot-starter-log4j2:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot-starter-web:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot-starter:jar:1.2.7.RELEASE:compile
org.springframework.boot:spring-boot:jar:1.2.7.RELEASE:compile
org.springframework.data:spring-data-commons:jar:1.10.1.RELEASE:compile
org.springframework.data:spring-data-jpa:jar:1.8.1.RELEASE:compile
org.springframework.data:spring-data-mongodb:jar:1.7.1.RELEASE:compile
org.springframework.data:spring-data-redis:jar:1.5.1.RELEASE:compile
org.springframework.hateoas:spring-hateoas:jar:0.16.0.RELEASE:compile
org.springframework.plugin:spring-plugin-core:jar:1.1.0.RELEASE:compile
org.springframework.plugin:spring-plugin-metadata:jar:1.2.0.RELEASE:compile
org.springframework.retry:spring-retry:jar:1.1.2.RELEASE:compile
org.springframework.security:spring-security-aspects:jar:3.2.8.RELEASE:compile
org.springframework.security:spring-security-config:jar:3.2.8.RELEASE:compile
org.springframework.security:spring-security-core:jar:3.2.8.RELEASE:compile
org.springframework.security:spring-security-web:jar:3.2.8.RELEASE:compile
org.springframework:spring-aop:jar:4.1.8.RELEASE:compile
org.springframework:spring-aspects:jar:4.1.8.RELEASE:compile
org.springframework:spring-beans:jar:4.1.8.RELEASE:compile
org.springframework:spring-context-support:jar:4.1.8.RELEASE:compile
org.springframework:spring-context:jar:4.1.8.RELEASE:compile
org.springframework:spring-core:jar:4.1.8.RELEASE:compile
org.springframework:spring-expression:jar:4.1.8.RELEASE:compile
org.springframework:spring-jdbc:jar:4.1.8.RELEASE:compile
org.springframework:spring-messaging:jar:4.1.8.RELEASE:compile
org.springframework:spring-orm:jar:4.1.8.RELEASE:compile
org.springframework:spring-tx:jar:4.1.8.RELEASE:compile
org.springframework:spring-web:jar:4.1.8.RELEASE:compile
org.springframework:spring-webmvc:jar:4.1.8.RELEASE:compile
org.vaadin.addons.lazyquerycontainer:vaadin-lazyquerycontainer:jar:7.4.0.1:compile
org.vaadin.addons:flexibleoptiongroup:jar:2.2.0:compile
org.vaadin.addons:tokenfield:jar:7.0.1:compile
org.vaadin.spring.addons:vaadin-spring-addon-eventbus:jar:0.0.6.RELEASE:compile
org.vaadin.spring.extensions:vaadin-spring-ext-core:jar:0.0.6.RELEASE:compile
org.vaadin.spring.extensions:vaadin-spring-ext-security:jar:0.0.6.RELEASE:compile
org.w3c.css:sac:jar:1.3:compile
org.yaml:snakeyaml:jar:1.14:compile
redis.clients:jedis:jar:2.5.2:compile

8
3rd-dependencies/listDeps.sh Executable file
View File

@@ -0,0 +1,8 @@
# This script
cd ..
mvn dependency:list -DexcludeGroupIds=org.eclipse.hawkbit -Dsort=true -DoutputFile=dependencies.txt
find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:compile'|sort|uniq > 3rd-dependencies/compile.txt
find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:test'|sort|uniq > 3rd-dependencies/test.txt
find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:provided'|sort|uniq > 3rd-dependencies/provided.txt
find . -name dependencies.txt|while read i; do rm $i;done
cd 3rd-dependencies/

6
3rd-dependencies/provided.txt Executable file
View File

@@ -0,0 +1,6 @@
javax.servlet:javax.servlet-api:jar:3.1.0:provided
org.apache.tomcat.embed:tomcat-embed-core:jar:8.0.28:provided
org.apache.tomcat.embed:tomcat-embed-el:jar:8.0.28:provided
org.apache.tomcat.embed:tomcat-embed-logging-juli:jar:8.0.28:provided
org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.0.28:provided
org.springframework.boot:spring-boot-starter-tomcat:jar:1.2.7.RELEASE:provided

55
3rd-dependencies/test.txt Executable file
View File

@@ -0,0 +1,55 @@
com.fasterxml.jackson.core:jackson-core:jar:2.5.0:test
com.fasterxml.jackson.core:jackson-databind:jar:2.5.0:test
com.github.fge:btf:jar:1.2:test
com.github.fge:jackson-coreutils:jar:1.6:test
com.github.fge:json-patch:jar:1.7:test
com.github.fge:msg-simple:jar:1.1:test
com.google.code.findbugs:jsr305:jar:2.0.1:test
com.h2database:h2:jar:1.4.186:test
com.jayway.jsonpath:json-path:jar:0.9.1:test
commons-beanutils:commons-beanutils-core:jar:1.8.3:test
commons-io:commons-io:jar:2.4:test
commons-logging:commons-logging:jar:1.1.1:test
de.flapdoodle.embed:de.flapdoodle.embed.mongo:jar:1.50.0:test
de.flapdoodle.embed:de.flapdoodle.embed.process:jar:1.50.0:test
io.swagger:swagger-annotations:jar:1.5.0:test
javax.el:javax.el-api:jar:2.2.4:test
junit:junit:jar:4.12:test
net.java.dev.jna:jna-platform:jar:4.0.0:test
net.java.dev.jna:jna:jar:3.3.0:test
net.java.dev.jna:jna:jar:4.0.0:test
net.java.dev.jna:jna:jar:platform:3.3.0:test
net.minidev:json-smart:jar:1.2:test
org.apache.commons:commons-compress:jar:1.3:test
org.apache.commons:commons-lang3:jar:3.1:test
org.apache.tika:tika-core:jar:1.7:test
org.aspectj:aspectjrt:jar:1.8.5:test
org.atteo:evo-inflector:jar:1.2.1:test
org.easytesting:fest-assert-core:jar:2.0M10:test
org.easytesting:fest-assert:jar:1.4:test
org.easytesting:fest-util:jar:1.2.5:test
org.hamcrest:hamcrest-core:jar:1.3:test
org.hamcrest:hamcrest-library:jar:1.3:test
org.json:json:jar:20141113:test
org.jvnet.jaxb2_commons:jaxb2-basics-runtime:jar:0.9.3:test
org.mariadb.jdbc:mariadb-java-client:jar:1.2.3:test
org.mockito:mockito-core:jar:1.10.19:test
org.objenesis:objenesis:jar:2.1:test
org.springframework.boot:spring-boot-starter-test:jar:1.2.7.RELEASE:test
org.springframework.data:spring-data-rest-core:jar:2.3.1.RELEASE:test
org.springframework.data:spring-data-rest-webmvc:jar:2.3.1.RELEASE:test
org.springframework.hateoas:spring-hateoas:jar:0.16.0.RELEASE:test
org.springframework.plugin:spring-plugin-core:jar:1.1.0.RELEASE:test
org.springframework.security:spring-security-aspects:jar:3.2.8.RELEASE:test
org.springframework.security:spring-security-config:jar:3.2.8.RELEASE:test
org.springframework.security:spring-security-web:jar:3.2.8.RELEASE:test
org.springframework:spring-context-support:jar:4.1.8.RELEASE:test
org.springframework:spring-test:jar:4.1.8.RELEASE:test
org.springframework:spring-web:jar:4.1.8.RELEASE:test
org.springframework:spring-webmvc:jar:4.1.8.RELEASE:test
ru.yandex.qatools.allure:allure-java-adaptor-api:jar:1.4.15:test
ru.yandex.qatools.allure:allure-java-annotations:jar:1.4.15:test
ru.yandex.qatools.allure:allure-java-aspects:jar:1.4.15:test
ru.yandex.qatools.allure:allure-junit-adaptor:jar:1.4.15:test
ru.yandex.qatools.allure:allure-model:jar:1.4.15:test
ru.yandex.qatools.properties:properties-loader:jar:1.5:test

70
CONTRUBUTING.md Executable file
View File

@@ -0,0 +1,70 @@
# Contributing to eclipse hawkBit
:+1: First off, thanks for taking the time to contribute! We really appreciate this. :+1:
Please read this if you intend to contribute to the project.
## Code Conventions
* Java files:
* we follow the standard eclipse IDE (built in) code formatter with the following changes:
* Tab policy: spaces only: 4
* XML files:
* we follow the standard eclipse IDE XML formatter with the following changes:
* Indent using spaces only: 3
* SCSS files:
* we follow the standard [scss-lint](https://github.com/brigade/scss-lint/) rules with the following exception:
* disabled rules: ImportantRule, PropertySortOrder
* Sonarqube:
* Our rule set is defined [here](http://sonar.eu-gb.mybluemix.net)
## Legal considerations for your contribution
The following steps are necessary to comply with the Eclipse Foundation's IP policy.
Please also read [this](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git)
In order for any contributions to be accepted you MUST do the following things.
* Sign the [Eclipse Foundation Contributor License Agreement](http://www.eclipse.org/legal/CLA.php).
To sign the Eclipse CLA you need to:
* Obtain an Eclipse Foundation userid. Anyone who currently uses Eclipse Bugzilla or Gerrit systems already has one of those.
If you dont, you need to [register](https://dev.eclipse.org/site_login/createaccount.php).
* Login into the [projects portal](https://projects.eclipse.org/), select “My Account”, and then the “Contributor License Agreement” tab.
* Add your github username in your Eclipse Foundation account settings. Log in it to Eclipse and go to account settings.
* "Sign-off" your commits (NOTE! This is only necessary if contributing to the vertx-core project)
Every commit you make in your patch or pull request MUST be "signed off".
You do this by adding the `-s` flag when you make the commit(s), e.g.
git commit -s -m "Shave the yak some more"
## Making your changes
* Fork the repository on GitHub
* Create a new branch for your changes
* Make your changes
* Make sure you include tests
* Make sure the tests pass after your changes
* Commit your changes into that branch
* Use descriptive and meaningful commit messages
* If you have a lot of commits squash them into a single commit
* Make sure you use the `-s` flag when committing as explained above.
* Push your changes to your branch in your forked repository
## Submitting the changes
Submit a pull request via the normal GitHub UI (desktop or web).
## After submitting
* Do not use your branch for any other development, otherwise further changes that you make will be visible in the PR.
# Further information
* [Eclipse Project Page](http://projects.eclipse.org/projects/iot.hawkbit)

131
DDIA.md Executable file
View File

@@ -0,0 +1,131 @@
## Direct Device Integration API (HTTP)
This API is based on HTTP standards and based on a polling mechanism.
The hawkbit update server provides REST resources which are consumed by the device to retrieve software update tasks.
##### Base Poll Resource
```
GET /{tenant}/controller/v1/{controllerId}
```
In the answer to the baseUrl polling the backend can send action links. A possible action is a deploy command.
The client makes a GET request on the given link:
_Example Response_
```
{
"config": {
"polling": {
"sleep": "00:05:00"
}
},
"_links": {
"deploymentBase": {
"href": "http://localhost:8080/default/controller/v1/example/deploymentBase/1?c=644088541"
},
"configData": {
"href": "http://localhost:8080/default/controller/v1/example/configData"
}
}
}
```
##### Deployment Base Resource
```
GET /{tenant}/controller/v1/{controllerId}/deploymentBase/{id}
```
_Example Response_
```
{
"deployment": {
"download": "forced",
"update": "forced",
"chunks": [
{
"part": "os",
"version": "1.0.0",
"name": "Linux",
"artifacts": [
{
"filename": "linux.zip",
"hashes": {
"sha1": "46fc56de883ec027759d8513458fe1010aa7e793",
"md5": "5813e9655bd6871d0c25b8d510fd8605"
},
"size": 52167,
"_links": {
"download": {
"href": "http://localhost:8080/default/controller/v1/example/softwaremodules/1/artifacts/linux.zip"
},
"md5sum": {
"href": "http://localhost:8080/default/controller/v1/example/softwaremodules/1/artifacts/linux.zip.MD5SUM"
}
}
}
]
}
]
},
"id": "1"
}
```
##### Deployment Feedback Resource
To every deployment the client can post feedback back to the update-server about the deployment status.
```
POST /{tenant}/controller/v1/{controllerId}/deploymentBase/{id}/feedback
Content-Type: application/json
```
_Example Body Deployment Success_
```
{
"id": 1,
"time": "20140511T121314",
"status": {
"execution": "closed",
"result": {
"finished": "success",
"progress": {}
}
}
}
```
_Example Body Deployment Proceeding_
```
{
"id": "1",
"time": "20140511T121314",
"status": {
"execution": "proceeding",
"result": {
"finished": "none",
"progress": {
"cnt": 2,
"of": 5
}
},
"details": [
"checking hash sums"
]
}
}
```
_Example Body Deployment Error_
```
{
"id": 1,
"time": "20140511T121314",
"status": {
"execution": "rejected",
"result": {
"finished": "failure",
"progress": {}
},
"details": [
"something bad happend"
]
}
}
```

6
LICENCE_HEADER_TEMPLATE.txt Executable file
View File

@@ -0,0 +1,6 @@
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html

37
README.md Executable file
View File

@@ -0,0 +1,37 @@
# hawkbit-update-server
Build Status: [![Circle CI](https://circleci.com/gh/bsinno/hawkbit-update-server/tree/master.svg?style=svg&circle-token=507140469c35791e36af04f5b8816ec79be4c82c)](https://circleci.com/gh/bsinno/hawkbit-update-server/tree/master)
[hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure.
# Compile
```
mvn install
```
## Try out examples
#### Standalone Test Application Server
[Example Application](examples/hawkbit-example-app)
#### Device Simulator using the DMF AMQP API
[Device Simulator](examples/hawkbit-device-simulator)
# Modules
`hawkbit-core` : core elements.
`hawkbit-security-core` : core security elements.
`hawkbit-security-integration` : security integration elements to integrate security into hawkbit.
`hawkbit-artifact-repository-mongo` : artifact repository implementation to mongoDB.
`hawkbit-autoconfigure` : spring-boot auto-configuration.
`hawkbit-dmf-api` : API for the Device Management Integration.
`hawkbit-dmf-amqp` : AMQP endpoint implementation for the DMF API.
`hawkbit-repository` : repository implemenation based on SQL for all meta-data.
`hawkbit-http-security` : implementation for security filters for HTTP.
`hawkbit-rest-api` : API classes for the REST Management API.
`hawkbit-rest-resource` : HTTP REST endpoints for the Management and the Direct Device API.
`hawkbit-rest-resource` : Vaadin UI.
`hawkbit-cache-redis` : spring cache manager configuration and implementation with redis, distributed cache and distributed events.
# Device Integration
There are two device integration APIs provided by the hawkbit update server.
* [Direct Device Integration API (HTTP)](DDIA.md)
* Device Management Federation API (AMQP)

33
circle.yml Executable file
View File

@@ -0,0 +1,33 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
machine:
java:
version: oraclejdk8
environment:
_JAVA_OPTIONS: "-Xms512m -Xmx1024m"
dependencies:
cache_directories:
- "~/.embedmongo"
override:
- mvn install -DskipTests
test:
override:
- sh sonarCircleCi.sh
post:
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
general:
artifacts:
- "*.jar"
- "pom.xml"
notify:
webhooks:
# gitter hook
- url: https://webhooks.gitter.im/e/a20a6bc2bda5a8a77d39

295
eclipse_codeformatter.xml Executable file
View File

@@ -0,0 +1,295 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="hawkBit" version="12">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

2
examples/README.md Executable file
View File

@@ -0,0 +1,2 @@
# Examples
TODO

View File

@@ -0,0 +1,2 @@
/bin/
/target/

View File

@@ -0,0 +1,18 @@
# hawkBit Device Simulator
The device simulator handles software update commands from the update server.
## Run
```
java -jar examples/hawkbit-device-simulator/target/hawkbit-device-simulator-*-SNAPSHOT.jar
```
Or:
```
run org.eclipse.hawkbit.simulator.DeviceSimulator
```
## Usage
The device simulator exposes an REST-API which can be used to trigger device creation.
```
http://localhost:8083/start?amount=10
```

View File

@@ -0,0 +1,61 @@
<?xml version="1.0"?>
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<version>0.1.0-SNAPSHOT</version>
<artifactId>hawkbit-examples-parent</artifactId>
</parent>
<artifactId>hawkbit-device-simulator</artifactId>
<name>hawkBit :: Device Simulator</name>
<description>Device Management Federation API based simulator</description>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-dmf-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* The main-method to start the Spring-Boot application.
*
*
*
*/
@SpringBootApplication
public class DeviceSimulator {
/**
* Start the Spring Boot Application.
*
* @param args
* the args
*/
public static void main(final String[] args) {
SpringApplication.run(DeviceSimulator.class, args);
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* REST endpoint for controlling the device simulator.
*
*
*
*/
@RestController
public class SimulationController {
@Autowired
private SpSenderService spSenderService;
/**
* The start resource to start a device creation.
*
* @param name
* the name prefix of the generated device naming
* @param amount
* the amount of devices to be created
* @param tenant
* the tenant to create the device to
* @return a response string that devices has been created
*/
@RequestMapping("/start")
String start(@RequestParam(value = "name", defaultValue = "dmfSimulated") final String name,
@RequestParam(value = "amount", defaultValue = "20") final int amount,
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
for (int i = 0; i < amount; i++) {
spSenderService.createOrUpdateThing(tenant, name + i);
}
return "Updated " + amount + " DMF connected targets!";
}
}

View File

@@ -0,0 +1,144 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import java.util.HashMap;
import java.util.Map;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* The spring AMQP configuration to use a AMQP for communication with SP update
* server.
*
*
*
*/
@Configuration
@EnableConfigurationProperties(AmqpProperties.class)
public class AmqpConfiguration {
@Autowired
protected AmqpProperties amqpProperties;
@Autowired
private ConnectionFactory connectionFactory;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* Create jackson message converter bean.
*
* @return the jackson message converter
*/
@Bean
public MessageConverter jsonMessageConverter() {
final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
return jackson2JsonMessageConverter;
}
/**
* Create the receiver queue from sp. Receive messages from sp.
*
* @return the queue
*/
@Bean
public Queue receiverConnectorQueueFromSp() {
return new Queue(amqpProperties.getReceiverConnectorQueueFromSp(), true, false, false,
getDeadLetterExchangeArgs());
}
/**
* Create the recevier exchange. Sp send messages to this exchange.
*
* @return the exchange
*/
@Bean
public FanoutExchange exchangeQueueToConnector() {
return new FanoutExchange(amqpProperties.getSenderForSpExchange());
}
/**
* Create the Binding
* {@link AmqpConfiguration#receiverConnectorQueueFromSp()} to
* {@link AmqpConfiguration#exchangeQueueToConnector()}.
*
* @return the binding and create the queue and exchange
*/
@Bean
public Binding bindReceiverQueueToSpExchange() {
return BindingBuilder.bind(receiverConnectorQueueFromSp()).to(exchangeQueueToConnector());
}
/**
* Create dead letter queue.
*
* @return the queue
*/
@Bean
public Queue deadLetterQueue() {
return new Queue(amqpProperties.getDeadLetterQueue(), true, false, true);
}
/**
* Create the dead letter fanout exchange.
*
* @return the fanout exchange
*/
@Bean
public FanoutExchange exchangeDeadLetter() {
return new FanoutExchange(amqpProperties.getDeadLetterExchange());
}
/**
* Create the Binding deadLetterQueue to exchangeDeadLetter.
*
* @return the binding
*/
@Bean
public Binding bindDeadLetterQueueToLwm2mExchange() {
return BindingBuilder.bind(deadLetterQueue()).to(exchangeDeadLetter());
}
/**
* Returns the Listener factory.
*
* @return the {@link SimpleMessageListenerContainer} that gets used receive
* AMQP messages
*/
@Bean(name = { "listenerContainerFactory" })
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory);
return containerFactory;
}
private Map<String, Object> getDeadLetterExchangeArgs() {
final Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", amqpProperties.getDeadLetterExchange());
return args;
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Bean which holds the necessary properties for configuring the AMQP
* connection.
*
*
*
*/
@ConfigurationProperties("hawkbit.device.simulator.amqp")
public class AmqpProperties {
private String receiverConnectorQueueFromSp;
private String senderForSpExchange;
private String deadLetterQueue;
private String deadLetterExchange;
public String getReceiverConnectorQueueFromSp() {
return receiverConnectorQueueFromSp;
}
public void setReceiverConnectorQueueFromSp(final String receiverConnectorQueueFromSp) {
this.receiverConnectorQueueFromSp = receiverConnectorQueueFromSp;
}
public String getDeadLetterExchange() {
return deadLetterExchange;
}
public void setDeadLetterExchange(final String deadLetterExchange) {
this.deadLetterExchange = deadLetterExchange;
}
public String getDeadLetterQueue() {
return deadLetterQueue;
}
public void setDeadLetterQueue(final String deadLetterQueue) {
this.deadLetterQueue = deadLetterQueue;
}
public String getSenderForSpExchange() {
return senderForSpExchange;
}
public void setSenderForSpExchange(final String senderForSpExchange) {
this.senderForSpExchange = senderForSpExchange;
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
/**
* The constants for the caching.
*
*
*
*/
public final class MessageHandlerCacheConstant {
/**
* The name of the cache.
*/
public static final String CACHE_NAME = "hawkbit.device.simulator.states";
private MessageHandlerCacheConstant() {
}
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Abstract class for sender and receiver service.
*
*
*
*/
public class MessageService {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageService.class);
protected RabbitTemplate rabbitTemplate;
protected AmqpProperties amqpProperties;
/**
* Constructor.
*
* @param rabbitTemplate
* the rabbit template
* @param amqpProperties
* the amqp properties
* @param messageConverter
* the message converter
*/
@Autowired
public MessageService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties) {
this.rabbitTemplate = rabbitTemplate;
this.amqpProperties = amqpProperties;
}
public void setAmqpProperties(final AmqpProperties amqpProperties) {
this.amqpProperties = amqpProperties;
}
public void setRabbitTemplate(final RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
/**
* Method to call when error emerges.
*
* @param message
* the message that triggered the error
* @param error
* the error
*/
public void logAndThrowMessageError(final Message message, final String error) {
LOGGER.error("Error \"{}\" reported by message {}", error, message.getMessageProperties().getMessageId());
throw new IllegalArgumentException(error);
}
/**
* Convert a message body to a given class and set the message header
* AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME for Jackson converter.
*
* @param message
* which body will converted
* @param clazz
* the body class
* @return the converted body
*/
@SuppressWarnings("unchecked")
public <T> T convertMessage(final Message message, final Class<T> clazz) {
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
clazz.getTypeName());
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
}
/**
* Method to verify if lwm2m header is set.
*
* @param message
* the message with the header
* @param header
* the header to verify
*/
public void checkIfLwm2mHeaderEmpty(final Message message, final String header) {
final Object headerObject = message.getMessageProperties().getHeaders().get(header);
if (null == headerObject) {
logAndThrowMessageError(message, "Header of " + header + "empty.");
}
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Abstract class for all receiver objects.
*
*
*
*/
public abstract class ReceiverService extends MessageService {
/**
* Constructor.
*
* @param rabbitTemplate
* RabbitTemplate
* @param amqpProperties
* AmqpProperties
* @param messageConverter
* MessageConverter
*/
@Autowired
public ReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties) {
super(rabbitTemplate, amqpProperties);
}
/**
* Method to validate if content type is set in the message properties.
*
* @param message
* the message to get validated
*/
protected void checkContentTypeJson(final Message message) {
if (message.getBody().length == 0) {
return;
}
final MessageProperties messageProperties = message.getMessageProperties();
final String headerContentType = (String) messageProperties.getHeaders().get("content-type");
if (null != headerContentType) {
messageProperties.setContentType(headerContentType);
}
final String contentType = messageProperties.getContentType();
if (contentType != null && contentType.contains("json")) {
return;
}
throw new IllegalArgumentException("Content-Type is not JSON compatible");
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Abstract calls for sender service objects.
*
*
*
*/
public abstract class SenderService extends MessageService {
/**
* Constructor for sender service.
*
* @param rabbitTemplate
* the rabbit template
* @param amqpProperties
* the amqp properties
* @param cacheManager
* the cache manager
*/
@Autowired
public SenderService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties) {
super(rabbitTemplate, amqpProperties);
}
/**
* Send a message if the message is not null.
*
* @param adress
* the exchange name
* @param message
* the amqp message which will be send if its not null
*/
public void sendMessage(final String adress, final Message message) {
if (message == null) {
return;
}
rabbitTemplate.setExchange(adress);
rabbitTemplate.send(message);
}
/**
* Convert object and message properties to message.
*
* @param object
* to get converted
* @param messageProperties
* to get converted
* @return converted message
*/
public Message convertMessage(final Object object, final MessageProperties messageProperties) {
return rabbitTemplate.getMessageConverter().toMessage(object, messageProperties);
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* Object for holding attributes for a simulated update for the device
* simulator.
*
*
*
*/
public class SimulatedUpdate implements Serializable {
private static final long serialVersionUID = 1L;
protected final String tenant;
protected final String thingId;
protected final Long actionId;
protected LocalDateTime startCacheTime;
/**
* Constructor of the class.
*
* @param tenant
* the tenant name.
* @param thingId
* the thing id.
* @param actionId
* the action id
* @param softwareModuleId
* software modul id
* @param urls
* the artifact download urls.
*/
public SimulatedUpdate(final String tenant, final String thingId, final Long actionId) {
this.tenant = tenant;
this.thingId = thingId;
this.actionId = actionId;
this.startCacheTime = LocalDateTime.now();
}
public String getTenant() {
return tenant;
}
public String getThingId() {
return thingId;
}
public Long getActionId() {
return actionId;
}
public LocalDateTime getStartCacheTime() {
return startCacheTime;
}
}

View File

@@ -0,0 +1,142 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
/**
* Handle all incoming Messages from SP.
*
*
*
*/
@Component
public class SpReceiverService extends ReceiverService {
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);
public static final String SOFTWARE_MODULE_FIRMWARE = "firmware";
private final SpSenderService spSenderService;
/**
* Constructor.
*
* @param rabbitTemplate
* the rabbit template
* @param amqpProperties
* the amqp properties
* @param lwm2mSenderService
* the lwm2mSenderService
* @param spSenderService
* the spSenderService
*/
@Autowired
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
final SpSenderService spSenderService) {
super(rabbitTemplate, amqpProperties);
this.spSenderService = spSenderService;
}
/**
* Handle the incoming Message from Queue with the property
* (com.bosch.sp.lwm2m.connector.amqp.receiverConnectorQueueFromSp).
*
* @param message
* the incoming message
* @param type
* the action type
* @param contentType
* the content type in message header
* @param thingId
* the thing id in message header
*/
@RabbitListener(queues = "${hawkbit.device.simulator.amqp.receiverConnectorQueueFromSp}", containerFactory = "listenerContainerFactory")
public void recieveMessageSp(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
@Header(MessageHeaderKey.THING_ID) final String thingId) {
checkContentTypeJson(message);
delegateMessage(message, type, thingId);
}
private void delegateMessage(final Message message, final String type, final String thingId) {
final MessageType messageType = MessageType.valueOf(type);
switch (messageType) {
case EVENT:
handleEventMessage(message, thingId);
break;
default:
LOGGER.info("No valid message type property.");
break;
}
}
private void handleEventMessage(final Message message, final String thingId) {
final Object eventHeader = message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC);
if (eventHeader == null) {
logAndThrowMessageError(message, "Event Topic is not set");
}
final EventTopic eventTopic = EventTopic.valueOf(eventHeader.toString());
switch (eventTopic) {
case DOWNLOAD_AND_INSTALL:
handleUpdateProcess(message, thingId);
break;
case CANCEL_DOWNLOAD:
handleCancelDownloadAction(message, thingId);
break;
default:
LOGGER.info("No valid event property.");
break;
}
}
private void handleCancelDownloadAction(final Message message, final String thingId) {
final MessageProperties messageProperties = message.getMessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders();
final String tenant = (String) headers.get(MessageHeaderKey.TENANT);
final Long actionId = convertMessage(message, Long.class);
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
spSenderService.finishUpdateProcess(update, "Simulation canceled");
}
private void handleUpdateProcess(final Message message, final String thingId) {
final MessageProperties messageProperties = message.getMessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders();
final String tenant = (String) headers.get(MessageHeaderKey.TENANT);
final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message,
DownloadAndUpdateRequest.class);
final Long actionId = downloadAndUpdateRequest.getActionId();
spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
"device Simulator retrieved update request. proceeding with simulation.", actionId);
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
spSenderService.finishUpdateProcess(update, "Simulation complete!");
}
}

View File

@@ -0,0 +1,224 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.amqp;
import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* Sender service to send message to SP.
*
*
*
*/
@Service
public class SpSenderService extends SenderService {
private static final Logger LOGGER = LoggerFactory.getLogger(SpSenderService.class);
private final String spExchange;
/**
*
* @param rabbitTemplate
* the rabbit template
* @param amqpProperties
* the amqp properties
*/
@Autowired
public SpSenderService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties) {
super(rabbitTemplate, amqpProperties);
this.spExchange = AmqpSettings.DMF_EXCHANGE;
}
/**
* Finish the update process. This will send a action status to SP.
*
* @param update
* the simulated update object
* @param description
* a description according the update process
*/
public void finishUpdateProcess(final SimulatedUpdate update, final String description) {
final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED, description);
sendMessage(spExchange, updateResultMessage);
}
/**
* Finish update process with error and send error to SP.
*
* @param update
* the simulated update object
* @param messageDescription
* a description according the update process
*/
public void finishUpdateProcessWithError(final SimulatedUpdate update, final String messageDescription) {
sendErrorgMessage(update, messageDescription);
LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", messageDescription,
update.getThingId());
}
/**
* Send an error message to SP.
*
* @param tenant
* the tenant
* @param messageDescription
* the error message description to send
* @param actionId
* the ID of the action for the error message
*/
public void sendErrorMessage(final String tenant, final String messageDescription, final Long actionId) {
final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, messageDescription, actionId);
sendMessage(spExchange, message);
}
/**
* Send a warning message to SP.
*
* @param update
* the simulated update object
* @param warningMessage
* a warning description
*/
public void sendWarningMessage(final SimulatedUpdate update, final String warningMessage) {
final Message message = createActionStatusMessage(update, warningMessage, ActionStatus.WARNING);
sendMessage(spExchange, message);
}
/**
* Method to send a action status to SP.
*
* @param tenant
* the tenant
* @param actionStatus
* the action status
* @param actionMessage
* the message to get send
* @param actionId
* the cached value
*/
public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus,
final String actionMessage, final Long actionId) {
final Message message = createActionStatusMessage(tenant, actionStatus, actionMessage, actionId);
sendMessage(message);
}
/**
* Create new thing created message and send to SP.
*
* @param tenant
* the tenant to create the target
* @param targetId
* the ID of the target to create or update
*/
public void createOrUpdateThing(final String tenant, final String targetId) {
final MessageProperties messagePropertiesForSP = new MessageProperties();
messagePropertiesForSP.setHeader(MessageHeaderKey.TYPE, MessageType.THING_CREATED.name());
messagePropertiesForSP.setHeader(MessageHeaderKey.TENANT, tenant);
messagePropertiesForSP.setHeader(MessageHeaderKey.THING_ID, targetId);
messagePropertiesForSP.setHeader(MessageHeaderKey.SENDER, "simulator");
messagePropertiesForSP.setContentType(MessageProperties.CONTENT_TYPE_JSON);
messagePropertiesForSP.setReplyTo(amqpProperties.getSenderForSpExchange());
final Message convertedMessage = new Message(null, messagePropertiesForSP);
sendMessage(spExchange, convertedMessage);
LOGGER.debug("Created thing created message and send to SP for Thing \"{}\"", targetId);
}
/**
* Send a created message to SP.
*
* @param message
* the message to get send
*/
private void sendMessage(final Message message) {
sendMessage(spExchange, message);
}
/**
* Send error message to SP.
*
* @param context
* the current context
* @param messageDescription
* a description according the update process
*/
private void sendErrorgMessage(final SimulatedUpdate update, final String messageDescription) {
final Message message = createActionStatusMessage(update, messageDescription, ActionStatus.ERROR);
sendMessage(spExchange, message);
}
/**
* Create a action status message.
*
* @param actionStatus
* the ActionStatus
* @param actionMessage
* the message description
* @param actionId
* the action id
* @param cacheValue
* the cacheValue value
*/
private Message createActionStatusMessage(final String tenant, final ActionStatus actionStatus,
final String actionMessage, final Long actionId) {
final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
actionUpdateStatus.setActionStatus(actionStatus);
headers.put(MessageHeaderKey.TYPE, MessageType.EVENT.name());
headers.put(MessageHeaderKey.TENANT, tenant);
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
if (!StringUtils.isEmpty(actionMessage)) {
actionUpdateStatus.getMessage().add(actionMessage);
}
actionUpdateStatus.setActionId(actionId);
return convertMessage(actionUpdateStatus, messageProperties);
}
private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus,
final String updateResultMessage) {
final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
actionUpdateStatus.setActionStatus(actionStatus);
headers.put(MessageHeaderKey.TYPE, MessageType.EVENT.name());
headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant());
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
actionUpdateStatus.getMessage().add(updateResultMessage);
actionUpdateStatus.setActionId(cacheValue.getActionId());
return convertMessage(actionUpdateStatus, messageProperties);
}
private Message createActionStatusMessage(final SimulatedUpdate update, final String messageDescription,
final ActionStatus status) {
final Message sendMessage = createActionStatusMessage(update.getTenant(), status, messageDescription,
update.getActionId());
return sendMessage;
}
}

View File

@@ -0,0 +1,101 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
#########################################################################################
# PUBLIC configuration, i.e. can be changed by users at runtime (defaults provided here)
#########################################################################################
## Configuration for RabbitMQ communication
hawkbit.device.simulator.amqp.receiverConnectorQueueFromSp=sp_connector_receiver
hawkbit.device.simulator.amqp.deadLetterQueue=simulator_deadletter
hawkbit.device.simulator.amqp.deadLetterExchange=simulator.deadletter
hawkbit.device.simulator.amqp.senderForSpExchange=simulator.replyTo
## Configuration for RabbitMQ integration
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtualHost=/
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.dynamic=true
#disable expose jmx beans
spring.jmx.enabled=false
# the context path of the monitor spring actuator which offers following endpoints
# /autoconfig, /beans, /configprops, /dump, /env, /health, /info, /metrics, /mappings, /trace
management.context-path=/system
management.security.enabled=true
management.security.sessions=stateless
# ENDPOINTS
endpoints.autoconfig.id=autoconfig
endpoints.autoconfig.sensitive=true
endpoints.autoconfig.enabled=false
endpoints.beans.id=beans
endpoints.beans.sensitive=true
endpoints.beans.enabled=false
endpoints.configprops.id=configprops
endpoints.configprops.sensitive=true
endpoints.configprops.enabled=true
#endpoints.configprops.keys-to-sanitize=password,secret,key # suffix or regex
endpoints.dump.id=dump
endpoints.dump.sensitive=true
endpoints.dump.enabled=false
endpoints.env.id=env
endpoints.env.sensitive=true
endpoints.env.enabled=true
#endpoints.env.keys-to-sanitize=password,secret,key # suffix or regex
endpoints.health.id=health
endpoints.health.sensitive=true
endpoints.health.enabled=true
endpoints.info.id=info
endpoints.info.sensitive=true
endpoints.info.enabled=true
endpoints.metrics.id=metrics
endpoints.metrics.sensitive=true
endpoints.metrics.enabled=true
endpoints.shutdown.id=shutdown
endpoints.shutdown.sensitive=true
endpoints.shutdown.enabled=false
endpoints.trace.id=trace
endpoints.trace.sensitive=true
endpoints.trace.enabled=true
# HEALTH INDICATORS (previously health.*)
management.health.db.enabled=true
management.health.diskspace.enabled=true
management.health.mongo.enabled=true
management.health.rabbit.enabled=true
management.health.redis.enabled=false
management.health.solr.enabled=false
management.health.diskspace.path=.
management.health.diskspace.threshold=10485760
management.health.status.order=DOWN, OUT_OF_SERVICE, UNKNOWN, UP
# SECURITY (SecurityProperties)
security.user.name=${BASIC_USERNAME:admin}
security.user.password=${BASIC_PASSWORD:admin}
security.user.role=USER
security.require-ssl=false
security.enable-csrf=false
security.basic.enabled=true
security.basic.realm=DeviceSimulator
security.basic.path= /system/**
security.basic.authorize-mode=ROLE
security.filter-order=0
security.headers.xss=false
security.headers.cache=false
security.headers.frame=false
security.headers.content-type=false
security.headers.hsts=all
security.sessions=stateless
security.ignored=
server.port=8083

1
examples/hawkbit-example-app/.gitignore vendored Executable file
View File

@@ -0,0 +1 @@
/target/

View File

@@ -0,0 +1,15 @@
# hawkBit Example Application
The hawkBit example application is a standalone spring-boot application with an embedded servlet container to start the hawkBit.
## Run
```
java -jar examples/hawkbit-example-app/target/hawkbit-example-app-*-SNAPSHOT.jar
```
Or:
```
run org eclipse.hawkbit.app.Start
```
## Usage
The UI can be accessed via _http://localhost:8080/UI_.
The REST API can be accessed via _http://localhost:8080/rest/v1_.

View File

@@ -0,0 +1,105 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-examples-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-example-app</artifactId>
<name>hawkBit :: Example App</name>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<outputDirectory>${baseDir}</outputDirectory>
<addResources>false</addResources>
<mainClass>org.eclipse.hawkbit.app.Start</mainClass>
<layout>JAR</layout>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Hawkbit -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-dmf-amqp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-security-integration</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-http-security</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.app;
import org.eclipse.hawkbit.ui.login.HawkbitLoginUI;
import org.eclipse.hawkbit.ui.themes.HawkbitTheme;
import com.vaadin.spring.annotation.SpringUI;
/**
* Example hawkBit login UI implementation.
*
* A {@link SpringUI} annotated class must be present in the classpath for the
* login path. The easiest way to get an hawkBit login UI running is to extend
* the {@link HawkbitLoginUI} and to annotated it with {@link SpringUI} as in
* this example to the defined {@link HawkbitTheme#LOGIN_UI_PATH}.
*
*
*
*/
@SpringUI(path = HawkbitTheme.LOGIN_UI_PATH)
public class MyLoginUI extends HawkbitLoginUI {
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.app;
import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.eclipse.hawkbit.eventbus.event.EntityEvent;
import org.eclipse.hawkbit.ui.DispatcherRunnable;
import org.eclipse.hawkbit.ui.HawkbitUI;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.vaadin.spring.events.EventBus.SessionEventBus;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.vaadin.annotations.Push;
import com.vaadin.server.VaadinSession;
import com.vaadin.server.VaadinSession.State;
import com.vaadin.server.WrappedSession;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.spring.annotation.SpringUI;
/**
* Example hawkBit UI implementation.
*
* A {@link SpringUI} annotated class must be present in the classpath. The
* easiest way to get an hawkBit UI running is to extend the {@link HawkbitUI}
* and to annotated it with {@link SpringUI} as in this example.
*
*
*
*/
@SpringUI
@Push(value = PushMode.AUTOMATIC, transport = Transport.WEBSOCKET)
@EventSubscriber
public class MyUI extends HawkbitUI {
private static final long serialVersionUID = 1L;
/**
* An {@link com.google.common.eventbus.EventBus} subscriber which
* subscribes {@link EntityEvent} from the repository to dispatch these
* events to the UI {@link SessionEventBus}.
*
* @param event
* the entity event which has been published from the repository
*/
@Override
@Subscribe
@AllowConcurrentEvents
public void dispatch(final org.eclipse.hawkbit.eventbus.event.Event event) {
final VaadinSession session = getSession();
if (session != null && session.getState() == State.OPEN) {
final WrappedSession wrappedSession = session.getSession();
if (wrappedSession != null) {
final SecurityContext userContext = (SecurityContext) wrappedSession
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
if (eventSecurityCheck(userContext, event)) {
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
access(new DispatcherRunnable(eventBus, session, userContext, event));
} finally {
SecurityContextHolder.setContext(oldContext);
}
}
}
}
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.app;
import org.eclipse.hawkbit.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
/**
* A {@link SpringBootApplication} annoated class with a main method to start.
* The minimal configuration for the standalone hawkBit server.
*
*
*
*/
@SpringBootApplication
@Import({ RepositoryApplicationConfiguration.class })
@EnableHawkbitManagedSecurityConfiguration
public class Start {
/**
* Main method to start the spring-boot application.
*
* @param args
* the VM arguments.
*/
public static void main(final String[] args) {
SpringApplication.run(Start.class, args);
}
}

View File

@@ -0,0 +1,29 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# need to re-name these properties in the defaulthawkbit.properties and code!
hawkbit.server.controller.security.authentication.anonymous.enabled=true
hawkbit.server.controller.security.authentication.header.enabled=false
hawkbit.server.controller.security.authentication.targettoken.enabled=false
hawkbit.server.controller.security.authentication.gatewaytoken.enabled=false
spring.profiles.active=amqp
vaadin.servlet.params.productionMode=false
vaadin.static.servlet.params.productionMode=false
## Configuration for RabbitMQ integration
hawkbit.server.amqp.username=guest
hawkbit.server.amqp.password=guest
hawkbit.server.amqp.virtualHost=/
hawkbit.server.amqp.host=localhost
hawkbit.server.amqp.port=5672
hawkbit.server.amqp.deadLetterQueue=sp_deadletter
hawkbit.server.amqp.deadLetterExchange=sp.deadletter
hawkbit.server.amqp.receiverQueue=sp_receiver

30
examples/pom.xml Executable file
View File

@@ -0,0 +1,30 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-examples-parent</artifactId>
<name>hawkBit :: Examples Parent</name>
<packaging>pom</packaging>
<modules>
<module>hawkbit-device-simulator</module>
<module>hawkbit-example-app</module>
</modules>
</project>

View File

@@ -0,0 +1,3 @@
# HawkBit Artifact Repositoy
HawkBit Artifact Repository is library for storing binary artifacts and metadata into MongoDB.
It has an spring-boot autoconfiguration for easily integration into spring-boot projects.

View File

@@ -0,0 +1,83 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<version>0.1.0-SNAPSHOT</version>
<artifactId>hawkbit-parent</artifactId>
</parent>
<artifactId>hawkbit-artifact-repository-mongo</artifactId>
<name>hawkBit :: Artifact Repository Mongo</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,280 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClientException;
import com.mongodb.MongoException;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSFile;
/**
* The file management which looks up all the file in the filestore.
*
*
*
*
*/
public class ArtifactStore implements ArtifactRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactStore.class);
/**
* The mongoDB field which holds the filename of the file to download. SP
* Server uses the SHA hash as a filename and lookup in the mongoDB.
*/
private static final String FILENAME = "filename";
/**
* The mongoDB field which automatically calculated by the mongoDB.
*/
private static final String MD5 = "md5";
/**
* The mongoDB field which holds the SHA1 hash, stored in the metadata
* object.
*/
private static final String SHA1 = "sha1";
private static final String ID = "_id";
@Autowired
private GridFsOperations gridFs;
MongoTemplate mongoTemplate;
/**
* Retrieves a {@link GridFSDBFile} from the store by it's SHA1 hash.
*
* @param tenant
* the tenant to retrieve the artifacts from, ignore case.
* @param sha1Hash
* the sha1-hash of the file to lookup.
* @return The gridfs file object or {@code null} if no file exists.
*/
@Override
public DbArtifact getArtifactBySha1(final String sha1Hash) {
return map(gridFs.findOne(new Query().addCriteria(Criteria.where(FILENAME).is(sha1Hash))));
}
/**
* Retrieves a {@link GridFSDBFile} from the store by it's MD5 hash.
*
* @param tenant
* the tenant to retrieve the artifacts from, ignore case.
* @param md5Hash
* the md5-hash of the file to lookup.
* @return The gridfs file object or {@code null} if no file exists.
*/
public DbArtifact getArtifactByMd5(final String md5Hash) {
return map(gridFs.findOne(new Query().addCriteria(Criteria.where(MD5).is(md5Hash))));
}
/**
* Retrieves a {@link GridFSDBFile} from the store by it's object id.
*
* @param tenant
* the tenant to retrieve the artifacts from, ignore case.
* @param id
* the id of the file to lookup.
* @return The gridfs file object or {@code null} if no file exists.
*/
@Override
public DbArtifact getArtifactById(final String id) {
return map(gridFs.findOne(new Query().addCriteria(Criteria.where(ID).is(id))));
}
@Override
public DbArtifact store(final InputStream content, final String filename, final String contentType) {
return store(content, filename, contentType, null);
}
@Override
public DbArtifact store(final InputStream content, final String filename, final String contentType,
final DbArtifactHash hash) {
File tempFile = null;
try {
LOGGER.debug("storing file {} of content {}", filename, contentType);
tempFile = File.createTempFile("uploadFile", null);
try (final FileOutputStream os = new FileOutputStream(tempFile)) {
return store(content, contentType, os, tempFile, hash);
}
} catch (final IOException | MongoException e1) {
throw new ArtifactStoreException(e1.getMessage(), e1);
} finally {
if (tempFile != null && !tempFile.delete()) {
LOGGER.error("Could not delete temporary file: {}", tempFile);
}
}
}
@Override
public void deleteById(final String artifactId) {
try {
deleteArtifact(gridFs.findOne(new Query().addCriteria(Criteria.where(ID).is(artifactId))));
} catch (final MongoException e) {
throw new ArtifactStoreException(e.getMessage(), e);
}
}
@Override
public void deleteBySha1(final String sha1Hash) {
try {
deleteArtifact(gridFs.findOne(new Query().addCriteria(Criteria.where(FILENAME).is(sha1Hash))));
} catch (final MongoException e) {
throw new ArtifactStoreException(e.getMessage(), e);
}
}
private void deleteArtifact(final GridFSDBFile dbFile) {
if (dbFile != null) {
try {
gridFs.delete(new Query().addCriteria(Criteria.where(ID).is(dbFile.getId())));
} catch (final MongoClientException e) {
throw new ArtifactStoreException(e.getMessage(), e);
}
}
}
private DbArtifact store(final InputStream content, final String contentType, final FileOutputStream os,
final File tempFile, final DbArtifactHash hash) {
final GridFsArtifact storedArtifact;
try {
final String sha1Hash = computeSHA1Hash(content, os, hash != null ? hash.getSha1() : null);
// upload if it does not exist already, check if file exists, not
// tenant specific.
final GridFSDBFile result = gridFs.findOne(new Query().addCriteria(Criteria.where(FILENAME).is(sha1Hash)));
if (null == result) {
try (FileInputStream inputStream = new FileInputStream(tempFile)) {
final BasicDBObject metadata = new BasicDBObject();
metadata.put(SHA1, sha1Hash);
storedArtifact = map(gridFs.store(inputStream, sha1Hash, contentType, metadata));
}
} else {
LOGGER.info("file with sha1 hash {} already exists in database, increase reference counter", sha1Hash);
result.save();
storedArtifact = map(result);
}
} catch (final NoSuchAlgorithmException | IOException e) {
throw new ArtifactStoreException(e.getMessage(), e);
}
if (hash != null && hash.getMd5() != null && !storedArtifact.getHashes().getMd5().equals(hash.getMd5())) {
throw new HashNotMatchException("The given md5 hash " + hash.getMd5()
+ " not matching the calculated md5 hash " + storedArtifact.getHashes().getMd5(),
HashNotMatchException.MD5);
}
return storedArtifact;
}
private String computeSHA1Hash(final InputStream stream, final FileOutputStream os, final String providedSHA1Sum)
throws NoSuchAlgorithmException, IOException {
String sha1Hash;
// compute digest
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final DigestOutputStream dos = new DigestOutputStream(os, md);
ByteStreams.copy(stream, dos);
dos.close();
sha1Hash = BaseEncoding.base16().lowerCase().encode(md.digest());
if (providedSHA1Sum != null && !providedSHA1Sum.equalsIgnoreCase(sha1Hash)) {
throw new HashNotMatchException(
"The given sha1 hash " + providedSHA1Sum + " not matching the calculated sha1 hash " + sha1Hash,
HashNotMatchException.SHA1);
}
return sha1Hash;
}
/**
* Maps a list of {@link GridFSDBFile} to paged list of {@link DbArtifact}s.
*
* @param tenant
* the tenant
* @param dbFiles
* the list of mongoDB gridFs files.
* @return a paged list of artifacts mapped from the given dbFiles
*/
private List<DbArtifact> map(final List<GridFSDBFile> dbFiles) {
final List<DbArtifact> collect = dbFiles.stream().map(dbFile -> map(dbFile)).collect(Collectors.toList());
return collect;
}
/**
* Retrieves a list of {@link GridFSDBFile} from the store by all SHA1
* hashes.
*
* @param tenant
* the tenant to retrieve the artifacts from, ignore case.
* @param sha1Hashes
* the sha1-hashes of the files to lookup.
* @return list of artfiacts
*/
public List<DbArtifact> getArtifactsBySha1(final List<String> sha1Hashes) {
return map(gridFs.find(new Query().addCriteria(Criteria.where(SHA1).in(sha1Hashes))));
}
/**
* Retrieves a list of {@link GridFSDBFile} from the store by all ids.
*
* @param tenant
* the tenant to retrieve the artifacts from, ignore case.
* @param ids
* the ids of the files to lookup.
* @return list of artfiacts
*/
public List<DbArtifact> getArtifactsByIds(final List<String> ids) {
return map(gridFs.find(new Query().addCriteria(Criteria.where(ID).in(ids))));
}
/**
* Maps a single {@link GridFSFile} to {@link DbArtifact}.
*
* @param tenant
* the tenant
* @param dbFile
* the mongoDB gridFs file.
* @return a mapped artifact from the given dbFile
*/
private GridFsArtifact map(final GridFSFile fsFile) {
if (fsFile == null) {
return null;
}
final GridFsArtifact artifact = new GridFsArtifact(fsFile);
artifact.setArtifactId(fsFile.getId().toString());
artifact.setSize(fsFile.getLength());
artifact.setContentType(fsFile.getContentType());
artifact.setHashes(new DbArtifactHash(fsFile.getFilename(), fsFile.getMD5()));
return artifact;
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Auto configuration for the {@link ArtifactStore}.
*
*
*
*/
@Configuration
@ComponentScan
@ConditionalOnMissingBean(value = ArtifactRepository.class)
@Import(value = MongoConfiguration.class)
public class ArtifactStoreAutoConfiguration {
/**
* @return Default {@link ArtifactRepository} implementation.
*/
@Bean
public ArtifactRepository artifactRepository() {
return new ArtifactStore();
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import java.io.InputStream;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSFile;
/**
* A wrapper object for the {@link DbArtifact} object which returns the
* {@link InputStream} directly from {@link GridFSDBFile#getInputStream()} which
* retrieves when calling {@link #getFileInputStream()} always a new
* {@link InputStream} and not the same.
*
*
*
*/
public class GridFsArtifact extends DbArtifact {
private final GridFSFile dbFile;
/**
* @param dbFile
*/
public GridFsArtifact(final GridFSFile dbFile) {
this.dbFile = dbFile;
}
@Override
public InputStream getFileInputStream() {
if (dbFile instanceof GridFSDBFile) {
return ((GridFSDBFile) dbFile).getInputStream();
}
return null;
}
}

View File

@@ -0,0 +1,117 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import java.net.UnknownHostException;
import java.util.Arrays;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientOptions.Builder;
import com.mongodb.MongoClientURI;
import com.mongodb.ServerAddress;
/**
* {@link AbstractMongoConfiguration} that uses {@link MongoClientURI} even when
* port is configured for NON {@link Cloud} use cases.
*
*/
@Configuration
@EnableConfigurationProperties(MongoProperties.class)
@ConditionalOnClass(Mongo.class)
@ConditionalOnMissingBean(type = "org.springframework.data.mongodb.MongoDbFactory")
@Profile({ "!cloud" })
public class MongoConfiguration extends AbstractMongoConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(MongoConfiguration.class);
@Autowired
private MongoProperties properties;
@Autowired(required = false)
private MongoClientOptions options;
private Mongo mongo;
@Override
public String getDatabaseName() {
return properties.getMongoClientDatabase();
}
/**
* Closes mongo client when destroyd.
*/
@PreDestroy
public void close() {
if (this.mongo != null) {
this.mongo.close();
}
}
@Override
@Bean
@ConditionalOnMissingBean
public Mongo mongo() throws UnknownHostException {
final MongoClientURI uri = new MongoClientURI(properties.getUri(), createBuilderOutOfOptions(options));
if (properties.getPort() != null) {
LOG.debug("Create mongo by properties (host: {}, port: {})", uri.getHosts().get(0), properties.getPort());
this.mongo = new MongoClient(Arrays.asList(new ServerAddress(uri.getHosts().get(0), properties.getPort())),
uri.getOptions());
} else {
LOG.debug("Create mongo by URI : {}", uri);
this.mongo = new MongoClient(uri);
}
return this.mongo;
}
/*
* Creates {@link MongoClientOptions} builder out of existing options as the
* {@link MongoClientURI} expects a builder.
*
* Based on MongoProperties#builder method.
*/
private Builder createBuilderOutOfOptions(final MongoClientOptions options) {
final Builder builder = MongoClientOptions.builder();
if (options != null) {
builder.alwaysUseMBeans(options.isAlwaysUseMBeans());
builder.connectionsPerHost(options.getConnectionsPerHost());
builder.connectTimeout(options.getConnectTimeout());
builder.cursorFinalizerEnabled(options.isCursorFinalizerEnabled());
builder.dbDecoderFactory(options.getDbDecoderFactory());
builder.dbEncoderFactory(options.getDbEncoderFactory());
builder.description(options.getDescription());
builder.maxWaitTime(options.getMaxWaitTime());
builder.readPreference(options.getReadPreference());
builder.serverSelectionTimeout(options.getServerSelectionTimeout());
builder.socketFactory(options.getSocketFactory());
builder.socketKeepAlive(options.isSocketKeepAlive());
builder.socketTimeout(options.getSocketTimeout());
builder.threadsAllowedToBlockForConnectionMultiplier(options
.getThreadsAllowedToBlockForConnectionMultiplier());
builder.writeConcern(options.getWriteConcern());
}
return builder;
}
}

View File

@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.eclipse.hawkbit.artifact.repository.ArtifactStoreAutoConfiguration

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
/**
*
*
*/
public class FreePortFileWriter {
private final String filePortPath;
private final int from;
private final int to;
/**
* @param from
* @param to
*/
public FreePortFileWriter(final int from, final int to, final String filePortPath) {
this.from = from;
this.to = to;
this.filePortPath = filePortPath;
}
public int getPort() {
return findFree();
}
protected int findFree() {
for (int i = from; i <= to; i++) {
if (isFree(i)) {
return i;
}
}
throw new RuntimeException("No free port in range " + from + ":" + to);
}
boolean isFree(final int port) {
try {
final File portFile = new File(filePortPath + File.separator + port + ".port");
portFile.getParentFile().mkdirs();
if (portFile.exists()) {
return false;
} else {
boolean isFree = false;
final ServerSocket sock = new ServerSocket();
sock.setReuseAddress(true);
sock.bind(new InetSocketAddress(port));
if (portFile.createNewFile()) {
portFile.deleteOnExit();
isFree = true;
}
sock.close();
// is free:
return isFree;
// We rely on an exception thrown to determine availability or
// not availability.
}
} catch (final Exception e) {
// not free.
return false;
}
}
}

View File

@@ -0,0 +1,115 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.UUID;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.flapdoodle.embed.mongo.Command;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.ArtifactStoreBuilder;
import de.flapdoodle.embed.mongo.config.DownloadConfigBuilder;
import de.flapdoodle.embed.mongo.config.IMongodConfig;
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.config.RuntimeConfigBuilder;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.config.store.HttpProxyFactory;
import de.flapdoodle.embed.process.runtime.Network;
/**
*
*
*/
public class MongoDBTestRule implements TestRule {
private static final Logger LOG = LoggerFactory.getLogger(MongoDBTestRule.class);
private static volatile MongodExecutable mongodExecutable = null;
private static volatile MongodProcess mongod;
private final String id = UUID.randomUUID().toString();
@Override
public Statement apply(final Statement base, final Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
before(base, description);
try {
base.evaluate();
} finally {
after();
}
}
};
}
private void after() {
if (mongodExecutable != null) {
LOG.info("Stop MongoDB...");
mongodExecutable.stop();
mongodExecutable = null;
if (mongod != null) {
mongod.stop();
mongod = null;
}
LOG.info("MongoDB stopped... {}", id);
}
}
private void before(final Statement base, final Description description) throws UnknownHostException, IOException {
final Command command = Command.MongoD;
final RuntimeConfigBuilder runtimeConfig = new RuntimeConfigBuilder().defaults(command);
int port = -1;
if (System.getProperty("spring.data.mongodb.port") != null) {
port = Integer.parseInt(System.getProperty("spring.data.mongodb.port"));
} else {
port = new FreePortFileWriter(27017, 27100, "./target/freeports").getPort();
System.setProperty("spring.data.mongodb.port", String.valueOf(port));
}
Version version = Version.V3_1_0;
if (System.getProperty("inf.mongodb.version") != null) {
version = Version.valueOf("V" + System.getProperty("inf.mongodb.version").trim().replaceAll("\\.", "_"));
}
if (System.getProperty("http.proxyHost") != null) {
runtimeConfig.artifactStore(new ArtifactStoreBuilder().defaults(command)
.download(new DownloadConfigBuilder().defaultsForCommand(command)
.proxyFactory(new HttpProxyFactory(System.getProperty("http.proxyHost").trim(),
Integer.valueOf(System.getProperty("http.proxyPort"))))));
}
final IMongodConfig mongodConfig = new MongodConfigBuilder().version(version)
.net(new Net("127.0.0.1", port, Network.localhostIsIPv6())).build();
final MongodStarter starter = MongodStarter.getInstance(runtimeConfig.build());
mongodExecutable = starter.prepare(mongodConfig);
LOG.info("Start MongoDB...");
mongod = mongodExecutable.start();
final Net net = mongod.getConfig().net();
LOG.info("MongoDB started id {} on bind ip :{} Port:{} and version {}", id, net.getBindIp(), net.getPort(),
mongodConfig.version().toString());
}
}

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class TestConfiguration {
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import static org.fest.assertions.api.Assertions.assertThat;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import org.eclipse.hawkbit.artifact.MongoDBTestRule;
import org.eclipse.hawkbit.artifact.TestConfiguration;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.google.common.io.BaseEncoding;
import com.mongodb.gridfs.GridFSDBFile;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { ArtifactStoreAutoConfiguration.class, TestConfiguration.class })
public class ArtifactStoreTest {
@ClassRule
public static final MongoDBTestRule mongoDBRule = new MongoDBTestRule();
@Autowired
private ArtifactStore artifactStoreUnderTest;
@Autowired
private GridFsOperations gridFs;
@Test
public void storeArtifactInMongoDB() {
final int filelengthBytes = 128;
final String filename = "testfile.json";
final String contentType = "application/json";
final DbArtifact storedFile = artifactStoreUnderTest.store(generateInputStream(filelengthBytes), filename,
contentType);
assertThat(storedFile).isNotNull();
assertThat(artifactStoreUnderTest.getArtifactById(storedFile.getArtifactId())).isNotNull();
}
@Test
public void findArtifactBySHA1Hash() throws NoSuchAlgorithmException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
final String contentType = "application/json";
final DigestInputStream digestInputStream = digestInputStream(generateInputStream(filelengthBytes), "SHA-1");
artifactStoreUnderTest.store(digestInputStream, filename, contentType);
assertThat(artifactStoreUnderTest.getArtifactBySha1(
BaseEncoding.base16().lowerCase().encode(digestInputStream.getMessageDigest().digest()))).isNotNull();
}
@Test
public void findArtifactByMD5Hash() throws NoSuchAlgorithmException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
final String contentType = "application/json";
final DigestInputStream digestInputStream = digestInputStream(generateInputStream(filelengthBytes), "MD5");
artifactStoreUnderTest.store(digestInputStream, filename, contentType);
assertThat(artifactStoreUnderTest.getArtifactByMd5(
BaseEncoding.base16().lowerCase().encode(digestInputStream.getMessageDigest().digest()))).isNotNull();
}
@Test
public void getInputStreamFromArtifact() throws IOException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
final String contentType = "application/json";
final ByteArrayInputStream inputStream = generateInputStream(filelengthBytes);
final DbArtifact artifact = artifactStoreUnderTest
.getArtifactById(artifactStoreUnderTest.store(inputStream, filename, contentType).getArtifactId());
inputStream.reset();
final byte[] artifactBytes = new byte[filelengthBytes];
final byte[] artifactStoredBytes = new byte[filelengthBytes];
IOUtils.readFully(inputStream, artifactBytes);
IOUtils.readFully(artifact.getFileInputStream(), artifactStoredBytes);
assertThat(artifactBytes).isEqualTo(artifactStoredBytes);
}
@Test
public void deleteArtifactWithOnlyOneTenantLast() throws NoSuchAlgorithmException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
final String contentType = "application/json";
final DigestInputStream digestInputStream = digestInputStream(generateInputStream(filelengthBytes), "SHA-1");
final DbArtifact store = artifactStoreUnderTest.store(digestInputStream, filename, contentType);
artifactStoreUnderTest.deleteById(store.getArtifactId());
final GridFSDBFile findOne = gridFs
.findOne(new Query().addCriteria(Criteria.where("_id").is(store.getArtifactId())));
assertThat(findOne).isNull();
}
private static ByteArrayInputStream generateInputStream(final int length) {
final byte[] bytes = new byte[length];
new Random().nextBytes(bytes);
return new ByteArrayInputStream(bytes);
}
private static DigestInputStream digestInputStream(final ByteArrayInputStream stream, final String digest)
throws NoSuchAlgorithmException {
return new DigestInputStream(stream, MessageDigest.getInstance(digest));
}
}

81
hawkbit-autoconfigure/pom.xml Executable file
View File

@@ -0,0 +1,81 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-autoconfigure</artifactId>
<name>hawkBit :: Spring Boot Autoconfigure</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-core</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-cache-redis</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-dmf-amqp</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ui</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-http-security</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-security-core</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-resource</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.amqp;
import org.eclipse.hawkbit.amqp.AmqpConfiguration;
import org.eclipse.hawkbit.amqp.annotation.EnableAmqp;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
/**
* The amqp autoconfiguration.
*
*
*
*/
@Configuration
@ConditionalOnClass(value = AmqpConfiguration.class)
@EnableAmqp
public class AmqpAutoConfiguration {
}

View File

@@ -0,0 +1,197 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import java.util.Collection;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* A configuration for configuring the spring {@link CacheManager} for specific
* multi-tenancy caching. The caches between tenants must not interfere each
* other.
*
* This is done by providing a special {@link TenantCacheResolver} which
* generates a cache name included the current tenant.
*
*
*
*/
@Configuration
@EnableCaching
public class CacheAutoConfiguration extends CachingConfigurerSupport {
@Autowired
private TenantAware tenantAware;
/**
* @return the default cache manager bean if none other cache manager is
* existing.
*/
@Override
@Bean
@ConditionalOnMissingBean
public TenancyCacheManager cacheManager() {
return new TenantAwareCacheManager(new GuavaCacheManager(), tenantAware);
}
/**
* A {@link SimpleCacheResolver} implementation which includes the
* {@link TenantAware#getCurrentTenant()} into the cache name before
* resolving it.
*
*
*
*
*/
public class TenantCacheResolver extends SimpleCacheResolver {
/*
* (non-Javadoc)
*
* @see org.springframework.cache.interceptor.AbstractCacheResolver#
* resolveCaches(org.springframework
* .cache.interceptor.CacheOperationInvocationContext)
*/
@Override
public Collection<Cache> resolveCaches(final CacheOperationInvocationContext<?> context) {
return super.resolveCaches(context).stream().map(cache -> new TenantCacheWrapper(cache))
.collect(Collectors.toList());
}
/*
* (non-Javadoc)
*
* @see org.springframework.cache.interceptor.SimpleCacheResolver#
* getCacheNames(org.springframework
* .cache.interceptor.CacheOperationInvocationContext)
*/
@Override
protected Collection<String> getCacheNames(final CacheOperationInvocationContext<?> context) {
return super.getCacheNames(context).stream()
.map(cacheName -> tenantAware.getCurrentTenant() + "." + cacheName).collect(Collectors.toList());
}
}
/**
* An {@link Cache} wrapper which returns the name of the cache include the
* {@link TenantAware#getCurrentTenant()}.
*
*
*
*
*/
public class TenantCacheWrapper implements Cache {
private final Cache delegate;
/**
* @param delegate
*/
public TenantCacheWrapper(final Cache delegate) {
this.delegate = delegate;
}
/**
* @return
* @see org.springframework.cache.Cache#getName()
*/
@Override
public String getName() {
return tenantAware.getCurrentTenant() + "." + delegate.getName();
}
/**
* @return
* @see org.springframework.cache.Cache#getNativeCache()
*/
@Override
public Object getNativeCache() {
return delegate.getNativeCache();
}
/**
* @param key
* @return
* @see org.springframework.cache.Cache#get(java.lang.Object)
*/
@Override
public ValueWrapper get(final Object key) {
return delegate.get(key);
}
/**
* @param key
* @param type
* @return
* @see org.springframework.cache.Cache#get(java.lang.Object,
* java.lang.Class)
*/
@Override
public <T> T get(final Object key, final Class<T> type) {
return delegate.get(key, type);
}
/**
* @param key
* @param value
* @see org.springframework.cache.Cache#put(java.lang.Object,
* java.lang.Object)
*/
@Override
public void put(final Object key, final Object value) {
delegate.put(key, value);
}
/**
* @param key
* @param value
* @return
* @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object,
* java.lang.Object)
*/
@Override
public ValueWrapper putIfAbsent(final Object key, final Object value) {
return delegate.putIfAbsent(key, value);
}
/**
* @param key
* @see org.springframework.cache.Cache#evict(java.lang.Object)
*/
@Override
public void evict(final Object key) {
delegate.evict(key);
}
/**
*
* @see org.springframework.cache.Cache#clear()
*/
@Override
public void clear() {
delegate.clear();
}
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import org.eclipse.hawkbit.cache.CacheConstants;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* A configuration for configuring a cache for the download id's.
*
* This is done by providing a named cache.
*
*
*
*/
@Configuration
public class DownloadIdCacheAutoConfiguration {
@Autowired
private CacheManager cacheManager;
/**
* Bean for the downlod id cache.
*
* @return the cache
*/
@Bean(name = CacheConstants.DOWNLOAD_ID_CACHE)
public Cache downloadIdCache() {
if (cacheManager instanceof TenancyCacheManager) {
return ((TenancyCacheManager) cacheManager).getDirectCache(CacheConstants.DOWNLOAD_ID_CACHE);
}
return cacheManager.getCache(CacheConstants.DOWNLOAD_ID_CACHE);
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import org.eclipse.hawkbit.cache.RedisConfiguration;
import org.eclipse.hawkbit.cache.annotation.EnableRedis;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
/**
* A configuration for configuring the redis configuration.
*
*
*
*/
@Configuration
@ConditionalOnClass(value = RedisConfiguration.class)
@EnableRedis
public class RedisAutoConfiguration {
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.conf;
import org.eclipse.hawkbit.ControllerPollProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Enable the Controlle Poll.
*
*
*
*/
@Configuration
@ConditionalOnClass(ControllerPollProperties.class)
@EnableConfigurationProperties(ControllerPollProperties.class)
public class ControllerPollAutoConfiguration {
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.conf;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* Autoconfiguration which loads the default configuration properties for
* hawkbit.
*
*
*
*/
@Configuration
@PropertySource("classpath:/hawkbitdefaults.properties")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultPropertiesAutoConfiguration {
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.eventbus;
import java.util.concurrent.Executor;
import org.eclipse.hawkbit.eventbus.EventBusSubscriberProcessor;
import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
/**
*
*
*/
@Configuration
public class EventBusAutoConfiguration {
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
/**
* Server internal eventBus that allows parallel event processing if the
* subscriber is marked as so.
*
* @return eventbus bean
*/
@Bean
@ConditionalOnMissingBean
public EventBus eventBus() {
return new AsyncEventBus(executor);
}
/**
* @return the {@link EventBusSubscriberProcessor} to find classes annotated
* with {@link EventSubscriber}.
*/
@Bean
@ConditionalOnMissingBean
public EventBusSubscriberProcessor eventBusSubscriberProcessor() {
return new EventBusSubscriberProcessor();
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.scheduling;
import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
/**
*
*
*/
@Configuration
@EnableAsync
@ConditionalOnMissingBean(AsyncConfigurer.class)
public class AsyncConfigurerAutoConfiguration implements AsyncConfigurer {
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
@Override
public Executor getAsyncExecutor() {
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.scheduling;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for the async configurer.
*
*
*/
@ConfigurationProperties("hawkbit.threadpool")
public class AsyncConfigurerThreadpoolProperties {
private Integer queuesize = 250;
private Integer corethreads = 5;
private Integer maxthreads = 50;
private Long idletimeout = 10000L;
public Integer getQueuesize() {
return queuesize;
}
public void setQueuesize(final Integer queuesize) {
this.queuesize = queuesize;
}
public Integer getCorethreads() {
return corethreads;
}
public void setCorethreads(final Integer corethreads) {
this.corethreads = corethreads;
}
public Integer getMaxthreads() {
return maxthreads;
}
public void setMaxthreads(final Integer maxthreads) {
this.maxthreads = maxthreads;
}
public Long getIdletimeout() {
return idletimeout;
}
public void setIdletimeout(final Long idletimeout) {
this.idletimeout = idletimeout;
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.scheduling;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
*
*
*/
@Configuration
@EnableConfigurationProperties(AsyncConfigurerThreadpoolProperties.class)
public class ExecutorAutoConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorAutoConfiguration.class);
@Autowired
private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties;
/**
* @return ExecutorService for general pupose multi threaded operations
*/
@Bean
@ConditionalOnMissingBean
public Executor asyncExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(
asyncConfigurerProperties.getQueuesize());
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
asyncConfigurerProperties.getCorethreads(), asyncConfigurerProperties.getMaxthreads(),
asyncConfigurerProperties.getIdletimeout(), TimeUnit.MILLISECONDS, blockingQueue,
new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
threadPoolExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(final Runnable r, final java.util.concurrent.ThreadPoolExecutor executor) {
LOGGER.warn("Reject runnable for centralExecutorService, reached limit of queue size {}", executor
.getQueue().size());
}
});
return new DelegatingSecurityContextExecutor(threadPoolExecutor);
}
/**
* @return the executor for UI background processes.
*/
@Bean(name = "uiExecutor")
@ConditionalOnMissingBean(name = "uiExecutor")
public Executor uiExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(20);
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 20, 10000, TimeUnit.MILLISECONDS,
blockingQueue, new ThreadFactoryBuilder().setNameFormat("ui-executor-pool-%d").build());
threadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return new DelegatingSecurityContextExecutor(threadPoolExecutor);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.security;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
* Annotation to enable the managed security configuration.
*
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SecurityManagedConfiguration.class)
public @interface EnableHawkbitManagedSecurityConfiguration {
}

View File

@@ -0,0 +1,206 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.security;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator;
import org.eclipse.hawkbit.im.authentication.PermissionService;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
/**
* {@link EnableAutoConfiguration Auto-configuration} for security.
*
*
*
*/
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityAutoConfiguration {
/**
* @return the {@link TenantAware} singleton bean which holds the current
* {@link TenantAware} service and make it accessible in beans which
* cannot access the service directly, e.g. JPA entities.
*/
@Bean
@ConditionalOnMissingBean
public TenantAware tenantAware() {
return new SecurityContextTenantAware();
}
/**
* @return permission service to check if current user has the necessary
* permissions.
*/
@Bean
@ConditionalOnMissingBean
public PermissionService permissionService() {
return new PermissionService();
}
/**
* Creates the auditore aware.
*
* @return the spring security auditore aware
*/
@Bean
@ConditionalOnMissingBean
public AuditorAware<String> auditorAware() {
return new SpringSecurityAuditorAware();
}
/**
* Auto-configuration for the in-memory-user-management.
*
*
*
*/
@Configuration
@ConditionalOnMissingBean(value = { UserAuthenticationFilter.class })
public static class InMemoryUserManagementConfiguration extends GlobalAuthenticationConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryUserManagementConfiguration.class);
@Autowired
private AuthenticationConfiguration configuration;
/*
* (non-Javadoc)
*
* @see org.springframework.security.config.annotation.authentication.
* configurers. GlobalAuthenticationConfigurerAdapter
* #configure(org.springframework.security.config.annotation.
* authentication.builders.AuthenticationManagerBuilder)
*/
@Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
final DaoAuthenticationProvider userDaoAuthenticationProvider = new TenantDaoAuthenticationProvider();
userDaoAuthenticationProvider.setUserDetailsService(userDetailsService());
auth.authenticationProvider(userDaoAuthenticationProvider);
}
/**
* @return the user details service to load a user from memory user
* manager.
*/
@Bean
public UserDetailsService userDetailsService() {
final InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(
new ArrayList<>());
inMemoryUserDetailsManager.setAuthenticationManager(null);
inMemoryUserDetailsManager.createUser(new User("admin", "admin", getAllAuthorities()));
return inMemoryUserDetailsManager;
}
/**
* @return the multi-tenancy indicator to disallow multi-tenancy
*/
@Bean
public MultitenancyIndicator multiTenancyIndicator() {
return new MultitenancyIndicator() {
@Override
public boolean isMultiTenancySupported() {
return false;
}
};
}
private Collection<SimpleGrantedAuthority> getAllAuthorities() {
final List<SimpleGrantedAuthority> allPermissions = new ArrayList<>();
final Field[] declaredFields = SpPermission.class.getDeclaredFields();
for (final Field field : declaredFields) {
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
try {
final String permissionName = (String) field.get(null);
allPermissions.add(new SimpleGrantedAuthority(permissionName));
} catch (final IllegalAccessException e) {
LOGGER.error(e.getMessage(), e);
}
}
}
return allPermissions;
}
private static class TenantDaoAuthenticationProvider extends DaoAuthenticationProvider {
/*
* (non-Javadoc)
*
* @see org.springframework.security.authentication.dao.
* AbstractUserDetailsAuthenticationProvider
* #createSuccessAuthentication(java.lang.Object,
* org.springframework.security.core.Authentication,
* org.springframework.security.core.userdetails.UserDetails)
*/
@Override
protected Authentication createSuccessAuthentication(final Object principal,
final Authentication authentication, final UserDetails user) {
final UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
authentication.getCredentials(), user.getAuthorities());
result.setDetails(new TenantAwareAuthenticationDetails("DEFAULT", false));
return result;
}
}
/**
* @return the {@link UserAuthenticationFilter} to include into the SP
* security configuration.
* @throws Exception
* lazy bean exception maybe if the authentication manager
* cannot be instantiated
*/
@Bean
public UserAuthenticationFilter userAuthenticationFilter() throws Exception {
return new UserAuthenticationFilterBasicAuth(configuration.getAuthenticationManager());
}
private static final class UserAuthenticationFilterBasicAuth extends BasicAuthenticationFilter
implements UserAuthenticationFilter {
private UserAuthenticationFilterBasicAuth(final AuthenticationManager authenticationManager) {
super(authenticationManager);
}
}
}
}

View File

@@ -0,0 +1,563 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.security;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter;
import org.eclipse.hawkbit.cache.CacheConstants;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationToken;
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.rest.resource.RestConstants;
import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSource;
import org.eclipse.hawkbit.security.DosFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.HttpDownloadAuthenticationFilter;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.cache.Cache;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
import org.vaadin.spring.security.VaadinSecurityContext;
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
import org.vaadin.spring.security.web.VaadinDefaultRedirectStrategy;
import org.vaadin.spring.security.web.VaadinRedirectStrategy;
import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler;
/**
* All configurations related to SP authentication and authorization layer.
*
*
*
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true)
@EnableWebMvcSecurity
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class SecurityManagedConfiguration implements EnvironmentAware {
private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class);
private static final String SP_SERVER_CONFIG_PREFIX = "hawkbit.server.";
private RelaxedPropertyResolver environment;
@Override
public void setEnvironment(final Environment environment) {
this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX);
}
/**
* {@link WebSecurityConfigurer} for the internal SP controller API.
*
*
*
*/
@Configuration
@Order(300)
static class ControllerSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private ControllerManagement controllerManagement;
@Autowired
private SystemManagement systemManagement;
@Autowired
private TenantAware tenantAware;
@Autowired
private SecurityProperties securityConfiguration;
@Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Override
protected void configure(final HttpSecurity http) throws Exception {
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
securityConfiguration.getRpCnHeader(), securityConfiguration.getRpSslIssuerHashHeader(),
systemManagement, tenantAware);
securityHeaderFilter.setAuthenticationManager(authenticationManager());
securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
systemManagement, tenantAware, controllerManagement);
securityTokenFilter.setAuthenticationManager(authenticationManager());
securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
systemManagement, tenantAware);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager());
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
HttpSecurity httpSec = http.csrf().disable().headers()
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY)).contentTypeOptions()
.xssProtection().httpStrictTransportSecurity().and();
if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
}
if (securityConfiguration.getAnonymousEnabled()) {
LOG.info(
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous", Collections.singletonList(
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and()
.securityContext().disable().anonymous().authenticationFilter(anoymousFilter);
} else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).antMatcher("/*/controller/**").anonymous().disable()
.authorizeRequests().anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
}).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.config.annotation.web.configuration.
* WebSecurityConfigurerAdapter
* #configure(org.springframework.security.config.annotation.
* authentication.builders. AuthenticationManagerBuilder)
*/
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(
new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs()));
}
}
/**
* Filter to protect the SP server against denial of service attacks.
*
* @return he spring filter registration bean for registering an denial of
* service protection filter in the filter chain
*/
@Bean
@Order(50)
public FilterRegistrationBean dosFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean
.setFilter(
new DosFilter(environment.getProperty("security.dos.filter.maxRead", Integer.class, 100),
environment.getProperty("security.dos.filter.maxWrite", Integer.class, 10),
environment.getProperty("security.dos.filter.whitelist"), environment
.getProperty("security.clients.blacklist"),
environment.getProperty("security.rp.remote_ip_header", String.class, "X-Forwarded-For")));
filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*");
return filterRegBean;
}
/**
* Filter registration bean for spring etag filter.
*
* @return the spring filter registration bean for registering an etag
* filter in the filter chain
*/
@Bean
@Order(100)
public FilterRegistrationBean eTagFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
// eclude the URLs for downloading artifacts, so no eTag is generated in
// the
// ShallowEtagHeaderFilter, just using the SH1 hash of the artifact
// itself as 'ETag', because
// otherwise the file will be copied in memory!
filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter(
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/{tenant}/controller/artifacts/**",
"/{targetid}/softwaremodules/{softwareModuleId}/artifacts/**"));
return filterRegBean;
}
/**
* Security configuration for the REST management API of the health url.
*
*
*/
@Configuration
@Order(310)
public static class HealthSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.regexMatcher("/system/health").csrf().disable().httpBasic().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
/**
* Security configuration for the REST management API.
*
*
*/
@Configuration
@Order(350)
public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private UserAuthenticationFilter userAuthenticationFilter;
@Autowired
private SystemManagement systemManagement;
@Autowired
private TenantAware tenantAware;
@Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Override
protected void configure(final HttpSecurity http) throws Exception {
HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system.*").csrf().disable();
if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
}
httpSec.addFilterBefore(new Filter() {
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
userAuthenticationFilter.init(filterConfig);
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
userAuthenticationFilter.doFilter(request, response, chain);
}
@Override
public void destroy() {
userAuthenticationFilter.destroy();
}
}, RequestHeaderAuthenticationFilter.class)
.addFilterAfter(
new AuthenticationSuccessTenantMetadataCreationFilter(tenantAware, systemManagement),
RequestHeaderAuthenticationFilter.class)
.authorizeRequests().anyRequest().authenticated()
.antMatchers(RestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN).antMatchers(RestConstants.BASE_SYSTEM_MAPPING + "/**")
.hasAnyAuthority(SpPermission.SYSTEM_DIAG);
}
}
/**
* {@link WebSecurityConfigurer} for external (management) access.
*
*
*/
@Configuration
@Order(400)
@EnableVaadinSecurity
public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter
implements EnvironmentAware {
private static final String XFRAME_OPTION_DENY = "DENY";
private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN";
private static final String XFAME_OPTION_ALLOW_FROM = "ALLOW-FROM";
@Autowired
private VaadinSecurityContext vaadinSecurityContext;
@Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
private RelaxedPropertyResolver environment;
@Override
public void setEnvironment(final Environment environment) {
this.environment = new RelaxedPropertyResolver(environment, SP_SERVER_CONFIG_PREFIX);
}
/**
* post construct for setting the authentication success handler for the
* vaadin security context.
*/
@PostConstruct
public void afterPropertiesSet() {
this.vaadinSecurityContext.addAuthenticationSuccessHandler(redirectSaveHandler());
}
@Bean(name = "authenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* @return The VaadinRedirectStategy
*/
@Bean
public VaadinRedirectStrategy vaadinRedirectStrategy() {
return new VaadinDefaultRedirectStrategy();
}
/**
* @return the vaadin success authentication handler
*/
@Bean
public VaadinAuthenticationSuccessHandler redirectSaveHandler() {
final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler();
handler.setRedirectStrategy(vaadinRedirectStrategy());
handler.setDefaultTargetUrl("/UI/");
handler.setTargetUrlParameter("r");
return handler;
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
// configuration xframe-option
final String confXframeOption = environment.getProperty("security.xframe.option", XFRAME_OPTION_DENY);
final String confAllowFromUri = environment.getProperty("security.xframe.option.allowfrom");
if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri == null) {
// if allow-from option is specified but no allowFromUri throw
// exception
throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM"
+ " but no hawkbit.server.security.xframe.option.allowfrom has been set, "
+ "please ensure to set allow from URIs");
}
// workaround regex: we need to exclude the URL /UI/HEARTBEAT here
// because we bound the
// vaadin application to /UI and not to root, described in
// vaadin-forum:
// https://vaadin.com/forum#!/thread/3200565.
HttpSecurity httpSec = http.regexMatcher("(?!.*HEARTBEAT)^.*\\/UI.*$")
// disable as CSRF is handled by Vaadin
.csrf().disable();
if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} else {
LOG.info(
"\"******************\\n** Requires HTTPS Security has been disabled for UI, should only use for developing purposes **\\n******************\"");
}
// for UI integrator we allow frame integration on same origin
httpSec.headers()
.addHeaderWriter(confXframeOption.equals(XFAME_OPTION_ALLOW_FROM)
? new XFrameOptionsHeaderWriter(new StaticAllowFromStrategy(new URI(confAllowFromUri)))
: new XFrameOptionsHeaderWriter(xframeOptionFromStr(confXframeOption)))
.contentTypeOptions().xssProtection().httpStrictTransportSecurity().and()
// UI
.authorizeRequests().antMatchers("/UI/login/**").permitAll().antMatchers("/UI/UIDL/**").permitAll()
.anyRequest().authenticated().and()
// UI login / logout
.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/UI/login/#/"))
.and().logout().logoutUrl("/UI/logout").logoutSuccessUrl("/UI/login/#/");
}
/**
* Converts a given string into the {@link XFrameOptionsMode} enum. Only
* {@link XFrameOptionsMode#DENY} and
* {@link XFrameOptionsMode#SAMEORIGIN} any other string will be
* converted to the default {@link XFrameOptionsMode#SAMEORIGIN}.
*
* @param xframeOption
* the string of the xframe option
* @return an {@link XFrameOptionsMode} by the given string, in case
* string does not match an option then
* {@link XFrameOptionsMode#SAMEORIGIN} is returned
*/
private XFrameOptionsMode xframeOptionFromStr(@NotNull final String xframeOption) {
switch (xframeOption) {
case XFRAME_OPTION_DENY:
return XFrameOptionsMode.DENY;
case XFRAME_OPTION_SAMEORIGIN:
// fall through to default because the same
default:
return XFrameOptionsMode.SAMEORIGIN;
}
}
@Override
public void configure(final WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/v2/api-docs/**",
"/docs/**");
}
}
/**
* A Websecruity config to handle and filter the download ids.
*
*
*
*/
@Configuration
@EnableWebSecurity
@Order(200)
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties securityConfiguration;
@Autowired
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE)
private Cache downloadIdCache;
@Override
protected void configure(final HttpSecurity http) throws Exception {
final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter(
downloadIdCache);
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager());
http.csrf().disable();
http.anonymous().disable();
http.regexMatcher(HttpDownloadAuthenticationFilter.REQUEST_ID_REGEX_PATTERN)
.addFilterBefore(downloadIdAuthenticationFilter, FilterSecurityInterceptor.class);
http.authorizeRequests().anyRequest().authenticated();
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(
new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs()));
}
}
}
/**
* After a successful login on the UI we need to ensure to create the tenant
* meta data within SP.
*
*
*/
class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler {
@Autowired
private SystemManagement systemManagement;
/*
* (non-Javadoc)
*
* @see org.vaadin.spring.security.web.authentication.
* SavedRequestAwareVaadinAuthenticationSuccessHandler
* #onAuthenticationSuccess(org.springframework.security.core.
* Authentication)
*/
@Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) {
systemManagement
.getTenantMetadata(((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString());
} else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) {
// TODO: MECS-1078 vaadin4spring-ext-security does not give us the
// fullyAuthenticatedToken
// in the GenericVaadinSecurity class. Only the token which has been
// created in the
// LoginView. This needs to be changed with the update of
// vaadin4spring 0.0.7 because it
// has been fixed.
systemManagement.getTenantMetadata("DEFAULT");
}
super.onAuthenticationSuccess(authentication);
}
}
/**
* Sevletfilter to create metadata after successful authentication over RESTful.
*
*
*/
class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
private final TenantAware tenantAware;
private final SystemManagement systemManagement;
AuthenticationSuccessTenantMetadataCreationFilter(final TenantAware tenantAware,
final SystemManagement systemManagement) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement;
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
// not needed
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant != null) {
// lazy initialize tenant meta data after successful authentication
systemManagement.getTenantMetadata(currentTenant);
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
// not needed
}
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.swagger;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.rest.resource.RestConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger configuration for RESTful SP server APIs.
*
*
*
*
*/
@Configuration
@EnableSwagger2
public class SwaggerApiDocAutoConfiguration {
/**
* Create the Springfox Docket, which generates the Information for the
* REST-Swagger-UI. Rest paths are: /rest/v1/. and ./controller/v1/.
*
* @see springfox.documentation.spring.web.plugins.Docket
*
* @return the v1 docket
*/
@Bean
public Docket customImplementation() {
return createDocket();
}
private Predicate<String> selectApiPaths() {
return Predicates.or(PathSelectors.regex("/rest/v1/.*"), PathSelectors.regex("/.*/controller/v1/.*"));
}
private Docket createDocket() {
final List<ResponseMessage> authorizationMessages = globalAuhtorizationMessages();
return new Docket(DocumentationType.SWAGGER_2).select().paths(selectApiPaths()).build()
.useDefaultResponseMessages(true).globalResponseMessage(RequestMethod.GET, authorizationMessages)
.globalResponseMessage(RequestMethod.POST, authorizationMessages)
.globalResponseMessage(RequestMethod.PUT, authorizationMessages)
.globalResponseMessage(RequestMethod.DELETE, authorizationMessages).apiInfo(apiInfo());
}
private List<ResponseMessage> globalAuhtorizationMessages() {
final List<ResponseMessage> messageList = new ArrayList<>();
messageList.add(new ResponseMessage(200, "Request sucessfull", new ModelRef("com.")));
messageList.add(new ResponseMessage(400, "Bad Request - e.g. invalid parameters", null));
messageList.add(new ResponseMessage(401, "Unauthorized - The request requires user authentication.",
null));
messageList.add(
new ResponseMessage(403, "Forbidden - Insufficient permissions or data volume restriction applies.",
new ModelRef("ExceptionInfo")));
messageList.add(new ResponseMessage(405, "Method Not Allowed", null));
messageList.add(new ResponseMessage(406,
"Not Acceptable - In case accept header is specified and not application/json", null));
messageList.add(new ResponseMessage(429,
"Too many requests. The server will refuse further attemps and the client has to wait another second.",
null));
return messageList;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Software Provisioning API Descriptions").version(RestConstants.API_VERSION)
.build();
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.ui;
import org.eclipse.hawkbit.DistributedResourceBundleMessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.vaadin.spring.annotation.EnableVaadinExtensions;
import org.vaadin.spring.events.annotation.EnableEventBus;
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
/**
* The hawkbit-ui autoconfiguration.
*
*
*
*/
@Configuration
@EnableVaadinSecurity
@EnableVaadinExtensions
@EnableEventBus
public class UIAutoConfiguration {
/**
* A message source bean to add distributed message sources.
*
* @return the message bean.
*/
@Bean(name = "messageSource")
public DistributedResourceBundleMessageSource messageSource() {
return new DistributedResourceBundleMessageSource();
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.url;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Throwables;
/**
* Autoconfiguration of the {@link HostnameResolver} based on a property.
*
*
*
*/
@Configuration
@EnableConfigurationProperties(ServerProperties.class)
public class PropertyHostnameResolverAutoConfiguration {
@Autowired
private ServerProperties serverProperties;
/**
* @return the default autoconfigure hostname resolver implementation which
* is property based specified by the property {@link #url}
*/
@Bean
@ConditionalOnMissingBean(value = HostnameResolver.class)
public HostnameResolver hostnameResolver() {
return new HostnameResolver() {
@Override
public URL resolveHostname() {
try {
return new URL(serverProperties.getUrl());
} catch (final MalformedURLException e) {
throw Throwables.propagate(e);
}
}
};
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.url;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for the server e.g. the server's URL which must be configured.
*
*
*/
@ConfigurationProperties("hawkbit.server")
public class ServerProperties {
private String url = "http://localhost:8080";
public String getUrl() {
return url;
}
public void setUrl(final String url) {
this.url = url;
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.web;
import org.eclipse.hawkbit.controller.EnableDirectDeviceApi;
import org.eclipse.hawkbit.rest.resource.EnableRestResources;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Auto-Configuration for enabling the REST-Resources.
*
*
*
*/
@Configuration
@ConditionalOnClass({ EnableDirectDeviceApi.class, EnableRestResources.class })
@Import({ EnableDirectDeviceApi.class, EnableRestResources.class })
public class ResourceControllerAutoConfiguration {
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.web;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* A configuration bean which disables the {@code useSuffixPatternMatch} feature
* from Spring because it will truncate the dot in a REST URL which leads to
* problem in case a controllerId contains dots and is a path parameter or
* filename ending.
*
*
*/
@Configuration
public class WebMvcAutoConfiguration extends WebMvcConfigurerAdapter {
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* #configurePathMatch
* (org.springframework.web.servlet.config.annotation.PathMatchConfigurer)
*/
@Override
public void configurePathMatch(final PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
configurer.setUseRegisteredSuffixPatternMatch(false);
}
/*
* (non-Javadoc)
*
* @see org.springframework.web.servlet.config.annotation.
* WebMvcConfigurerAdapter# configureContentNegotiation
* (org.springframework.web.servlet.config.annotation.
* ContentNegotiationConfigurer)
*/
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}

View File

@@ -0,0 +1,16 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.eclipse.hawkbit.autoconfigure.conf.DefaultPropertiesAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.ui.UIAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.swagger.SwaggerApiDocAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.url.PropertyHostnameResolverAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.web.WebMvcAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.CacheAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.DownloadIdCacheAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.eventbus.EventBusAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.RedisAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.conf.ControllerPollAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration

View File

@@ -0,0 +1,57 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Tomcat / Server
server.tomcat.compression=on
spring.http.gzip.mime-types=text/html,text/xml,text/plain,application/json,application/javascript,text/css,application/x-javascript,text/javascript,application/vnd.ms-fontobject,application/x-font-opentype,application/x-font-truetype,application/x-font-ttf,application/xml,font/eot,font/opentype,font/otf,image/svg+xml,image/vnd.microsoft.icon
server.tomcat.compressable-mime-types=${spring.http.gzip.mime-types}
spring.http.gzip.min-gzip-size=256
# JPA / Datasource
spring.jpa.eclipselink.eclipselink.weaving=false
spring.jpa.database=H2
spring.jpa.show-sql=false
spring.datasource.driverClassName=org.h2.Driver
# MongoDB for artifact-repository
spring.data.mongodb.uri=mongodb://localhost/artifactrepo
spring.data.mongo.repositories.enabled=true
# Flyway DDL
flyway.enabled=true
flyway.initOnMigrate=true
flyway.sqlMigrationSuffix=${spring.jpa.database}.sql
# Vaadin Servlet
vaadin.static.servlet.params.resourceCacheTime=${spring.resources.cache-period}
vaadin.static.servlet.params.productionMode=true
vaadin.servlet.params.productionMode=true
vaadin.servlet.params.resourceCacheTime=${spring.resources.cache-period}
vaadin.servlet.urlMapping=/UI/*
vaadin.servlet.params.heartbeatInterval=60
vaadin.servlet.params.closeIdleSessions=false
# Spring MVC
spring.mvc.favicon.enabled=false
# Defines the thread pool executor
hawkbit.threadpool.corethreads=5
hawkbit.threadpool.maxthreads=20
hawkbit.threadpool.idletimeout=10000
hawkbit.threadpool.queuesize=250
# Defines the polling time for the controllers in HH:MM:SS notation
hawkbit.controller.pollingTime=00:05:00
hawkbit.controller.pollingOverdueTime=00:05:00
## Configuration for RabbitMQ integration
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver

70
hawkbit-cache-redis/pom.xml Executable file
View File

@@ -0,0 +1,70 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-cache-redis</artifactId>
<name>hawkBit :: Redis Cache Manager</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
import org.eclipse.hawkbit.cache.eventbus.EventDistributor;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
/**
* The spring Redis configuration which is enabled by using the profile
* {@code redis} to use a Redis server as cache.
*
*
*
*/
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfiguration {
@Autowired
private RedisProperties redisProperties;
@Autowired
private TenantAware tenantAware;
/**
* @return the {@link EventDistributor} to distribute and consume the events
* from Redis
*/
@Bean
public EventDistributor eventDistributor() {
return new EventDistributor();
}
/**
* @return the spring redis cache manager.
*/
@Bean
public CacheManager cacheManager() {
return new TenantAwareCacheManager(new RedisCacheManager(redisTemplate()), tenantAware);
}
/**
* @return the redis connection factory to create a redis connection based
* on the {@link RedisProperties}
*/
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
final JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(redisProperties.getHost());
factory.setPort(redisProperties.getPort());
factory.setUsePool(true);
return factory;
}
/**
* @return the spring {@link RedisTemplate} configured with the necessary
* object serializers
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
/**
* @return the spring-redis message listener adapter to consume messages
* from the Redis server
*/
@Bean
public MessageListenerAdapter messageListenerAdapter() {
final MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(eventDistributor());
messageListenerAdapter.setSerializer(new JdkSerializationRedisSerializer());
return messageListenerAdapter;
}
/**
* @return the spring-redis message listener container to register the
* message listener adapter
*/
@Bean
public RedisMessageListenerContainer redisContainer() {
final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(jedisConnectionFactory());
container.addMessageListener(messageListenerAdapter(), eventDistributor().getTopics());
return container;
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Bean which holds the necessary properties for configuring the Redis
* connection.
*
*
*
*
*/
@ConfigurationProperties("hawkbit.server.redis")
public class RedisProperties {
private String host;
private int port;
/**
* @return the host
*/
public String getHost() {
return host;
}
/**
* @param host
* the host to set
*/
public void setHost(final String host) {
this.host = host;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final int port) {
this.port = port;
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.eclipse.hawkbit.cache.RedisConfiguration;
import org.springframework.context.annotation.Import;
/**
* Annotation to enable redis caching.
*
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(RedisConfiguration.class)
public @interface EnableRedis {
}

View File

@@ -0,0 +1,138 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache.eventbus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.eclipse.hawkbit.eventbus.event.DistributedEvent;
import org.eclipse.hawkbit.eventbus.event.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.Topic;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
/**
*
*
*/
@EventSubscriber
public class EventDistributor {
private static final Logger LOGGER = LoggerFactory.getLogger(EventDistributor.class);
/**
* the node id to filter self published events in the redis message
* subscriber.
*/
private static final String NODE_ID = UUID.randomUUID().toString();
private static final String DISTRIBUTION_CHANNEL_TOPIC = "com/bosch/sp/distEvent";
private static final String SEND_DISTRIBUTION_CHANNEL = DISTRIBUTION_CHANNEL_TOPIC + "/" + NODE_ID;
private static final String SUB_DISTRIBUTION_CHANNEL = DISTRIBUTION_CHANNEL_TOPIC + "*";
@Autowired(required = false)
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private EventBus eventBus;
/**
* consumes all {@link DistributedEvent}s posted on the {@link EventBus} and
* distribute them to the Redis server.
*
* @param event
* the distributed event posted on the {@link EventBus}
*/
@Subscribe
public void distribute(final DistributedEvent event) {
if (redisTemplate != null) {
if (!NODE_ID.equals(event.getNodeId())) {
logDistributingEvent(event, SEND_DISTRIBUTION_CHANNEL);
event.setOriginNodeId(NODE_ID);
redisTemplate.convertAndSend(SEND_DISTRIBUTION_CHANNEL, event);
}
} else {
logNotDistributingEvent(event, SEND_DISTRIBUTION_CHANNEL);
}
}
/**
* message listener callback method for the {@link MessageListenerAdapter}
* which calls the method in case a message is received from the Redis
* server of the type {@link DistributedEventWrapper}.
*
* @param event
* the {@link DistributedEventWrapper} event which was received
* by the Redis client
* @param channel
* the on which the event was received
*/
public void handleMessage(final DistributedEvent event, final String channel) {
LOGGER.trace("retrieving event from redis {} on channel {}, posting to the local event bus", event, channel);
if (!NODE_ID.equals(event.getOriginNodeId())) {
event.setNodeId(NODE_ID);
eventBus.post(event);
}
}
/**
* @return a collection of all topics which this Redis message listener
* wants to subscribe
*/
public Collection<Topic> getTopics() {
final List<Topic> topics = new ArrayList<Topic>();
topics.add(new PatternTopic(SUB_DISTRIBUTION_CHANNEL));
return topics;
}
private void logDistributingEvent(final Event event, final String channel) {
LOGGER.trace("distributing event {} from node {} to topic {}", event, NODE_ID, channel);
}
private void logNotDistributingEvent(final Event event, final String channel) {
LOGGER.debug("no redis template configured, event {} will not be distributed to channel {} from node {}", event,
channel, NODE_ID);
}
/**
* testing purposes.
*
* @param redisTemplate
* the redisTemplate to set
*/
void setRedisTemplate(final RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* testing purposes.
*
* @param eventBus
* the eventBus to set
*/
void setEventBus(final EventBus eventBus) {
this.eventBus = eventBus;
}
/**
* @return the nodeId
*/
static String getNodeId() {
return NODE_ID;
}
}

View File

@@ -0,0 +1,222 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache.eventbus;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Collection;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.eventbus.event.EntityEvent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.Topic;
import org.springframework.hateoas.Identifiable;
import com.google.common.eventbus.EventBus;
@RunWith(MockitoJUnitRunner.class)
public class EventDistributorTest {
@Mock
private RedisTemplate<String, Object> redisTemplateMock;
@Mock
private EventBus eventBusMock;
private EventDistributor underTest;
@Before
public void before() {
underTest = new EventDistributor();
underTest.setEventBus(eventBusMock);
underTest.setRedisTemplate(redisTemplateMock);
}
@Test
public void distributeDistributedEventSendsToRedis() {
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
underTest.distribute(event);
// origin node ID should be set by distributing the event
assertThat(event.getOriginNodeId()).isNotNull();
verify(redisTemplateMock).convertAndSend(anyString(), eq(event));
}
@Test
public void dontDistributeDistributedEventIfSameNode() {
final String knownNodeId = EventDistributor.getNodeId();
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
event.setNodeId(knownNodeId);
// test
underTest.distribute(event);
assertThat(event.getOriginNodeId()).isNull();
verify(redisTemplateMock, times(0)).convertAndSend(anyString(), eq(event));
}
@Test
public void handleDistributedMessageFromRedis() {
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
final String knownChannel = "someChannel";
underTest.handleMessage(event, knownChannel);
assertThat(event.getNodeId()).isEqualTo(EventDistributor.getNodeId());
verify(eventBusMock).post(eq(event));
}
@Test
public void handleDistributedMessageFilteredIfSameNodeId() {
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
final String knownChannel = "someChannel";
event.setOriginNodeId(EventDistributor.getNodeId());
underTest.handleMessage(event, knownChannel);
assertThat(event.getNodeId()).isNull();
verify(eventBusMock, times(0)).post(eq(event));
}
@Test
public void subscribedTopicsContains2PatternTopics() {
final Collection<Topic> topics = underTest.getTopics();
assertThat(topics).hasSize(1);
}
private class TestEntityEvent implements EntityEvent {
private String originNodeId;
private String nodeId;
private final String tenant;
private final MyEntity entity;
/**
* @param myEntity
*/
public TestEntityEvent(final MyEntity myEntity, final String tenant) {
this.entity = myEntity;
this.tenant = tenant;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.Event#getRevision()
*/
@Override
public long getRevision() {
return 0;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.NodeAware#getOriginNodeId()
*/
@Override
public String getOriginNodeId() {
return originNodeId;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.NodeAware#setOriginNodeId(
* java. lang.String)
*/
@Override
public void setOriginNodeId(final String originNodeId) {
this.originNodeId = originNodeId;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.NodeAware#getNodeId()
*/
@Override
public String getNodeId() {
return nodeId;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.NodeAware#setNodeId(java.
* lang. String)
*/
@Override
public void setNodeId(final String nodeId) {
this.nodeId = nodeId;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getEntity(java.
* lang. Class)
*/
@Override
public <E> E getEntity(final Class<E> entityClass) {
return entityClass.cast(entity);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getEntity()
*/
@Override
public Object getEntity() {
return entity;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getTenant()
*/
@Override
public String getTenant() {
return tenant;
}
}
private class MyEntity implements Identifiable<String> {
private final String id = "123";
/*
* (non-Javadoc)
*
* @see org.springframework.hateoas.Identifiable#getId()
*/
@Override
public String getId() {
return id;
}
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache.redis;
import static org.fest.assertions.api.Assertions.assertThat;
import org.eclipse.hawkbit.cache.RedisProperties;
import org.junit.Test;
public class RedisPropertiesTest {
@Test
public void setAndGetProps() {
final String knownHost = "bumlux";
final int knownPort = 1234;
final RedisProperties underTest = new RedisProperties();
underTest.setHost(knownHost);
underTest.setPort(knownPort);
assertThat(underTest.getHost()).isEqualTo(knownHost);
assertThat(underTest.getPort()).isEqualTo(knownPort);
}
}

50
hawkbit-core/pom.xml Executable file
View File

@@ -0,0 +1,50 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-core</artifactId>
<name>hawkBit :: Core</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Defines the polling time for the controllers in HH:MM:SS notation.
*
*
*
*/
@ConfigurationProperties(prefix = "hawkbit.controller")
public class ControllerPollProperties {
private String pollingTime = "00:05:00";
private String pollingOverdueTime = "00:05:00";
public String getPollingTime() {
return pollingTime;
}
public void setPollingTime(final String pollingTime) {
this.pollingTime = pollingTime;
}
public String getPollingOverdueTime() {
return pollingOverdueTime;
}
public void setPollingOverdueTime(final String pollingOverdue) {
this.pollingOverdueTime = pollingOverdue;
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit;
import java.io.IOException;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* This resource bundles using specified basenames, to resource loading. This
* MessageSource implementation supports more than 1 properties file with the
* same name. All properties files will be merged.
*
*
*
*/
public class DistributedResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
private static final Logger LOGGER = LoggerFactory.getLogger(DistributedResourceBundleMessageSource.class);
private static final String PROPERTIES_SUFFIX = ".properties";
private ResourceLoader resourceLoader;
/**
* Constructor to set Defaults. Default base name is classpath*:/messages.
* So all messages_ will be found.
*/
public DistributedResourceBundleMessageSource() {
setBasename("classpath*:/messages");
setDefaultEncoding("UTF-8");
setUseCodeAsDefaultMessage(true);
}
@Override
protected PropertiesHolder refreshProperties(final String filename, final PropertiesHolder propHolder) {
final Properties properties = new Properties();
long lastModified = -1;
if (!(resourceLoader instanceof ResourcePatternResolver)) {
LOGGER.warn(
"Resource Loader {} doensn't support getting multiple resources. Default properties mechanism will used",
resourceLoader.getClass().getName());
return super.refreshProperties(filename, propHolder);
}
try {
final Resource[] resources = ((ResourcePatternResolver) resourceLoader)
.getResources(filename + PROPERTIES_SUFFIX);
for (final Resource resource : resources) {
final String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, "");
final PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder);
properties.putAll(holder.getProperties());
if (lastModified < resource.lastModified()) {
lastModified = resource.lastModified();
}
}
} catch (final IOException ignored) {
LOGGER.warn("Resource with filname " + filename + " couldn't load", ignored);
}
return new PropertiesHolder(properties, lastModified);
}
@Override
public void setResourceLoader(final ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
super.setResourceLoader(resourceLoader);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.api;
import java.net.URL;
/**
* Resolve to reach the server url.
*
*
*
*/
public interface HostnameResolver {
/**
*
*
* @return
*/
URL resolveHostname();
}

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import java.io.InputStream;
import java.util.List;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
/**
* ArtifactRepository service interface.
*
*
*
*/
public interface ArtifactRepository {
/**
* Stores an artifact into the repository.
*
* @param tenant
* the tenant to store the artifact
* @param content
* the content to store
* @param filename
* the filename of the artifact
* @param contentType
* the content type of the artifact
* @return the stored artifact
* @throws ArtifactStoreException
* in case storing of the artifact was not successful
*/
DbArtifact store(final InputStream content, final String filename, final String contentType);
/**
* Stores an artifact into the repository.
*
* @param content
* the content to store
* @param filename
* the filename of the artifact
* @param contentType
* the content type of the artifact
* @param hash
* the hashes of the artifact to do hash-checks after storing the
* artifact, might be {@code null}
* @return the stored artifact
* @throws ArtifactStoreException
* in case storing of the artifact was not successful
* @throws HashNotMatchException
* in case {@code hash} is provided and not matching to the
* calculated hashes during storing
*/
DbArtifact store(final InputStream content, final String filename, final String contentType, DbArtifactHash hash);
/**
* Deletes an artifact by its ID.
*
* @param artifactId
* the ID of the artifact to delete
*/
void deleteById(final String artifactId);
/**
* Deletes an artifact by its SHA1 hash.
*
* @param sha1Hash
* the sha1-hash of the artifact to delete
*/
void deleteBySha1(final String sha1Hash);
/**
* Retrieves a {@link DbArtifact} from the store by it's SHA1 hash.
*
* @param sha1Hash
* the sha1-hash of the file to lookup.
* @return The artifact file object or {@code null} if no file exists.
*/
DbArtifact getArtifactBySha1(String sha1);
/**
* Retrieves a {@link DbArtifact} from the store by it's ID.
*
* @param id
* the ID of the artifact to retrieve
* @return The artifact file object or {@code null} if no file exists.
*/
DbArtifact getArtifactById(final String id);
/**
* Retrieves a list of {@link GridFSDBFile} from the store by all SHA1
* hashes.
*
* @param tenant
* the tenant to retrieve the artifacts from, ignore case.
* @param sha1Hashes
* the sha1-hashes of the files to lookup.
* @return list of artfiacts
*/
List<DbArtifact> getArtifactsBySha1(final List<String> sha1Hashes);
}

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
/**
* {@link ArtifactStoreException} is thrown in case storing of an artifact was
* not successful.
*
*
*
*/
public class ArtifactStoreException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Constructs a ArtifactStoreException with message and cause.
*
* @param message
* the message of the exception
* @param cause
* the cause of the exception
*/
public ArtifactStoreException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Constructs a ArtifactStoreException with message.
*
* @param message
* the message of the exception
*/
public ArtifactStoreException(final String message) {
super(message);
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
/**
* Thrown when provided hashes and hashes caluclated during storing are not
* matching.
*
*
*
*/
public class HashNotMatchException extends RuntimeException {
private static final long serialVersionUID = 1L;
public static final String SHA1 = "SHA-1";
public static final String MD5 = "MD5";
private final String hashFunction;
/**
* Constructs a HashNotMatchException with message and cause.
*
* @param message
* the message of the exception
* @param cause
* the cause of the exception
* @param hashFunction
* the hash function which caused this exception
*/
public HashNotMatchException(final String message, final Throwable cause, final String hashFunction) {
super(message, cause);
this.hashFunction = hashFunction;
}
/**
* Constructs a HashNotMatchException with message.
*
* @param message
* the message of the exception
* @param hashFunction
* the hash function which caused this exception
*/
public HashNotMatchException(final String message, final String hashFunction) {
super(message);
this.hashFunction = hashFunction;
}
/**
* @return the hashFunction
*/
public String getHashFunction() {
return hashFunction;
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository.model;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Database representation of artifact.
*
*
*
*
*/
public class DbArtifact {
private String artifactId;
private DbArtifactHash hashes;
private Long size;
private String contentType;
private InputStream fileInputStream;
private OutputStream fileOutputStream;
public void setArtifactId(final String artifactId) {
this.artifactId = artifactId;
}
public String getArtifactId() {
return artifactId;
}
public DbArtifactHash getHashes() {
return hashes;
}
public void setHashes(final DbArtifactHash hashes) {
this.hashes = hashes;
}
public Long getSize() {
return size;
}
public void setSize(final Long size) {
this.size = size;
}
public void setContentType(final String contentType) {
this.contentType = contentType;
}
public String getContentType() {
return contentType;
}
public void setFileInputStream(final InputStream fileInputStream) {
this.fileInputStream = fileInputStream;
}
public InputStream getFileInputStream() {
return fileInputStream;
}
public OutputStream getFileOutputStream() {
return fileOutputStream;
}
public void setFileOutputStream(final OutputStream fileOutputStream) {
this.fileOutputStream = fileOutputStream;
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository.model;
/**
* Database representation of artifact hash.
*
*
*
*
*/
public class DbArtifactHash {
private String sha1;
private String md5;
/**
* Constructor.
*
* @param sha1
* the sha1 hash
* @param md5
* the md5 hash
*/
public DbArtifactHash(final String sha1, final String md5) {
super();
this.sha1 = sha1;
this.md5 = md5;
}
public void setSha1(final String sha1) {
this.sha1 = sha1;
}
public void setMd5(final String md5) {
this.md5 = md5;
}
public String getSha1() {
return sha1;
}
public String getMd5() {
return md5;
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
/**
* Cache Constants.
*
*
*
*/
public final class CacheConstants {
/**
* Constant for download cache id.
*/
public static final String DOWNLOAD_ID_CACHE = "DowonloadIdCache";
private CacheConstants() {
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
/**
* Cache Object for download a Artifact.
*
*
*
*/
public class DownloadArtifactCache {
private final DownloadType downloadType;
private final String id;
/**
* Constructor.
*
* @param downloadType
* the type for searching the artifact.
* @param id
* the searching id e.g. sha1, md5
*/
public DownloadArtifactCache(final DownloadType downloadType, final String id) {
super();
this.downloadType = downloadType;
this.id = id;
}
public String getId() {
return id;
}
public DownloadType getDownloadType() {
return downloadType;
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
/**
* The type of the id which is saved.
*
*
*
*/
public enum DownloadType {
BY_SHA1
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
/**
*
*
*/
public interface TenancyCacheManager extends CacheManager {
/**
* A direct access for retrieving the cache without including the current
* tenant key. This is necessary e.g. for retrieving caches not for the
* current tenant.
*
* @param name
* the name of the cache to retrieve directly
* @return the cache associated with the name without tenancy separation
*/
Cache getDirectCache(String name);
/**
* Evicts all caches for a given tenant. All caches under a certain tenant
* gets evicted.
*
* @param tenant
* the tenant to evict caches
*/
void evictCaches(final String tenant);
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.cache;
import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
/**
* A {@link CacheManager} delegator which wraps the
* {@link CacheManager#getCache(String)} and
* {@link CacheManager#getCacheNames()} to include the
* {@link TenantAware#getCurrentTenant()} when accessing a cache, so caches are
* seperated.
*
* Additionally it also provide functionality to retrieve all caches overall
* tenants at once, for monitoring and system access.
*
*
*
*
*/
public class TenantAwareCacheManager implements TenancyCacheManager {
private static final String TENANT_CACHE_DELIMITER = "|";
private final CacheManager delegate;
private final TenantAware tenantAware;
/**
* @param delegate
* the {@link CacheManager} to delegate to.
* @param tenantAware
* the tenant aware to retrieve the current tenant
*/
public TenantAwareCacheManager(final CacheManager delegate, final TenantAware tenantAware) {
this.delegate = delegate;
this.tenantAware = tenantAware;
}
@Override
public Cache getCache(final String name) {
final String currentTenant = tenantAware.getCurrentTenant().toUpperCase();
if (currentTenant.contains(TENANT_CACHE_DELIMITER)) {
return null;
}
return delegate.getCache(currentTenant + TENANT_CACHE_DELIMITER + name);
}
@Override
public Collection<String> getCacheNames() {
final String currentTenant = tenantAware.getCurrentTenant().toUpperCase();
if (currentTenant.contains(TENANT_CACHE_DELIMITER)) {
return Collections.emptyList();
}
return getCacheNames(currentTenant);
}
/**
* A direct access for retrieving all cache names overall tenants.
*
* @return all cache names without tenant check
*/
public Collection<String> getDirectCacheNames() {
return delegate.getCacheNames();
}
@Override
public Cache getDirectCache(final String name) {
return delegate.getCache(name);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.cache.TenancyCacheManager#evictCaches(java.
* lang. String)
*/
@Override
public void evictCaches(final String tenant) {
getCacheNames(tenant)
.forEach(cachename -> delegate.getCache(tenant + TENANT_CACHE_DELIMITER + cachename).clear());
}
private Collection<String> getCacheNames(final String tenant) {
final String tenantWithDelimiter = tenant + TENANT_CACHE_DELIMITER;
return delegate.getCacheNames().parallelStream().filter(cacheName -> cacheName.startsWith(tenantWithDelimiter))
.map(cacheName -> cacheName.substring(tenantWithDelimiter.length())).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
/**
* Catches all dead events by means of events with no fitting subscriber on the
* bus.
*
*
*
*/
@EventSubscriber
public class DeadEventListener {
private static final Logger LOG = LoggerFactory.getLogger(DeadEventListener.class);
/**
* Listens for dead vents and prints them into LOG.
*
* @param event
* to print
*/
@Subscribe
public void listen(final DeadEvent event) {
LOG.info("DeadEvent on bus! {}", event.getEvent());
}
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
/**
* An {@link BeanPostProcessor} implementation which registers all beans as a
* event bus subscriber if the classes are annotated with
* {@link EventSubscriber} and have at least one method annotated with the
* guava's {@link Subscribe} annoation.
*
*
*/
public class EventBusSubscriberProcessor implements BeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(EventBusSubscriberProcessor.class);
@Autowired
private EventBus eventBus;
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#
* postProcessBeforeInitialization (java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) {
return bean;
}
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#
* postProcessAfterInitialization( java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) {
final Class<? extends Object> beanClass = bean.getClass();
final EventSubscriber eventSubscriber = beanClass.getAnnotation(EventSubscriber.class);
if (eventSubscriber != null) {
LOGGER.trace("Found bean {} with {} annotation ", bean.getClass().getName(),
EventSubscriber.class.getSimpleName());
final Method[] declaredMethods = beanClass.getDeclaredMethods();
for (final Method method : declaredMethods) {
final Subscribe subscriber = method.getAnnotation(Subscribe.class);
if (subscriber != null) {
LOGGER.trace("Found method {} for bean {} with {} annotation", method.getName(),
bean.getClass().getName(), Subscribe.class.getSimpleName());
eventBus.register(bean);
return bean;
}
}
}
if (eventSubscriber != null) {
LOGGER.debug("Found bean {} with {} annotation but without any method with necessary {} annotation",
bean.getClass().getName(), EventSubscriber.class.getSimpleName(), Subscribe.class.getSimpleName());
}
return bean;
}
/**
* package private setter for testing purposes.
*
* @param eventBus
* the event bus
*/
void setEventBus(final EventBus eventBus) {
this.eventBus = eventBus;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.stereotype.Service;
/**
* Marks an class as an event subscriber to listen on event on the event bus
* without explicit register this class to the event bus.
*
* <pre>
* &#064;EventSubscriber
* public class MySubscriber {
* &#064;Subscribe
* public void listen(MyEvent event) {
* System.out.println(&quot;event received: &quot; + event);
* }
* }
* </pre>
*
*
*
*
*/
@Target({ TYPE })
@Retention(RUNTIME)
@Service
public @interface EventSubscriber {
}

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* An abstract class of the {@link DistributedEvent} implementation which holds
* all the necessary information of distributing events to other nodes.
*
*
*
*
*/
public abstract class AbstractDistributedEvent implements DistributedEvent {
private static final long serialVersionUID = 1L;
private final long revision;
private String originNodeId;
private String nodeId;
private final String tenant;
/**
*
* @param revision
* the revision of this event
* @param tenant
* the tenant for this event
*/
protected AbstractDistributedEvent(final long revision, final String tenant) {
this.revision = revision;
this.tenant = tenant;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.NodeAware#setOriginNodeId(java.
* lang. String)
*/
@Override
public void setOriginNodeId(final String originNodeId) {
this.originNodeId = originNodeId;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.NodeAware#setNodeId(java.lang.
* String)
*/
@Override
public void setNodeId(final String nodeId) {
this.nodeId = nodeId;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.NodeAware#getOriginNodeId()
*/
@Override
public String getOriginNodeId() {
return this.originNodeId;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.NodeAware#getNodeId()
*/
@Override
public String getNodeId() {
return this.nodeId;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.Event#getRevision()
*/
@Override
public long getRevision() {
return revision;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getTenant()
*/
@Override
public String getTenant() {
return tenant;
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import java.net.URI;
/**
* Event that gets sent when the assignment of a distribution set to a target
* gets canceled.
*
*
*
*/
public class CancelTargetAssignmentEvent {
private final String controllerId;
private final Long actionId;
private final URI targetAdress;
/**
* Creates a new {@link CancelTargetAssignmentEvent}.
*
* @param controllerId
* the ID of the controller
* @param actionId
* the action id of the assignment
* @param targetAdress
* the targetAdress of the target
*/
public CancelTargetAssignmentEvent(final String controllerId, final Long actionId, final URI targetAdress) {
this.controllerId = controllerId;
this.actionId = actionId;
this.targetAdress = targetAdress;
}
/**
* @return the action id of the assignment
*/
public Long getActionId() {
return actionId;
}
/**
* @return the controllerId of the Target which has been assigned to the
* distribution set
*/
public String getControllerId() {
return controllerId;
}
/**
*
* @return the targetr adress.
*/
public URI getTargetAdress() {
return targetAdress;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "TargetAssignDistributionSetEvent [targetAdress=" + targetAdress + ", controllerId=" + controllerId
+ ", actionId=" + actionId + "]";
}
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* Events to be published to refresh data on UI.
*
*
*
*
*/
public enum CustomEvents {
TARGETS_CREATED_EVENT,
DISTRIBUTION_CREATED_EVENT
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import java.io.Serializable;
/**
* Marks an event to as an distributed event which will be distributed to other
* nodes.
*
*
*
*
*/
public interface DistributedEvent extends Event, NodeAware, Serializable {
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* Event that contains an updated download progress for a given Action.
*
*
*
*
*/
public class DownloadProgressEvent extends AbstractDistributedEvent {
private static final long serialVersionUID = 1L;
private final Long statusId;
private final int progressPercent;
/**
* Constructor.
*
* @param tenant
* the tenant for this event
* @param statusId
* of {@link UpdateActionStatus}
* @param progressPercent
* number (1-100)
*/
public DownloadProgressEvent(final String tenant, final Long statusId, final int progressPercent) {
// the revision of the DownloadProgressEvent is just equal the
// progressPercentage due the
// percentage is going from 0 to 100.
super(statusId, tenant);
this.statusId = statusId;
this.progressPercent = progressPercent;
}
/**
* @return the statusId
*/
public Long getStatusId() {
return statusId;
}
/**
* @return the progressPercent
*/
public int getProgressPercent() {
return progressPercent;
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* An event interface which declares event types that an entity has been
* changed. {@link EntityEvent}s should not implement {@link DistributedEvent}
* due all {@link EntityEvent}s will be distributed to other nodes.
*
* Retrieving an {@link EntityEvent} on a different node the entity will be load
* lazy.
*
*
*
*
*/
public interface EntityEvent extends Event, NodeAware {
/**
* A typesafe way to retrieve the entity from the event, which might be
* loaded lazy in case the event has been distributed from another node.
*
* @param entityClass
* the class of the entity to retrieve
* @return the entity might be lazy loaded. Might be {@code null} in case
* the entity e.g. is queried lazy on a different node and has been
* already deleted from the database
* @throws ClassCastException
* in case a wrong entity class is given for this event
*/
<E> E getEntity(Class<E> entityClass);
/**
* An unsafe way to retrieve the entity from this event which might be
* loaded lazy in case the event has been distributed from another node.
*
* @return the entity might be lazy loaded. Might be {@code null} in case
* the entity e.g. is queried lazy on a different node and has been
* already deleted from the database
*/
Object getEntity();
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* An event declaration which holds an revision for each event so consumers have
* the chance to know if they might already retrieved an newer event.
*
*
*
*
*/
public interface Event {
/**
* @return the revision of this event which should be increment or each new
* event in case the event have a causalität. Might be {@code -1} in
* case the events does not provide any revision.
*/
long getRevision();
/**
* @return the tenant of the entity.
*/
String getTenant();
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* Interface to be implemented by events which are distributed to other nodes
* where it's necessary to know which node sent an event and which nodes
* retrieved the event from which node. Using the EventDistributor the
* implementation only needs to contain the necessary node IDs, setting and
* retrieving the node IDs is transparent by the EventDistributor so the event
* distributor does not hang in an endless loop of distributing the events which
* self posted.
*
*
*
*
*/
public interface NodeAware {
/**
* @return the origin node ID in case the event has been forwarded to other
* nodes or {@code null} if the event has not been forwarded to
* other nodes
*/
String getOriginNodeId();
/**
* @param originNodeId
* the origin node ID where this event has been sent originally
*/
void setOriginNodeId(String originNodeId);
/**
* @return the node ID which is processing this event locally, set by the
* EventDistributor so he can determine if this event has been
* received by another node and is processing on the current node.
*/
String getNodeId();
/**
* @param nodeId
* the ID of the node this event is processing.
*/
void setNodeId(String nodeId);
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
*
*
*
*/
public class TargetDeletedEvent extends AbstractDistributedEvent {
private static final long serialVersionUID = 1L;
private final long targetId;
/**
* @param tenant
* the tenant for this event
* @param targetId
* the ID of the target which has been deleted
*/
public TargetDeletedEvent(final String tenant, final long targetId) {
super(-1, tenant);
this.targetId = targetId;
}
/**
* @return the targetId
*/
public long getTargetId() {
return targetId;
}
}

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