-
-### Step 2: Begin of update process ###
-Afterwards an distribution set can be assigned to the created thing. This can be done on the UI or with the REST API. To start the update process the SP update server sends a message over the registered queue back to the connecting service.
-
-Inside the message send from the Sp update service the header specifies the target which has to be updated. Therefor the *tenent* and the unique *thingId* are used. The body contains an identifier which defines the update process (*actionId*) and the information of the software modules. The *actionId* is important to send the progress of the update process to the server. The information about the software describes all modules which are included in the current update process, their identifier, their type, their version and the corresponding artifacts with the urls where the target can download the artifacts.
-
-**Header**
-
-
-### Step 3: Sending the state update process ###
-
-During the update progress the connected service can inform the sp update service about the progress and eventual warnings or errors. Therefore the *actionId* and the *softwareModuleId* are used to specify for which process and for which software module the progress update is for. Additionally in the Body there is a field *actionStatus*. This field is used to describe the type of the progress update and there are several allowed values:
-
-- **RETRIEVED**: general acknowledge message
-- **DOWNLOAD**: describes all messages related to the download prorgess
-- **RUNNING**:
-- **WARNING**: A smaller problem which influence the update process, but no need for stop it.
-- **ERROR**: A big issues which not allows continuing the update process.
-- **FINISHED**: Successfully finished the update process.
-- **CANCELED**: Response to a cancel request, when the process is canceled.
-- **CANCEL_REJECTED**: Response to a cancel request, when canceling is not possible.
-
-The update process is marked as running until the connected Server sends a **FINISHED**, an **ERROR** or a **CANCELED** message. Additionally the connected service can put some human readable information into the message, which describes the state of the action more detailed.
-
-**Header**
-
-{
-"actionId":115,
-"softwareModuleId":3,
-"actionStatus":"DOWNLOAD",
-"message":["The download has started"]
-}
-
-
-## Cancel process ##
-
-During the update process the SP update server has the chance to cancel an update. Therefore it sends the following message. This message includes the *tenant* and the corresponding *thingId* on theheader.
-
-**Header**
-
-
-Receiving this message the connected service has to decide if an update can be canceled or not. If it is the update has to be canceled and a **CANCELED** message has to be sent to the SP server. Otherwise a **CANCEL_REJECTED** message has to be sent.
-
- 
\ No newline at end of file
diff --git a/MIGRATION.md b/MIGRATION.md
new file mode 100644
index 000000000..eacce864d
--- /dev/null
+++ b/MIGRATION.md
@@ -0,0 +1,9 @@
+# hawkBit Migration Guides
+## Release 0.2
+### Configuration Property changes
+- hawkbit.server.controller._ have changed to hawkbit.server.ddi._
+- info.build._ have changed to hawkbit.server.build._
+- hawkbit.server.demo._ have changed to hawkbit.server.ui.demo._
+- hawkbit.server.email.support has changed to hawkbit.server.ui.links.support
+- hawkbit.server.email.request.account has changed to hawkbit.server.ui.links.requestAccount
+- hawkbit.server.im.login.url has changed to hawkbit.server.ui.links.userManagement
diff --git a/README.md b/README.md
index e824971e9..fb649633b 100644
--- a/README.md
+++ b/README.md
@@ -4,21 +4,43 @@ Build: [](htt
[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.
+# Documentation
+
+see [hawkBit Wiki](https://github.com/eclipse/hawkbit/wiki)
+
# Contact us
* Want to chat with the team behind hawkBit? [](https://gitter.im/eclipse/hawkbit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+* Having issues with hawkBit? Open a [GitHub issue](https://github.com/eclipse/hawkbit/issues).
* You can also check out our [Project Homepage](https://projects.eclipse.org/projects/iot.hawkbit) for further contact options.
-# Compile
-```
-mvn install
-```
-
-# Run and use
+# Compile, Run and Getting Started
We are not providing an off the shelf installation ready hawkBit update server. However, we recommend to check out the [Example Application](examples/hawkbit-example-app) for a runtime ready Spring Boot based update server that is empowered by hawkBit.
+#### Clone and build hawkBit
+```
+$ git clone https://github.com/eclipse/hawkbit.git
+$ cd hawkbit
+$ mvn clean install
+```
+#### Start hawkBit example app
+[Example Application](examples/hawkbit-example-app)
+```
+$ java -jar ./examples/hawkbit-example-app/target/hawkbit-example-app-#version#.jar
+```
+#### Start hawkBit device simulator
+[Device Simulator](examples/hawkbit-device-simulator)
+```
+$ java -jar ./examples/hawkbit-device-simulator/target/hawkbit-device-simulator-#version#.jar
+```
+#### Generate Getting Started data
+[Example Management API Client](examples/hawkbit-mgmt-api-client)
+```
+$ java -jar ./examples/hawkbit-mgmt-api-client/target/hawkbit-mgmt-api-client-#version#.jar
+```
+
# Releases and Roadmap
* We are currently working on the first formal release under the Eclipse banner: 0.1 (see [Release 0.1 branch](https://github.com/eclipse/hawkbit/tree/release-train-0.1)).
@@ -29,12 +51,6 @@ We are not providing an off the shelf installation ready hawkBit update server.
* And of course tons of usability improvements and bug fixes.
-## 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.
@@ -49,9 +65,3 @@ We are not providing an off the shelf installation ready hawkBit update server.
`hawkbit-rest-resource` : HTTP REST endpoints for the Management and the Direct Device API.
`hawkbit-ui` : 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)](DMFA.md)
diff --git a/examples/hawkbit-device-simulator/README.md b/examples/hawkbit-device-simulator/README.md
index 610a1d256..869f80511 100644
--- a/examples/hawkbit-device-simulator/README.md
+++ b/examples/hawkbit-device-simulator/README.md
@@ -13,7 +13,7 @@ run org.eclipse.hawkbit.simulator.DeviceSimulator
## Notes
-The simulator has user authentication enabled by default. Default credentials:
+The simulator has user authentication enabled in **cloud profile**. Default credentials:
* username : admin
* passwd : admin
@@ -29,8 +29,6 @@ The UI can be accessed via the URL:
http://localhost:8083
```
-`Basic Authentication Credentials are admin / admin`
-


@@ -45,6 +43,10 @@ Optional parameters:
* name : name prefix simulated devices (default: "dmfSimulated"), followed by counter
* amount : number of simulated devices (default: 20, capped at: 4000)
* tenant : in a multi-tenenat ready hawkBit installation (default: "DEFAULT")
+* api : the API which should be used for the simulated device either `dmf` or `ddi` (default: "ddi")
+* endpoint : URL which defines the hawkbit DDI base endpoint (deffault: "http://localhost:8080")
+* polldelay : number in milliseconds of the delay when DDI simulated devices should poll the endpoint (default: "30")
+* gatewaytoken : an hawkbit gateway token to be used in case hawkbit does not allow anonymous access for DDI devices (default: "")
Example: for 20 simulated devices (default)
@@ -56,3 +58,8 @@ Example: for 10 simulated devices that start with the name prefix "activeSim":
```
http://localhost:8083/start?amount=10&name=activeSim
```
+
+Example: for 5 simulated devices that start with the name prefix "ddi" using the Direct Device Integration API (http):
+```
+http://localhost:8083/start?amount=5&name=ddi?api=ddi
+```
diff --git a/examples/hawkbit-device-simulator/pom.xml b/examples/hawkbit-device-simulator/pom.xml
index 9a84d13f5..a2575e9db 100644
--- a/examples/hawkbit-device-simulator/pom.xml
+++ b/examples/hawkbit-device-simulator/pom.xml
@@ -100,7 +100,6 @@
com.google.guavaguava
- 19.0com.netflix.feign
@@ -116,13 +115,18 @@
com.jayway.jsonpathjson-path
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+ com.vaadinvaadin-bom
- 7.5.5
+ 7.6.3pomimport
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java
index 1417c3153..b58d3a413 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java
@@ -10,7 +10,6 @@ package org.eclipse.hawkbit.simulator;
import java.util.concurrent.ScheduledExecutorService;
-import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
import org.eclipse.hawkbit.simulator.http.ControllerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,28 +81,27 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId);
final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version");
currentActionId = actionId;
- deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, new UpdaterCallback() {
- @Override
- public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
- switch (device.getResponseStatus()) {
- case SUCCESSFUL:
- controllerResource.postSuccessFeedback(getTenant(), getId(), actionId);
- break;
- case ERROR:
- controllerResource.postErrorFeedback(getTenant(), getId(), actionId);
- break;
- default:
- throw new IllegalStateException("simulated device has an unknown response status + "
- + device.getResponseStatus());
- }
- currentActionId = null;
+ deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, (device, actionId1) -> {
+ switch (device.getResponseStatus()) {
+ case SUCCESSFUL:
+ controllerResource.postSuccessFeedback(getTenant(), getId(),
+ actionId1);
+ break;
+ case ERROR:
+ controllerResource.postErrorFeedback(getTenant(), getId(),
+ actionId1);
+ break;
+ default:
+ throw new IllegalStateException(
+ "simulated device has an unknown response status + " + device.getResponseStatus());
}
+ currentActionId = null;
});
}
} catch (final PathNotFoundException e) {
// href might not be in the json response, so ignore
// exception here.
- LOGGER.trace("Response does not contain a deploymentbase href link, ignoring.");
+ LOGGER.trace("Response does not contain a deploymentbase href link, ignoring.", e);
}
}
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java
index 6e93b7d04..b8b5011aa 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java
@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.simulator;
+import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -41,7 +42,7 @@ public class DeviceSimulatorUpdater {
/**
* Starting an simulated update process of an simulated device.
- *
+ *
* @param tenant
* the tenant of the device
* @param id
@@ -61,12 +62,13 @@ public class DeviceSimulatorUpdater {
device.setProgress(0.0);
device.setSwversion(swVersion);
eventbus.post(new InitUpdate(device));
+
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback),
- 2000, TimeUnit.MILLISECONDS);
+ 2_000, TimeUnit.MILLISECONDS);
}
private static final class DeviceSimulatorUpdateThread implements Runnable {
- private static final Random rndSleep = new Random();
+ private static final Random rndSleep = new SecureRandom();
private final AbstractSimulatedDevice device;
private final SpSenderService spSenderService;
@@ -74,9 +76,8 @@ public class DeviceSimulatorUpdater {
private final EventBus eventbus;
private final UpdaterCallback callback;
- private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device,
- final SpSenderService spSenderService, final long actionId, final EventBus eventbus,
- final UpdaterCallback callback) {
+ private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device, final SpSenderService spSenderService,
+ final long actionId, final EventBus eventbus, final UpdaterCallback callback) {
this.device = device;
this.spSenderService = spSenderService;
this.actionId = actionId;
@@ -89,8 +90,9 @@ public class DeviceSimulatorUpdater {
final double newProgress = device.getProgress() + 0.2;
device.setProgress(newProgress);
if (newProgress < 1.0) {
- threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus,
- callback), rndSleep.nextInt(3000), TimeUnit.MILLISECONDS);
+ threadPool.schedule(
+ new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback),
+ rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS);
} else {
callback.updateFinished(device, actionId);
}
@@ -102,7 +104,7 @@ public class DeviceSimulatorUpdater {
* Callback interface which is called when the simulated update process has
* been finished and the caller of starting the simulated update process can
* send the result to the hawkbit update server back.
- *
+ *
* @author Michael Hirsch
*
*/
@@ -111,7 +113,7 @@ public class DeviceSimulatorUpdater {
/**
* Callback method to indicate that the simulated update process has
* been finished.
- *
+ *
* @param device
* the device which has been updated
* @param actionId
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java
index 81acf897e..9c78ec65a 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java
@@ -16,6 +16,8 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -24,13 +26,15 @@ import com.google.common.eventbus.EventBus;
/**
* Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every
* second.
- *
+ *
* @author Michael Hirsch
*
*/
@Component
public class NextPollTimeController {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NextPollTimeController.class);
+
private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private static final ExecutorService pollService = Executors.newFixedThreadPool(1);
@@ -58,14 +62,9 @@ public class NextPollTimeController {
if (nextCounter < 0) {
if (device instanceof DDISimulatedDevice) {
try {
- pollService.submit(new Runnable() {
- @Override
- public void run() {
- ((DDISimulatedDevice) device).poll();
- }
- });
- } catch (final Exception e) {
-
+ pollService.submit(() -> ((DDISimulatedDevice) device).poll());
+ } catch (final IllegalStateException e) {
+ LOGGER.trace("Device could not be polled", e);
}
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
}
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java
index 6f94bd319..c1f358d89 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java
@@ -8,9 +8,13 @@
*/
package org.eclipse.hawkbit.simulator;
+import java.net.MalformedURLException;
+import java.net.URL;
+
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@@ -42,19 +46,49 @@ public class SimulationController {
* the amount of devices to be created
* @param tenant
* the tenant to create the device to
+ * @param api
+ * the api-protocol to be used either {@code dmf} or {@code ddi}
+ * @param endpoint
+ * the URL endpoint to be used of the hawkbit-update-server for
+ * DDI devices
+ * @param pollDelay
+ * number of delay in milliseconds to delay polling of DDI
+ * devices
+ * @param gatewayToken
+ * the hawkbit-update-server gatwaytoken in case authentication
+ * is enforced in hawkbit
* @return a response string that devices has been created
+ * @throws MalformedURLException
*/
@RequestMapping("/start")
- String start(@RequestParam(value = "name", defaultValue = "dmfSimulated") final String name,
+ ResponseEntity start(@RequestParam(value = "name", defaultValue = "simulated") final String name,
@RequestParam(value = "amount", defaultValue = "20") final int amount,
- @RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
+ @RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant,
+ @RequestParam(value = "api", defaultValue = "dmf") final String api,
+ @RequestParam(value = "endpoint", defaultValue = "http://localhost:8080") final String endpoint,
+ @RequestParam(value = "polldelay", defaultValue = "30") final int pollDelay,
+ @RequestParam(value = "gatewaytoken", defaultValue = "") final String gatewayToken)
+ throws MalformedURLException {
+
+ final Protocol protocol;
+ switch (api.toLowerCase()) {
+ case "dmf":
+ protocol = Protocol.DMF_AMQP;
+ break;
+ case "ddi":
+ protocol = Protocol.DDI_HTTP;
+ break;
+ default:
+ return ResponseEntity.badRequest().body("query param api only allows value of 'dmf' or 'ddi'");
+ }
for (int i = 0; i < amount; i++) {
final String deviceId = name + i;
- repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, Protocol.DMF_AMQP));
+ repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint),
+ gatewayToken));
spSenderService.createOrUpdateThing(tenant, deviceId);
}
- return "Updated " + amount + " DMF connected targets!";
+ return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");
}
}
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java
index ff9762c5d..f9e6ab23d 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpProperties.java
@@ -9,21 +9,35 @@
package org.eclipse.hawkbit.simulator.amqp;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
/**
* Bean which holds the necessary properties for configuring the AMQP
* connection.
*
- *
- *
*/
+@Component
@ConfigurationProperties("hawkbit.device.simulator.amqp")
public class AmqpProperties {
+ /**
+ * Queue for receiving DMF messages from update server.
+ */
private String receiverConnectorQueueFromSp;
+
+ /**
+ * Exchange for sending DMF messages to update server.
+ */
private String senderForSpExchange;
+ /**
+ * Simulator dead letter queue.
+ */
private String deadLetterQueue;
+
+ /**
+ * Simulator dead letter exchange.
+ */
private String deadLetterExchange;
public String getReceiverConnectorQueueFromSp() {
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java
index 6f0ac732e..1c314d56f 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java
@@ -13,11 +13,8 @@ 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.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater;
-import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
@@ -139,31 +136,22 @@ public class SpReceiverService extends ReceiverService {
DownloadAndUpdateRequest.class);
final Long actionId = downloadAndUpdateRequest.getActionId();
- try {
- Thread.sleep(1_000);
- } catch (final InterruptedException e) {
- LOGGER.error("Sleep interrupted", e);
- }
-
- spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
- "device Simulator retrieved update request. proceeding with simulation.", actionId);
- deviceUpdater.startUpdate(tenant, thingId, actionId, downloadAndUpdateRequest.getSoftwareModules().get(0)
- .getModuleVersion(), new UpdaterCallback() {
- @Override
- public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
- switch (device.getResponseStatus()) {
- case SUCCESSFUL:
- spSenderService.finishUpdateProcess(new SimulatedUpdate(device.getTenant(), device.getId(),
- actionId), "Simulation complete!");
- break;
- case ERROR:
- spSenderService.finishUpdateProcessWithError(new SimulatedUpdate(device.getTenant(),
- device.getId(), actionId), "Simulation complete with error!");
- break;
- default:
- break;
- }
- }
- });
+ deviceUpdater.startUpdate(tenant, thingId, actionId,
+ downloadAndUpdateRequest.getSoftwareModules().get(0).getModuleVersion(), (device, actionId1) -> {
+ switch (device.getResponseStatus()) {
+ case SUCCESSFUL:
+ spSenderService.finishUpdateProcess(
+ new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
+ "Simulation complete!");
+ break;
+ case ERROR:
+ spSenderService.finishUpdateProcessWithError(
+ new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
+ "Simulation complete with error!");
+ break;
+ default:
+ break;
+ }
+ });
}
}
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java
index 1400ec0e1..e337e86e7 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java
@@ -15,16 +15,15 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.vaadin.data.Property.ValueChangeEvent;
-import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.Property;
+import com.vaadin.data.Validator;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.validator.NullValidator;
import com.vaadin.data.validator.RangeValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.server.FontAwesome;
+import com.vaadin.server.Resource;
import com.vaadin.ui.Button;
-import com.vaadin.ui.Button.ClickEvent;
-import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TextField;
@@ -44,6 +43,15 @@ public class GenerateDialog extends Window {
private final FormLayout formLayout = new FormLayout();
+ private final TextField namePrefixTextField;
+ private final TextField amountTextField;
+ private final TextField tenantTextField;
+ private final TextField pollDelayTextField;
+ private final TextField pollUrlTextField;
+ private final TextField gatewayTokenTextField;
+ private final OptionGroup protocolGroup;
+ private final Button buttonOk;
+
/**
* Creates a new pop window for setting the configuration of simulating
* devices.
@@ -57,97 +65,43 @@ public class GenerateDialog extends Window {
formLayout.setSpacing(true);
formLayout.setMargin(true);
- final TextField tf1 = new TextField("name prefix", "dmfSimulated");
- tf1.setIcon(FontAwesome.INFO);
- tf1.setRequired(true);
- tf1.addValidator(new NullValidator("Must be given", false));
+ namePrefixTextField = createRequiredTextfield("name prefix", "dmfSimulated", FontAwesome.INFO,
+ new NullValidator("Must be given", false));
- final TextField tf2 = new TextField("amount", new ObjectProperty(10));
- tf2.setIcon(FontAwesome.GEAR);
- tf2.setRequired(true);
- tf2.addValidator(new RangeValidator("Must be between 1 and 30000", Integer.class, 1, 30000));
+ amountTextField = createRequiredTextfield("amount", new ObjectProperty(10), FontAwesome.GEAR,
+ new RangeValidator("Must be between 1 and 30000", Integer.class, 1, 30000));
- final TextField tf3 = new TextField("tenant", "default");
- tf3.setIcon(FontAwesome.USER);
- tf3.setRequired(true);
- tf3.addValidator(new NullValidator("Must be given", false));
+ tenantTextField = createRequiredTextfield("tenant", "default", FontAwesome.USER,
+ new NullValidator("Must be given", false));
- final TextField tf4 = new TextField("poll delay (sec)", new ObjectProperty(10));
- tf4.setIcon(FontAwesome.CLOCK_O);
- tf4.setRequired(true);
- tf4.setVisible(false);
- tf4.addValidator(new RangeValidator("Must be between 1 and 60", Integer.class, 1, 60));
+ pollDelayTextField = createRequiredTextfield("poll delay (sec)", new ObjectProperty(10),
+ FontAwesome.CLOCK_O, new RangeValidator("Must be between 1 and 60", Integer.class, 1, 60));
+ pollDelayTextField.setVisible(false);
- final TextField tf5 = new TextField("base poll URL endpoint", "http://localhost:8080");
- tf5.setColumns(50);
- tf5.setIcon(FontAwesome.FLAG_O);
- tf5.setRequired(true);
- tf5.setVisible(false);
- tf5.addValidator(new RegexpValidator(
- "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "is not an URL"));
+ pollUrlTextField = createRequiredTextfield("base poll URL endpoint", "http://localhost:8080",
+ FontAwesome.FLAG_O, new RegexpValidator(
+ "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "is not an URL"));
+ pollUrlTextField.setColumns(50);
+ pollUrlTextField.setVisible(false);
- final TextField tf6 = new TextField("gateway token", "");
- tf6.setColumns(50);
- tf6.setIcon(FontAwesome.FLAG_O);
- tf6.setRequired(true);
- tf6.setVisible(false);
+ gatewayTokenTextField = createRequiredTextfield("gateway token", "", FontAwesome.FLAG_O, null);
+ gatewayTokenTextField.setColumns(50);
+ gatewayTokenTextField.setVisible(false);
- final OptionGroup protocolGroup = new OptionGroup("Simulated Device Protocol");
- protocolGroup.addItem(Protocol.DMF_AMQP);
- protocolGroup.addItem(Protocol.DDI_HTTP);
- protocolGroup.setItemCaption(Protocol.DMF_AMQP, "Device Management Federation API (AMQP push)");
- protocolGroup.setItemCaption(Protocol.DDI_HTTP, "Direct Device Interface (HTTP poll)");
- protocolGroup.setNullSelectionAllowed(false);
- protocolGroup.select(Protocol.DMF_AMQP);
- protocolGroup.addValueChangeListener(new ValueChangeListener() {
- private static final long serialVersionUID = 1L;
+ protocolGroup = createProtocolGroup();
+ buttonOk = createOkButton(callback);
- @Override
- public void valueChange(final ValueChangeEvent event) {
- if (event.getProperty().getValue().equals(Protocol.DDI_HTTP)) {
- tf4.setVisible(true);
- tf5.setVisible(true);
- tf6.setVisible(true);
- } else {
- tf4.setVisible(false);
- tf5.setVisible(false);
- tf6.setVisible(false);
- }
- }
- });
+ namePrefixTextField.addValueChangeListener(event -> checkValid());
+ amountTextField.addValueChangeListener(event -> checkValid());
+ tenantTextField.addValueChangeListener(event -> checkValid());
- final Button buttonOk = new Button("generate");
- buttonOk.setImmediate(true);
- buttonOk.setIcon(FontAwesome.GEARS);
- buttonOk.addClickListener(new ClickListener() {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void buttonClick(final ClickEvent event) {
- try {
- callback.okButton(tf1.getValue(), tf3.getValue(), Integer.valueOf(tf2.getValue().replace(".", "")),
- Integer.valueOf(tf4.getValue().replace(".", "")), new URL(tf5.getValue()), tf6.getValue(),
- (Protocol) protocolGroup.getValue());
- } catch (final NumberFormatException e) {
- LOGGER.info(e.getMessage(), e);
- } catch (final MalformedURLException e) {
- LOGGER.info(e.getMessage(), e);
- }
- GenerateDialog.this.close();
- }
- });
-
- tf1.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
- tf2.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
- tf3.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
-
- formLayout.addComponent(tf1);
- formLayout.addComponent(tf2);
- formLayout.addComponent(tf3);
+ formLayout.addComponent(namePrefixTextField);
+ formLayout.addComponent(amountTextField);
+ formLayout.addComponent(tenantTextField);
formLayout.addComponent(protocolGroup);
- formLayout.addComponent(tf4);
- formLayout.addComponent(tf5);
- formLayout.addComponent(tf6);
+ formLayout.addComponent(pollDelayTextField);
+ formLayout.addComponent(pollUrlTextField);
+ formLayout.addComponent(gatewayTokenTextField);
formLayout.addComponent(buttonOk);
setCaption("Simulate Devices");
@@ -156,13 +110,10 @@ public class GenerateDialog extends Window {
center();
}
- private void checkValid(final TextField tf1, final TextField tf2, final TextField tf3, final TextField tf4,
- final Button buttonOk) {
- if (tf1.isValid() && tf2.isValid() && tf3.isValid() && tf4.isValid()) {
- buttonOk.setEnabled(true);
- } else {
- buttonOk.setEnabled(false);
- }
+ private void checkValid() {
+ buttonOk.setEnabled(namePrefixTextField.isValid() && amountTextField.isValid() && tenantTextField.isValid()
+ && pollDelayTextField.isValid());
+
}
@Override
@@ -230,4 +181,66 @@ public class GenerateDialog extends Window {
void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay,
final URL basePollURL, final String gatewayToken, final Protocol protocol);
}
+
+ private OptionGroup createProtocolGroup() {
+
+ final OptionGroup protocolGroup = new OptionGroup("Simulated Device Protocol");
+ protocolGroup.addItem(Protocol.DMF_AMQP);
+ protocolGroup.addItem(Protocol.DDI_HTTP);
+ protocolGroup.setItemCaption(Protocol.DMF_AMQP, "Device Management Federation API (AMQP push)");
+ protocolGroup.setItemCaption(Protocol.DDI_HTTP, "Direct Device Interface (HTTP poll)");
+ protocolGroup.setNullSelectionAllowed(false);
+ protocolGroup.select(Protocol.DMF_AMQP);
+ protocolGroup.addValueChangeListener(event -> {
+ final boolean directDeviceOptionSelected = event.getProperty().getValue().equals(Protocol.DDI_HTTP);
+ pollDelayTextField.setVisible(directDeviceOptionSelected);
+ pollUrlTextField.setVisible(directDeviceOptionSelected);
+ gatewayTokenTextField.setVisible(directDeviceOptionSelected);
+ });
+ return protocolGroup;
+ }
+
+ private Button createOkButton(final GenerateDialogCallback callback) {
+
+ final Button buttonOk = new Button("generate");
+ buttonOk.setImmediate(true);
+ buttonOk.setIcon(FontAwesome.GEARS);
+ buttonOk.addClickListener(event -> {
+ try {
+ callback.okButton(namePrefixTextField.getValue(), tenantTextField.getValue(),
+ Integer.valueOf(amountTextField.getValue().replace(".", "").replace(",", "")),
+ Integer.valueOf(pollDelayTextField.getValue().replace(".", "")),
+ new URL(pollUrlTextField.getValue()), gatewayTokenTextField.getValue(),
+ (Protocol) protocolGroup.getValue());
+ } catch (final NumberFormatException e) {
+ LOGGER.info(e.getMessage(), e);
+ } catch (final MalformedURLException e) {
+ LOGGER.info(e.getMessage(), e);
+ }
+ GenerateDialog.this.close();
+ });
+ return buttonOk;
+ }
+
+ private TextField createRequiredTextfield(final String caption, final String value, final Resource icon,
+ final Validator validator) {
+ final TextField textField = new TextField(caption, value);
+ return addTextFieldValues(textField, icon, validator);
+ }
+
+ private TextField createRequiredTextfield(final String caption, final Property dataSource, final Resource icon,
+ final Validator validator) {
+ final TextField textField = new TextField(caption, dataSource);
+ return addTextFieldValues(textField, icon, validator);
+ }
+
+ private TextField addTextFieldValues(final TextField textField, final Resource icon, final Validator validator) {
+ textField.setIcon(icon);
+ textField.setRequired(true);
+ if (validator != null) {
+ textField.addValidator(validator);
+ }
+ return textField;
+ }
+
}
diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java
index 25498cea7..e1d6bd15b 100644
--- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java
+++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java
@@ -28,8 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
-import com.vaadin.data.Property.ValueChangeEvent;
-import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanContainer;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.converter.Converter;
@@ -76,8 +74,8 @@ public class SimulatorView extends VerticalLayout implements View {
private final Label caption = new Label("DMF/DDI Simulated Devices");
private final HorizontalLayout toolbar = new HorizontalLayout();
private final Grid grid = new Grid();
- private final ComboBox responseComboBox = new ComboBox("", Lists.newArrayList(ResponseStatus.SUCCESSFUL,
- ResponseStatus.ERROR));
+ private final ComboBox responseComboBox = new ComboBox("",
+ Lists.newArrayList(ResponseStatus.SUCCESSFUL, ResponseStatus.ERROR));
private BeanContainer beanContainer;
@@ -97,6 +95,9 @@ public class SimulatorView extends VerticalLayout implements View {
grid.setSizeFull();
grid.setCellStyleGenerator(new CellStyleGenerator() {
+
+ private static final long serialVersionUID = 1L;
+
@Override
public String getStyle(final CellReference cellReference) {
return cellReference.getPropertyId().equals("status") ? "centeralign" : null;
@@ -118,86 +119,8 @@ public class SimulatorView extends VerticalLayout implements View {
grid.getColumn("swversion").setHeaderCaption("SW Version");
grid.getColumn("responseStatus").setHeaderCaption("Response Update Status");
grid.getColumn("progress").setRenderer(new ProgressBarRenderer());
- grid.getColumn("protocol").setConverter(new Converter() {
- @Override
- public Protocol convertToModel(final String value, final Class extends Protocol> targetType,
- final Locale locale) {
- return null;
- }
-
- @Override
- public String convertToPresentation(final Protocol value, final Class extends String> targetType,
- final Locale locale) {
- switch (value) {
- case DDI_HTTP:
- return "DDI API (http)";
- case DMF_AMQP:
- return "DMF API (amqp)";
- default:
- return "unknown";
- }
- }
-
- @Override
- public Class getModelType() {
- return Protocol.class;
- }
-
- @Override
- public Class getPresentationType() {
- return String.class;
- }
- });
- grid.getColumn("status").setRenderer(new HtmlRenderer(), new Converter() {
- private static final long serialVersionUID = 1L;
-
- @Override
- public Status convertToModel(final String value, final Class extends Status> targetType,
- final Locale locale) {
- return null;
- }
-
- @Override
- public String convertToPresentation(final Status value, final Class extends String> targetType,
- final Locale locale) {
- String style = null;
- switch (value) {
- case UNKNWON:
- style = ""
- + Integer.toHexString(FontAwesome.QUESTION_CIRCLE.getCodepoint()) + ";";
- break;
- case PEDNING:
- style = "" + Integer.toHexString(FontAwesome.REFRESH.getCodepoint())
- + ";";
- break;
- case FINISH:
- style = ""
- + Integer.toHexString(FontAwesome.CHECK_CIRCLE.getCodepoint()) + ";";
- break;
- case ERROR:
- style = ""
- + Integer.toHexString(FontAwesome.EXCLAMATION_CIRCLE.getCodepoint()) + ";";
- break;
- default:
- throw new IllegalStateException("unknown value");
- }
- return style;
- }
-
- @Override
- public Class getModelType() {
- return Status.class;
- }
-
- @Override
- public Class getPresentationType() {
- return String.class;
- }
- });
+ grid.getColumn("protocol").setConverter(createProtocolConverter());
+ grid.getColumn("status").setRenderer(new HtmlRenderer(), createStatusConverter());
grid.removeColumn("tenant");
// grid combobox
@@ -205,13 +128,9 @@ public class SimulatorView extends VerticalLayout implements View {
responseComboBox.setItemIcon(ResponseStatus.ERROR, FontAwesome.EXCLAMATION_CIRCLE);
responseComboBox.setNullSelectionAllowed(false);
responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
- responseComboBox.addValueChangeListener(new ValueChangeListener() {
- @Override
- public void valueChange(final ValueChangeEvent event) {
- beanContainer.getItemIds().forEach(
- itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus")
- .setValue(event.getProperty().getValue()));
- }
+ responseComboBox.addValueChangeListener(valueChangeEvent -> {
+ beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus")
+ .setValue(valueChangeEvent.getProperty().getValue()));
});
// add all components
@@ -338,4 +257,90 @@ public class SimulatorView extends VerticalLayout implements View {
}
}));
}
+
+ private Converter createProtocolConverter() {
+
+ return new Converter() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Protocol convertToModel(final String value, final Class extends Protocol> targetType,
+ final Locale locale) {
+ return null;
+ }
+
+ @Override
+ public String convertToPresentation(final Protocol value, final Class extends String> targetType,
+ final Locale locale) {
+ switch (value) {
+ case DDI_HTTP:
+ return "DDI API (http)";
+ case DMF_AMQP:
+ return "DMF API (amqp)";
+ default:
+ return "unknown";
+ }
+ }
+
+ @Override
+ public Class getModelType() {
+ return Protocol.class;
+ }
+
+ @Override
+ public Class getPresentationType() {
+ return String.class;
+ }
+ };
+
+ }
+
+ private Converter createStatusConverter() {
+ return new Converter() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Status convertToModel(final String value, final Class extends Status> targetType,
+ final Locale locale) {
+ return null;
+ }
+
+ @Override
+ public String convertToPresentation(final Status value, final Class extends String> targetType,
+ final Locale locale) {
+ switch (value) {
+ case UNKNWON:
+ return ""
+ + Integer.toHexString(FontAwesome.QUESTION_CIRCLE.getCodepoint()) + ";";
+ case PEDNING:
+ return "" + Integer.toHexString(FontAwesome.REFRESH.getCodepoint())
+ + ";";
+ case FINISH:
+ return ""
+ + Integer.toHexString(FontAwesome.CHECK_CIRCLE.getCodepoint()) + ";";
+ case ERROR:
+ return ""
+ + Integer.toHexString(FontAwesome.EXCLAMATION_CIRCLE.getCodepoint()) + ";";
+ default:
+ throw new IllegalStateException("unknown value");
+ }
+ }
+
+ @Override
+ public Class getModelType() {
+ return Status.class;
+ }
+
+ @Override
+ public Class getPresentationType() {
+ return String.class;
+ }
+ };
+ }
+
}
diff --git a/examples/hawkbit-device-simulator/src/main/resources/application-cloud.properties b/examples/hawkbit-device-simulator/src/main/resources/application-cloud.properties
new file mode 100644
index 000000000..3e3edc90c
--- /dev/null
+++ b/examples/hawkbit-device-simulator/src/main/resources/application-cloud.properties
@@ -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
+#
+
+# SECURITY (SecurityProperties)
+security.basic.enabled=true
+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= /**
+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=/VAADIN/**
\ No newline at end of file
diff --git a/examples/hawkbit-device-simulator/src/main/resources/application.properties b/examples/hawkbit-device-simulator/src/main/resources/application.properties
index 402f71bfe..56d0190a7 100644
--- a/examples/hawkbit-device-simulator/src/main/resources/application.properties
+++ b/examples/hawkbit-device-simulator/src/main/resources/application.properties
@@ -27,23 +27,5 @@ spring.rabbitmq.port=5672
spring.rabbitmq.dynamic=true
spring.rabbitmq.listener.prefetch=100
-# 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= /**
-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=/VAADIN/**
-
+security.basic.enabled=false
server.port=8083
diff --git a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java
index cafe0749d..e55cb02d2 100644
--- a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java
+++ b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java
@@ -8,21 +8,12 @@
*/
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 org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy;
+import org.springframework.beans.factory.annotation.Autowired;
-import com.google.common.eventbus.AllowConcurrentEvents;
-import com.google.common.eventbus.Subscribe;
+import com.google.common.eventbus.EventBus;
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;
@@ -33,45 +24,16 @@ import com.vaadin.spring.annotation.SpringUI;
* 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);
- }
- }
- }
- }
+ @Autowired
+ public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus) {
+ super(new DelayedEventBusPushStrategy(eventBus, systemEventBus));
}
-
}
diff --git a/examples/hawkbit-example-app/src/main/resources/application.properties b/examples/hawkbit-example-app/src/main/resources/application.properties
index 7149dbb9c..7864087c8 100644
--- a/examples/hawkbit-example-app/src/main/resources/application.properties
+++ b/examples/hawkbit-example-app/src/main/resources/application.properties
@@ -7,23 +7,35 @@
# 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
+hawkbit.server.ddi.security.authentication.anonymous.enabled=true
+hawkbit.server.ddi.security.authentication.targettoken.enabled=false
+hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
spring.profiles.active=amqp
-vaadin.servlet.params.productionMode=false
-vaadin.static.servlet.params.productionMode=false
+vaadin.servlet.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
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+spring.rabbitmq.virtualHost=/
+spring.rabbitmq.host=localhost
+spring.rabbitmq.port=5672
+hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
+hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
+hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
+
+# UI demo account
+hawkbit.server.ui.demo.password=admin
+hawkbit.server.ui.demo.user=admin
+hawkbit.server.ui.demo.tenant=DEFAULT
+
+# UI help links
+hawkbit.server.ui.links.documentation.root=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.deployment-view=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.distribution-view=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.rollout-view=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.security=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.system-configuration-view=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.targetfilter-view=https://github.com/eclipse/hawkbit
+hawkbit.server.ui.links.documentation.upload-view=https://github.com/eclipse/hawkbit
diff --git a/examples/hawkbit-mgmt-api-client/.gitignore b/examples/hawkbit-mgmt-api-client/.gitignore
index 0f630157f..4b9b73073 100644
--- a/examples/hawkbit-mgmt-api-client/.gitignore
+++ b/examples/hawkbit-mgmt-api-client/.gitignore
@@ -1,2 +1,3 @@
/target/
/bin/
+/.apt_generated/
diff --git a/examples/hawkbit-mgmt-api-client/README.md b/examples/hawkbit-mgmt-api-client/README.md
index 7eb32f9e6..eff301e20 100644
--- a/examples/hawkbit-mgmt-api-client/README.md
+++ b/examples/hawkbit-mgmt-api-client/README.md
@@ -1,3 +1,39 @@
+# HawkBit management API example
+
Example client that shows how to efficiently use the hawkBit management API.
-Powered by [Feign](https://github.com/Netflix/feign).
\ No newline at end of file
+Powered by [Feign](https://github.com/Netflix/feign).
+
+## How to run the example client
+
+Run getting started example
+
+
+
+ $ java -jar hawkbit-mgmt-api-client-#version#.jar
+
+
+Run create and start rollout example
+
+
+ $ java -jar hawkbit-mgmt-api-client-#version#.jar --createrollout
+
+
+## This example shows
+
+In getting started example:
+* creating software modules type
+* creating distribution set type
+* creating distribution sets
+* creating software modules
+* assigning software modules to distribution sets
+
+In rollout mode:
+* creating software modules type
+* creating distribution set type
+* creating distribution sets
+* creating software modules
+* assigning software modules to distribution sets
+* creating a rollout
+* starting a rollout
+
diff --git a/examples/hawkbit-mgmt-api-client/pom.xml b/examples/hawkbit-mgmt-api-client/pom.xml
index cced3dfbb..9aaf53dc6 100644
--- a/examples/hawkbit-mgmt-api-client/pom.xml
+++ b/examples/hawkbit-mgmt-api-client/pom.xml
@@ -16,9 +16,43 @@
hawkbit-examples-parent0.2.0-SNAPSHOT
+ jarhawkbit-mgmt-api-clienthawkBit Management API example client
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+ ${baseDir}
+ false
+ org.eclipse.hawkbit.mgmt.client.Application
+ JAR
+
+
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-netflix
+ 1.0.7.RELEASE
+ pom
+ import
+
+
+
@@ -26,45 +60,37 @@
hawkbit-rest-api${project.version}
-
- com.netflix.feign
- feign-jackson
- 8.12.1
- com.netflix.feignfeign-core
- 8.12.1
+
+ 8.14.2
-
-
- org.eclipse.hawkbit
- hawkbit-example-app
- ${project.version}
- test
+ hibernate-validator
+ org.hibernateorg.springframework.boot
- spring-boot-starter-test
- test
+ spring-boot-starter
- ru.yandex.qatools.allure
- allure-junit-adaptor
- test
+ org.springframework.cloud
+ spring-cloud-starter-feign
- org.easytesting
- fest-assert-core
- test
+ org.springframework.boot
+ spring-boot-starter-logging
- org.easytesting
- fest-assert
- test
+ com.google.collections
+ google-collections
+ 1.0-rc2
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
-
-
\ No newline at end of file
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java
deleted file mode 100644
index 62c987ae8..000000000
--- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java
+++ /dev/null
@@ -1,38 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import java.util.List;
-
-import org.eclipse.hawkbit.rest.resource.RestConstants;
-import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetRequestBodyPost;
-import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetsRest;
-
-import feign.Headers;
-import feign.RequestLine;
-
-/**
- * Client binding for the Distribution resource of the management API.
- */
-@FunctionalInterface
-public interface DistributionSetResource {
-
- /**
- * Creates a list of distribution sets.
- *
- * @param sets
- * the request body java bean containing the necessary attributes
- * for creating a distribution set.
- * @return the list of targets which have been created
- */
- @RequestLine("POST " + RestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING)
- @Headers("Content-Type: application/json")
- DistributionSetsRest createDistributionSets(final List sets);
-
-}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistrubutionSetTagResource.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistrubutionSetTagResource.java
deleted file mode 100644
index fe7a147a8..000000000
--- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistrubutionSetTagResource.java
+++ /dev/null
@@ -1,138 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import java.util.List;
-
-import org.eclipse.hawkbit.rest.resource.RestConstants;
-import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetsRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.AssignedDistributionSetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.tag.DistributionSetTagAssigmentResultRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRequestBodyPut;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagsRest;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetsRest;
-
-import feign.Headers;
-import feign.Param;
-import feign.RequestLine;
-
-/**
- * Client binding for the DistributionSetTag resource of the management API.
- */
-public interface DistrubutionSetTagResource {
-
- /**
- * Retrieves a single distributionset tag based on the given ID.
- *
- * @param dsTagId
- * the ID of the distributionset tag to retrieve
- * @return a deserialized java bean containing the attributes of the
- * returned distributionset tag
- */
- @RequestLine("GET " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING + "/{dsTagId}")
- TagRest getDistributionSetTag(@Param("dsTagId") Long dsTagId);
-
- /**
- * Creates a list of distributionset tags.
- *
- * @param tags
- * the tags to be created
- * @return the created tag list
- */
- @RequestLine("POST " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING)
- @Headers("Content-Type: application/json")
- TagsRest createDistributionSetTags(List tags);
-
- /**
- * Update attributes of a distributionset tag.
- *
- * @param dsTagId
- * the distributionset tag id to be updated
- * @param tag
- * the request body
- * @return the updated distributionset tag
- */
- @RequestLine("PUT " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING + "/{dsTagId}")
- @Headers("Content-Type: application/json")
- TagRest updateDistributionSetTag(@Param("dsTagId") Long dsTagId, TagRequestBodyPut tag);
-
- /**
- * Deletes given distributionset tag on given ID.
- *
- * @param dsTagId
- * to be deleted
- */
- @RequestLine("DELETE " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING + "/{dsTagId}")
- void deleteDistributionSetTag(@Param("dsTagId") final Long dsTagId);
-
- /**
- * Retrieves a all assigned targets on the given distributionset tag id.
- *
- * @param dsTagId
- * the ID of the distributionset tag to retrieve
- * @return a list of targets
- */
- @RequestLine("GET " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING
- + RestConstants.DISTRIBUTIONSET_REQUEST_MAPPING)
- DistributionSetsRest getAssignedDistributionSets(@Param("dsTagId") final Long dsTagId);
-
- /**
- * Toggle the tag assignment all assigned targets will be unassigned and all
- * unassigned targets will be assigned.
- *
- * @param dsTagId
- * the ID of the distributionset tag to toggle
- * @param assignedTargetRequestBodies
- * a list of controller ids
- * @return a list of assigned and unassigned targets
- */
- @RequestLine("POST " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING
- + RestConstants.DISTRIBUTIONSET_REQUEST_MAPPING + "/toggleTagAssignment")
- @Headers("Content-Type: application/json")
- DistributionSetTagAssigmentResultRest toggleTagAssignment(@Param("dsTagId") final Long dsTagId,
- final List assignedTargetRequestBodies);
-
- /**
- * Assign targets to a given distributionset tag id.
- *
- * @param dsTagId
- * the ID of the distributionset tag to add the targets
- * @param assignedTargetRequestBodies
- * a list of controller ids
- * @return a list of assigned targets
- */
- @RequestLine("POST " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING
- + RestConstants.DISTRIBUTIONSET_REQUEST_MAPPING)
- @Headers("Content-Type: application/json")
- TargetsRest assignDistributionSets(@Param("dsTagId") final Long dsTagId,
- final List assignedTargetRequestBodies);
-
- /**
- * Unassign targets to a given distributionset tag id.
- *
- * @param dsTagId
- * the ID of the distributionset tag to add the targets
- */
- @RequestLine("DELETE " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING
- + RestConstants.DISTRIBUTIONSET_REQUEST_MAPPING)
- void unassignDistributionSets(@Param("dsTagId") final Long dsTagId);
-
- /**
- * Unassign one target to a given distributionset tag id.
- *
- * @param dsTagId
- * the ID of the distributionset tag to add the targets param
- * @param dsId
- * the distributionset id
- */
- @RequestLine("DELETE " + RestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING
- + RestConstants.DISTRIBUTIONSET_REQUEST_MAPPING + "/{dsId}")
- void unassignDistributionSet(@Param("dsTagId") final Long dsTagId, @Param("dsId") final Long dsId);
-}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/TargetResource.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/TargetResource.java
deleted file mode 100644
index 66c147426..000000000
--- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/TargetResource.java
+++ /dev/null
@@ -1,80 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import java.util.List;
-
-import org.eclipse.hawkbit.rest.resource.model.target.TargetPagedList;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetRest;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetsRest;
-
-import feign.Headers;
-import feign.Param;
-import feign.RequestLine;
-
-/**
- * Client binding for the Target resource of the management API.
- */
-public interface TargetResource {
-
- /**
- * Retrieves a single target based on the given ID.
- *
- * @param targetId
- * the ID of the target to retrieve
- * @return a deserialized java bean containing the attributes of the
- * returned target
- */
- @RequestLine("GET /rest/v1/targets/{targetId}")
- TargetRest getTarget(@Param("targetId") final String targetId);
-
- /**
- * Paged query of targets resource.
- *
- * @param pagingOffsetParam
- * of the paged query
- * @param pagingLimitParam
- * of the paged query
- * @return paged list of target entries
- */
- @RequestLine("GET /rest/v1/targets?offset={pagingOffsetParam}&limit={pagingLimitParam}")
- TargetPagedList getTargets(@Param("pagingOffsetParam") int pagingOffsetParam,
- @Param("pagingLimitParam") int pagingLimitParam);
-
- /**
- * Paged query of targets resource with default offset and limit.
- *
- * @return paged list of target entries
- */
- @RequestLine("GET /rest/v1/targets")
- TargetPagedList getTargets();
-
- /**
- * Deletes given target based on given ID.
- *
- * @param targetId
- * to be deleted
- */
- @RequestLine("DELETE /rest/v1/targets/{targetId}")
- void deleteTarget(@Param("targetId") final String targetId);
-
- /**
- * Creates a list of targets.
- *
- * @param targets
- * the request body java bean containing the necessary attributes
- * for creating a target.
- * @return the list of targets which have been created
- */
- @RequestLine("POST /rest/v1/targets/")
- @Headers("Content-Type: application/json")
- TargetsRest createTargets(List targets);
-
-}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/TargetTagResource.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/TargetTagResource.java
deleted file mode 100644
index bd74e52d7..000000000
--- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/TargetTagResource.java
+++ /dev/null
@@ -1,137 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import java.util.List;
-
-import org.eclipse.hawkbit.rest.resource.RestConstants;
-import org.eclipse.hawkbit.rest.resource.model.tag.AssignedTargetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRequestBodyPut;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagsRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TargetTagAssigmentResultRest;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetsRest;
-
-import feign.Headers;
-import feign.Param;
-import feign.RequestLine;
-
-/**
- * Client binding for the Target resource of the management API.
- */
-public interface TargetTagResource {
-
- /**
- * Retrieves a single target tag based on the given ID.
- *
- * @param targetTagId
- * the ID of the target tag to retrieve
- * @return a deserialized java bean containing the attributes of the
- * returned target tag
- */
- @RequestLine("GET " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING + "/{targetTagId}")
- TagRest getTargetTag(@Param("targetTagId") Long targetTagId);
-
- /**
- * Creates a list of target tags.
- *
- * @param tags
- * the tags to be created
- * @return the created tag list
- */
- @RequestLine("POST " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING)
- @Headers("Content-Type: application/json")
- TagsRest createTargetTag(List tags);
-
- /**
- * Update attributes of a target tag.
- *
- * @param targetTagId
- * the target tag id to be updated
- * @param tag
- * the request body
- * @return the updated target tag
- */
- @RequestLine("PUT " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING + "/{targetTagId}")
- @Headers("Content-Type: application/json")
- TagRest updateTagretTag(@Param("targetTagId") Long targetTagId, TagRequestBodyPut tag);
-
- /**
- * Deletes given target tag on given ID.
- *
- * @param targetTagId
- * to be deleted
- */
- @RequestLine("DELETE " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING + "/{targetTagId}")
- void deleteTargetTag(@Param("targetTagId") final Long targetTagId);
-
- /**
- * Retrieves a all assigned targets on the given target tag id.
- *
- * @param targetTagId
- * the ID of the target tag to retrieve
- * @return a list of targets
- */
- @RequestLine("GET " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING
- + RestConstants.TARGET_TAG_TAGERTS_REQUEST_MAPPING)
- TargetsRest getAssignedTargets(@Param("targetTagId") final Long targetTagId);
-
- /**
- * Toggle the tag assignment all assigned targets will be unassigned and all
- * unassigned targets will be assigned.
- *
- * @param targetTagId
- * the ID of the target tag to toggle
- * @param assignedTargetRequestBodies
- * a list of controller ids
- * @return a list of assigned and unassigned targets
- */
- @RequestLine("POST " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING
- + RestConstants.TARGET_TAG_TAGERTS_REQUEST_MAPPING + "/toggleTagAssignment")
- @Headers("Content-Type: application/json")
- TargetTagAssigmentResultRest toggleTagAssignment(@Param("targetTagId") final Long targetTagId,
- final List assignedTargetRequestBodies);
-
- /**
- * Assign targets to a given target tag id.
- *
- * @param targetTagId
- * the ID of the target tag to add the targets
- * @param assignedTargetRequestBodies
- * a list of controller ids
- * @return a list of assigned targets
- */
- @RequestLine("POST " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING
- + RestConstants.TARGET_TAG_TAGERTS_REQUEST_MAPPING)
- @Headers("Content-Type: application/json")
- TargetsRest assignTargets(@Param("targetTagId") final Long targetTagId,
- final List assignedTargetRequestBodies);
-
- /**
- * Unassign targets to a given target tag id.
- *
- * @param targetTagId
- * the ID of the target tag to add the targets
- */
- @RequestLine("DELETE " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING
- + RestConstants.TARGET_TAG_TAGERTS_REQUEST_MAPPING)
- void unassignTargets(@Param("targetTagId") final Long targetTagId);
-
- /**
- * Unassign one target to a given target tag id.
- *
- * @param targetTagId
- * the ID of the target tag to add the targets param
- * @param controllerId
- * the controller id
- */
- @RequestLine("DELETE " + RestConstants.TARGET_TAG_V1_REQUEST_MAPPING
- + RestConstants.TARGET_TAG_TAGERTS_REQUEST_MAPPING + "/{controllerId}")
- void unassignTarget(@Param("targetTagId") final Long targetTagId, @Param("controllerId") final String controllerId);
-}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java
new file mode 100644
index 000000000..27584b50a
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java
@@ -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.mgmt.client;
+
+import org.eclipse.hawkbit.mgmt.client.scenarios.CreateStartedRolloutExample;
+import org.eclipse.hawkbit.mgmt.client.scenarios.GettingStartedDefaultScenario;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+
+import feign.Contract;
+import feign.auth.BasicAuthRequestInterceptor;
+
+@SpringBootApplication
+@EnableFeignClients
+@EnableConfigurationProperties(ClientConfigurationProperties.class)
+public class Application implements CommandLineRunner {
+
+ @Autowired
+ private ClientConfigurationProperties configuration;
+
+ @Autowired
+ private GettingStartedDefaultScenario gettingStarted;
+
+ @Autowired
+ private CreateStartedRolloutExample gettingStartedRolloutScenario;
+
+ public static void main(final String[] args) {
+ new SpringApplicationBuilder().showBanner(false).sources(Application.class).run(args);
+ }
+
+ @Override
+ public void run(final String... args) throws Exception {
+ if (containsArg("--createrollout", args)) {
+ // run the create and start rollout example
+ gettingStartedRolloutScenario.run();
+ } else {
+ // run the getting started scenario which creates a setup of
+ // distribution set and software modules to be used
+ gettingStarted.run();
+ }
+ }
+
+ @Bean
+ public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
+ return new BasicAuthRequestInterceptor(configuration.getUsername(), configuration.getPassword());
+ }
+
+ @Bean
+ public ApplicationJsonRequestHeaderInterceptor jsonHeaderInterceptor() {
+ return new ApplicationJsonRequestHeaderInterceptor();
+ }
+
+ @Bean
+ public Contract feignContract() {
+ return new IgnoreMultipleConsumersProducersSpringMvcContract();
+ }
+
+ private boolean containsArg(final String containsArg, final String... args) {
+ for (final String arg : args) {
+ if (arg.equalsIgnoreCase(containsArg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ApplicationJsonRequestHeaderInterceptor.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ApplicationJsonRequestHeaderInterceptor.java
new file mode 100644
index 000000000..75f3b0dd1
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ApplicationJsonRequestHeaderInterceptor.java
@@ -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.mgmt.client;
+
+import org.springframework.http.MediaType;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+
+/**
+ * An feign request interceptor to set the defined {@code Accept} and
+ * {@code Content-Type} headers for each request to {@code application/json}.
+ */
+public class ApplicationJsonRequestHeaderInterceptor implements RequestInterceptor {
+
+ @Override
+ public void apply(final RequestTemplate template) {
+ template.header("Accept", MediaType.APPLICATION_JSON_VALUE);
+ template.header("Content-Type", MediaType.APPLICATION_JSON_VALUE);
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java
new file mode 100644
index 000000000..68f35b550
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java
@@ -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.mgmt.client;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Configuration bean which holds the configuration of the client e.g. the base
+ * URL of the hawkbit-server and the credentials to use the RESTful Management
+ * API.
+ */
+@ConfigurationProperties(prefix = "hawkbit")
+public class ClientConfigurationProperties {
+
+ /**
+ * Update server URI.
+ */
+ private String url = "localhost:8080";
+
+ /**
+ * Update server user name.
+ */
+ private String username = "admin";
+
+ /**
+ * Update server password.
+ */
+ private String password = "admin"; // NOSONAR this password is only used for
+ // examples
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(final String url) {
+ this.url = url;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(final String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(final String password) {
+ this.password = password;
+ }
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/IgnoreMultipleConsumersProducersSpringMvcContract.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/IgnoreMultipleConsumersProducersSpringMvcContract.java
new file mode 100644
index 000000000..c796aa019
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/IgnoreMultipleConsumersProducersSpringMvcContract.java
@@ -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.mgmt.client;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
+
+import feign.MethodMetadata;
+
+/**
+ * Own implementation of the {@link SpringMvcContract} which catches the
+ * {@link IllegalStateException} which occurs due multiple produces and consumes
+ * values in the request-mapping
+ * annoation.https://github.com/spring-cloud/spring-cloud-netflix/issues/808
+ */
+public class IgnoreMultipleConsumersProducersSpringMvcContract extends SpringMvcContract {
+
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(IgnoreMultipleConsumersProducersSpringMvcContract.class);
+
+ @Override
+ protected void processAnnotationOnMethod(final MethodMetadata data, final Annotation methodAnnotation,
+ final Method method) {
+ try {
+ super.processAnnotationOnMethod(data, methodAnnotation, method);
+ } catch (final IllegalStateException e) {
+ // ignore illegalstateexception here because it's thrown because of
+ // multiple consumers and produces, see
+ // https://github.com/spring-cloud/spring-cloud-netflix/issues/808
+ LOGGER.trace(e.getMessage(), e);
+ }
+ }
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetResourceClient.java
new file mode 100644
index 000000000..00a9b3fba
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.DistributionSetRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the DistributionSet resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/distributionsets")
+public interface DistributionSetResourceClient extends DistributionSetRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetTagResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetTagResourceClient.java
new file mode 100644
index 000000000..ea9f5d28a
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetTagResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.DistributionSetTagRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the DistributionSetTag resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/distributionsettags")
+public interface DistributionSetTagResourceClient extends DistributionSetTagRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetTypeResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetTypeResourceClient.java
new file mode 100644
index 000000000..08d40dfa5
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/DistributionSetTypeResourceClient.java
@@ -0,0 +1,21 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.DistributionSetTypeRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the DistributionSetType resource of the management API.
+ *
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/distributionsettypes")
+public interface DistributionSetTypeResourceClient extends DistributionSetTypeRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/RolloutResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/RolloutResourceClient.java
new file mode 100644
index 000000000..78b7413e7
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/RolloutResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.RolloutRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the Rollout resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/rollouts")
+public interface RolloutResourceClient extends RolloutRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/SoftwareModuleResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/SoftwareModuleResourceClient.java
new file mode 100644
index 000000000..88e664d78
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/SoftwareModuleResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.SoftwareModuleRestAPI;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the SoftwareModule resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/softwaremodules")
+public interface SoftwareModuleResourceClient extends SoftwareModuleRestAPI {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/SoftwareModuleTypeResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/SoftwareModuleTypeResourceClient.java
new file mode 100644
index 000000000..4896cb8d8
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/SoftwareModuleTypeResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.SoftwareModuleTypeRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the oftwareModuleType resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/softwaremoduletypes")
+public interface SoftwareModuleTypeResourceClient extends SoftwareModuleTypeRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/TargetResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/TargetResourceClient.java
new file mode 100644
index 000000000..a82aa5443
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/TargetResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.TargetRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the Target resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/targets")
+public interface TargetResourceClient extends TargetRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/TargetTagResourceClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/TargetTagResourceClient.java
new file mode 100644
index 000000000..fee30c686
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/TargetTagResourceClient.java
@@ -0,0 +1,20 @@
+/**
+ * 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.mgmt.client.resource;
+
+import org.eclipse.hawkbit.rest.resource.api.TargetTagRestApi;
+import org.springframework.cloud.netflix.feign.FeignClient;
+
+/**
+ * Client binding for the TargetTag resource of the management API.
+ */
+@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/targettags")
+public interface TargetTagResourceClient extends TargetTagRestApi {
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetBuilder.java
new file mode 100644
index 000000000..c821b106c
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetBuilder.java
@@ -0,0 +1,99 @@
+/**
+ * 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.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetRequestBodyPost;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ * Builder pattern for building {@link DistributionSetRequestBodyPost}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class DistributionSetBuilder {
+
+ private String name;
+ private String version;
+ private String type;
+
+ /**
+ * @param name
+ * the name of the distribution set
+ * @return the builder itself
+ */
+ public DistributionSetBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @param version
+ * the version of the distribution set
+ * @return the builder itself
+ */
+ public DistributionSetBuilder version(final String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * @param type
+ * the distribution set type name for this distribution set
+ * @return the builder itself
+ */
+ public DistributionSetBuilder type(final String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of
+ * {@link DistributionSetRequestBodyPost} which can directly be used to post
+ * on the RESTful-API.
+ *
+ * @return a single entry list of {@link DistributionSetRequestBodyPost}
+ */
+ public List build() {
+ return Lists.newArrayList(doBuild(name));
+ }
+
+ /**
+ * Builds a list of multiple {@link DistributionSetRequestBodyPost} to
+ * create multiple distribution sets at once. An increasing number will be
+ * added to the name of the distribution set. The version and type will
+ * remain the same.
+ *
+ * @param count
+ * the amount of distribution sets body which should be created
+ * @return a list of {@link DistributionSetRequestBodyPost}
+ */
+ public List buildAsList(final int count) {
+ final ArrayList bodyList = Lists.newArrayList();
+ for (int index = 0; index < count; index++) {
+ bodyList.add(doBuild(name + index));
+ }
+
+ return bodyList;
+ }
+
+ private DistributionSetRequestBodyPost doBuild(final String prefixName) {
+ final DistributionSetRequestBodyPost body = new DistributionSetRequestBodyPost();
+ body.setName(prefixName);
+ body.setVersion(version);
+ body.setType(type);
+ return body;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetTypeBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetTypeBuilder.java
new file mode 100644
index 000000000..e1da1f048
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetTypeBuilder.java
@@ -0,0 +1,124 @@
+/**
+ * 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.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.distributionsettype.DistributionSetTypeRequestBodyPost;
+import org.eclipse.hawkbit.rest.resource.model.softwaremoduletype.SoftwareModuleTypeAssigmentRest;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ * Builder pattern for building {@link DistributionSetTypeRequestBodyPost}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class DistributionSetTypeBuilder {
+
+ private String key;
+ private String name;
+ private final List mandatorymodules = Lists.newArrayList();
+ private final List optionalmodules = Lists.newArrayList();
+
+ /**
+ * @param key
+ * the key of the distribution set type
+ * @return the builder itself
+ */
+ public DistributionSetTypeBuilder key(final String key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * @param name
+ * the name of the distribution set type
+ * @return the builder itself
+ */
+ public DistributionSetTypeBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @param softwareModuleTypeIds
+ * the IDs of the software module types which should be mandatory
+ * for the distribution set type
+ * @return the builder itself
+ */
+ public DistributionSetTypeBuilder mandatorymodules(final Long... softwareModuleTypeIds) {
+ for (final Long id : softwareModuleTypeIds) {
+ final SoftwareModuleTypeAssigmentRest softwareModuleTypeAssigmentRest = new SoftwareModuleTypeAssigmentRest();
+ softwareModuleTypeAssigmentRest.setId(id);
+ this.mandatorymodules.add(softwareModuleTypeAssigmentRest);
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @param softwareModuleTypeIds
+ * the IDs of the software module types which should be optional
+ * for the distribution set type
+ * @return the builder itself
+ */
+ public DistributionSetTypeBuilder optionalmodules(final Long... softwareModuleTypeIds) {
+ for (final Long id : softwareModuleTypeIds) {
+ final SoftwareModuleTypeAssigmentRest softwareModuleTypeAssigmentRest = new SoftwareModuleTypeAssigmentRest();
+ softwareModuleTypeAssigmentRest.setId(id);
+ this.optionalmodules.add(softwareModuleTypeAssigmentRest);
+ }
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of
+ * {@link DistributionSetTypeRequestBodyPost} which can directly be used in
+ * the RESTful-API.
+ *
+ * @return a single entry list of {@link DistributionSetTypeRequestBodyPost}
+ */
+ public List build() {
+ return Lists.newArrayList(doBuild(name, key));
+ }
+
+ /**
+ * Builds a list of multiple {@link DistributionSetTypeRequestBodyPost} to
+ * create multiple distribution set types at once. An increasing number will
+ * be added to the name and key of the distribution set type. The optional
+ * and mandatory software module types will remain the same.
+ *
+ * @param count
+ * the amount of distribution sets type body which should be
+ * created
+ * @return a list of {@link DistributionSetTypeRequestBodyPost}
+ */
+ public List buildAsList(final int count) {
+ final ArrayList bodyList = Lists.newArrayList();
+ for (int index = 0; index < count; index++) {
+ bodyList.add(doBuild(name + index, key + index));
+ }
+ return bodyList;
+
+ }
+
+ private DistributionSetTypeRequestBodyPost doBuild(final String prefixName, final String prefixKey) {
+ final DistributionSetTypeRequestBodyPost body = new DistributionSetTypeRequestBodyPost();
+ body.setKey(prefixKey);
+ body.setName(prefixName);
+ body.setMandatorymodules(mandatorymodules);
+ body.setOptionalmodules(optionalmodules);
+ return body;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/RolloutBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/RolloutBuilder.java
new file mode 100644
index 000000000..bea0fd9a4
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/RolloutBuilder.java
@@ -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.mgmt.client.resource.builder;
+
+import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutCondition;
+import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutCondition.Condition;
+import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutRestRequestBody;
+
+/**
+ *
+ * Builder pattern for building {@link RolloutRestRequestBody}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class RolloutBuilder {
+
+ private String name;
+ private int groupSize;
+ private String targetFilterQuery;
+ private long distributionSetId;
+ private String successThreshold;
+ private String errorThreshold;
+
+ /**
+ * @param name
+ * the name of the rollout
+ * @return the builder itself
+ */
+ public RolloutBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @param groupSize
+ * the amount of groups the rollout should be split into
+ * @return the builder itself
+ */
+ public RolloutBuilder groupSize(final int groupSize) {
+ this.groupSize = groupSize;
+ return this;
+ }
+
+ /**
+ * @param targetFilterQuery
+ * the FIQL query language to filter targets to contain in the
+ * rollout
+ * @return the builder itself
+ */
+ public RolloutBuilder targetFilterQuery(final String targetFilterQuery) {
+ this.targetFilterQuery = targetFilterQuery;
+ return this;
+ }
+
+ /**
+ * @param distributionSetId
+ * the ID of the distribution set to assign to the target in the
+ * rollout
+ * @return the builder itself
+ */
+ public RolloutBuilder distributionSetId(final long distributionSetId) {
+ this.distributionSetId = distributionSetId;
+ return this;
+ }
+
+ /**
+ * @param successThreshold
+ * the threshold to be used to indicate if a deployment group is
+ * successful, to trigger the success action
+ * @return the builder itself
+ */
+ public RolloutBuilder successThreshold(final String successThreshold) {
+ this.successThreshold = successThreshold;
+ return this;
+ }
+
+ /**
+ * @param errorThreshold
+ * the threshold to be used to indicate if a deployment group is
+ * failing, to trigger the error action
+ * @return the builder itself
+ */
+ public RolloutBuilder errorThreshold(final String errorThreshold) {
+ this.errorThreshold = errorThreshold;
+ return this;
+ }
+
+ /**
+ * Builds the rollout rest body to creating a rollout.
+ *
+ * @return the rest request body for creating a rollout
+ */
+ public RolloutRestRequestBody build() {
+ return doBuild();
+ }
+
+ private RolloutRestRequestBody doBuild() {
+ final RolloutRestRequestBody body = new RolloutRestRequestBody();
+ body.setName(name);
+ body.setAmountGroups(groupSize);
+ body.setTargetFilterQuery(targetFilterQuery);
+ body.setDistributionSetId(distributionSetId);
+ body.setSuccessCondition(new RolloutCondition(Condition.THRESHOLD, successThreshold));
+ body.setErrorCondition(new RolloutCondition(Condition.THRESHOLD, errorThreshold));
+ return body;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleAssigmentBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleAssigmentBuilder.java
new file mode 100644
index 000000000..b209dbe8b
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleAssigmentBuilder.java
@@ -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.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.softwaremodule.SoftwareModuleAssigmentRest;
+
+/**
+ *
+ * Builder pattern for building {@link SoftwareModuleAssigmentRest}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class SoftwareModuleAssigmentBuilder {
+
+ private final List ids;
+
+ public SoftwareModuleAssigmentBuilder() {
+ ids = new ArrayList<>();
+ }
+
+ /**
+ * @param id
+ * the id of the software module
+ * @return the builder itself
+ */
+ public SoftwareModuleAssigmentBuilder id(final Long id) {
+ ids.add(id);
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of {@link SoftwareModuleAssigmentRest}
+ * which can directly be used in the RESTful-API.
+ *
+ * @return a single entry list of {@link SoftwareModuleAssigmentRest}
+ */
+ public List build() {
+ final List softwareModuleAssigmentRestList = new ArrayList<>();
+ for (final Long id : ids) {
+ final SoftwareModuleAssigmentRest softwareModuleAssigmentRest = new SoftwareModuleAssigmentRest();
+ softwareModuleAssigmentRest.setId(id);
+ softwareModuleAssigmentRestList.add(softwareModuleAssigmentRest);
+ }
+ return softwareModuleAssigmentRestList;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleBuilder.java
new file mode 100644
index 000000000..30b85d901
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleBuilder.java
@@ -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
+ */
+package org.eclipse.hawkbit.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.distributionsettype.DistributionSetTypeRequestBodyPost;
+import org.eclipse.hawkbit.rest.resource.model.softwaremodule.SoftwareModuleRequestBodyPost;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ * Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class SoftwareModuleBuilder {
+
+ private String name;
+ private String version;
+ private String type;
+
+ /**
+ * @param name
+ * the name of the software module
+ * @return the builder itself
+ */
+ public SoftwareModuleBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @param version
+ * the version of the software module
+ * @return the builder itsefl
+ */
+ public SoftwareModuleBuilder version(final String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * @param type
+ * the key of the software module type to be used for this
+ * software module
+ * @return the builder itself
+ */
+ public SoftwareModuleBuilder type(final String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of
+ * {@link SoftwareModuleRequestBodyPost} which can directly be used in the
+ * RESTful-API.
+ *
+ * @return a single entry list of {@link SoftwareModuleRequestBodyPost}
+ */
+ public List build() {
+ return Lists.newArrayList(doBuild(name));
+ }
+
+ /**
+ * Builds a list of multiple {@link SoftwareModuleRequestBodyPost} to create
+ * multiple software module at once. An increasing number will be added to
+ * the name of the software module. The version and type will remain the
+ * same.
+ *
+ * @param count
+ * the amount of software module body which should be created
+ * @return a list of {@link DistributionSetTypeRequestBodyPost}
+ */
+ public List buildAsList(final int count) {
+ final ArrayList bodyList = Lists.newArrayList();
+ for (int index = 0; index < count; index++) {
+ bodyList.add(doBuild(name + index));
+ }
+
+ return bodyList;
+ }
+
+ private SoftwareModuleRequestBodyPost doBuild(final String prefixName) {
+ final SoftwareModuleRequestBodyPost body = new SoftwareModuleRequestBodyPost();
+ body.setName(prefixName);
+ body.setVersion(version);
+ body.setType(type);
+ return body;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java
new file mode 100644
index 000000000..ce128d592
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java
@@ -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
+ */
+package org.eclipse.hawkbit.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.softwaremodule.SoftwareModuleRequestBodyPost;
+import org.eclipse.hawkbit.rest.resource.model.softwaremoduletype.SoftwareModuleTypeRequestBodyPost;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ * Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class SoftwareModuleTypeBuilder {
+
+ private String key;
+ private String name;
+ private String description;
+ private int maxAssignments;
+
+ /**
+ * @param key
+ * the key of the software module type
+ * @return the builder itself
+ */
+ public SoftwareModuleTypeBuilder key(final String key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * @param name
+ * the name of the software module type
+ * @return the builder itself
+ */
+ public SoftwareModuleTypeBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ public SoftwareModuleTypeBuilder description(final String description) {
+ this.description = description;
+ return this;
+ }
+
+ public SoftwareModuleTypeBuilder maxAssignments(final int maxAssignments) {
+ this.maxAssignments = maxAssignments;
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of
+ * {@link SoftwareModuleTypeRequestBodyPost} which can directly be used in
+ * the RESTful-API.
+ *
+ * @return a single entry list of {@link SoftwareModuleTypeRequestBodyPost}
+ */
+ public List build() {
+ return Lists.newArrayList(doBuild(key, name));
+ }
+
+ /**
+ * Builds a list of multiple {@link SoftwareModuleTypeRequestBodyPost} to
+ * create multiple software module types at once. An increasing number will
+ * be added to the name and key of the software module type.
+ *
+ * @param count
+ * the amount of software module type bodies which should be
+ * created
+ * @return a list of {@link SoftwareModuleTypeRequestBodyPost}
+ */
+ public List buildAsList(final int count) {
+ final ArrayList bodyList = Lists.newArrayList();
+ for (int index = 0; index < count; index++) {
+ bodyList.add(doBuild(key + index, name + index));
+ }
+ return bodyList;
+ }
+
+ private SoftwareModuleTypeRequestBodyPost doBuild(final String prefixKey, final String prefixName) {
+ final SoftwareModuleTypeRequestBodyPost body = new SoftwareModuleTypeRequestBodyPost();
+ body.setKey(prefixKey);
+ body.setName(prefixName);
+ body.setDescription(description);
+ body.setMaxAssignments(maxAssignments);
+ return body;
+ }
+
+}
\ No newline at end of file
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TagBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TagBuilder.java
new file mode 100644
index 000000000..f3888de54
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TagBuilder.java
@@ -0,0 +1,96 @@
+/**
+ * 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.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.tag.TagRequestBodyPut;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Builder pattern for building {@link TagRequestBodyPut}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class TagBuilder {
+
+ private String name;
+ private String description;
+ private String color;
+
+ /**
+ * @param name
+ * the name of the tag
+ * @return the builder itself
+ */
+ public TagBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @param description
+ * the description of the tag
+ * @return the builder itself
+ */
+ public TagBuilder description(final String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * @param color
+ * the colour of the tag
+ * @return the builder itself
+ */
+ public TagBuilder color(final String color) {
+ this.color = color;
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of {@link TagRequestBodyPut} which can
+ * directly be used in the RESTful-API.
+ *
+ * @return a single entry list of {@link TagRequestBodyPut}
+ */
+ public List build() {
+ return Lists.newArrayList(doBuild(name));
+ }
+
+ /**
+ * Builds a list of multiple {@link TagRequestBodyPut} to create multiple
+ * tags at once. An increasing number will be added to the name of the tag.
+ * The color and description will remain the same.
+ *
+ * @param count
+ * the amount of distribution sets body which should be created
+ * @return a list of {@link TagRequestBodyPut}
+ */
+ public List buildAsList(final int count) {
+ final ArrayList bodyList = Lists.newArrayList();
+ for (int index = 0; index < count; index++) {
+ bodyList.add(doBuild(name + index));
+ }
+
+ return bodyList;
+ }
+
+ private TagRequestBodyPut doBuild(final String prefixName) {
+ final TagRequestBodyPut body = new TagRequestBodyPut();
+ body.setName(prefixName);
+ body.setDescription(description);
+ body.setColour(color);
+ return body;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TargetBuilder.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TargetBuilder.java
new file mode 100644
index 000000000..5123903d9
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TargetBuilder.java
@@ -0,0 +1,98 @@
+/**
+ * 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.mgmt.client.resource.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.rest.resource.model.softwaremoduletype.SoftwareModuleTypeRequestBodyPost;
+import org.eclipse.hawkbit.rest.resource.model.target.TargetRequestBody;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ * Builder pattern for building {@link TargetRequestBody}.
+ *
+ * @author Jonathan Knoblauch
+ *
+ */
+public class TargetBuilder {
+
+ private String controllerId;
+ private String name;
+ private String description;
+
+ /**
+ * @param controllerId
+ * the ID of the controller/target
+ * @return the builder itself
+ */
+ public TargetBuilder controllerId(final String controllerId) {
+ this.controllerId = controllerId;
+ return this;
+ }
+
+ /**
+ * @param name
+ * the name of the target
+ * @return the builder itself
+ */
+ public TargetBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @param description
+ * the description of the target
+ * @return the builder itself
+ */
+ public TargetBuilder description(final String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Builds a list with a single entry of {@link TargetRequestBody} which can
+ * directly be used in the RESTful-API.
+ *
+ * @return a single entry list of {@link TargetRequestBody}
+ */
+ public List build() {
+ return Lists.newArrayList(doBuild(controllerId));
+ }
+
+ /**
+ * Builds a list of multiple {@link TargetRequestBody} to create multiple
+ * targets at once. An increasing number will be added to the controllerId
+ * of the target. The name and description will remain.
+ *
+ * @param count
+ * the amount of software module type bodies which should be
+ * created
+ * @return a list of {@link SoftwareModuleTypeRequestBodyPost}
+ */
+ public List buildAsList(final int count) {
+ final ArrayList bodyList = Lists.newArrayList();
+ for (int index = 0; index < count; index++) {
+ bodyList.add(doBuild(controllerId + index));
+ }
+ return bodyList;
+ }
+
+ private TargetRequestBody doBuild(final String prefixControllerId) {
+ final TargetRequestBody body = new TargetRequestBody();
+ body.setControllerId(prefixControllerId);
+ body.setName(name);
+ body.setDescription(description);
+ return body;
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/CreateStartedRolloutExample.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/CreateStartedRolloutExample.java
new file mode 100644
index 000000000..35b0f31ea
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/CreateStartedRolloutExample.java
@@ -0,0 +1,107 @@
+/**
+ * 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.mgmt.client.scenarios;
+
+import org.eclipse.hawkbit.mgmt.client.resource.DistributionSetResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.DistributionSetTypeResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.RolloutResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.SoftwareModuleResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.SoftwareModuleTypeResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.TargetResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetTypeBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.RolloutBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleAssigmentBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleTypeBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.TargetBuilder;
+import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetsRest;
+import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutResponseBody;
+import org.eclipse.hawkbit.rest.resource.model.softwaremodule.SoftwareModulesRest;
+import org.eclipse.hawkbit.rest.resource.model.softwaremoduletype.SoftwareModuleTypesRest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Example for creating and starting a Rollout.
+ *
+ */
+@Component
+public class CreateStartedRolloutExample {
+
+ /* known software module type name and key */
+ private static final String SM_MODULE_TYPE = "firmware";
+
+ /* known distribution set type name and key */
+ private static final String DS_MODULE_TYPE = "firmware";
+
+ @Autowired
+ private DistributionSetResourceClient distributionSetResource;
+
+ @Autowired
+ private SoftwareModuleResourceClient softwareModuleResource;
+
+ @Autowired
+ private TargetResourceClient targetResource;
+
+ @Autowired
+ private RolloutResourceClient rolloutResource;
+
+ @Autowired
+ private DistributionSetTypeResourceClient distributionSetTypeResource;
+
+ @Autowired
+ private SoftwareModuleTypeResourceClient softwareModuleTypeResource;
+
+ /**
+ * Run the Rollout scenario.
+ */
+ public void run() {
+
+ // create three SoftwareModuleTypes
+ final SoftwareModuleTypesRest createdSoftwareModuleTypes = softwareModuleTypeResource.createSoftwareModuleTypes(
+ new SoftwareModuleTypeBuilder().key(SM_MODULE_TYPE).name(SM_MODULE_TYPE).maxAssignments(1).build())
+ .getBody();
+
+ // create one DistributionSetType
+ distributionSetTypeResource.createDistributionSetTypes(new DistributionSetTypeBuilder().key(DS_MODULE_TYPE)
+ .name(DS_MODULE_TYPE).mandatorymodules(createdSoftwareModuleTypes.get(0).getModuleId()).build())
+ .getBody();
+
+ // create one DistributionSet
+ final DistributionSetsRest distributionSetsRest = distributionSetResource.createDistributionSets(
+ new DistributionSetBuilder().name("rollout-example").version("1.0.0").type(DS_MODULE_TYPE).build())
+ .getBody();
+
+ // create three SoftwareModules
+ final SoftwareModulesRest softwareModulesRest = softwareModuleResource
+ .createSoftwareModules(
+ new SoftwareModuleBuilder().name("firmware").version("1.0.0").type(SM_MODULE_TYPE).build())
+ .getBody();
+
+ // Assign SoftwareModule to DistributionSet
+ distributionSetResource.assignSoftwareModules(distributionSetsRest.get(0).getDsId(),
+ new SoftwareModuleAssigmentBuilder().id(softwareModulesRest.get(0).getModuleId()).build());
+
+ // create ten targets
+ targetResource.createTargets(new TargetBuilder().controllerId("00-FF-AA-0").name("00-FF-AA-0")
+ .description("Targets used for rollout example").buildAsList(10));
+
+ // create a Rollout
+ final RolloutResponseBody rolloutResponseBody = rolloutResource
+ .create(new RolloutBuilder().name("MyRollout").groupSize(2).targetFilterQuery("name==00-FF-AA-0*")
+ .distributionSetId(distributionSetsRest.get(0).getDsId()).successThreshold("80")
+ .errorThreshold("50").build())
+ .getBody();
+
+ // start the created Rollout
+ rolloutResource.start(rolloutResponseBody.getRolloutId(), false);
+ }
+
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/GettingStartedDefaultScenario.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/GettingStartedDefaultScenario.java
new file mode 100644
index 000000000..5767ab9e7
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/GettingStartedDefaultScenario.java
@@ -0,0 +1,134 @@
+/**
+ * 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.mgmt.client.scenarios;
+
+import org.eclipse.hawkbit.mgmt.client.resource.DistributionSetResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.DistributionSetTypeResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.SoftwareModuleResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.SoftwareModuleTypeResourceClient;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetTypeBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleAssigmentBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleBuilder;
+import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleTypeBuilder;
+import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetsRest;
+import org.eclipse.hawkbit.rest.resource.model.softwaremodule.SoftwareModulesRest;
+import org.eclipse.hawkbit.rest.resource.model.softwaremoduletype.SoftwareModuleTypesRest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Default getting started scenario.
+ *
+ */
+@Component
+public class GettingStartedDefaultScenario {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GettingStartedDefaultScenario.class);
+
+ /* known software module type name and key */
+ private static final String SM_MODULE_TYPE = "gettingstarted";
+
+ /* known distribution set type name and key */
+ private static final String DS_MODULE_TYPE = "gettingstarted";
+
+ /* known distribution name of this getting started example */
+ private static final String SM_EXAMPLE_NAME = "gettingstarted-example";
+
+ /* known distribution name of this getting started example */
+ private static final String DS_EXAMPLE_NAME = "gettingstarted-example";
+
+ @Autowired
+ private DistributionSetResourceClient distributionSetResource;
+
+ @Autowired
+ private DistributionSetTypeResourceClient distributionSetTypeResource;
+
+ @Autowired
+ private SoftwareModuleResourceClient softwareModuleResource;
+
+ @Autowired
+ private SoftwareModuleTypeResourceClient softwareModuleTypeResource;
+
+ /**
+ * Run the default getting started scenario.
+ */
+ public void run() {
+
+ LOGGER.info("Running Getting-Started-Scenario...");
+
+ // create one SoftwareModuleTypes
+ LOGGER.info("Creating software module type {}", SM_MODULE_TYPE);
+ final SoftwareModuleTypesRest createdSoftwareModuleTypes = softwareModuleTypeResource.createSoftwareModuleTypes(
+ new SoftwareModuleTypeBuilder().key(SM_MODULE_TYPE).name(SM_MODULE_TYPE).maxAssignments(1).build())
+ .getBody();
+
+ // create one DistributionSetType
+ LOGGER.info("Creating distribution set type {}", DS_MODULE_TYPE);
+ distributionSetTypeResource.createDistributionSetTypes(new DistributionSetTypeBuilder().key(DS_MODULE_TYPE)
+ .name(DS_MODULE_TYPE).mandatorymodules(createdSoftwareModuleTypes.get(0).getModuleId()).build());
+
+ // create three DistributionSet
+ final String dsVersion1 = "1.0.0";
+ final String dsVersion2 = "2.0.0";
+ final String dsVersion3 = "2.1.0";
+
+ LOGGER.info("Creating distribution set {}:{}", DS_EXAMPLE_NAME, dsVersion1);
+ final DistributionSetsRest distributionSetsRest1 = distributionSetResource.createDistributionSets(
+ new DistributionSetBuilder().name(DS_EXAMPLE_NAME).version(dsVersion1).type(DS_MODULE_TYPE).build())
+ .getBody();
+
+ LOGGER.info("Creating distribution set {}:{}", DS_EXAMPLE_NAME, dsVersion2);
+ final DistributionSetsRest distributionSetsRest2 = distributionSetResource.createDistributionSets(
+ new DistributionSetBuilder().name(DS_EXAMPLE_NAME).version(dsVersion2).type(DS_MODULE_TYPE).build())
+ .getBody();
+
+ LOGGER.info("Creating distribution set {}:{}", DS_EXAMPLE_NAME, dsVersion3);
+ final DistributionSetsRest distributionSetsRest3 = distributionSetResource.createDistributionSets(
+ new DistributionSetBuilder().name(DS_EXAMPLE_NAME).version(dsVersion3).type(DS_MODULE_TYPE).build())
+ .getBody();
+
+ // create three SoftwareModules
+ final String swVersion1 = "1";
+ final String swVersion2 = "2";
+ final String swVersion3 = "3";
+
+ LOGGER.info("Creating distribution set {}:{}", SM_EXAMPLE_NAME, swVersion1);
+ final SoftwareModulesRest softwareModulesRest1 = softwareModuleResource.createSoftwareModules(
+ new SoftwareModuleBuilder().name(SM_EXAMPLE_NAME).version(swVersion1).type(SM_MODULE_TYPE).build())
+ .getBody();
+ LOGGER.info("Creating distribution set {}:{}", SM_EXAMPLE_NAME, swVersion2);
+ final SoftwareModulesRest softwareModulesRest2 = softwareModuleResource.createSoftwareModules(
+ new SoftwareModuleBuilder().name(SM_EXAMPLE_NAME).version(swVersion2).type(SM_MODULE_TYPE).build())
+ .getBody();
+ LOGGER.info("Creating distribution set {}:{}", SM_EXAMPLE_NAME, swVersion3);
+ final SoftwareModulesRest softwareModulesRest3 = softwareModuleResource.createSoftwareModules(
+ new SoftwareModuleBuilder().name(SM_EXAMPLE_NAME).version(swVersion3).type(SM_MODULE_TYPE).build())
+ .getBody();
+
+ // Assign SoftwareModules to DistributionSet
+ LOGGER.info("Assign software module {}:{} to distribution set {}:{}", SM_EXAMPLE_NAME, swVersion1,
+ DS_EXAMPLE_NAME, dsVersion1);
+ distributionSetResource.assignSoftwareModules(distributionSetsRest1.get(0).getDsId(),
+ new SoftwareModuleAssigmentBuilder().id(softwareModulesRest1.get(0).getModuleId()).build());
+
+ LOGGER.info("Assign software module {}:{} to distribution set {}:{}", SM_EXAMPLE_NAME, swVersion2,
+ DS_EXAMPLE_NAME, dsVersion2);
+ distributionSetResource.assignSoftwareModules(distributionSetsRest2.get(0).getDsId(),
+ new SoftwareModuleAssigmentBuilder().id(softwareModulesRest2.get(0).getModuleId()).build());
+
+ LOGGER.info("Assign software module {}:{} to distribution set {}:{}", SM_EXAMPLE_NAME, swVersion3,
+ DS_EXAMPLE_NAME, dsVersion3);
+ distributionSetResource.assignSoftwareModules(distributionSetsRest3.get(0).getDsId(),
+ new SoftwareModuleAssigmentBuilder().id(softwareModulesRest3.get(0).getModuleId()).build());
+ }
+}
diff --git a/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties b/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties
new file mode 100644
index 000000000..d3a3eb969
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/resources/application.properties
@@ -0,0 +1,14 @@
+#
+# 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
+#
+
+hawkbit.url=localhost:8080
+hawkbit.username=admin
+hawkbit.password=admin
+
+spring.main.show-banner=false
\ No newline at end of file
diff --git a/examples/hawkbit-mgmt-api-client/src/main/resources/logback.xml b/examples/hawkbit-mgmt-api-client/src/main/resources/logback.xml
new file mode 100644
index 000000000..0174611e6
--- /dev/null
+++ b/examples/hawkbit-mgmt-api-client/src/main/resources/logback.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetTagTest.java b/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetTagTest.java
deleted file mode 100644
index 17384c211..000000000
--- a/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetTagTest.java
+++ /dev/null
@@ -1,186 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import static org.fest.assertions.api.Assertions.assertThat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.hawkbit.app.Start;
-import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetRequestBodyPost;
-import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetRest;
-import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetsRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.AssignedDistributionSetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.tag.DistributionSetTagAssigmentResultRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRequestBodyPut;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagsRest;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.springframework.boot.SpringApplication;
-import org.springframework.context.annotation.Description;
-
-import ru.yandex.qatools.allure.annotations.Features;
-import ru.yandex.qatools.allure.annotations.Stories;
-import feign.Feign;
-import feign.Logger;
-import feign.auth.BasicAuthRequestInterceptor;
-import feign.jackson.JacksonDecoder;
-import feign.jackson.JacksonEncoder;
-
-@Features("Example Tests - Management RESTful API Client")
-@Stories("DistrubutionSet Tag Resource")
-public class DistributionSetTagTest {
-
- private DistrubutionSetTagResource distrubutionSetTagResource;
-
- private static List assignedTargetRequestBodies;
-
- @BeforeClass
- public static void startupServer() {
- SpringApplication.run(Start.class, new String[0]);
- createTargetsAssignment();
- assignedTargetRequestBodies.add(new AssignedDistributionSetRequestBody().setDistributionSetId(100L));
- }
-
- @Before
- public void setup() {
- this.distrubutionSetTagResource = createDistrubutionSetTagResource();
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of distrubutionset tag by ID")
- @Ignore
- public void getDistributionSetTag() {
- final TagsRest result = createDistributionSetTags(2);
-
- assertThat(distrubutionSetTagResource.getDistributionSetTag(result.get(0).getTagId()).getName()).isEqualTo(
- "Tag0");
-
- deleteDistributionSets(result);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple update of a distrubutionset tag")
- @Ignore
- public void updateDistributionSetTag() {
- final TagsRest created = createDistributionSetTags(10);
-
- distrubutionSetTagResource.updateDistributionSetTag(created.get(0).getTagId(), new TagRequestBodyPut()
- .setDescription("Test").setName("Test").setColour("Green"));
-
- final TagRest targetTag = distrubutionSetTagResource.getDistributionSetTag(created.get(0).getTagId());
- assertThat(targetTag.getName()).isEqualTo("Test");
- assertThat(targetTag.getDescription()).isEqualTo("Test");
- assertThat(targetTag.getColour()).isEqualTo("Green");
-
- deleteDistributionSets(created);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of all assigned distrubutionsets by a distrubutionset tag.")
- @Ignore
- public void getDistributionSetByTagId() {
- final TagsRest created = createDistributionSetTags(10);
- distrubutionSetTagResource.assignDistributionSets(created.get(2).getTagId(), assignedTargetRequestBodies);
-
- final DistributionSetsRest distributionSetsRest = distrubutionSetTagResource
- .getAssignedDistributionSets(created.get(2).getTagId());
- assertThat(distributionSetsRest).hasSize(5);
-
- distrubutionSetTagResource.unassignDistributionSets(created.get(2).getTagId());
- deleteDistributionSets(created);
- }
-
- @Test
- @Description("Toggle request to unassigned all assigned distrubutionset and assign all unassigned distrubutionset.")
- @Ignore
- public void toggleTagAssignment() {
- final TagsRest created = createDistributionSetTags(10);
- final Long id = created.get(2).getTagId();
-
- distrubutionSetTagResource.assignDistributionSets(id, assignedTargetRequestBodies);
- distrubutionSetTagResource.unassignDistributionSet(id, assignedTargetRequestBodies.get(0)
- .getDistributionSetId());
-
- DistributionSetTagAssigmentResultRest assigmentResultRest = distrubutionSetTagResource.toggleTagAssignment(id,
- assignedTargetRequestBodies);
-
- final TagRest targetTag = distrubutionSetTagResource.getDistributionSetTag(created.get(2).getTagId());
- assertThat(assigmentResultRest.getAssignedDistributionSets()).hasSize(1);
- assertThat(assigmentResultRest.getUnassignedDistributionSets()).hasSize(0);
-
- assigmentResultRest = distrubutionSetTagResource.toggleTagAssignment(id, assignedTargetRequestBodies);
- assertThat(assigmentResultRest.getAssignedDistributionSets()).hasSize(0);
- assertThat(assigmentResultRest.getUnassignedDistributionSets()).hasSize(5);
-
- distrubutionSetTagResource.unassignDistributionSets(targetTag.getTagId());
- deleteDistributionSets(created);
- }
-
- private void deleteDistributionSets(final List tags) {
- for (final TagRest tag : tags) {
- distrubutionSetTagResource.deleteDistributionSetTag(tag.getTagId());
- }
- }
-
- private TagsRest createDistributionSetTags(final int number) {
-
- final List tags = new ArrayList<>();
- for (int i = 0; i < number; i++) {
- tags.add(new TagRequestBodyPut().setDescription("Tag " + i).setName("Tag" + i).setColour("Red"));
- }
-
- final TagsRest result = distrubutionSetTagResource.createDistributionSetTags(tags);
-
- assertThat(result).hasSize(number);
- return result;
- }
-
- private DistrubutionSetTagResource createDistrubutionSetTagResource() {
- final DistrubutionSetTagResource distrubutionSetTagResource = Feign.builder().logger(new Logger.ErrorLogger())
- .logLevel(Logger.Level.BASIC).decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
- .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
- .target(DistrubutionSetTagResource.class, "http://localhost:8080");
- return distrubutionSetTagResource;
- }
-
- private static void createTargetsAssignment() {
-
- final List sets = new ArrayList<>();
- assignedTargetRequestBodies = new ArrayList<>();
- for (int i = 0; i < 5; i++) {
- final DistributionSetRequestBodyPost bodyPost = (DistributionSetRequestBodyPost) new DistributionSetRequestBodyPost()
- .setName("Ds" + i).setDescription("Ds" + i).setVersion("" + i);
- sets.add(bodyPost);
- }
-
- final DistributionSetsRest result = createDistributionSetResource().createDistributionSets(sets);
- for (final DistributionSetRest rest : result) {
- assignedTargetRequestBodies.add(new AssignedDistributionSetRequestBody().setDistributionSetId(rest
- .getDsId()));
- }
-
- }
-
- private static DistributionSetResource createDistributionSetResource() {
- final DistributionSetResource distributionSetResource = Feign.builder().logger(new Logger.ErrorLogger())
- .logLevel(Logger.Level.BASIC).decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
- .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
- .target(DistributionSetResource.class, "http://localhost:8080");
- return distributionSetResource;
- }
-
-}
diff --git a/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/TargetTagTest.java b/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/TargetTagTest.java
deleted file mode 100644
index 10f18b704..000000000
--- a/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/TargetTagTest.java
+++ /dev/null
@@ -1,182 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import static org.fest.assertions.api.Assertions.assertThat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.hawkbit.app.Start;
-import org.eclipse.hawkbit.rest.resource.model.tag.AssignedTargetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRequestBodyPut;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TagsRest;
-import org.eclipse.hawkbit.rest.resource.model.tag.TargetTagAssigmentResultRest;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetRest;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetsRest;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.springframework.boot.SpringApplication;
-import org.springframework.context.annotation.Description;
-
-import ru.yandex.qatools.allure.annotations.Features;
-import ru.yandex.qatools.allure.annotations.Stories;
-import feign.Feign;
-import feign.Logger;
-import feign.auth.BasicAuthRequestInterceptor;
-import feign.jackson.JacksonDecoder;
-import feign.jackson.JacksonEncoder;
-
-@Features("Example Tests - Management RESTful API Client")
-@Stories("Target Tag Resource")
-public class TargetTagTest {
-
- private TargetTagResource targetTagResource;
-
- private static List assignedTargetRequestBodies;
-
- @BeforeClass
- public static void startupServer() {
- SpringApplication.run(Start.class, new String[0]);
- createTargetsAssignment();
- assignedTargetRequestBodies.add(new AssignedTargetRequestBody().setControllerId("NotExist"));
- }
-
- @Before
- public void setup() {
- this.targetTagResource = createTargetTagResource();
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of target tag by ID")
- @Ignore
- public void getTargetTag() {
- final TagsRest result = createTargetTags(2);
-
- assertThat(targetTagResource.getTargetTag(result.get(0).getTagId()).getName()).isEqualTo("Tag0");
-
- deleteTargets(result);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple update of a target tag")
- @Ignore
- public void updateTargetTag() {
- final TagsRest created = createTargetTags(10);
-
- targetTagResource.updateTagretTag(created.get(0).getTagId(), new TagRequestBodyPut().setDescription("Test")
- .setName("Test").setColour("Green"));
-
- final TagRest targetTag = targetTagResource.getTargetTag(created.get(0).getTagId());
- assertThat(targetTag.getName()).isEqualTo("Test");
- assertThat(targetTag.getDescription()).isEqualTo("Test");
- assertThat(targetTag.getColour()).isEqualTo("Green");
-
- deleteTargets(created);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of all assigned targets by a target tag.")
- @Ignore
- public void getTargetsByTargetTagId() {
- final TagsRest created = createTargetTags(10);
- final Long tagId = created.get(2).getTagId();
- targetTagResource.assignTargets(tagId, assignedTargetRequestBodies);
-
- final TagRest targetTag = targetTagResource.getTargetTag(tagId);
- assertThat(targetTagResource.getAssignedTargets(tagId)).hasSize(5);
-
- targetTagResource.unassignTargets(targetTag.getTagId());
- deleteTargets(created);
- }
-
- @Test
- @Description("Toggle request to unassigned all assigned targets and assign all unassigned targets.")
- @Ignore
- public void toggleTagAssignment() {
- final TagsRest created = createTargetTags(10);
- final Long id = created.get(2).getTagId();
-
- targetTagResource.assignTargets(id, assignedTargetRequestBodies);
- targetTagResource.unassignTarget(id, assignedTargetRequestBodies.get(0).getControllerId());
-
- TargetTagAssigmentResultRest assigmentResultRest = targetTagResource.toggleTagAssignment(id,
- assignedTargetRequestBodies);
-
- final TagRest targetTag = targetTagResource.getTargetTag(created.get(2).getTagId());
- assertThat(assigmentResultRest.getAssignedTargets()).hasSize(1);
- assertThat(assigmentResultRest.getUnassignedTargets()).hasSize(0);
-
- assigmentResultRest = targetTagResource.toggleTagAssignment(id, assignedTargetRequestBodies);
- assertThat(assigmentResultRest.getAssignedTargets()).hasSize(0);
- assertThat(assigmentResultRest.getUnassignedTargets()).hasSize(5);
-
- targetTagResource.unassignTargets(targetTag.getTagId());
- deleteTargets(created);
- }
-
- private void deleteTargets(final List tags) {
- for (final TagRest tag : tags) {
- targetTagResource.deleteTargetTag(tag.getTagId());
- }
- }
-
- private TagsRest createTargetTags(final int number) {
-
- final List tags = new ArrayList<>();
- for (int i = 0; i < number; i++) {
- tags.add(new TagRequestBodyPut().setDescription("Tag " + i).setName("Tag" + i).setColour("Red"));
- }
-
- final TagsRest result = targetTagResource.createTargetTag(tags);
-
- assertThat(result).hasSize(number);
- return result;
- }
-
- private TargetTagResource createTargetTagResource() {
- final TargetTagResource targetResource = Feign.builder().logger(new Logger.ErrorLogger())
- .logLevel(Logger.Level.BASIC).decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
- .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
- .target(TargetTagResource.class, "http://localhost:8080");
- return targetResource;
- }
-
- private static void createTargetsAssignment() {
-
- final List targets = new ArrayList<>();
- assignedTargetRequestBodies = new ArrayList<>();
- for (int i = 0; i < 5; i++) {
-
- targets.add(new TargetRequestBody().setControllerId("test" + i).setName("testDevice"));
- }
-
- final TargetsRest result = createTargetResource().createTargets(targets);
- for (final TargetRest rest : result) {
- assignedTargetRequestBodies.add(new AssignedTargetRequestBody().setControllerId(rest.getControllerId()));
- }
-
- }
-
- private static TargetResource createTargetResource() {
- final TargetResource targetResource = Feign.builder().logger(new Logger.ErrorLogger())
- .logLevel(Logger.Level.BASIC).decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
- .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
- .target(TargetResource.class, "http://localhost:8080");
- return targetResource;
- }
-
-}
diff --git a/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/TargetTest.java b/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/TargetTest.java
deleted file mode 100644
index 745b119f6..000000000
--- a/examples/hawkbit-mgmt-api-client/src/test/java/org/eclipse/hawkbit/mgmt/api/client/TargetTest.java
+++ /dev/null
@@ -1,119 +0,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
- */
-package org.eclipse.hawkbit.mgmt.api.client;
-
-import static org.fest.assertions.api.Assertions.assertThat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.hawkbit.app.Start;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetPagedList;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetRequestBody;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetRest;
-import org.eclipse.hawkbit.rest.resource.model.target.TargetsRest;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.springframework.boot.SpringApplication;
-import org.springframework.context.annotation.Description;
-
-import feign.Feign;
-import feign.Logger;
-import feign.auth.BasicAuthRequestInterceptor;
-import feign.jackson.JacksonDecoder;
-import feign.jackson.JacksonEncoder;
-import ru.yandex.qatools.allure.annotations.Features;
-import ru.yandex.qatools.allure.annotations.Stories;
-
-@Features("Example Tests - Management RESTful API Client")
-@Stories("Target Resource")
-public class TargetTest {
-
- @BeforeClass
- public static void startupServer() {
- SpringApplication.run(Start.class, new String[0]);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of target by ID")
- @Ignore
- public void getTarget() {
- final TargetResource targetResource = createTargetResource();
- final TargetsRest result = createTargets(targetResource, 1);
-
- assertThat(targetResource.getTarget("test0").getName()).isEqualTo("testDevice");
-
- deleteTargets(targetResource, result);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of all targets with defined page sizing information (i.e. offset and limit).")
- @Ignore
- public void getTargetsAsPagedListWithDefinedPageSizing() {
- final TargetResource targetResource = createTargetResource();
- final TargetsRest created = createTargets(targetResource, 20);
-
- final TargetPagedList queryResult = targetResource.getTargets(0, 10);
-
- assertThat(queryResult.getContent()).hasSize(10);
- assertThat(queryResult.getTotal()).isEqualTo(20);
- assertThat(queryResult.getSize()).isEqualTo(10);
-
- deleteTargets(targetResource, created);
- }
-
- // disabled as this runs not on CI environments.
- @Test
- @Description("Simple request of all targets with defualt paging parameters.")
- @Ignore
- public void getTargetsAsPagedListWithDefaultPageSizing() {
- final TargetResource targetResource = createTargetResource();
- final TargetsRest created = createTargets(targetResource, 20);
-
- final TargetPagedList queryResult = targetResource.getTargets();
-
- assertThat(queryResult.getContent()).hasSize(20);
- assertThat(queryResult.getTotal()).isEqualTo(20);
- assertThat(queryResult.getSize()).isEqualTo(20);
-
- deleteTargets(targetResource, created);
- }
-
- private void deleteTargets(final TargetResource targetResource, final List targets) {
- for (final TargetRest targetRest : targets) {
- targetResource.deleteTarget(targetRest.getControllerId());
- }
- }
-
- private TargetsRest createTargets(final TargetResource targetResource, final int number) {
-
- final List targets = new ArrayList<>();
- for (int i = 0; i < number; i++) {
-
- targets.add(new TargetRequestBody().setControllerId("test" + i).setName("testDevice"));
- }
-
- final TargetsRest result = targetResource.createTargets(targets);
-
- assertThat(result).hasSize(number);
- return result;
- }
-
- private TargetResource createTargetResource() {
- final TargetResource targetResource = Feign.builder().logger(new Logger.ErrorLogger())
- .logLevel(Logger.Level.BASIC).decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
- .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
- .target(TargetResource.class, "http://localhost:8080");
- return targetResource;
- }
-
-}
diff --git a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/MongoDBTestRule.java b/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/MongoDBTestRule.java
index d8485787e..f5d88ceaa 100644
--- a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/MongoDBTestRule.java
+++ b/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/MongoDBTestRule.java
@@ -88,7 +88,7 @@ public class MongoDBTestRule implements TestRule {
System.setProperty("spring.data.mongodb.port", String.valueOf(port));
}
- Version version = Version.V3_1_0;
+ Version version = Version.V3_0_8;
if (System.getProperty("inf.mongodb.version") != null) {
version = Version.valueOf("V" + System.getProperty("inf.mongodb.version").trim().replaceAll("\\.", "_"));
}
diff --git a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java b/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java
index 2a8441ee6..c5aca8e1a 100644
--- a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java
+++ b/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java
@@ -34,6 +34,12 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.google.common.io.BaseEncoding;
import com.mongodb.gridfs.GridFSDBFile;
+import ru.yandex.qatools.allure.annotations.Description;
+import ru.yandex.qatools.allure.annotations.Features;
+import ru.yandex.qatools.allure.annotations.Stories;
+
+@Features("Component Tests - Repository")
+@Stories("Artifact Store MongoDB")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { ArtifactStoreAutoConfiguration.class, TestConfiguration.class })
public class ArtifactStoreTest {
@@ -48,6 +54,7 @@ public class ArtifactStoreTest {
private GridFsOperations gridFs;
@Test
+ @Description("Ensures that storage in MongoDB is correctly executed.s")
public void storeArtifactInMongoDB() {
final int filelengthBytes = 128;
final String filename = "testfile.json";
@@ -60,6 +67,7 @@ public class ArtifactStoreTest {
}
@Test
+ @Description("Ensures that search by SHA1 hash (which is used by hawkBit as artifact ID) finds the expected results.")
public void findArtifactBySHA1Hash() throws NoSuchAlgorithmException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
@@ -72,6 +80,7 @@ public class ArtifactStoreTest {
}
@Test
+ @Description("Ensures that search by MD5 hash finds the expected results.")
public void findArtifactByMD5Hash() throws NoSuchAlgorithmException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
@@ -84,6 +93,7 @@ public class ArtifactStoreTest {
}
@Test
+ @Description("Ensures that artifact content can be read through InputStream.")
public void getInputStreamFromArtifact() throws IOException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
@@ -103,7 +113,8 @@ public class ArtifactStoreTest {
}
@Test
- public void deleteArtifactWithOnlyOneTenantLast() throws NoSuchAlgorithmException {
+ @Description("Ensures that artifact delete actually results in deletion from database.")
+ public void deleteArtifact() throws NoSuchAlgorithmException {
final int filelengthBytes = 128;
final String filename = "testfile.json";
final String contentType = "application/json";
diff --git a/hawkbit-autoconfigure/pom.xml b/hawkbit-autoconfigure/pom.xml
index 11a106a4b..7670da406 100644
--- a/hawkbit-autoconfigure/pom.xml
+++ b/hawkbit-autoconfigure/pom.xml
@@ -72,5 +72,10 @@
org.springframeworkspring-context-support
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/conf/ControllerPollAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/conf/ControllerPollAutoConfiguration.java
deleted file mode 100644
index bcbc5ec16..000000000
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/conf/ControllerPollAutoConfiguration.java
+++ /dev/null
@@ -1,27 +0,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
- */
-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 {
-
-}
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/eventbus/EventBusAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/eventbus/EventBusAutoConfiguration.java
index e09c0a68e..37e72880c 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/eventbus/EventBusAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/eventbus/EventBusAutoConfiguration.java
@@ -12,6 +12,7 @@ import java.util.concurrent.Executor;
import org.eclipse.hawkbit.eventbus.EventBusSubscriberProcessor;
import org.eclipse.hawkbit.eventbus.EventSubscriber;
+import org.eclipse.hawkbit.repository.model.helper.EventBusHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -54,4 +55,12 @@ public class EventBusAutoConfiguration {
return new EventBusSubscriberProcessor();
}
+ /**
+ * @return the singleton instance of the {@link EventBusHolder}
+ */
+ @Bean
+ public EventBusHolder eventBusHolder() {
+ return EventBusHolder.getInstance();
+ }
+
}
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java
index f7f18f1b0..35996a114 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/AsyncConfigurerThreadpoolProperties.java
@@ -13,17 +13,29 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for the async configurer.
*
- *
*/
@ConfigurationProperties("hawkbit.threadpool")
public class AsyncConfigurerThreadpoolProperties {
+ /**
+ * Max queue size for central event executor.
+ */
private Integer queuesize = 250;
+ /**
+ * Core processing threads for central event executor.
+ */
private Integer corethreads = 5;
+ /**
+ * Maximum thread pool size for central event executor.
+ */
private Integer maxthreads = 50;
+ /**
+ * When the number of threads is greater than the core, this is the maximum
+ * time that excess idle threads will wait for new tasks before terminating.
+ */
private Long idletimeout = 10000L;
public Integer getQueuesize() {
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java
index f8a86d1bc..43a096e7d 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/scheduling/ExecutorAutoConfiguration.java
@@ -26,10 +26,9 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
- *
+ * Central event processors inside update server.
*
*/
-
@Configuration
@EnableConfigurationProperties(AsyncConfigurerThreadpoolProperties.class)
public class ExecutorAutoConfiguration {
@@ -40,7 +39,7 @@ public class ExecutorAutoConfiguration {
private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties;
/**
- * @return ExecutorService for general pupose multi threaded operations
+ * @return ExecutorService for general purpose multi threaded operations
*/
@Bean
@ConditionalOnMissingBean
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java
index 326ff4774..8a3a50ad2 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java
@@ -20,7 +20,7 @@ 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.DdiSecurityProperties;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
@@ -53,7 +53,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
*
*/
@Configuration
-@EnableConfigurationProperties(SecurityProperties.class)
+@EnableConfigurationProperties(DdiSecurityProperties.class)
public class SecurityAutoConfiguration {
/**
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java
index ca55f1711..24e9667c2 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java
@@ -29,31 +29,31 @@ import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationTok
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.rest.resource.RestConstants;
import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSource;
+import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DosFilter;
+import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
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.security.SystemSecurityContext;
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.boot.context.embedded.ServletListenerRegistrationBean;
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;
@@ -94,17 +94,11 @@ import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSucc
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true)
@EnableWebMvcSecurity
@Order(value = Ordered.HIGHEST_PRECEDENCE)
-public class SecurityManagedConfiguration implements EnvironmentAware {
+public class SecurityManagedConfiguration {
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);
-
- }
+ @Autowired
+ private HawkbitSecurityProperties securityProperties;
/**
* {@link WebSecurityConfigurer} for the internal SP controller API.
@@ -119,33 +113,36 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Autowired
private ControllerManagement controllerManagement;
@Autowired
- private SystemManagement systemManagement;
+ private TenantConfigurationManagement tenantConfigurationManagement;
@Autowired
private TenantAware tenantAware;
@Autowired
- private SecurityProperties securityConfiguration;
+ private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
+ @Autowired
+ private SystemSecurityContext systemSecurityContext;
@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);
+ ddiSecurityConfiguration.getRp().getCnHeader(),
+ ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
+ tenantAware, systemSecurityContext);
securityHeaderFilter.setAuthenticationManager(authenticationManager());
securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
- systemManagement, tenantAware, controllerManagement);
+ tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
securityTokenFilter.setAuthenticationManager(authenticationManager());
securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
- systemManagement, tenantAware);
+ tenantConfigurationManagement, tenantAware, systemSecurityContext);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager());
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
@@ -158,7 +155,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
}
- if (securityConfiguration.getAnonymousEnabled()) {
+ if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info(
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
@@ -177,19 +174,10 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
}
}
- /*
- * (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()));
+ auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
+ ddiSecurityConfiguration.getRp().getTrustedIPs()));
}
}
@@ -204,13 +192,10 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
public FilterRegistrationBean dosFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
- filterRegBean
- .setFilter(
- new DosFilter(environment.getProperty("security.dos.filter.maxRead", Integer.class, 200),
- environment.getProperty("security.dos.filter.maxWrite", Integer.class, 50),
- environment.getProperty("security.dos.filter.whitelist"), environment
- .getProperty("security.clients.blacklist"),
- environment.getProperty("security.rp.remote_ip_header", String.class, "X-Forwarded-For")));
+ filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(),
+ securityProperties.getDos().getFilter().getMaxWrite(),
+ securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(),
+ securityProperties.getClients().getRemoteIpHeader()));
filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*");
return filterRegBean;
}
@@ -310,8 +295,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Configuration
@Order(400)
@EnableVaadinSecurity
- public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter
- implements EnvironmentAware {
+ public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private static final String XFRAME_OPTION_DENY = "DENY";
private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN";
@@ -320,13 +304,8 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
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);
- }
+ @Autowired
+ private HawkbitSecurityProperties securityProperties;
/**
* post construct for setting the authentication success handler for the
@@ -379,13 +358,13 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
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) {
+ final String confXframeOption = securityProperties.getXframe().getOption();
+ final String confAllowFromUri = securityProperties.getXframe().getAllowfrom();
+ if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri.isEmpty()) {
// 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, "
+ + " but no hawkbit.server.security.xframe.allowfrom has been set, "
+ "please ensure to set allow from URIs");
}
@@ -461,7 +440,7 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired
- private SecurityProperties securityConfiguration;
+ private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE)
@@ -483,8 +462,8 @@ public class SecurityManagedConfiguration implements EnvironmentAware {
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
- auth.authenticationProvider(
- new PreAuthTokenSourceTrustAuthenticationProvider(securityConfiguration.getRpTrustedIPs()));
+ auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
+ ddiSecurityConfiguration.getRp().getTrustedIPs()));
}
}
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java
index eb925e281..0bd4a8240 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.autoconfigure.url;
import java.net.MalformedURLException;
import java.net.URL;
+import org.eclipse.hawkbit.HawkbitServerProperties;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -27,11 +28,11 @@ import com.google.common.base.Throwables;
*
*/
@Configuration
-@EnableConfigurationProperties(ServerProperties.class)
+@EnableConfigurationProperties(HawkbitServerProperties.class)
public class PropertyHostnameResolverAutoConfiguration {
@Autowired
- private ServerProperties serverProperties;
+ private HawkbitServerProperties serverProperties;
/**
* @return the default autoconfigure hostname resolver implementation which
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/ServerProperties.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/ServerProperties.java
deleted file mode 100644
index 24c9dfb0e..000000000
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/ServerProperties.java
+++ /dev/null
@@ -1,30 +0,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
- */
-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;
- }
-}
diff --git a/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories b/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories
index 5d8d005d9..335054585 100644
--- a/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -11,5 +11,4 @@ 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
diff --git a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties
index 69c9a47d2..198fce255 100644
--- a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties
+++ b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties
@@ -7,12 +7,6 @@
# 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
@@ -21,7 +15,6 @@ 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
@@ -29,29 +22,26 @@ 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.productionMode=true
vaadin.servlet.urlMapping=/UI/*
-vaadin.servlet.params.heartbeatInterval=60
-vaadin.servlet.params.closeIdleSessions=false
-
-# Spring MVC
-spring.mvc.favicon.enabled=false
-
+vaadin.servlet.heartbeatInterval=60
+vaadin.servlet.closeIdleSessions=false
# Defines the thread pool executor
hawkbit.threadpool.corethreads=5
hawkbit.threadpool.maxthreads=20
hawkbit.threadpool.idletimeout=10000
-hawkbit.threadpool.queuesize=250
+hawkbit.threadpool.queuesize=20000
# Defines the polling time for the controllers in HH:MM:SS notation
hawkbit.controller.pollingTime=00:05:00
hawkbit.controller.pollingOverdueTime=00:05:00
+hawkbit.controller.maxPollingTime=23:59:59
+hawkbit.controller.minPollingTime=00:00:30
+# Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window
-## Configuration for RabbitMQ integration
+
+# Configuration for RabbitMQ integration
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
-hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
+hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
\ No newline at end of file
diff --git a/hawkbit-cache-redis/pom.xml b/hawkbit-cache-redis/pom.xml
index 99c8328b5..09567291b 100644
--- a/hawkbit-cache-redis/pom.xml
+++ b/hawkbit-cache-redis/pom.xml
@@ -37,6 +37,11 @@
redis.clientsjedis
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
diff --git a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java
index 2d1b99c98..edc183b17 100644
--- a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java
+++ b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisConfiguration.java
@@ -26,8 +26,6 @@ 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)
@@ -75,7 +73,7 @@ public class RedisConfiguration {
*/
@Bean
public RedisTemplate redisTemplate() {
- final RedisTemplate redisTemplate = new RedisTemplate();
+ final RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
diff --git a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java
index c228cde4c..ab409bbf5 100644
--- a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java
+++ b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/RedisProperties.java
@@ -14,14 +14,18 @@ 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 {
+ /**
+ * Redis server hostname.
+ */
private String host;
+
+ /**
+ * Redis server port.
+ */
private int port;
/**
diff --git a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/eventbus/EventDistributor.java b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/eventbus/EventDistributor.java
index e52128cbc..8749340f0 100644
--- a/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/eventbus/EventDistributor.java
+++ b/hawkbit-cache-redis/src/main/java/org/eclipse/hawkbit/cache/eventbus/EventDistributor.java
@@ -95,7 +95,7 @@ public class EventDistributor {
* wants to subscribe
*/
public Collection getTopics() {
- final List topics = new ArrayList();
+ final List topics = new ArrayList<>();
topics.add(new PatternTopic(SUB_DISTRIBUTION_CHANNEL));
return topics;
}
diff --git a/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/eventbus/EventDistributorTest.java b/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/eventbus/EventDistributorTest.java
index e44889f60..c1ff54961 100644
--- a/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/eventbus/EventDistributorTest.java
+++ b/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/eventbus/EventDistributorTest.java
@@ -29,7 +29,13 @@ import org.springframework.hateoas.Identifiable;
import com.google.common.eventbus.EventBus;
+import ru.yandex.qatools.allure.annotations.Features;
+import ru.yandex.qatools.allure.annotations.Stories;
+
+@Features("Unit Tests - Cluster Cache")
+@Stories("EventDistributor Test")
@RunWith(MockitoJUnitRunner.class)
+// TODO: create description annotations
public class EventDistributorTest {
@Mock
diff --git a/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/redis/RedisPropertiesTest.java b/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/redis/RedisPropertiesTest.java
index 6aac1b03f..ab57dd541 100644
--- a/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/redis/RedisPropertiesTest.java
+++ b/hawkbit-cache-redis/src/test/java/org/eclipse/hawkbit/cache/redis/RedisPropertiesTest.java
@@ -13,6 +13,11 @@ import static org.fest.assertions.api.Assertions.assertThat;
import org.eclipse.hawkbit.cache.RedisProperties;
import org.junit.Test;
+import ru.yandex.qatools.allure.annotations.Features;
+import ru.yandex.qatools.allure.annotations.Stories;
+
+@Features("Unit Tests - Cluster Cache")
+@Stories("Redis Properties Test")
public class RedisPropertiesTest {
@Test
diff --git a/hawkbit-core/.gitignore b/hawkbit-core/.gitignore
new file mode 100644
index 000000000..c71ea97ab
--- /dev/null
+++ b/hawkbit-core/.gitignore
@@ -0,0 +1 @@
+/.apt_generated/
diff --git a/hawkbit-core/pom.xml b/hawkbit-core/pom.xml
index e5d0f65fd..f9e140d40 100644
--- a/hawkbit-core/pom.xml
+++ b/hawkbit-core/pom.xml
@@ -32,7 +32,6 @@
com.google.guavaguava
-
org.springframework.boot
@@ -44,6 +43,11 @@
allure-junit-adaptortest
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
\ No newline at end of file
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java
index 694d5c016..c923963d2 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/ControllerPollProperties.java
@@ -8,35 +8,51 @@
*/
package org.eclipse.hawkbit;
+import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.stereotype.Component;
/**
- * Defines the polling time for the controllers in HH:MM:SS notation.
+ * Defines global configuration for the controllers/clients on the provisioning
+ * targets/devices.
+ *
+ *
+ * Note: many of the controller related properties can be overridden on tenant
+ * level. As a result they are not defined here but in
+ * {@link TenantConfigurationKey} and injected using {@link EnvironmentAware}.
*
- *
- *
*/
-
+@Component
@ConfigurationProperties(prefix = "hawkbit.controller")
public class ControllerPollProperties {
- private String pollingTime = "00:05:00";
- private String pollingOverdueTime = "00:05:00";
+ /**
+ * Maximum polling time that can be configured by a tenant in HH:MM:SS
+ * notation.
+ */
+ private String maxPollingTime = "23:59:00";
- public String getPollingTime() {
- return pollingTime;
+ /**
+ * Minimum polling time that can be configured by a tenant in HH:MM:SS
+ * notation.
+ */
+ private String minPollingTime = "00:00:30";
+
+ public String getMaxPollingTime() {
+ return maxPollingTime;
}
- public void setPollingTime(final String pollingTime) {
- this.pollingTime = pollingTime;
+ public void setMaxPollingTime(final String maxPollingTime) {
+ this.maxPollingTime = maxPollingTime;
}
- public String getPollingOverdueTime() {
- return pollingOverdueTime;
+ public String getMinPollingTime() {
+ return minPollingTime;
}
- public void setPollingOverdueTime(final String pollingOverdue) {
- this.pollingOverdueTime = pollingOverdue;
+ public void setMinPollingTime(final String minPollingTime) {
+ this.minPollingTime = minPollingTime;
}
}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitServerProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitServerProperties.java
new file mode 100644
index 000000000..878965102
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitServerProperties.java
@@ -0,0 +1,97 @@
+/**
+ * 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;
+
+/**
+ * Properties for the server e.g. the server's URL which must be configured.
+ *
+ */
+@ConfigurationProperties("hawkbit.server")
+public class HawkbitServerProperties {
+ /**
+ * Defines under which URI the update server can be reached. Used to
+ * calculate download URLs for DMF transmitted update actions.
+ */
+ private String url = "http://localhost:8080";
+
+ private final Build build = new Build();
+
+ public Build getBuild() {
+ return build;
+ }
+
+ /**
+ * Build information of the hawkBit instance. Influenced by maven.
+ *
+ */
+ public static class Build {
+ /**
+ * Project artifact ID.
+ */
+ private String artifact = "";
+
+ /**
+ * Project name.
+ */
+ private String name = "";
+
+ /**
+ * Project description.
+ */
+ private String description = "";
+
+ /**
+ * Project version.
+ */
+ private String version = "";
+
+ public String getArtifact() {
+ return artifact;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setArtifact(final String artifact) {
+ this.artifact = artifact;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ public void setVersion(final String version) {
+ this.version = version;
+ }
+
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(final String url) {
+ this.url = url;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/TenantAwareCacheManager.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/TenantAwareCacheManager.java
index 06d6e1719..435f1b2e1 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/TenantAwareCacheManager.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/TenantAwareCacheManager.java
@@ -51,7 +51,12 @@ public class TenantAwareCacheManager implements TenancyCacheManager {
@Override
public Cache getCache(final String name) {
- final String currentTenant = tenantAware.getCurrentTenant().toUpperCase();
+ String currentTenant = tenantAware.getCurrentTenant();
+ if (currentTenant == null) {
+ return null;
+ }
+
+ currentTenant = currentTenant.toUpperCase();
if (currentTenant.contains(TENANT_CACHE_DELIMITER)) {
return null;
}
@@ -60,7 +65,12 @@ public class TenantAwareCacheManager implements TenancyCacheManager {
@Override
public Collection getCacheNames() {
- final String currentTenant = tenantAware.getCurrentTenant().toUpperCase();
+ String currentTenant = tenantAware.getCurrentTenant();
+ if (currentTenant == null) {
+ return null;
+ }
+
+ currentTenant = currentTenant.toUpperCase();
if (currentTenant.contains(TENANT_CACHE_DELIMITER)) {
return Collections.emptyList();
}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractEvent.java
new file mode 100644
index 000000000..498418169
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractEvent.java
@@ -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.eventbus.event;
+
+/**
+ * Abstract event definition class which holds the necessary revsion and tenant
+ * information which every event needs.
+ *
+ * @author Michael Hirsch
+ * @see AbstractDistributedEvent for events which should be distributed to other
+ * cluster nodes
+ */
+public class AbstractEvent implements Event {
+
+ private final long revision;
+ private final String tenant;
+
+ /**
+ * @param revision
+ * the revision number of the event
+ * @param tenant
+ * the tenant of the event
+ */
+ protected AbstractEvent(final long revision, final String tenant) {
+ this.revision = revision;
+ this.tenant = tenant;
+ }
+
+ @Override
+ public long getRevision() {
+ return revision;
+ }
+
+ @Override
+ public String getTenant() {
+ return tenant;
+ }
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java
index ad421b266..da93cc1a3 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java
@@ -17,7 +17,7 @@ import java.net.URI;
*
*
*/
-public class CancelTargetAssignmentEvent {
+public class CancelTargetAssignmentEvent extends AbstractEvent {
private final String controllerId;
private final Long actionId;
@@ -26,6 +26,10 @@ public class CancelTargetAssignmentEvent {
/**
* Creates a new {@link CancelTargetAssignmentEvent}.
*
+ * @param revision
+ * the revision for this event
+ * @param tenant
+ * the tenant for this event
* @param controllerId
* the ID of the controller
* @param actionId
@@ -33,7 +37,9 @@ public class CancelTargetAssignmentEvent {
* @param targetAdress
* the targetAdress of the target
*/
- public CancelTargetAssignmentEvent(final String controllerId, final Long actionId, final URI targetAdress) {
+ public CancelTargetAssignmentEvent(final long revision, final String tenant, final String controllerId,
+ final Long actionId, final URI targetAdress) {
+ super(revision, tenant);
this.controllerId = controllerId;
this.actionId = actionId;
this.targetAdress = targetAdress;
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
index 91559cc88..003b31cd7 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
@@ -65,6 +65,7 @@ public enum SpServerError {
*/
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection",
"The given sort parameter direction does not exist"),
+
/**
*
*/
@@ -174,7 +175,23 @@ public enum SpServerError {
*
*/
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly",
- "The given entity is read only and the change cannot be completed.");
+ "The given entity is read only and the change cannot be completed."),
+
+ /**
+ *
+ */
+ SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid",
+ "The given configuration value is invalid."),
+ /**
+ *
+ */
+ SP_CONFIGURATION_KEY_INVALID("hawkbit.server.error.configKeyInvalid", "The given configuration key is invalid."),
+
+ /**
+ *
+ */
+ SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate",
+ "The rollout is currently in the wrong state for the current operation");
private final String key;
private final String message;
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerRtException.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerRtException.java
index a605f7745..ba691bb5f 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerRtException.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerRtException.java
@@ -74,6 +74,9 @@ public abstract class SpServerRtException extends RuntimeException {
this.error = error;
}
+ /**
+ * @return the SpServerError which is wrapped by this exception
+ */
public SpServerError getError() {
return error;
}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java
index 22fa42474..ef8bf3c98 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionStatusFields.java
@@ -20,7 +20,12 @@ public enum ActionStatusFields implements FieldNameProvider {
/**
* The id field.
*/
- ID("id");
+ ID("id"),
+
+ /**
+ * The reportedAt field.
+ */
+ REPORTEDAT("createdAt");
private final String fieldName;
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java
index eef36f552..fb9166529 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java
@@ -20,7 +20,7 @@ import java.util.Map;
*/
public interface FieldNameProvider {
/**
- * Seperator for the sub attributes
+ * Separator for the sub attributes
*/
public static final String SUB_ATTRIBUTE_SEPERATOR = ".";
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/RolloutFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/RolloutFields.java
new file mode 100644
index 000000000..2c7f7b971
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/RolloutFields.java
@@ -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.repository;
+
+/**
+ * Describing the fields of the Rollout model which can be used in the REST API
+ * e.g. for sorting etc.
+ *
+ */
+public enum RolloutFields implements FieldNameProvider {
+ /**
+ * The name field.
+ */
+ NAME("name"),
+ /**
+ * The description field.
+ */
+ DESCRIPTION("description"),
+ /**
+ * The id field.
+ */
+ ID("id");
+
+ private final String fieldName;
+
+ private RolloutFields(final String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ @Override
+ public String getFieldName() {
+ return fieldName;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupFields.java
new file mode 100644
index 000000000..2c8ebf8ac
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupFields.java
@@ -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.repository;
+
+/**
+ * Describing the fields of the RolloutGroup model which can be used in the REST
+ * API e.g. for sorting etc.
+ *
+ */
+public enum RolloutGroupFields implements FieldNameProvider {
+ /**
+ * The name field.
+ */
+ NAME("name"),
+ /**
+ * The description field.
+ */
+ DESCRIPTION("description"),
+ /**
+ * The id field.
+ */
+ ID("id");
+
+ private final String fieldName;
+
+ private RolloutGroupFields(final String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ @Override
+ public String getFieldName() {
+ return fieldName;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeFields.java
index beb46a9d7..68e4f4427 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeFields.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeFields.java
@@ -36,7 +36,7 @@ public enum SoftwareModuleTypeFields implements FieldNameProvider {
/**
* The max ds assignments field.
*/
- MAX("maxAssignments");
+ MAXASSIGNMENTS("maxAssignments");
private final String fieldName;
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java
index 1b4daaf66..0a696fdb6 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java
@@ -51,7 +51,16 @@ public enum TargetFields implements FieldNameProvider {
*/
ATTRIBUTE("targetInfo.controllerAttributes", true),
+ /**
+ * distribution sets which is assigned to the target.
+ */
ASSIGNEDDS("assignedDistributionSet", "name", "version"),
+
+ /**
+ * distribution sets which is installed on the target.
+ */
+ INSTALLEDDS("targetInfo.installedDistributionSet", "name", "version"),
+
/**
* The tags field.
*/
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/DurationHelper.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/DurationHelper.java
new file mode 100644
index 000000000..8e7c46223
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/DurationHelper.java
@@ -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.tenancy.configuration;
+
+import java.time.Duration;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalAccessor;
+
+/**
+ * This class is a helper for converting a duration into a string and for the
+ * other way. The string is in the format expected in configuration and database
+ * {@link #DURATION_FORMAT}.
+ *
+ */
+public final class DurationHelper {
+
+ /**
+ * Duration validation utility class. Checks if the requested duration is in
+ * the defined min/max range.
+ *
+ */
+ public static class DurationRangeValidator {
+ final Duration min;
+ final Duration max;
+
+ private DurationRangeValidator(final Duration min, final Duration max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ public boolean isWithinRange(final Duration duration) {
+ return duration.compareTo(min) > 0 && duration.compareTo(max) < 0;
+ }
+ }
+
+ /**
+ * Format of the String expected in configuration file and in the database.
+ */
+ public static final String DURATION_FORMAT = "HH:mm:ss";
+
+ private DurationHelper() {
+ // utility class
+ }
+
+ /**
+ * Creates a {@link DurationRangeValidator}.
+ *
+ * @param min
+ * imum of range.
+ * @param max
+ * imum of range.
+ * @return {@link DurationRangeValidator} range.
+ */
+ public static DurationRangeValidator durationRangeValidator(final Duration min, final Duration max) {
+ return new DurationRangeValidator(min, max);
+ }
+
+ /**
+ * Converts a Duration into a formatted String
+ *
+ * @param duration
+ * duration, which will be converted into a formatted String
+ * @return String in the duration format, specified at
+ * {@link #DURATION_FORMAT}
+ */
+ public static String durationToFormattedString(final Duration duration) {
+ if (duration == null) {
+ return null;
+ }
+
+ return LocalTime.ofNanoOfDay(duration.toNanos()).format(DateTimeFormatter.ofPattern(DURATION_FORMAT));
+ }
+
+ /**
+ * Converts a formatted String into a Duration object.
+ *
+ * @param formattedDuration
+ * String in {@link #DURATION_FORMAT}
+ * @return duration
+ * @throws DateTimeParseException
+ * when String is in wrong format
+ */
+ public static Duration formattedStringToDuration(final String formattedDuration) {
+ if (formattedDuration == null) {
+ return null;
+ }
+
+ final TemporalAccessor ta = DateTimeFormatter.ofPattern(DURATION_FORMAT).parse(formattedDuration.trim());
+ return Duration.between(LocalTime.MIDNIGHT, LocalTime.from(ta));
+ }
+
+ /**
+ * converts values of time constants to a Duration object..
+ *
+ * @param hours
+ * count of hours
+ * @param minutes
+ * count of minutes
+ * @param seconds
+ * count of seconds
+ * @return duration
+ */
+ public static Duration getDurationByTimeValues(final long hours, final long minutes, final long seconds) {
+ return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds);
+ }
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/InvalidTenantConfigurationKeyException.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/InvalidTenantConfigurationKeyException.java
new file mode 100644
index 000000000..dcc42065c
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/InvalidTenantConfigurationKeyException.java
@@ -0,0 +1,63 @@
+/**
+ * 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.tenancy.configuration;
+
+import org.eclipse.hawkbit.exception.SpServerError;
+import org.eclipse.hawkbit.exception.SpServerRtException;
+
+/**
+ * The {@link #InvalidTenantConfigurationKeyException} is thrown when an invalid
+ * configuration key is used.
+ *
+ */
+public class InvalidTenantConfigurationKeyException extends SpServerRtException {
+
+ private static final long serialVersionUID = 1L;
+ private static final SpServerError THIS_ERROR = SpServerError.SP_CONFIGURATION_KEY_INVALID;
+
+ /**
+ * Default constructor.
+ */
+ public InvalidTenantConfigurationKeyException() {
+ super(THIS_ERROR);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param cause
+ * of the exception
+ */
+ public InvalidTenantConfigurationKeyException(final Throwable cause) {
+ super(THIS_ERROR, cause);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param message
+ * of the exception
+ * @param cause
+ * of the exception
+ */
+ public InvalidTenantConfigurationKeyException(final String message, final Throwable cause) {
+ super(message, THIS_ERROR, cause);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param message
+ * of the exception
+ */
+ public InvalidTenantConfigurationKeyException(final String message) {
+ super(message, THIS_ERROR);
+ }
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java
index 477cd654e..c83f24dc2 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java
@@ -8,12 +8,21 @@
*/
package org.eclipse.hawkbit.tenancy.configuration;
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.eclipse.hawkbit.ControllerPollProperties;
+import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator;
+import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator;
+import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationStringValidator;
+import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidator;
+import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
+import org.springframework.context.ApplicationContext;
+
/**
* An enum which defines the tenant specific configurations which can be
- * configured for each tenant seperately.
- *
- *
- *
+ * configured for each tenant separately. The non overridable properties are
+ * configured in {@link ControllerPollProperties} instead.
*
*/
public enum TenantConfigurationKey {
@@ -21,51 +30,68 @@ public enum TenantConfigurationKey {
/**
* boolean value {@code true} {@code false}.
*/
- AUTHENTICATION_MODE_HEADER_ENABLED("authentication.header.enabled",
- "hawkbit.server.controller.security.authentication.header.enabled", Boolean.FALSE.toString()),
+ AUTHENTICATION_MODE_HEADER_ENABLED("authentication.header.enabled", "hawkbit.server.ddi.security.authentication.header.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class),
/**
*
*/
- AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME("authentication.header.authority",
- "hawkbit.server.controller.security.authentication.header.authority", Boolean.FALSE.toString()),
+ AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME("authentication.header.authority", "hawkbit.server.ddi.security.authentication.header.authority", String.class, Boolean.FALSE.toString(), TenantConfigurationStringValidator.class),
/**
* boolean value {@code true} {@code false}.
*/
- AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED("authentication.targettoken.enabled",
- "hawkbit.server.controller.security.authentication.targettoken.enabled", Boolean.FALSE.toString()),
+ AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED("authentication.targettoken.enabled", "hawkbit.server.ddi.security.authentication.targettoken.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class),
/**
* boolean value {@code true} {@code false}.
*/
- AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED("authentication.gatewaytoken.enabled",
- "hawkbit.server.controller.security.authentication.gatewaytoken.enabled", Boolean.FALSE.toString()),
+ AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED("authentication.gatewaytoken.enabled", "hawkbit.server.ddi.security.authentication.gatewaytoken.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class),
/**
* string value which holds the name of the security token key.
*/
- AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME("authentication.gatewaytoken.name",
- "hawkbit.server.controller.security.authentication.gatewaytoken.name", null),
+ AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME("authentication.gatewaytoken.name", "hawkbit.server.ddi.security.authentication.gatewaytoken.name", String.class, null, TenantConfigurationStringValidator.class),
/**
* string value which holds the actual security-key of the gateway security
* token.
*/
- AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY("authentication.gatewaytoken.key",
- "hawkbit.server.controller.security.authentication.gatewaytoken.key", null);
+ AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY("authentication.gatewaytoken.key", "hawkbit.server.ddi.security.authentication.gatewaytoken.key", String.class, null, TenantConfigurationStringValidator.class),
+
+ /**
+ * string value which holds the polling time interval in the format HH:mm:ss
+ */
+ POLLING_TIME_INTERVAL("pollingOverdueTime", "hawkbit.controller.pollingOverdueTime", String.class, null, TenantConfigurationPollingDurationValidator.class),
+
+ /**
+ * string value which holds the polling time interval in the format HH:mm:ss
+ */
+ POLLING_OVERDUE_TIME_INTERVAL("pollingTime", "hawkbit.controller.pollingTime", String.class, null, TenantConfigurationPollingDurationValidator.class);
private final String keyName;
private final String defaultKeyName;
+ private final Class> dataType;
private final String defaultValue;
+ private final Class extends TenantConfigurationValidator> validator;
/**
+ *
* @param key
* the property key name
- * @param allowedValues
+ * @param defaultKeyName
* the allowed values for this specific key
+ * @param dataType
+ * the class of the property
+ * @param defaultValue
+ * value which should be returned, in case there is no value in
+ * the database
+ * @param validator
+ * Validator which validates, that property is of correct format
*/
- private TenantConfigurationKey(final String key, final String defaultKeyName, final String defaultValue) {
+ private TenantConfigurationKey(final String key, final String defaultKeyName, final Class> dataType,
+ final String defaultValue, final Class extends TenantConfigurationValidator> validator) {
this.keyName = key;
+ this.dataType = dataType;
this.defaultKeyName = defaultKeyName;
this.defaultValue = defaultValue;
+ this.validator = validator;
}
/**
@@ -88,4 +114,49 @@ public enum TenantConfigurationKey {
public String getDefaultValue() {
return defaultValue;
}
+
+ /**
+ *
+ * @return the data type of the tenant configuration value. (e.g.
+ * Integer.class, String.class)
+ */
+ public Class> getDataType() {
+ return dataType;
+ }
+
+ /**
+ * validates if a object matches the allowed data format of the
+ * corresponding key
+ *
+ * @param context
+ * application context
+ * @param value
+ * which will be validated
+ * @throws TenantConfigurationValidatorException
+ * is thrown, when object is invalid
+ */
+ public void validate(final ApplicationContext context, final Object value) {
+ final TenantConfigurationValidator createdBean = context.getAutowireCapableBeanFactory().createBean(validator);
+ try {
+ createdBean.validate(value);
+ } finally {
+ context.getAutowireCapableBeanFactory().destroyBean(createdBean);
+ }
+ }
+
+ /**
+ * @param keyName
+ * name of the TenantConfigurationKey
+ * @return the TenantConfigurationKey with the name keyName
+ */
+ public static TenantConfigurationKey fromKeyName(final String keyName) {
+
+ final Optional optKey = Arrays.stream(TenantConfigurationKey.values())
+ .filter(conf -> conf.getKeyName().equals(keyName)).findFirst();
+
+ if (optKey.isPresent()) {
+ return optKey.get();
+ }
+ throw new InvalidTenantConfigurationKeyException("The given configuration key name does not exist.");
+ }
}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationBooleanValidator.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationBooleanValidator.java
new file mode 100644
index 000000000..8836d8578
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationBooleanValidator.java
@@ -0,0 +1,22 @@
+/**
+ * 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.tenancy.configuration.validator;
+
+/**
+ * specific tenant configuration validator, which validates that the given value
+ * is a booleans.
+ */
+public class TenantConfigurationBooleanValidator implements TenantConfigurationValidator {
+
+ @Override
+ public Class> validateToClass() {
+ return Boolean.class;
+ }
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationPollingDurationValidator.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationPollingDurationValidator.java
new file mode 100644
index 000000000..11c9666e8
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationPollingDurationValidator.java
@@ -0,0 +1,67 @@
+/**
+ * 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.tenancy.configuration.validator;
+
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
+
+import org.eclipse.hawkbit.ControllerPollProperties;
+import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * This class is used to validate, that the property is a String and that it is
+ * in the correct duration format.
+ *
+ */
+public class TenantConfigurationPollingDurationValidator implements TenantConfigurationValidator {
+
+ private final Duration minDuration;
+
+ private final Duration maxDuration;
+
+ /**
+ * Constructor.
+ *
+ * @param properties
+ * property accessor for poll configuration
+ */
+ @Autowired
+ public TenantConfigurationPollingDurationValidator(final ControllerPollProperties properties) {
+ minDuration = DurationHelper.formattedStringToDuration(properties.getMinPollingTime());
+ maxDuration = DurationHelper.formattedStringToDuration(properties.getMaxPollingTime());
+ }
+
+ @Override
+ public void validate(final Object tenantConfigurationObject) {
+ TenantConfigurationValidator.super.validate(tenantConfigurationObject);
+ final String tenantConfigurationString = (String) tenantConfigurationObject;
+
+ final Duration tenantConfigurationValue;
+ try {
+ tenantConfigurationValue = DurationHelper.formattedStringToDuration(tenantConfigurationString);
+ } catch (final DateTimeParseException ex) {
+ throw new TenantConfigurationValidatorException(
+ String.format("The given configuration value is expected as a string in the format %s.",
+ DurationHelper.DURATION_FORMAT));
+ }
+
+ if (!DurationHelper.durationRangeValidator(minDuration, maxDuration).isWithinRange(tenantConfigurationValue)) {
+ throw new TenantConfigurationValidatorException(
+ String.format("The given configuration value is not in the allowed range from %s to %s.",
+ DurationHelper.durationToFormattedString(minDuration),
+ DurationHelper.durationToFormattedString(maxDuration)));
+ }
+ }
+
+ @Override
+ public Class> validateToClass() {
+ return String.class;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationStringValidator.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationStringValidator.java
new file mode 100644
index 000000000..fc427f4ac
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationStringValidator.java
@@ -0,0 +1,21 @@
+/**
+ * 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.tenancy.configuration.validator;
+
+/**
+ * specific tenant configuration validator, which validates Strings.
+ */
+public class TenantConfigurationStringValidator implements TenantConfigurationValidator {
+
+ @Override
+ public Class> validateToClass() {
+ return String.class;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationValidator.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationValidator.java
new file mode 100644
index 000000000..dccf515e6
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationValidator.java
@@ -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.tenancy.configuration.validator;
+
+/**
+ * base interface for clases which can validate tenant configuration values.
+ *
+ */
+public interface TenantConfigurationValidator {
+
+ /**
+ * validates the tenant configuration value
+ *
+ * @param tenantConfigurationValue
+ * value which will be validated.
+ * @throws TenantConfigurationValidatorException
+ * is thrown, when parameter is invalid.
+ */
+ default void validate(final Object tenantConfigurationValue) throws TenantConfigurationValidatorException {
+ if (tenantConfigurationValue != null
+ && validateToClass().isAssignableFrom(tenantConfigurationValue.getClass())) {
+ return;
+ }
+ throw new TenantConfigurationValidatorException(
+ "The given configuration value is expected as a " + validateToClass().getSimpleName());
+ }
+
+ /**
+ * Return the generic class to check the object
+ *
+ * @return the class to check the value
+ */
+ default Class> validateToClass() {
+ return Object.class;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationValidatorException.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationValidatorException.java
new file mode 100644
index 000000000..dffc13dcb
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/validator/TenantConfigurationValidatorException.java
@@ -0,0 +1,63 @@
+/**
+ * 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.tenancy.configuration.validator;
+
+import org.eclipse.hawkbit.exception.SpServerError;
+import org.eclipse.hawkbit.exception.SpServerRtException;
+
+/**
+ * Exception which is thrown, when the validation of the configuration value has
+ * not been successful.
+ *
+ */
+public class TenantConfigurationValidatorException extends SpServerRtException {
+
+ private static final long serialVersionUID = 1L;
+ private static final SpServerError THIS_ERROR = SpServerError.SP_CONFIGURATION_VALUE_INVALID;
+
+ /**
+ * Default constructor.
+ */
+ public TenantConfigurationValidatorException() {
+ super(THIS_ERROR);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param cause
+ * of the exception
+ */
+ public TenantConfigurationValidatorException(final Throwable cause) {
+ super(THIS_ERROR, cause);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param message
+ * of the exception
+ * @param cause
+ * of the exception
+ */
+ public TenantConfigurationValidatorException(final String message, final Throwable cause) {
+ super(message, THIS_ERROR, cause);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param message
+ * of the exception
+ */
+ public TenantConfigurationValidatorException(final String message) {
+ super(message, THIS_ERROR);
+ }
+
+}
diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/eventbus/EventBusSubscriberProcessorTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/eventbus/EventBusSubscriberProcessorTest.java
index ac02aaad1..548bf7e7e 100644
--- a/hawkbit-core/src/test/java/org/eclipse/hawkbit/eventbus/EventBusSubscriberProcessorTest.java
+++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/eventbus/EventBusSubscriberProcessorTest.java
@@ -21,7 +21,13 @@ import org.mockito.runners.MockitoJUnitRunner;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
+import ru.yandex.qatools.allure.annotations.Features;
+import ru.yandex.qatools.allure.annotations.Stories;
+
+@Features("Unit Tests - Cluster Event Bus")
+@Stories("EventBus Subscriber Processor Test")
@RunWith(MockitoJUnitRunner.class)
+// TODO: create description annotations
public class EventBusSubscriberProcessorTest {
@Mock
diff --git a/hawkbit-dmf-amqp/pom.xml b/hawkbit-dmf-amqp/pom.xml
index a7dfc5b42..2fded8559 100644
--- a/hawkbit-dmf-amqp/pom.xml
+++ b/hawkbit-dmf-amqp/pom.xml
@@ -60,6 +60,11 @@
org.slf4jslf4j-api
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java
index 988a68ada..acbcb453b 100644
--- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java
@@ -21,8 +21,8 @@ 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.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -30,8 +30,6 @@ import org.springframework.context.annotation.Bean;
* The spring AMQP configuration which is enabled by using the profile
* {@code amqp} to use a AMQP for communication with SP enabled devices.
*
- *
- *
*/
@EnableConfigurationProperties(AmqpProperties.class)
public class AmqpConfiguration {
@@ -42,19 +40,16 @@ public class AmqpConfiguration {
@Autowired
private ConnectionFactory connectionFactory;
- @Autowired
- private RabbitTemplate rabbitTemplate;
-
/**
* Method to set the Jackson2JsonMessageConverter.
*
* @return the Jackson2JsonMessageConverter
*/
@Bean
- public MessageConverter jsonMessageConverter() {
- final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
- rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
- return jackson2JsonMessageConverter;
+ public RabbitTemplate rabbitTemplate() {
+ final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
+ rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
+ return rabbitTemplate;
}
/**
@@ -121,11 +116,22 @@ public class AmqpConfiguration {
/**
* Create amqp handler service bean.
*
- * @return
+ * @return handler service bean
*/
@Bean
public AmqpMessageHandlerService amqpMessageHandlerService() {
- return new AmqpMessageHandlerService();
+ return new AmqpMessageHandlerService(rabbitTemplate());
+ }
+
+ /**
+ * Create default amqp sender service bean.
+ *
+ * @return the default amqp sender service bean
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public AmqpSenderService amqpSenderServiceBean() {
+ return new DefaultAmqpSenderService(rabbitTemplate());
}
/**
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java
index dd36ef1fd..8f19d9f02 100644
--- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentfication.java
@@ -16,14 +16,15 @@ import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement;
-import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
+import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.PreAuthenficationFilter;
-import org.eclipse.hawkbit.security.SecurityProperties;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -49,13 +50,16 @@ public class AmqpControllerAuthentfication {
private ControllerManagement controllerManagement;
@Autowired
- private SystemManagement systemManagement;
+ private TenantConfigurationManagement tenantConfigurationManagement;
@Autowired
private TenantAware tenantAware;
@Autowired
- private SecurityProperties secruityProperties;
+ private DdiSecurityProperties ddiSecruityProperties;
+
+ @Autowired
+ private SystemSecurityContext systemSecurityContext;
/**
* Constructor.
@@ -74,16 +78,16 @@ public class AmqpControllerAuthentfication {
private void addFilter() {
final ControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new ControllerPreAuthenticatedGatewaySecurityTokenFilter(
- systemManagement, tenantAware);
+ tenantConfigurationManagement, tenantAware, systemSecurityContext);
filterChain.add(gatewaySecurityTokenFilter);
final ControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new ControllerPreAuthenticatedSecurityHeaderFilter(
- secruityProperties.getRpCnHeader(), secruityProperties.getRpSslIssuerHashHeader(), systemManagement,
- tenantAware);
+ ddiSecruityProperties.getRp().getCnHeader(), ddiSecruityProperties.getRp().getSslIssuerHashHeader(),
+ tenantConfigurationManagement, tenantAware, systemSecurityContext);
filterChain.add(securityHeaderFilter);
final ControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new ControllerPreAuthenticateSecurityTokenFilter(
- systemManagement, controllerManagement, tenantAware);
+ tenantConfigurationManagement, controllerManagement, tenantAware, systemSecurityContext);
filterChain.add(securityTokenFilter);
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
@@ -127,26 +131,26 @@ public class AmqpControllerAuthentfication {
LOGGER.debug("preAuthenticatedPrincipal = {} trying to authenticate", principal);
- final PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal,
- credentials);
-
- return authRequest;
+ return new PreAuthenticatedAuthenticationToken(principal, credentials);
}
public void setControllerManagement(final ControllerManagement controllerManagement) {
this.controllerManagement = controllerManagement;
}
- public void setSecruityProperties(final SecurityProperties secruityProperties) {
- this.secruityProperties = secruityProperties;
+ public void setSecruityProperties(final DdiSecurityProperties secruityProperties) {
+ this.ddiSecruityProperties = secruityProperties;
}
- public void setSystemManagement(final SystemManagement systemManagement) {
- this.systemManagement = systemManagement;
+ public void setTenantConfigurationManagement(final TenantConfigurationManagement tenantConfigurationManagement) {
+ this.tenantConfigurationManagement = tenantConfigurationManagement;
}
public void setTenantAware(final TenantAware tenantAware) {
this.tenantAware = tenantAware;
}
+ void setSystemSecurityContext(final SystemSecurityContext systemSecurityContext) {
+ this.systemSecurityContext = systemSecurityContext;
+ }
}
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java
index 7469e6768..b9e6fe9da 100644
--- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java
@@ -25,35 +25,43 @@ import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
-import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.ArtifactUrlHandler;
import org.eclipse.hawkbit.util.IpUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
-import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.eventbus.Subscribe;
/**
- * {@link AmqpMessageDispatcherService} handles all outgoing AMQP messages.
- *
- *
+ * {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and
+ * delegate the messages to a {@link AmqpSenderService}.
+ *
+ * Additionally the dispatcher listener/subscribe for some target events e.g.
+ * assignment.
*
*/
@EventSubscriber
-public class AmqpMessageDispatcherService {
-
- @Autowired
- private RabbitTemplate rabbitTemplate;
-
- @Autowired
- private TenantAware tenantAware;
+public class AmqpMessageDispatcherService extends BaseAmqpService {
@Autowired
private ArtifactUrlHandler artifactUrlHandler;
+ @Autowired
+ private AmqpSenderService amqpSenderService;
+
+ /**
+ * Constructor.
+ *
+ * @param messageConverter
+ * message converter
+ */
+ @Autowired
+ public AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate) {
+ super(rabbitTemplate);
+ }
+
/**
* Method to send a message to a RabbitMQ Exchange after the Distribution
* set has been assign to a Target.
@@ -79,9 +87,10 @@ public class AmqpMessageDispatcherService {
downloadAndUpdateRequest.addSoftwareModule(amqpSoftwareModule);
}
- final Message message = rabbitTemplate.getMessageConverter().toMessage(downloadAndUpdateRequest,
- createConnectorMessageProperties(controllerId, EventTopic.DOWNLOAD_AND_INSTALL));
- sendMessage(targetAdress.getHost(), message);
+ final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
+ createConnectorMessageProperties(targetAssignDistributionSetEvent.getTenant(), controllerId,
+ EventTopic.DOWNLOAD_AND_INSTALL));
+ amqpSenderService.sendMessage(message, targetAdress);
}
/**
@@ -96,32 +105,19 @@ public class AmqpMessageDispatcherService {
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent) {
final String controllerId = cancelTargetAssignmentDistributionSetEvent.getControllerId();
final Long actionId = cancelTargetAssignmentDistributionSetEvent.getActionId();
- final Message message = rabbitTemplate.getMessageConverter().toMessage(actionId,
- createConnectorMessageProperties(controllerId, EventTopic.CANCEL_DOWNLOAD));
+ final Message message = getMessageConverter().toMessage(actionId, createConnectorMessageProperties(
+ cancelTargetAssignmentDistributionSetEvent.getTenant(), controllerId, EventTopic.CANCEL_DOWNLOAD));
- sendMessage(cancelTargetAssignmentDistributionSetEvent.getTargetAdress().getHost(), message);
+ amqpSenderService.sendMessage(message, cancelTargetAssignmentDistributionSetEvent.getTargetAdress());
}
- /**
- * Send message to exchange.
- *
- * @param exchange
- * the exchange
- * @param message
- * the message
- */
- public void sendMessage(final String exchange, final Message message) {
- message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
- rabbitTemplate.setExchange(exchange);
- rabbitTemplate.send(message);
- }
-
- private MessageProperties createConnectorMessageProperties(final String controllerId, final EventTopic topic) {
+ private MessageProperties createConnectorMessageProperties(final String tenant, final String controllerId,
+ final EventTopic topic) {
final MessageProperties messageProperties = createMessageProperties();
messageProperties.setHeader(MessageHeaderKey.TOPIC, topic);
messageProperties.setHeader(MessageHeaderKey.THING_ID, controllerId);
- messageProperties.setHeader(MessageHeaderKey.TENANT, tenantAware.getCurrentTenant());
+ messageProperties.setHeader(MessageHeaderKey.TENANT, tenant);
messageProperties.setHeader(MessageHeaderKey.TYPE, MessageType.EVENT);
return messageProperties;
}
@@ -150,9 +146,8 @@ public class AmqpMessageDispatcherService {
return Collections.emptyList();
}
- final List convertedArtifacts = localArtifacts.stream()
- .map(localArtifact -> convertArtifact(targetId, localArtifact)).collect(Collectors.toList());
- return convertedArtifacts;
+ return localArtifacts.stream().map(localArtifact -> convertArtifact(targetId, localArtifact))
+ .collect(Collectors.toList());
}
private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) {
@@ -170,15 +165,11 @@ public class AmqpMessageDispatcherService {
return artifact;
}
- public void setTenantAware(final TenantAware tenantAware) {
- this.tenantAware = tenantAware;
- }
-
- public void setRabbitTemplate(final RabbitTemplate rabbitTemplate) {
- this.rabbitTemplate = rabbitTemplate;
- }
-
public void setArtifactUrlHandler(final ArtifactUrlHandler artifactUrlHandler) {
this.artifactUrlHandler = artifactUrlHandler;
}
+
+ public void setAmqpSenderService(final AmqpSenderService amqpSenderService) {
+ this.amqpSenderService = amqpSenderService;
+ }
}
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java
index 640c0e270..cfd5485a6 100644
--- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java
@@ -12,7 +12,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
@@ -50,8 +49,6 @@ 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.amqp.support.converter.AbstractJavaTypeMapper;
-import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
@@ -72,19 +69,14 @@ import com.google.common.eventbus.EventBus;
/**
*
- * {@link AmqpMessageHandlerService} handles all incoming AMQP messages.
- *
- *
- *
+ * {@link AmqpMessageHandlerService} handles all incoming AMQP messages for the
+ * queue which is configure for the property hawkbit.dmf.rabbitmq.receiverQueue.
*
*/
-public class AmqpMessageHandlerService {
+public class AmqpMessageHandlerService extends BaseAmqpService {
private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageHandlerService.class);
- @Autowired
- private RabbitTemplate rabbitTemplate;
-
@Autowired
private ControllerManagement controllerManagement;
@@ -105,7 +97,23 @@ public class AmqpMessageHandlerService {
private HostnameResolver hostnameResolver;
/**
- * /** Method to handle all incoming amqp messages.
+ * Constructor.
+ *
+ * @param defaultTemplate
+ * the configured amqp template.
+ */
+ public AmqpMessageHandlerService(final RabbitTemplate defaultTemplate) {
+ super(defaultTemplate);
+ }
+
+ @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
+ private Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
+ @Header(MessageHeaderKey.TENANT) final String tenant) {
+ return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost());
+ }
+
+ /**
+ * Method to handle all incoming amqp messages.
*
* @param message
* incoming message
@@ -115,11 +123,11 @@ public class AmqpMessageHandlerService {
* the contentType of the message
* @param tenant
* the contentType of the message
+ * @param virtualHost
+ * the virtual host
* @return a message if no message is send back to sender
*/
- @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
- public Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
- @Header(MessageHeaderKey.TENANT) final String tenant) {
+ public Message onMessage(final Message message, final String type, final String tenant, final String virtualHost) {
checkContentTypeJson(message);
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
@@ -127,7 +135,7 @@ public class AmqpMessageHandlerService {
switch (messageType) {
case THING_CREATED:
setTenantSecurityContext(tenant);
- registerTarget(message);
+ registerTarget(message, virtualHost);
break;
case EVENT:
setTenantSecurityContext(tenant);
@@ -196,7 +204,7 @@ public class AmqpMessageHandlerService {
authentificationResponse.setMessage(errorMessage);
}
- return rabbitTemplate.getMessageConverter().toMessage(authentificationResponse, messageProperties);
+ return getMessageConverter().toMessage(authentificationResponse, messageProperties);
}
private static Artifact convertDbArtifact(final DbArtifact dbArtifact) {
@@ -207,11 +215,6 @@ public class AmqpMessageHandlerService {
return artifact;
}
- protected void logAndThrowMessageError(final Message message, final String error) {
- LOG.error("Error \"{}\" reported by message {}", error, message.getMessageProperties().getMessageId());
- throw new IllegalArgumentException(error);
- }
-
private static void setSecurityContext(final Authentication authentication) {
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
securityContextImpl.setAuthentication(authentication);
@@ -226,15 +229,6 @@ public class AmqpMessageHandlerService {
setSecurityContext(authenticationToken);
}
- private String getStringHeaderKey(final Message message, final String key, final String errorMessageIfNull) {
- final Map header = message.getMessageProperties().getHeaders();
- final Object value = header.get(key);
- if (value == null) {
- logAndThrowMessageError(message, errorMessageIfNull);
- }
- return value.toString();
- }
-
/**
* Method to create a new target or to find the target if it already exists.
*
@@ -243,14 +237,15 @@ public class AmqpMessageHandlerService {
* @param ip
* the ip of the target/thing
*/
- private void registerTarget(final Message message) {
+ private void registerTarget(final Message message, final String virtualHost) {
final String thingId = getStringHeaderKey(message, MessageHeaderKey.THING_ID, "ThingId is null");
final String replyTo = message.getMessageProperties().getReplyTo();
if (StringUtils.isEmpty(replyTo)) {
logAndThrowMessageError(message, "No ReplyTo was set for the createThing Event.");
}
- final URI amqpUri = IpUtil.createAmqpUri(replyTo);
+
+ final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(thingId, amqpUri);
LOG.debug("Target {} reported online state.", thingId);
@@ -267,8 +262,8 @@ public class AmqpMessageHandlerService {
final DistributionSet distributionSet = action.getDistributionSet();
final List softwareModuleList = controllerManagement
.findSoftwareModulesByDistributionSet(distributionSet);
- eventBus.post(new TargetAssignDistributionSetEvent(target.getControllerId(), action.getId(), softwareModuleList,
- target.getTargetInfo().getAddress()));
+ eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
+ target.getControllerId(), action.getId(), softwareModuleList, target.getTargetInfo().getAddress()));
}
@@ -281,13 +276,11 @@ public class AmqpMessageHandlerService {
* the topic of the event.
*/
private void handleIncomingEvent(final Message message, final EventTopic topic) {
- switch (topic) {
- case UPDATE_ACTION_STATUS:
+ if (EventTopic.UPDATE_ACTION_STATUS.equals(topic)) {
updateActionStatus(message);
return;
- default:
- logAndThrowMessageError(message, "Got event without appropriate topic.");
}
+ logAndThrowMessageError(message, "Got event without appropriate topic.");
}
/**
@@ -298,20 +291,7 @@ public class AmqpMessageHandlerService {
*/
private void updateActionStatus(final Message message) {
final ActionUpdateStatus actionUpdateStatus = convertMessage(message, ActionUpdateStatus.class);
- final Long actionId = actionUpdateStatus.getActionId();
- LOG.debug("Target notifies intermediate about action {} with status {}.", actionId,
- actionUpdateStatus.getActionStatus().name());
-
- if (actionId == null) {
- logAndThrowMessageError(message, "Invalid message no action id");
- }
-
- final Action action = controllerManagement.findActionWithDetails(actionId);
-
- if (action == null) {
- logAndThrowMessageError(message,
- "Got intermediate notification about action " + actionId + " but action does not exist");
- }
+ final Action action = checkActionExist(message, actionUpdateStatus);
final ActionStatus actionStatus = new ActionStatus();
final List messageText = actionUpdateStatus.getMessage();
@@ -349,19 +329,38 @@ public class AmqpMessageHandlerService {
logAndThrowMessageError(message, "Status for action does not exisit.");
}
- Action addUpdateActionStatus;
-
- if (!actionStatus.getStatus().equals(Status.CANCELED)) {
- addUpdateActionStatus = controllerManagement.addUpdateActionStatus(actionStatus, action);
- } else {
- addUpdateActionStatus = controllerManagement.addCancelActionStatus(actionStatus, action);
- }
+ final Action addUpdateActionStatus = getUpdateActionStatus(action, actionStatus);
if (!addUpdateActionStatus.isActive()) {
lookIfUpdateAvailable(action.getTarget());
}
}
+ private Action getUpdateActionStatus(final Action action, final ActionStatus actionStatus) {
+ if (actionStatus.getStatus().equals(Status.CANCELED)) {
+ return controllerManagement.addCancelActionStatus(actionStatus, action);
+ }
+ return controllerManagement.addUpdateActionStatus(actionStatus, action);
+ }
+
+ private Action checkActionExist(final Message message, final ActionUpdateStatus actionUpdateStatus) {
+ final Long actionId = actionUpdateStatus.getActionId();
+ LOG.debug("Target notifies intermediate about action {} with status {}.", actionId,
+ actionUpdateStatus.getActionStatus().name());
+
+ if (actionId == null) {
+ logAndThrowMessageError(message, "Invalid message no action id");
+ }
+
+ final Action action = controllerManagement.findActionWithDetails(actionId);
+
+ if (action == null) {
+ logAndThrowMessageError(message,
+ "Got intermediate notification about action " + actionId + " but action does not exist");
+ }
+ return action;
+ }
+
private void handleCancelRejected(final Message message, final Action action, final ActionStatus actionStatus) {
if (action.isCancelingOrCanceled()) {
@@ -372,37 +371,11 @@ public class AmqpMessageHandlerService {
} else {
logAndThrowMessageError(message,
- "Cancel Recjected message is not allowed, if action is on state: " + action.getStatus());
+ "Cancel recjected message is not allowed, if action is on state: " + action.getStatus());
}
}
- /**
- * Is needed to convert a incoming message to is originally object type.
- *
- * @param message
- * the message to convert.
- * @param clazz
- * the class of the originally object.
- * @return
- */
- @SuppressWarnings("unchecked")
- private T convertMessage(final Message message, final Class clazz) {
- message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
- clazz.getTypeName());
- return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
- }
-
- /**
- * Is needed to verify if an incoming message has the content type json.
- *
- * @param message
- * the to verify
- * @param contentType
- * the content type
- * @return true if the content type has json, false it not.
- */
-
- private static void checkContentTypeJson(final Message message) {
+ private void checkContentTypeJson(final Message message) {
final MessageProperties messageProperties = message.getMessageProperties();
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
return;
@@ -418,14 +391,6 @@ public class AmqpMessageHandlerService {
this.hostnameResolver = hostnameResolver;
}
- void setRabbitTemplate(final RabbitTemplate rabbitTemplate) {
- this.rabbitTemplate = rabbitTemplate;
- }
-
- MessageConverter getMessageConverter() {
- return rabbitTemplate.getMessageConverter();
- }
-
void setAuthenticationManager(final AmqpControllerAuthentfication authenticationManager) {
this.authenticationManager = authenticationManager;
}
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java
index ecd2dc3d7..38c6d34b3 100644
--- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java
@@ -15,16 +15,27 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Bean which holds the necessary properties for configuring the AMQP
* connection.
*
- *
- *
- *
*/
@ConfigurationProperties("hawkbit.dmf.rabbitmq")
public class AmqpProperties {
-
+ /**
+ * DMF API dead letter queue.
+ */
private String deadLetterQueue = "dmf_connector_deadletter";
+
+ /**
+ * DMF API dead letter exchange.
+ */
private String deadLetterExchange = "dmf.connector.deadletter";
+
+ /**
+ * DMF API receiving queue.
+ */
private String receiverQueue = "dmf_receiver";
+
+ /**
+ * Missing queue fatal.
+ */
private boolean missingQueuesFatal = false;
/**
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpSenderService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpSenderService.java
new file mode 100644
index 000000000..6cb3dd9be
--- /dev/null
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpSenderService.java
@@ -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.amqp;
+
+import java.net.URI;
+
+import org.springframework.amqp.core.Message;
+
+/**
+ * Interface to send a amqp message.
+ */
+@FunctionalInterface
+public interface AmqpSenderService {
+
+ /**
+ * Send the given message to the given uri. The uri contains the (virtual)
+ * host and exchange e.g amqp://host/exchange.
+ *
+ * @param message
+ * the amqp message
+ * @param uri
+ * the reply to uri
+ */
+ void sendMessage(Message message, URI uri);
+
+ /**
+ * Extract the exchange from the uri. Default implementation removes the
+ * first /.
+ *
+ * @param amqpUri
+ * the amqp uri
+ * @return the exchange.
+ */
+ default String extractExchange(final URI amqpUri) {
+ return amqpUri.getPath().substring(1);
+ }
+
+}
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java
new file mode 100644
index 000000000..8a054165b
--- /dev/null
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java
@@ -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.amqp;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+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.amqp.support.converter.MessageConverter;
+
+/**
+ * A base class which provide basis amqp staff.
+ */
+public class BaseAmqpService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BaseAmqpService.class);
+ private final RabbitTemplate rabbitTemplate;
+
+ /**
+ * Constructor.
+ *
+ * @param rabbitTemplate
+ * the rabbit template.
+ */
+ public BaseAmqpService(final RabbitTemplate rabbitTemplate) {
+ this.rabbitTemplate = rabbitTemplate;
+ }
+
+ /**
+ * Clean message properties before sending a message.
+ *
+ * @param message
+ * the message to cleaned up
+ */
+ protected void cleanMessageHeaderProperties(final Message message) {
+ message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
+ }
+
+ /**
+ * Is needed to convert a incoming message to is originally object type.
+ *
+ * @param message
+ * the message to convert.
+ * @param clazz
+ * the class of the originally object.
+ * @return the converted object
+ */
+ @SuppressWarnings("unchecked")
+ public T convertMessage(final Message message, final Class clazz) {
+ if (message == null || message.getBody() == null) {
+ return null;
+ }
+ message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
+ clazz.getName());
+ return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
+ }
+
+ /**
+ * Is needed to convert a incoming message to is originally list object
+ * type.
+ *
+ * @param message
+ * the message to convert.
+ * @param clazz
+ * the class of the list content.
+ * @return the list of converted objects
+ */
+ @SuppressWarnings("unchecked")
+ public List convertMessageList(final Message message, final Class clazz) {
+ if (message == null || message.getBody() == null) {
+ return Collections.emptyList();
+ }
+ message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
+ ArrayList.class.getName());
+ message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
+ clazz.getName());
+ return (List) rabbitTemplate.getMessageConverter().fromMessage(message);
+ }
+
+ public MessageConverter getMessageConverter() {
+ return rabbitTemplate.getMessageConverter();
+ }
+
+ protected final String getStringHeaderKey(final Message message, final String key,
+ final String errorMessageIfNull) {
+ final Map header = message.getMessageProperties().getHeaders();
+ final Object value = header.get(key);
+ if (value == null) {
+ logAndThrowMessageError(message, errorMessageIfNull);
+ return null;
+ }
+ return value.toString();
+ }
+
+ protected final void logAndThrowMessageError(final Message message, final String error) {
+ LOGGER.error("Error \"{}\" reported by message {}", error, message.getMessageProperties().getMessageId());
+ throw new IllegalArgumentException(error);
+ }
+
+ protected RabbitTemplate getRabbitTemplate() {
+ return rabbitTemplate;
+ }
+}
diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DefaultAmqpSenderService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DefaultAmqpSenderService.java
new file mode 100644
index 000000000..9586633bf
--- /dev/null
+++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DefaultAmqpSenderService.java
@@ -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.amqp;
+
+import java.net.URI;
+
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+
+/**
+ * A default implementation for the sender service. The service sends all amqp
+ * message to the configured spring rabbitmq connections. The exchange is
+ * extracted from the uri.
+ */
+public class DefaultAmqpSenderService implements AmqpSenderService {
+
+ private final RabbitTemplate internalAmqpTemplate;
+
+ /**
+ * Constructor.
+ *
+ * @param internalAmqpTemplate
+ * the amqp template
+ */
+ public DefaultAmqpSenderService(final RabbitTemplate internalAmqpTemplate) {
+ this.internalAmqpTemplate = internalAmqpTemplate;
+ }
+
+ @Override
+ public void sendMessage(final Message message, final URI uri) {
+ internalAmqpTemplate.send(extractExchange(uri), null, message);
+ }
+
+}
diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/AmqpTestConfiguration.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/AmqpTestConfiguration.java
new file mode 100644
index 000000000..a1dd54710
--- /dev/null
+++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/AmqpTestConfiguration.java
@@ -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;
+
+import org.eclipse.hawkbit.amqp.AmqpSenderService;
+import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ *
+ */
+@Configuration
+public class AmqpTestConfiguration {
+
+ /**
+ * Method to set the Jackson2JsonMessageConverter.
+ *
+ * @return the Jackson2JsonMessageConverter
+ */
+ @Bean
+ public MessageConverter jsonMessageConverter() {
+ return new Jackson2JsonMessageConverter();
+ }
+
+ /**
+ * Create default amqp sender service bean.
+ *
+ * @param rabbitTemplate
+ *
+ * @return the default amqp sender service bean
+ */
+ @Bean
+ @Autowired
+ public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
+ return new DefaultAmqpSenderService(rabbitTemplate);
+ }
+}
diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentficationTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java
similarity index 71%
rename from hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentficationTest.java
rename to hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java
index 5a77c5fce..66527727a 100644
--- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentficationTest.java
+++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java
@@ -22,9 +22,12 @@ import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
-import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
+import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
+import org.eclipse.hawkbit.security.DdiSecurityProperties;
+import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
-import org.eclipse.hawkbit.security.SecurityProperties;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.junit.Before;
import org.junit.Test;
@@ -45,74 +48,97 @@ import ru.yandex.qatools.allure.annotations.Stories;
/**
*
- *
+ * Test Amqp controller authentication.
*/
-@Features("AMQP Authenfication Test")
-@Stories("Tests the authenfication")
-public class AmqpControllerAuthentficationTest {
+@Features("Component Tests - Device Management Federation API")
+@Stories("AmqpController Authentication Test")
+public class AmqpControllerAuthenticationTest {
private static final String TENANT = "DEFAULT";
private static String CONTROLLLER_ID = "123";
private AmqpMessageHandlerService amqpMessageHandlerService;
private MessageConverter messageConverter;
- private SystemManagement systemManagement;
+ private TenantConfigurationManagement tenantConfigurationManagement;
private AmqpControllerAuthentfication authenticationManager;
+ private static final TenantConfigurationValue CONFIG_VALUE_FALSE = TenantConfigurationValue
+ . builder().value(Boolean.FALSE).build();
+
+ private static final TenantConfigurationValue CONFIG_VALUE_TRUE = TenantConfigurationValue
+ . builder().value(Boolean.TRUE).build();
+
@Before
public void before() throws Exception {
- amqpMessageHandlerService = new AmqpMessageHandlerService();
messageConverter = new Jackson2JsonMessageConverter();
- final RabbitTemplate rabbitTemplate = new RabbitTemplate();
- rabbitTemplate.setMessageConverter(messageConverter);
- amqpMessageHandlerService.setRabbitTemplate(rabbitTemplate);
+ final RabbitTemplate rabbitTemplate = mock(RabbitTemplate.class);
+ when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
+ amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate);
authenticationManager = new AmqpControllerAuthentfication();
authenticationManager.setControllerManagement(mock(ControllerManagement.class));
- final SecurityProperties secruityProperties = mock(SecurityProperties.class);
- when(secruityProperties.getRpSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d");
+
+ final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class);
+ final Rp rp = mock(Rp.class);
+ when(secruityProperties.getRp()).thenReturn(rp);
+ when(rp.getSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d");
authenticationManager.setSecruityProperties(secruityProperties);
- systemManagement = mock(SystemManagement.class);
- authenticationManager.setSystemManagement(systemManagement);
- when(systemManagement.getConfigurationValue(any(), any())).thenReturn(Boolean.FALSE);
+
+ tenantConfigurationManagement = mock(TenantConfigurationManagement.class);
+ authenticationManager.setTenantConfigurationManagement(tenantConfigurationManagement);
+
+ when(tenantConfigurationManagement.getConfigurationValue(any(), eq(Boolean.class)))
+ .thenReturn(CONFIG_VALUE_FALSE);
final ControllerManagement controllerManagement = mock(ControllerManagement.class);
when(controllerManagement.getSecurityTokenByControllerId(anyString())).thenReturn(CONTROLLLER_ID);
authenticationManager.setControllerManagement(controllerManagement);
-
amqpMessageHandlerService.setArtifactManagement(mock(ArtifactManagement.class));
- authenticationManager.setTenantAware(new SecurityContextTenantAware());
+ final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware();
+ authenticationManager.setTenantAware(tenantAware);
+ final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware);
+ authenticationManager.setSystemSecurityContext(systemSecurityContext);
authenticationManager.postConstruct();
amqpMessageHandlerService.setAuthenticationManager(authenticationManager);
}
- @Test(expected = BadCredentialsException.class)
+ @Test
@Description("Tests authentication manager without principal")
public void testAuthenticationeBadCredantialsWithoutPricipal() {
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
- authenticationManager.doAuthenticate(securityToken);
- fail();
+ try {
+ authenticationManager.doAuthenticate(securityToken);
+ fail("BadCredentialsException was excepeted since principal was missing");
+ } catch (final BadCredentialsException exception) {
+ // test ok - exception was excepted
+ }
+
}
- @Test(expected = BadCredentialsException.class)
- @Description("Tests authentication manager without wrong credential")
+ @Test
+ @Description("Tests authentication manager without wrong credential")
public void testAuthenticationBadCredantialsWithWrongCredential() {
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
- when(systemManagement.getConfigurationValue(
- eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), any()))
- .thenReturn(Boolean.TRUE);
+ when(tenantConfigurationManagement.getConfigurationValue(
+ eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
+ .thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
- authenticationManager.doAuthenticate(securityToken);
- fail();
+ try {
+ authenticationManager.doAuthenticate(securityToken);
+ fail("BadCredentialsException was excepeted due to wrong credential");
+ } catch (final BadCredentialsException exception) {
+ // test ok - exception was excepted
+ }
+
}
@Test
@Description("Tests authentication successfull")
public void testSuccessfullAuthentication() {
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
- when(systemManagement.getConfigurationValue(
- eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), any()))
- .thenReturn(Boolean.TRUE);
+ when(tenantConfigurationManagement.getConfigurationValue(
+ eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
+ .thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
final Authentication authentication = authenticationManager.doAuthenticate(securityToken);
assertThat(authentication).isNotNull();
@@ -129,7 +155,7 @@ public class AmqpControllerAuthentficationTest {
// test
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
- TENANT);
+ TENANT, "vHost");
// verify
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
@@ -142,16 +168,16 @@ public class AmqpControllerAuthentficationTest {
public void testAuthenticationMessageBadCredantialsWithWrongCredential() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
- when(systemManagement.getConfigurationValue(
- eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), any()))
- .thenReturn(Boolean.TRUE);
+ when(tenantConfigurationManagement.getConfigurationValue(
+ eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
+ .thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);
// test
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
- TENANT);
+ TENANT, "vHost");
// verify
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
@@ -164,16 +190,16 @@ public class AmqpControllerAuthentficationTest {
public void testSuccessfullMessageAuthentication() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
- when(systemManagement.getConfigurationValue(
- eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), any()))
- .thenReturn(Boolean.TRUE);
+ when(tenantConfigurationManagement.getConfigurationValue(
+ eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
+ .thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);
// test
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
- TENANT);
+ TENANT, "vHost");
// verify
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java
index c423b76bf..46ddd35cc 100644
--- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java
+++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java
@@ -19,6 +19,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -45,7 +46,6 @@ import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
-import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.test.context.ActiveProfiles;
import ru.yandex.qatools.allure.annotations.Description;
@@ -53,45 +53,47 @@ import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@ActiveProfiles({ "test" })
-@Features("AMQP Dispatcher Test")
-@Stories("Tests send messages")
+@Features("Component Tests - Device Management Federation API")
+@Stories("AmqpMessage Dispatcher Service Test")
public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWithMongoDB {
private AmqpMessageDispatcherService amqpMessageDispatcherService;
- private MessageConverter messageConverter;
-
private RabbitTemplate rabbitTemplate;
+ private DefaultAmqpSenderService senderService;
+
private static final String CONTROLLER_ID = "1";
@Override
public void before() throws Exception {
super.before();
- amqpMessageDispatcherService = new AmqpMessageDispatcherService();
+ this.rabbitTemplate = Mockito.mock(RabbitTemplate.class);
+ when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
+ amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate);
amqpMessageDispatcherService = spy(amqpMessageDispatcherService);
- messageConverter = new Jackson2JsonMessageConverter();
+
+ senderService = Mockito.mock(DefaultAmqpSenderService.class);
+ amqpMessageDispatcherService.setAmqpSenderService(senderService);
final ArtifactUrlHandler artifactUrlHandlerMock = Mockito.mock(ArtifactUrlHandler.class);
when(artifactUrlHandlerMock.getUrl(anyString(), any(), anyObject())).thenReturn("http://mockurl");
- this.rabbitTemplate = Mockito.mock(RabbitTemplate.class);
- when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
-
- amqpMessageDispatcherService.setRabbitTemplate(rabbitTemplate);
- amqpMessageDispatcherService.setTenantAware(tenantAware);
amqpMessageDispatcherService.setArtifactUrlHandler(artifactUrlHandlerMock);
+
}
@Test
@Description("Verfies that download and install event with no software modul works")
public void testSendDownloadRequesWithEmptySoftwareModules() {
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
- CONTROLLER_ID, 1l, new ArrayList(), IpUtil.createAmqpUri("mytest"));
+ 1L, "default", CONTROLLER_ID, 1l, new ArrayList(),
+ IpUtil.createAmqpUri("vHost", "mytest"));
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
- final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress().getHost());
+ final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
- assertTrue(downloadAndUpdateRequest.getSoftwareModules().isEmpty());
+ assertTrue("No softwaremmodule should be contained in the request",
+ downloadAndUpdateRequest.getSoftwareModules().isEmpty());
}
@Test
@@ -100,21 +102,26 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
final DistributionSet dsA = TestDataUtil.generateDistributionSet("", softwareManagement,
distributionSetManagement);
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
- CONTROLLER_ID, 1l, dsA.getModules(), IpUtil.createAmqpUri("mytest"));
+ 1L, "default", CONTROLLER_ID, 1l, dsA.getModules(), IpUtil.createAmqpUri("vHost", "mytest"));
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
- final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress().getHost());
+ final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
- assertEquals(3, downloadAndUpdateRequest.getSoftwareModules().size());
+ assertEquals("Expecting a size of 3 software modules in the reuqest", 3,
+ downloadAndUpdateRequest.getSoftwareModules().size());
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule softwareModule : downloadAndUpdateRequest
.getSoftwareModules()) {
- assertTrue(softwareModule.getArtifacts().isEmpty());
+ assertTrue("Artifact list for softwaremodule should be empty", softwareModule.getArtifacts().isEmpty());
for (final SoftwareModule softwareModule2 : dsA.getModules()) {
- assertNotNull(softwareModule.getModuleId());
+ assertNotNull("Sofware module ID should be set", softwareModule.getModuleId());
if (!softwareModule.getModuleId().equals(softwareModule2.getId())) {
continue;
}
- assertEquals(softwareModule.getModuleType(), softwareModule2.getType().getKey());
- assertEquals(softwareModule.getModuleVersion(), softwareModule2.getVersion());
+ assertEquals(
+ "Software module type in event should be the same as the softwaremodule in the distribution set",
+ softwareModule.getModuleType(), softwareModule2.getType().getKey());
+ assertEquals(
+ "Software module version in event should be the same as the softwaremodule in the distribution set",
+ softwareModule.getModuleVersion(), softwareModule2.getVersion());
}
}
}
@@ -134,17 +141,18 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList);
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
- CONTROLLER_ID, 1l, dsA.getModules(), IpUtil.createAmqpUri("mytest"));
+ 1L, "default", CONTROLLER_ID, 1l, dsA.getModules(), IpUtil.createAmqpUri("vHost", "mytest"));
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
- final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress().getHost());
+ final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
- assertEquals(3, downloadAndUpdateRequest.getSoftwareModules().size());
+ assertEquals("DownloadAndUpdateRequest event should contains 3 software modules", 3,
+ downloadAndUpdateRequest.getSoftwareModules().size());
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule softwareModule : downloadAndUpdateRequest
.getSoftwareModules()) {
if (!softwareModule.getModuleId().equals(module.getId())) {
continue;
}
- assertFalse(softwareModule.getArtifacts().isEmpty());
+ assertFalse("The software module artifacts should not be empty", softwareModule.getArtifacts().isEmpty());
}
}
@@ -152,11 +160,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
@Description("Verfies that send cancel event works")
public void testSendCancelRequest() {
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent = new CancelTargetAssignmentEvent(
- CONTROLLER_ID, 1l, IpUtil.createAmqpUri("mytest"));
+ 1L, "default", CONTROLLER_ID, 1l, IpUtil.createAmqpUri("vHost", "mytest"));
amqpMessageDispatcherService
.targetCancelAssignmentToDistributionSet(cancelTargetAssignmentDistributionSetEvent);
- final Message sendMessage = createArgumentCapture(
- cancelTargetAssignmentDistributionSetEvent.getTargetAdress().getHost());
+ final Message sendMessage = createArgumentCapture(cancelTargetAssignmentDistributionSetEvent.getTargetAdress());
assertCancelMessage(sendMessage);
}
@@ -164,8 +171,8 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
private void assertCancelMessage(final Message sendMessage) {
assertEventMessage(sendMessage);
final Long actionId = convertMessage(sendMessage, Long.class);
- assertEquals(actionId, Long.valueOf(1));
- assertEquals(EventTopic.CANCEL_DOWNLOAD,
+ assertEquals("Action ID should be 1", actionId, Long.valueOf(1));
+ assertEquals("The topc in the message should be a CANCEL_DOWNLOAD value", EventTopic.CANCEL_DOWNLOAD,
sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC));
}
@@ -174,8 +181,9 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
assertEventMessage(sendMessage);
final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(sendMessage,
DownloadAndUpdateRequest.class);
- assertEquals(downloadAndUpdateRequest.getActionId(), Long.valueOf(1));
- assertEquals(EventTopic.DOWNLOAD_AND_INSTALL,
+ assertEquals("The action ID of the downloadAndUpdateRequest event shuold be 1",
+ downloadAndUpdateRequest.getActionId(), Long.valueOf(1));
+ assertEquals("The topic of the event shuold contain DOWNLOAD_AND_INSTALL", EventTopic.DOWNLOAD_AND_INSTALL,
sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC));
return downloadAndUpdateRequest;
@@ -185,16 +193,19 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
* @param sendMessage
*/
private void assertEventMessage(final Message sendMessage) {
- assertNotNull(sendMessage);
+ assertNotNull("The message should not be null", sendMessage);
- assertEquals(CONTROLLER_ID, sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID));
- assertEquals(MessageType.EVENT, sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TYPE));
- assertEquals(MessageProperties.CONTENT_TYPE_JSON, sendMessage.getMessageProperties().getContentType());
+ assertEquals("The value of the message header THING_ID should be " + CONTROLLER_ID, CONTROLLER_ID,
+ sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID));
+ assertEquals("The value of the message header TYPE should be EVENT", MessageType.EVENT,
+ sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TYPE));
+ assertEquals("The content type message should be " + MessageProperties.CONTENT_TYPE_JSON,
+ MessageProperties.CONTENT_TYPE_JSON, sendMessage.getMessageProperties().getContentType());
}
- protected Message createArgumentCapture(final String exchange) {
+ protected Message createArgumentCapture(final URI uri) {
final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Message.class);
- Mockito.verify(amqpMessageDispatcherService).sendMessage(eq(exchange), argumentCaptor.capture());
+ Mockito.verify(senderService).sendMessage(argumentCaptor.capture(), eq(uri));
return argumentCaptor.getValue();
}
diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
index bea75e9d6..9d6ae3ba7 100644
--- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
+++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
@@ -68,8 +68,8 @@ import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@RunWith(MockitoJUnitRunner.class)
-@Features("AMQP Controller Test")
-@Stories("Tests the servcies for message handler and dispatcher")
+@Features("Component Tests - Device Management Federation API")
+@Stories("AmqpMessage Handler Service Test")
public class AmqpMessageHandlerServiceTest {
private static final String TENANT = "DEFAULT";
@@ -99,14 +99,15 @@ public class AmqpMessageHandlerServiceTest {
@Mock
private EventBus eventBus;
+ @Mock
+ private RabbitTemplate rabbitTemplate;
+
@Before
public void before() throws Exception {
- amqpMessageHandlerService = new AmqpMessageHandlerService();
- amqpMessageHandlerService.setControllerManagement(controllerManagementMock);
messageConverter = new Jackson2JsonMessageConverter();
- final RabbitTemplate rabbitTemplate = new RabbitTemplate();
- rabbitTemplate.setMessageConverter(messageConverter);
- amqpMessageHandlerService.setRabbitTemplate(rabbitTemplate);
+ when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
+ amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate);
+ amqpMessageHandlerService.setControllerManagement(controllerManagementMock);
amqpMessageHandlerService.setAuthenticationManager(authenticationManagerMock);
amqpMessageHandlerService.setArtifactManagement(artifactManagementMock);
amqpMessageHandlerService.setCache(cacheMock);
@@ -115,14 +116,17 @@ public class AmqpMessageHandlerServiceTest {
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
@Description("Tests not allowed content-type in message")
public void testWrongContentType() {
final MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("xml");
final Message message = new Message(new byte[0], messageProperties);
- amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT);
- fail();
+ try {
+ amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
+ fail("IllegalArgumentException was excepeted due to worng content type");
+ } catch (final IllegalArgumentException e) {
+ }
}
@Test
@@ -133,18 +137,16 @@ public class AmqpMessageHandlerServiceTest {
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
- // mock
final ArgumentCaptor targetIdCaptor = ArgumentCaptor.forClass(String.class);
final ArgumentCaptor uriCaptor = ArgumentCaptor.forClass(URI.class);
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotexist(targetIdCaptor.capture(),
uriCaptor.capture())).thenReturn(null);
- // test
- amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
// verify
- assertThat(targetIdCaptor.getValue()).isEqualTo(knownThingId);
- assertThat(uriCaptor.getValue().toString()).isEqualTo("amqp://MyTest");
+ assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
+ assertThat(uriCaptor.getValue().toString()).as("Uri is not right").isEqualTo("amqp://vHost/MyTest");
}
@@ -156,7 +158,7 @@ public class AmqpMessageHandlerServiceTest {
final Message message = messageConverter.toMessage("", messageProperties);
try {
- amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no replyTo header was set");
} catch (final IllegalArgumentException exception) {
// test ok - exception was excepted
@@ -170,7 +172,7 @@ public class AmqpMessageHandlerServiceTest {
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
try {
- amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no thingID was set");
} catch (final IllegalArgumentException exception) {
// test ok - exception was excepted
@@ -186,7 +188,7 @@ public class AmqpMessageHandlerServiceTest {
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
try {
- amqpMessageHandlerService.onMessage(message, type, TENANT);
+ amqpMessageHandlerService.onMessage(message, type, TENANT, "vHost");
fail("IllegalArgumentException was excepeted due to unknown message type");
} catch (final IllegalArgumentException exception) {
// test ok - exception was excepted
@@ -199,21 +201,21 @@ public class AmqpMessageHandlerServiceTest {
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
final Message message = new Message(new byte[0], messageProperties);
try {
- amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT);
- fail();
+ amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
+ fail("IllegalArgumentException was excepeted due to unknown message type");
} catch (final IllegalArgumentException e) {
}
try {
messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic");
- amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT);
- fail();
+ amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
+ fail("IllegalArgumentException was excepeted due to unknown topic");
} catch (final IllegalArgumentException e) {
}
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name());
try {
- amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted because there was no event topic");
} catch (final IllegalArgumentException exception) {
// test ok - exception was excepted
@@ -232,7 +234,7 @@ public class AmqpMessageHandlerServiceTest {
messageProperties);
try {
- amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no action id was set");
} catch (final IllegalArgumentException exception) {
// test ok - exception was excepted
@@ -249,7 +251,7 @@ public class AmqpMessageHandlerServiceTest {
messageProperties);
try {
- amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no action id was set");
} catch (final IllegalArgumentException exception) {
// test ok - exception was excepted
@@ -267,12 +269,13 @@ public class AmqpMessageHandlerServiceTest {
// test
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
- TENANT);
+ TENANT, "vHost");
// verify
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
- assertThat(downloadResponse).isNotNull();
- assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.NOT_FOUND.value());
+ assertThat(downloadResponse).as("Message body should not null").isNotNull();
+ assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong")
+ .isEqualTo(HttpStatus.NOT_FOUND.value());
}
@Test
@@ -290,12 +293,13 @@ public class AmqpMessageHandlerServiceTest {
// test
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
- TENANT);
+ TENANT, "vHost");
// verify
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
- assertThat(downloadResponse).isNotNull();
- assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.NOT_FOUND.value());
+ assertThat(downloadResponse).as("Message body should not null").isNotNull();
+ assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong")
+ .isEqualTo(HttpStatus.NOT_FOUND.value());
}
@Test
@@ -321,14 +325,16 @@ public class AmqpMessageHandlerServiceTest {
// test
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
- TENANT);
+ TENANT, "vHost");
// verify
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
- assertThat(downloadResponse).isNotNull();
- assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value());
- assertThat(downloadResponse.getArtifact().getSize()).isEqualTo(1L);
- assertThat(downloadResponse.getDownloadUrl()).startsWith("http://localhost/api/v1/downloadserver/downloadId/");
+ assertThat(downloadResponse).as("Message body should not null").isNotNull();
+ assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong")
+ .isEqualTo(HttpStatus.OK.value());
+ assertThat(downloadResponse.getArtifact().getSize()).as("Wrong artifact size in message body").isEqualTo(1L);
+ assertThat(downloadResponse.getDownloadUrl()).as("download url is wrong")
+ .startsWith("http://localhost/api/v1/downloadserver/downloadId/");
}
@Test
@@ -355,7 +361,7 @@ public class AmqpMessageHandlerServiceTest {
messageProperties);
// test
- amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT);
+ amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
// verify
final ArgumentCaptor captorTargetAssignDistributionSetEvent = ArgumentCaptor
@@ -364,9 +370,11 @@ public class AmqpMessageHandlerServiceTest {
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = captorTargetAssignDistributionSetEvent
.getValue();
- assertThat(targetAssignDistributionSetEvent.getControllerId()).isEqualTo("target1");
- assertThat(targetAssignDistributionSetEvent.getActionId()).isEqualTo(22L);
- assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).isEqualTo(softwareModuleList);
+ assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id")
+ .isEqualTo("target1");
+ assertThat(targetAssignDistributionSetEvent.getActionId()).as("event has wrong action id").isEqualTo(22L);
+ assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules")
+ .isEqualTo(softwareModuleList);
}
diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java
new file mode 100644
index 000000000..0bd8c164b
--- /dev/null
+++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java
@@ -0,0 +1,103 @@
+/**
+ * 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.amqp;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
+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.amqp.core.Message;
+import org.springframework.amqp.core.MessageProperties;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+
+import ru.yandex.qatools.allure.annotations.Description;
+import ru.yandex.qatools.allure.annotations.Features;
+import ru.yandex.qatools.allure.annotations.Stories;
+
+@RunWith(MockitoJUnitRunner.class)
+@Features("Component Tests - Device Management Federation API")
+@Stories("Base Amqp Service Test")
+public class BaseAmqpServiceTest {
+
+ @Mock
+ private RabbitTemplate rabbitTemplate;
+
+ private BaseAmqpService baseAmqpService;
+
+ @Before
+ public void setup() {
+ when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
+ baseAmqpService = new BaseAmqpService(rabbitTemplate);
+
+ }
+
+ @Test
+ @Description("Verify that the message conversion works")
+ public void convertMessageTest() {
+ final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
+ actionUpdateStatus.setActionId(1L);
+ actionUpdateStatus.setSoftwareModuleId(2L);
+
+ final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus,
+ new MessageProperties());
+ ActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message,
+ ActionUpdateStatus.class);
+
+ assertThat(convertedActionUpdateStatus).as("Converted Action Status is wrong")
+ .isEqualsToByComparingFields(actionUpdateStatus);
+
+ convertedActionUpdateStatus = baseAmqpService.convertMessage(null, ActionUpdateStatus.class);
+ assertThat(convertedActionUpdateStatus).as("Converted Object should be null when message is null").isNull();
+
+ convertedActionUpdateStatus = baseAmqpService.convertMessage(new Message(null, new MessageProperties()),
+ ActionUpdateStatus.class);
+ assertThat(convertedActionUpdateStatus).as("Converted Object should be null when message body is null")
+ .isNull();
+ }
+
+ @Test
+ @Description("Verify that a conversion of a list from a message works")
+ public void convertMessageListTest() {
+ final List actionUpdateStatusList = new ArrayList<>();
+ for (int i = 0; i < 5; i++) {
+ final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
+ actionUpdateStatus.setActionId(Long.valueOf(i));
+ actionUpdateStatus.setSoftwareModuleId(Long.valueOf(i));
+ actionUpdateStatusList.add(actionUpdateStatus);
+ }
+
+ final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatusList,
+ new MessageProperties());
+ List convertedActionUpdateStatus = baseAmqpService.convertMessageList(message,
+ ActionUpdateStatus.class);
+
+ assertThat(convertedActionUpdateStatus).as("Converted Action Status list is wrong")
+ .hasSameClassAs(actionUpdateStatusList);
+ assertThat(convertedActionUpdateStatus).as("Converted Action Status list is wrong")
+ .hasSameSizeAs(actionUpdateStatusList);
+
+ convertedActionUpdateStatus = baseAmqpService.convertMessageList(null, ActionUpdateStatus.class);
+ assertThat(convertedActionUpdateStatus).as("Converted list should be empty when message is null").isEmpty();
+
+ convertedActionUpdateStatus = baseAmqpService.convertMessageList(new Message(null, new MessageProperties()),
+ ActionUpdateStatus.class);
+ assertThat(convertedActionUpdateStatus).as("Converted list should be empty when message body is null")
+ .isEmpty();
+ }
+
+}
diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java
index fcafb23e4..e7ba06d19 100644
--- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java
+++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java
@@ -11,6 +11,9 @@ package org.eclipse.hawkbit.util;
import static org.junit.Assert.assertEquals;
import org.eclipse.hawkbit.AbstractIntegrationTestWithMongoDB;
+import org.eclipse.hawkbit.AmqpTestConfiguration;
+import org.eclipse.hawkbit.RepositoryApplicationConfiguration;
+import org.eclipse.hawkbit.TestConfiguration;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -20,6 +23,7 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
@@ -31,6 +35,8 @@ import ru.yandex.qatools.allure.annotations.Stories;
*/
@Features("Component Tests - Artifact URL Handler")
@Stories("Test to generate the artifact download URL")
+@SpringApplicationConfiguration(classes = { RepositoryApplicationConfiguration.class, TestConfiguration.class,
+ AmqpTestConfiguration.class })
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
@Autowired
@@ -50,29 +56,33 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
}
@Test
- @Description("Tests generate the http download url")
+ @Description("Tests the generation of http download url.")
public void testHttpUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTP);
- assertEquals("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
- + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
- + localArtifact.getFilename(), url);
+ assertEquals("http is build incorrect",
+ "http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
+ + localArtifact.getFilename(),
+ url);
}
@Test
- @Description("Tests generate the https download url")
+ @Description("Tests the generation of https download url.")
public void testHttpsUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTPS);
- assertEquals("https://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
- + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
- + localArtifact.getFilename(), url);
+ assertEquals("https is build incorrect",
+ "https://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
+ + localArtifact.getFilename(),
+ url);
}
@Test
- @Description("Tests generate the coap download url")
+ @Description("Tests the generation of coap download url.")
public void testCoapUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.COAP);
- assertEquals("coap://127.0.0.1:5683/fw/" + tenantAware.getCurrentTenant() + "/" + controllerId + "/sha1/"
- + localArtifact.getSha1Hash(), url);
+ assertEquals("coap is build incorrect", "coap://127.0.0.1:5683/fw/" + tenantAware.getCurrentTenant() + "/"
+ + controllerId + "/sha1/" + localArtifact.getSha1Hash(), url);
}
}
diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java
index 5782a48db..adb2858b9 100644
--- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java
+++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java
@@ -18,7 +18,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
-import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,8 +55,9 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
private static final String CONTROLLER_DL_REQUEST_ANT_PATTERN = "/{" + TENANT_PLACE_HOLDER
+ "}/controller/artifacts/v1/**";
- protected SystemManagement systemManagement;
+ protected TenantConfigurationManagement tenantConfigurationManagement;
protected TenantAware tenantAware;
+ protected SystemSecurityContext systemSecurityContext;
private final AntPathMatcher pathExtractor;
@@ -70,10 +71,11 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
* @param tenantAware
* the tenant aware service
*/
- public AbstractHttpControllerAuthenticationFilter(final SystemManagement systemManagement,
- final TenantAware tenantAware) {
- this.systemManagement = systemManagement;
+ public AbstractHttpControllerAuthenticationFilter(final TenantConfigurationManagement tenantConfigurationManagement,
+ final TenantAware tenantAware, final SystemSecurityContext systemSecurityContext) {
+ this.tenantConfigurationManagement = tenantConfigurationManagement;
this.tenantAware = tenantAware;
+ this.systemSecurityContext = systemSecurityContext;
pathExtractor = new AntPathMatcher();
}
diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticateSecurityTokenFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticateSecurityTokenFilter.java
index 91a0cdfa6..1eb155b9a 100644
--- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticateSecurityTokenFilter.java
+++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticateSecurityTokenFilter.java
@@ -9,7 +9,7 @@
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.repository.ControllerManagement;
-import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
/**
@@ -35,7 +35,7 @@ public class HttpControllerPreAuthenticateSecurityTokenFilter extends AbstractHt
/**
* Constructor.
*
- * @param systemManagement
+ * @param tenantConfigurationManagement
* the system management service to retrieve configuration
* properties
* @param tenantAware
@@ -44,16 +44,20 @@ public class HttpControllerPreAuthenticateSecurityTokenFilter extends AbstractHt
* @param controllerManagement
* the controller management to retrieve the specific target
* security token to verify
+ * @param systemSecurityContext
+ * the system security context
*/
- public HttpControllerPreAuthenticateSecurityTokenFilter(final SystemManagement systemManagement,
- final TenantAware tenantAware, final ControllerManagement controllerManagement) {
- super(systemManagement, tenantAware);
+ public HttpControllerPreAuthenticateSecurityTokenFilter(
+ final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
+ final ControllerManagement controllerManagement, final SystemSecurityContext systemSecurityContext) {
+ super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
this.controllerManagement = controllerManagement;
}
@Override
protected PreAuthenficationFilter createControllerAuthenticationFilter() {
- return new ControllerPreAuthenticateSecurityTokenFilter(systemManagement, controllerManagement, tenantAware);
+ return new ControllerPreAuthenticateSecurityTokenFilter(tenantConfigurationManagement, controllerManagement,
+ tenantAware, systemSecurityContext);
}
}
diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedGatewaySecurityTokenFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedGatewaySecurityTokenFilter.java
index 2b1a4d78f..930c66dca 100644
--- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedGatewaySecurityTokenFilter.java
+++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedGatewaySecurityTokenFilter.java
@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
-import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
/**
@@ -27,21 +27,25 @@ public class HttpControllerPreAuthenticatedGatewaySecurityTokenFilter
/**
* Constructor.
*
- * @param systemManagement
+ * @param tenantConfigurationManagement
* the system management service to retrieve configuration
* properties
* @param tenantAware
* the tenant aware service to get configuration for the specific
* tenant
+ * @param systemSecurityContext
+ * * @param systemSecurityContext the system security context
*/
- public HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(final SystemManagement systemManagement,
- final TenantAware tenantAware) {
- super(systemManagement, tenantAware);
+ public HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
+ final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
+ final SystemSecurityContext systemSecurityContext) {
+ super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
protected PreAuthenficationFilter createControllerAuthenticationFilter() {
- return new ControllerPreAuthenticatedGatewaySecurityTokenFilter(systemManagement, tenantAware);
+ return new ControllerPreAuthenticatedGatewaySecurityTokenFilter(tenantConfigurationManagement, tenantAware,
+ systemSecurityContext);
}
}
diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedSecurityHeaderFilter.java
index 3d6b43657..162e2688d 100644
--- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedSecurityHeaderFilter.java
+++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpControllerPreAuthenticatedSecurityHeaderFilter.java
@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
-import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
/**
@@ -35,18 +35,20 @@ public class HttpControllerPreAuthenticatedSecurityHeaderFilter extends Abstract
* @param caAuthorityNameHeader
* the http-header which holds the ca-authority name of the
* certificate
- * @param systemManagement
- * the system management service to retrieve configuration
- * properties to check if the header authentication is enabled
- * for this tenant
+ * @param tenantConfigurationManagement
+ * the tenant configuration management service to retrieve
+ * configuration properties to check if the header authentication
+ * is enabled for this tenant
* @param tenantAware
* the tenant aware service to get configuration for the specific
* tenant
+ * @param systemSecurityContext
+ * the system security context
*/
public HttpControllerPreAuthenticatedSecurityHeaderFilter(final String caCommonNameHeader,
- final String caAuthorityNameHeader, final SystemManagement systemManagement,
- final TenantAware tenantAware) {
- super(systemManagement, tenantAware);
+ final String caAuthorityNameHeader, final TenantConfigurationManagement tenantConfigurationManagement,
+ final TenantAware tenantAware, final SystemSecurityContext systemSecurityContext) {
+ super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
this.caCommonNameHeader = caCommonNameHeader;
this.caAuthorityNameHeader = caAuthorityNameHeader;
}
@@ -54,7 +56,7 @@ public class HttpControllerPreAuthenticatedSecurityHeaderFilter extends Abstract
@Override
protected PreAuthenficationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedSecurityHeaderFilter(caCommonNameHeader, caAuthorityNameHeader,
- systemManagement, tenantAware);
+ tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
}
diff --git a/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java b/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java
index f2ffe5c4c..fb961f9b2 100644
--- a/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java
+++ b/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java
@@ -20,7 +20,13 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
+import ru.yandex.qatools.allure.annotations.Features;
+import ru.yandex.qatools.allure.annotations.Stories;
+
+@Features("Unit Tests - Security")
+@Stories("PreAuthToken Source TrustAuthentication Provider Test")
@RunWith(MockitoJUnitRunner.class)
+// TODO: create description annotations
public class PreAuthTokenSourceTrustAuthenticationProviderTest {
private static final String REQUEST_SOURCE_IP = "127.0.0.1";
diff --git a/hawkbit-repository/README.md b/hawkbit-repository/README.md
index 6c1e35c5d..2d68a4e73 100644
--- a/hawkbit-repository/README.md
+++ b/hawkbit-repository/README.md
@@ -9,4 +9,39 @@ The repository is in charge for managing the meta data of the update server, e.g
$ mvn clean install
----
-Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
\ No newline at end of file
+Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
+
+#Concepts
+
+## Rollout
+A rollout consists of the distribution set and a target-query-filter which defines the targets to be updated in this rollout. The targets within this filter are split up in configured amount of _Deployment Groups_ at creation time.
+
+When starting a rollout, for all targets within this rollout deployment actions will be created. The deployment actions of the first group will be started immediately all other deployment actions will be scheduled.
+
+> Due rollouts might include a large number of targets and deployment group, creation as well as starting a rollout might take some time and therefore the creation and starting of an rollout is executed asynchronously. The creation and starting progress is reflected by the rollout's status attribute
+
+### Rollout Creation
+The targets reflected by the target-query-filter is interpreted at creation time. Only targets which have been created at that time are included in the rollout. Targets which are created after the rollout creation will not be included. At rollout creation time all necessary deployment groups containing the targets will be created. Dynamically adding targets to a created or running rollout is currently not supported.
+
+### Rollout Starting
+The rollout is using the same concept based on _deployment acionts_ per target. All necessary _deployment acionts_ will be created at starting point but not all _deployment acionts_ will be set to running. Only the _deployment acionts_ of the first _deployment group_ is set to running, all other actions are created in _scheduled_ state.
+If targets having not finished _deployment actions_ at rollout starting time, these action are tried to cancel (state: canceling). Scheduled actions from another rollout are canceled directly on server side because they were never running before and can be safely canceled.
+
+>Multiple rollouts can be running at the same time, but if the rollouts effect the same targets they will interfer the targets _deployment actions_ e.g. canceling/cancel already running/scheduled _deployment actions_.
+
+### Deployment Group Condition/Action
+#### Success Condition/Action
+A _success condition_ defines when a deployment group is interpreted as success and triggers the _success action_. E.g. a threshold of successfully updated targets within the group.
+
+The _success action_ is executed if the _success_condition_ is fullfilled. Currently only the _success action_ starting the next following deployment group is available.
+
+#### Error Condition/Action
+A _error condition_ defines when a deployment group is interpreted as failure and triggers the _error action_. E.g. a threshold of update failures of targets within the group.
+
+The _error action_ is executed if the _error condition_ is fullfilled. Currently only the _error action_ pausing the whole rollout is available, a paused rollout can be resumed by a user.
+
+### Rollout Scheduler
+The rollout is managed by a scheduler task which is checking for rollouts in _running_ state. This is done atomic by updating the row in the database, so only the rollouts are checked and processed for the current instance to avoid that multiple instances are processing a rollout.
+
+>Due the scheduler, it might take some time until a rollouts is processed and switching is state or starting the next deployment group.
+
diff --git a/hawkbit-repository/pom.xml b/hawkbit-repository/pom.xml
index 62d234ddb..7259262de 100644
--- a/hawkbit-repository/pom.xml
+++ b/hawkbit-repository/pom.xml
@@ -99,6 +99,11 @@
org.flywaydbflyway-core
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
@@ -215,7 +220,7 @@
com.ethlo.persistence.toolseclipselink-maven-plugin
- 1.1-SNAPSHOT
+ 2.6.2process-classes
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/MultiTenantJpaTransactionManager.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/MultiTenantJpaTransactionManager.java
index 2675510c4..7b97f5037 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/MultiTenantJpaTransactionManager.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/MultiTenantJpaTransactionManager.java
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit;
import javax.persistence.EntityManager;
import javax.transaction.Transaction;
+import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -26,8 +27,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* {@link TenantAware#getCurrentTenant()} in the eclipselink session. This has
* to be done in eclipselink after a {@link Transaction} has been started.
*
- *
- *
*/
public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
private static final long serialVersionUID = 1L;
@@ -43,10 +42,8 @@ public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
.getResource(getEntityManagerFactory());
final EntityManager em = emHolder.getEntityManager();
- if (!definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".findTenants")
- && !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".deleteTenant")
- && !definition.getName()
- .startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator")) {
+ if (notTenantManagement(definition) && notCurrentTenantKeyGenerator(definition)
+ && notRolloutScheduler(definition) && notGetOrCreateTenantMetadata(definition)) {
final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant == null) {
@@ -56,4 +53,23 @@ public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenant.toUpperCase());
}
}
+
+ private boolean notGetOrCreateTenantMetadata(final TransactionDefinition definition) {
+ return !definition.getName()
+ .startsWith(SystemManagement.class.getCanonicalName() + ".getOrCreateTenantMetadata");
+ }
+
+ private boolean notRolloutScheduler(final TransactionDefinition definition) {
+ return !definition.getName().startsWith(RolloutManagement.class.getCanonicalName() + ".rolloutScheduler");
+ }
+
+ private boolean notCurrentTenantKeyGenerator(final TransactionDefinition definition) {
+ return !definition.getName()
+ .startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator");
+ }
+
+ private boolean notTenantManagement(final TransactionDefinition definition) {
+ return !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".deleteTenant")
+ && !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".findTenants");
+ }
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java
index a621e94fc..bf805f3dd 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java
@@ -13,17 +13,16 @@ import java.util.Map;
import org.eclipse.hawkbit.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
-import org.eclipse.hawkbit.repository.model.helper.PollConfigurationHelper;
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.tenancy.TenantAware;
-import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
-import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -39,10 +38,6 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
/**
* General configuration for the SP Repository.
*
- *
- *
- *
- *
*/
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository" })
@EnableTransactionManagement
@@ -54,21 +49,13 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
- * @return simple default {@link AsyncUncaughtExceptionHandler}
- * implementation
- * @Bean public SimpleAsyncUncaughtExceptionHandler
- * simpleAsyncUncaughtExceptionHandler() { return new
- * SimpleAsyncUncaughtExceptionHandler(); }
- *
- * /**
- * @return the {@link PollConfigurationHelper} singleton bean which holds
- * the polling and polling overdue configuration and make it
- * accessible in beans which cannot not be autowired or retrieve
- * environment variables due {@link EnvironmentAware} interface.
+ * @return the {@link TenantConfigurationManagement} singleton bean which
+ * make it accessible in beans which cannot access the service
+ * directly, e.g. JPA entities.
*/
@Bean
- public PollConfigurationHelper pollConfigurationHelper() {
- return PollConfigurationHelper.getInstance();
+ public TenantConfigurationManagement tenantConfigurationManagement() {
+ return TenantConfigurationManagement.getInstance();
}
/**
@@ -112,6 +99,16 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
return CacheManagerHolder.getInstance();
}
+ /**
+ *
+ * @return the singleton instance of the
+ * {@link AfterTransactionCommitExecutorHolder}
+ */
+ @Bean
+ public AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
+ return AfterTransactionCommitExecutorHolder.getInstance();
+ }
+
/**
* Defines the validation processor bean.
*
@@ -150,7 +147,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Override
protected Map getVendorProperties() {
- final Map properties = new HashMap();
+ final Map properties = new HashMap<>();
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
properties.put("eclipselink.weaving", "false");
// needed for reports
@@ -174,5 +171,4 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
public PlatformTransactionManager transactionManager() {
return new MultiTenantJpaTransactionManager();
}
-
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheKeys.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheKeys.java
index 8eca2744a..9cb53a317 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheKeys.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheKeys.java
@@ -28,6 +28,8 @@ public final class CacheKeys {
* event.
*/
public static final String DOWNLOAD_PROGRESS_PERCENT = "download.progress.percent";
+ public static final String ROLLOUT_GROUP_CREATED = "rollout.group.created";
+ public static final String ROLLOUT_GROUP_TOTAL = "rollout.group.total";
/**
* utility class only private constructor.
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheWriteNotify.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheWriteNotify.java
index 812f4ca85..bf690e07e 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheWriteNotify.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/cache/CacheWriteNotify.java
@@ -9,8 +9,10 @@
package org.eclipse.hawkbit.cache;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
+import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
+import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
@@ -70,6 +72,43 @@ public class CacheWriteNotify {
eventBus.post(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId, progressPercent));
}
+ /**
+ * Writes the {@link CacheKeys#ROLLOUT_GROUP_CREATED} and
+ * {@link CacheKeys#ROLLOUT_GROUP_TOTAL} into the cache and notfies the
+ * {@link EventBus} with a {@link RolloutGroupCreatedEvent}.
+ *
+ * @param revision
+ * the revision of the event
+ * @param rolloutId
+ * the ID of the rollout the group has been created
+ * @param rolloutGroupId
+ * the ID of the rollout group which has been created
+ * @param totalRolloutGroup
+ * the total number of rollout groups for this rollout
+ * @param createdRolloutGroup
+ * the number of already created groups of the rollout
+ */
+ public void rolloutGroupCreated(final long revision, final Long rolloutId, final Long rolloutGroupId,
+ final int totalRolloutGroup, final int createdRolloutGroup) {
+
+ final Cache cache = cacheManager.getCache(Rollout.class.getName());
+ final String cacheKeyGroupTotal = CacheKeys.entitySpecificCacheKey(String.valueOf(rolloutId),
+ CacheKeys.ROLLOUT_GROUP_TOTAL);
+ final String cacheKeyGroupCreated = CacheKeys.entitySpecificCacheKey(String.valueOf(rolloutId),
+ CacheKeys.ROLLOUT_GROUP_CREATED);
+ if (createdRolloutGroup < totalRolloutGroup) {
+ cache.put(cacheKeyGroupTotal, totalRolloutGroup);
+ cache.put(cacheKeyGroupCreated, createdRolloutGroup);
+ } else {
+ // in case we reached progress 100 delete the cache value again
+ // because otherwise he will keep there forever
+ cache.evict(cacheKeyGroupTotal);
+ cache.evict(cacheKeyGroupCreated);
+ }
+ eventBus.post(new RolloutGroupCreatedEvent(tenantAware.getCurrentTenant(), revision, rolloutId, rolloutGroupId,
+ totalRolloutGroup, createdRolloutGroup));
+ }
+
/**
* @param cacheManager
* the cacheManager to set
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java
index e1ff377e5..b25a72fbc 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java
@@ -144,4 +144,5 @@ public class EntityChangeEventListener {
private boolean isTargetInfoNew(final Object targetInfo) {
return ((TargetInfo) targetInfo).isNew();
}
+
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityPropertyChangeListener.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityPropertyChangeListener.java
new file mode 100644
index 000000000..8b632ee6a
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityPropertyChangeListener.java
@@ -0,0 +1,107 @@
+/**
+ * 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.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.hawkbit.eventbus.event.AbstractPropertyChangeEvent;
+import org.eclipse.hawkbit.eventbus.event.ActionCreatedEvent;
+import org.eclipse.hawkbit.eventbus.event.ActionPropertyChangeEvent;
+import org.eclipse.hawkbit.eventbus.event.RolloutGroupPropertyChangeEvent;
+import org.eclipse.hawkbit.eventbus.event.RolloutPropertyChangeEvent;
+import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
+import org.eclipse.hawkbit.repository.model.Action;
+import org.eclipse.hawkbit.repository.model.BaseEntity;
+import org.eclipse.hawkbit.repository.model.Rollout;
+import org.eclipse.hawkbit.repository.model.RolloutGroup;
+import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder;
+import org.eclipse.hawkbit.repository.model.helper.EventBusHolder;
+import org.eclipse.persistence.descriptors.DescriptorEvent;
+import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
+import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
+import org.eclipse.persistence.queries.UpdateObjectQuery;
+import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
+
+import com.google.common.eventbus.EventBus;
+
+/**
+ * Listens to change in property values of an entity.
+ *
+ */
+public class EntityPropertyChangeListener extends DescriptorEventAdapter {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.persistence.descriptors.DescriptorEventAdapter#postInsert
+ * (org.eclipse.persistence.descriptors.DescriptorEvent)
+ */
+ @Override
+ public void postInsert(final DescriptorEvent event) {
+ if (event.getObject().getClass().equals(Action.class)) {
+ final Action action = (Action) event.getObject();
+ if (action.getRollout() != null) {
+ final EventBus eventBus = getEventBus();
+ final AfterTransactionCommitExecutor afterCommit = getAfterTransactionCommmitExecutor();
+ afterCommit.afterCommit(() -> eventBus.post(new ActionCreatedEvent(action)));
+ }
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.persistence.descriptors.DescriptorEventAdapter#postUpdate
+ * (org.eclipse.persistence.descriptors.DescriptorEvent)
+ */
+ @Override
+ public void postUpdate(final DescriptorEvent event) {
+ if (event.getObject().getClass().equals(Action.class)) {
+ getAfterTransactionCommmitExecutor().afterCommit(
+ () -> getEventBus()
+ .post(new ActionPropertyChangeEvent((Action) event.getObject(), getChangeSet(Action.class,
+ event))));
+ } else if (event.getObject().getClass().equals(Rollout.class)) {
+ getAfterTransactionCommmitExecutor().afterCommit(
+ () -> getEventBus().post(
+ new RolloutPropertyChangeEvent((Rollout) event.getObject(), getChangeSet(Rollout.class,
+ event))));
+ } else if (event.getObject().getClass().equals(RolloutGroup.class)) {
+ getAfterTransactionCommmitExecutor().afterCommit(
+ () -> getEventBus().post(
+ new RolloutGroupPropertyChangeEvent((RolloutGroup) event.getObject(), getChangeSet(
+ RolloutGroup.class, event))));
+ }
+ }
+
+ private Map.Values> getChangeSet(
+ final Class clazz, final DescriptorEvent event) {
+ final T rolloutGroup = clazz.cast(event.getObject());
+ final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
+ return changeSet
+ .getChanges()
+ .stream()
+ .filter(record -> record instanceof DirectToFieldChangeRecord)
+ .map(record -> (DirectToFieldChangeRecord) record)
+ .collect(
+ Collectors.toMap(record -> record.getAttribute(), record -> new AbstractPropertyChangeEvent(
+ rolloutGroup, null).new Values(record.getOldValue(), record.getNewValue())));
+ }
+
+ private AfterTransactionCommitExecutor getAfterTransactionCommmitExecutor() {
+ return AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit();
+ }
+
+ private EventBus getEventBus() {
+ return EventBusHolder.getInstance().getEventBus();
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractPropertyChangeEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractPropertyChangeEvent.java
new file mode 100644
index 000000000..8a596eeb5
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractPropertyChangeEvent.java
@@ -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
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+import java.util.Map;
+
+import org.eclipse.hawkbit.repository.model.BaseEntity;
+
+/**
+ * Property change event.
+ *
+ * @param
+ */
+public class AbstractPropertyChangeEvent extends AbstractBaseEntityEvent {
+
+ private static final long serialVersionUID = -3671601415138242311L;
+ private final transient Map changeSet;
+
+ /**
+ * Initialize base entity and property changed with old and new value.
+ *
+ * @param baseEntity
+ * entity changed
+ * @param changeSetValues
+ * details of properties changed and old value and new value of
+ * the changed properties
+ */
+ public AbstractPropertyChangeEvent(final E baseEntity, final Map changeSetValues) {
+ super(baseEntity);
+ this.changeSet = changeSetValues;
+ }
+
+ /**
+ * @return the changeSet
+ */
+ public Map getChangeSet() {
+ return changeSet;
+ }
+
+ /**
+ * Carries old value and new value of a property .
+ *
+ */
+ public class Values {
+ private final Object oldValue;
+ private final Object newValue;
+
+ /**
+ * Initialize old value and new changes value of property.
+ *
+ * @param oldValue
+ * old value before change
+ * @param newValue
+ * new value after change
+ */
+ public Values(final Object oldValue, final Object newValue) {
+ super();
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ }
+
+ /**
+ * @return the oldValue
+ */
+ public Object getOldValue() {
+ return oldValue;
+ }
+
+ /**
+ * @return the newValue
+ */
+ public Object getNewValue() {
+ return newValue;
+ }
+
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/ActionCreatedEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/ActionCreatedEvent.java
new file mode 100644
index 000000000..5efdca2a9
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/ActionCreatedEvent.java
@@ -0,0 +1,26 @@
+/**
+ * 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 org.eclipse.hawkbit.repository.model.Action;
+
+/**
+ * Defines the {@link AbstractBaseEntityEvent} of creating a new {@link Action}.
+ */
+public class ActionCreatedEvent extends AbstractBaseEntityEvent {
+ private static final long serialVersionUID = 181780358321768629L;
+
+ /**
+ * @param action
+ */
+ public ActionCreatedEvent(final Action action) {
+ super(action);
+ }
+
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/ActionPropertyChangeEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/ActionPropertyChangeEvent.java
new file mode 100644
index 000000000..822f9bbeb
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/ActionPropertyChangeEvent.java
@@ -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.eventbus.event;
+
+import java.util.Map;
+
+import org.eclipse.hawkbit.repository.model.Action;
+
+/**
+ * Defines the {@link AbstractPropertyChangeEvent} of {@link Action}.
+ */
+public class ActionPropertyChangeEvent extends AbstractPropertyChangeEvent {
+ private static final long serialVersionUID = 181780358321768629L;
+
+ /**
+ * @param action
+ * @param changeSetValues
+ */
+ public ActionPropertyChangeEvent(final Action action,
+ final Map.Values> changeSetValues) {
+ super(action, changeSetValues);
+ }
+
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/EventMerger.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/EventMerger.java
new file mode 100644
index 000000000..fe4cf4b34
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/EventMerger.java
@@ -0,0 +1,185 @@
+/**
+ * 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.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.hawkbit.eventbus.EventSubscriber;
+import org.eclipse.hawkbit.repository.model.Rollout;
+import org.eclipse.hawkbit.repository.model.RolloutGroup;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import com.google.common.eventbus.AllowConcurrentEvents;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+/**
+ * Collects and merges fine grained events to more generic events and push them
+ * in a fixed delay on the events bus. This helps for code which are not
+ * interested in all fine grained events, e.g. UI code. The UI code is not
+ * interested in handling the flood of events, so collecting the events and
+ * merge them to one event together and post them in a fixed interval is easier
+ * to consume e.g. for push notifcations on UI.
+ *
+ * @author Michael Hirsch
+ *
+ */
+@EventSubscriber
+public class EventMerger {
+
+ private static final Set rolloutEvents = ConcurrentHashMap.newKeySet();
+ private static final Set rolloutGroupEvents = ConcurrentHashMap.newKeySet();
+
+ @Autowired
+ private EventBus eventBus;
+
+ /**
+ * Checks if there are events to publish in the fixed interval.
+ */
+ @Scheduled(initialDelay = 10000, fixedDelay = 2000)
+ public void rolloutEventScheduler() {
+ final Iterator rolloutIterator = rolloutEvents.iterator();
+ while (rolloutIterator.hasNext()) {
+ final RolloutEventKey eventKey = rolloutIterator.next();
+ eventBus.post(new RolloutChangeEvent(1, eventKey.tenant, eventKey.rolloutId));
+ rolloutIterator.remove();
+ }
+
+ final Iterator rolloutGroupIterator = rolloutGroupEvents.iterator();
+ while (rolloutGroupIterator.hasNext()) {
+ final RolloutEventKey eventKey = rolloutGroupIterator.next();
+ eventBus.post(new RolloutGroupChangeEvent(1, eventKey.tenant, eventKey.rolloutId, eventKey.rolloutGroupId));
+ rolloutGroupIterator.remove();
+ }
+ }
+
+ /**
+ * Called by the event bus to retrieve all necessary events to collect and
+ * merge.
+ *
+ * @param event
+ * the event on the event bus
+ */
+ @Subscribe
+ @AllowConcurrentEvents
+ public void onEvent(final Event event) {
+ Long rolloutId = null;
+ Long rolloutGroupId = null;
+ if (event instanceof ActionCreatedEvent) {
+ rolloutId = getRolloutId(((ActionCreatedEvent) event).getEntity().getRollout());
+ rolloutGroupId = getRolloutGroupId(((ActionCreatedEvent) event).getEntity().getRolloutGroup());
+ } else if (event instanceof ActionPropertyChangeEvent) {
+ rolloutId = getRolloutId(((ActionPropertyChangeEvent) event).getEntity().getRollout());
+ rolloutGroupId = getRolloutGroupId(((ActionPropertyChangeEvent) event).getEntity().getRolloutGroup());
+ } else if (event instanceof RolloutPropertyChangeEvent) {
+ rolloutId = ((RolloutPropertyChangeEvent) event).getEntity().getId();
+ } else if (event instanceof RolloutGroupCreatedEvent) {
+ rolloutId = ((RolloutGroupCreatedEvent) event).getRolloutId();
+ rolloutGroupId = ((RolloutGroupCreatedEvent) event).getRolloutGroupId();
+ } else if (event instanceof RolloutGroupPropertyChangeEvent) {
+ final RolloutGroup rolloutGroup = ((RolloutGroupPropertyChangeEvent) event).getEntity();
+ rolloutId = rolloutGroup.getRollout().getId();
+ rolloutGroupId = rolloutGroup.getId();
+ }
+
+ if (rolloutId != null) {
+ rolloutEvents.add(new RolloutEventKey(rolloutId, event.getTenant()));
+ if (rolloutGroupId != null) {
+ rolloutGroupEvents.add(new RolloutEventKey(rolloutId, rolloutGroupId, event.getTenant()));
+ }
+ }
+ }
+
+ private Long getRolloutGroupId(final RolloutGroup rolloutGroup) {
+ if (rolloutGroup != null) {
+ return rolloutGroup.getId();
+ }
+ return null;
+ }
+
+ private Long getRolloutId(final Rollout rollout) {
+ if (rollout != null) {
+ return rollout.getId();
+ }
+ return null;
+ }
+
+ /**
+ * The rollout key in the concurrent set to be hold.
+ *
+ * @author Michael Hirsch
+ *
+ */
+ private static final class RolloutEventKey {
+ private final Long rolloutId;
+ private final String tenant;
+ private final Long rolloutGroupId;
+
+ private RolloutEventKey(final Long rolloutId, final Long rolloutGroupId, final String tenant) {
+ this.rolloutGroupId = rolloutGroupId;
+ this.rolloutId = rolloutId;
+ this.tenant = tenant;
+ }
+
+ private RolloutEventKey(final Long rolloutId, final String tenant) {
+ this(rolloutId, null, tenant);
+ }
+
+ @Override
+ public int hashCode() {// NOSONAR - as this is generated
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((rolloutGroupId == null) ? 0 : rolloutGroupId.hashCode());
+ result = prime * result + ((rolloutId == null) ? 0 : rolloutId.hashCode());
+ result = prime * result + ((tenant == null) ? 0 : tenant.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {// NOSONAR - as this is
+ // generated
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final RolloutEventKey other = (RolloutEventKey) obj;
+ if (rolloutGroupId == null) {
+ if (other.rolloutGroupId != null) {
+ return false;
+ }
+ } else if (!rolloutGroupId.equals(other.rolloutGroupId)) {
+ return false;
+ }
+ if (rolloutId == null) {
+ if (other.rolloutId != null) {
+ return false;
+ }
+ } else if (!rolloutId.equals(other.rolloutId)) {
+ return false;
+ }
+ if (tenant == null) {
+ if (other.tenant != null) {
+ return false;
+ }
+ } else if (!tenant.equals(other.tenant)) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutChangeEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutChangeEvent.java
new file mode 100644
index 000000000..8f15602b5
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutChangeEvent.java
@@ -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.eventbus.event;
+
+import org.eclipse.hawkbit.eventbus.event.AbstractEvent;
+
+/**
+ * Event declaration for the UI to notify the UI that a rollout has been
+ * changed.
+ *
+ * @author Michael Hirsch
+ *
+ */
+public class RolloutChangeEvent extends AbstractEvent {
+
+ private final Long rolloutId;
+
+ /**
+ * @param revision
+ * the revision of the event
+ * @param tenant
+ * the tenant of the event
+ * @param rolloutId
+ * the ID of the rollout which has been changed
+ */
+ public RolloutChangeEvent(final long revision, final String tenant, final Long rolloutId) {
+ super(revision, tenant);
+ this.rolloutId = rolloutId;
+ }
+
+ public Long getRolloutId() {
+ return rolloutId;
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupChangeEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupChangeEvent.java
new file mode 100644
index 000000000..cbda44ef5
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupChangeEvent.java
@@ -0,0 +1,47 @@
+/**
+ * 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 declaration for the UI to notify the UI that a rollout has been
+ * changed.
+ *
+ * @author Michael Hirsch
+ *
+ */
+public class RolloutGroupChangeEvent extends AbstractEvent {
+
+ private final Long rolloutId;
+ private final Long rolloutGroupId;
+
+ /**
+ * @param revision
+ * the revision of the event
+ * @param tenant
+ * the tenant of the event
+ * @param rolloutId
+ * the ID of the rollout which has been changed
+ * @param rolloutGroupId
+ * the ID of the rollout group which has been changed
+ */
+ public RolloutGroupChangeEvent(final long revision, final String tenant, final Long rolloutId,
+ final Long rolloutGroupId) {
+ super(revision, tenant);
+ this.rolloutId = rolloutId;
+ this.rolloutGroupId = rolloutGroupId;
+ }
+
+ public Long getRolloutId() {
+ return rolloutId;
+ }
+
+ public Long getRolloutGroupId() {
+ return rolloutGroupId;
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupCreatedEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupCreatedEvent.java
new file mode 100644
index 000000000..465ff6088
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupCreatedEvent.java
@@ -0,0 +1,65 @@
+/**
+ * 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 definition which is been published in case a rollout group has been
+ * created for a specific rollout.
+ *
+ * @author Michael Hirsch
+ *
+ */
+public class RolloutGroupCreatedEvent extends AbstractDistributedEvent {
+
+ private static final long serialVersionUID = 1L;
+ private final Long rolloutId;
+ private final Long rolloutGroupId;
+ private final int totalRolloutGroup;
+ private final int createdRolloutGroup;
+
+ /**
+ * Creating a new rollout group created event for a specific rollout.
+ *
+ * @param tenant
+ * the tenant of this event
+ * @param revision
+ * the revision of the event
+ * @param rolloutId
+ * the ID of the rollout the group has been created
+ * @param totalRolloutGroup
+ * the total number of rollout groups for this rollout
+ * @param createdRolloutGroup
+ * the number of already created groups of the rollout
+ */
+ public RolloutGroupCreatedEvent(final String tenant, final long revision, final Long rolloutId,
+ final Long rolloutGroupId, final int totalRolloutGroup, final int createdRolloutGroup) {
+ super(revision, tenant);
+ this.rolloutId = rolloutId;
+ this.rolloutGroupId = rolloutGroupId;
+ this.totalRolloutGroup = totalRolloutGroup;
+ this.createdRolloutGroup = createdRolloutGroup;
+
+ }
+
+ public Long getRolloutId() {
+ return rolloutId;
+ }
+
+ public int getTotalRolloutGroup() {
+ return totalRolloutGroup;
+ }
+
+ public int getCreatedRolloutGroup() {
+ return createdRolloutGroup;
+ }
+
+ public Long getRolloutGroupId() {
+ return rolloutGroupId;
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupPropertyChangeEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupPropertyChangeEvent.java
new file mode 100644
index 000000000..6caa46547
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutGroupPropertyChangeEvent.java
@@ -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;
+
+import java.util.Map;
+
+import org.eclipse.hawkbit.repository.model.RolloutGroup;
+
+/**
+ * Defines the {@link AbstractPropertyChangeEvent} of {@link RolloutGroup}.
+ */
+public class RolloutGroupPropertyChangeEvent extends AbstractPropertyChangeEvent {
+
+ private static final long serialVersionUID = 4026477044419472686L;
+
+ /**
+ *
+ * @param rolloutGroup
+ * @param changeSetValues
+ */
+ public RolloutGroupPropertyChangeEvent(final RolloutGroup rolloutGroup,
+ final Map.Values> changeSetValues) {
+ super(rolloutGroup, changeSetValues);
+ }
+
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutPropertyChangeEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutPropertyChangeEvent.java
new file mode 100644
index 000000000..5f72307df
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/RolloutPropertyChangeEvent.java
@@ -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.eventbus.event;
+
+import java.util.Map;
+
+import org.eclipse.hawkbit.repository.model.Rollout;
+
+/**
+ * Defines the {@link AbstractPropertyChangeEvent} of {@link Rollout}.
+ */
+public class RolloutPropertyChangeEvent extends AbstractPropertyChangeEvent {
+ private static final long serialVersionUID = 1056221355466373514L;
+
+ /**
+ *
+ * @param rollout
+ * @param changeSetValues
+ */
+ public RolloutPropertyChangeEvent(final Rollout rollout,
+ final Map.Values> changeSetValues) {
+ super(rollout, changeSetValues);
+ }
+
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetAssignDistributionSetEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetAssignDistributionSetEvent.java
index 8c13800c9..b286ac6ea 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetAssignDistributionSetEvent.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetAssignDistributionSetEvent.java
@@ -19,7 +19,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
*
*
*/
-public class TargetAssignDistributionSetEvent {
+public class TargetAssignDistributionSetEvent extends AbstractEvent {
private final Collection softwareModules;
private final String controllerId;
@@ -29,6 +29,10 @@ public class TargetAssignDistributionSetEvent {
/**
* Creates a new {@link TargetAssignDistributionSetEvent}.
*
+ * @param revision
+ * the revision of the event
+ * @param tenant
+ * the tenant of the event
* @param controllerId
* the ID of the controller
* @param actionId
@@ -38,8 +42,9 @@ public class TargetAssignDistributionSetEvent {
* @param targetAdress
* the targetAdress of the target
*/
- public TargetAssignDistributionSetEvent(final String controllerId, final Long actionId,
- final Collection softwareModules, final URI targetAdress) {
+ public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId,
+ final Long actionId, final Collection softwareModules, final URI targetAdress) {
+ super(revision, tenant);
this.controllerId = controllerId;
this.actionId = actionId;
this.softwareModules = softwareModules;
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetCreatedEvent.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetCreatedEvent.java
index 3e80e45a5..7e89565b1 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetCreatedEvent.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/event/TargetCreatedEvent.java
@@ -14,8 +14,6 @@ import org.eclipse.hawkbit.repository.model.Target;
* Defines the {@link AbstractBaseEntityEvent} of creating a new {@link Target}.
*
*
- *
- *
*/
public class TargetCreatedEvent extends AbstractBaseEntityEvent {
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeries.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeries.java
index 97623d9d0..39c8f5723 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeries.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeries.java
@@ -8,19 +8,22 @@
*/
package org.eclipse.hawkbit.report.model;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* A data report series which contains a list of {@link DataReportSeriesItem}.
- *
+ *
*
*
* @param
* the type of the report series item
*/
-public class DataReportSeries extends AbstractReportSeries {
+public class DataReportSeries extends AbstractReportSeries {
+
+ private static final long serialVersionUID = 1L;
private final List> data = new ArrayList<>();
@@ -46,7 +49,7 @@ public class DataReportSeries extends AbstractReportSeries {
}
private void setData(final List> values) {
- this.data.clear();
+ data.clear();
data.addAll(values);
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeriesItem.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeriesItem.java
index 3369ef1c0..492e27e1e 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeriesItem.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/DataReportSeriesItem.java
@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.report.model;
+import java.io.Serializable;
+
/**
* An data report series item which contains a type and a value.
*
@@ -16,8 +18,9 @@ package org.eclipse.hawkbit.report.model;
* @param
* the type of the report series item
*/
-public class DataReportSeriesItem {
+public class DataReportSeriesItem implements Serializable {
+ private static final long serialVersionUID = 1L;
private final T type;
private final Number data;
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/InnerOuterDataReportSeries.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/InnerOuterDataReportSeries.java
index 1568847de..b63935b39 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/InnerOuterDataReportSeries.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/InnerOuterDataReportSeries.java
@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.report.model;
+import java.io.Serializable;
+
/**
* A double data series which contains an inner and an outer series ideal for
* showing donut charts.
@@ -17,7 +19,7 @@ package org.eclipse.hawkbit.report.model;
* @param
* The type parameter for the report series data
*/
-public class InnerOuterDataReportSeries {
+public class InnerOuterDataReportSeries {
private final DataReportSeries innerSeries;
private final DataReportSeries outerSeries;
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/ListReportSeries.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/ListReportSeries.java
index 55125e046..79c496480 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/ListReportSeries.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/report/model/ListReportSeries.java
@@ -21,7 +21,7 @@ import java.util.List;
*/
public class ListReportSeries extends AbstractReportSeries {
- private final List data = new ArrayList();
+ private final List data = new ArrayList<>();
/**
* @param name
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java
index a97799ab4..8221fa0ca 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java
@@ -12,9 +12,13 @@ import java.util.Collection;
import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
+import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
+import org.eclipse.hawkbit.repository.model.Rollout;
+import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
+import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -30,26 +34,12 @@ import org.springframework.transaction.annotation.Transactional;
/**
* {@link Action} repository.
*
- *
- *
- *
- *
*/
@Transactional(readOnly = true)
public interface ActionRepository extends BaseEntityRepository, JpaSpecificationExecutor {
-
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.CrudRepository#findAll()
- */
- @Override
- @EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
- Iterable findAll();
-
/**
* Retrieves an Action with all lazy attributes.
- *
+ *
* @param actionId
* the ID of the action
* @return the found {@link Action}
@@ -67,13 +57,12 @@ public interface ActionRepository extends BaseEntityRepository, Jp
* the {@link DistributionSet} on which will be filtered
* @return the found {@link Action}s
*/
- @EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Page findByDistributionSet(final Pageable pageable, final DistributionSet ds);
/**
* Retrieves all {@link Action}s which are referring the given
* {@link Target}.
- *
+ *
* @param pageable
* page parameters
* @param target
@@ -84,8 +73,9 @@ public interface ActionRepository extends BaseEntityRepository, Jp
/**
* Retrieves all {@link Action}s which are active and referring the given
- * {@link Target} in a specified order.
- *
+ * {@link Target} in a specified order. Loads also the lazy
+ * {@link Action#getDistributionSet()} field.
+ *
* @param pageable
* page parameters
* @param target
@@ -125,14 +115,13 @@ public interface ActionRepository extends BaseEntityRepository, Jp
* @return the found {@link UpdateAction}s
*/
@Query("Select a from Action a where a.target = :target and a.distributionSet = :ds order by a.id")
- @EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Page findByTargetAndDistributionSet(final Pageable pageable, @Param("target") final Target target,
@Param("ds") DistributionSet ds);
/**
* Retrieves all {@link Action}s of a specific target, without pagination
* ordered by action ID.
- *
+ *
* @param target
* to search for
* @return a list of actions according to the searched target
@@ -143,7 +132,7 @@ public interface ActionRepository extends BaseEntityRepository, Jp
/**
* Retrieves all {@link Action}s of a specific target and given active flag
* ordered by action ID.
- *
+ *
* @param pageable
* the pagination parameter
* @param target
@@ -159,8 +148,9 @@ public interface ActionRepository extends BaseEntityRepository, Jp
/**
* Retrieves all {@link Action}s of a specific target and given active flag
- * ordered by action ID.
- *
+ * ordered by action ID. Loads also the lazy
+ * {@link Action#getDistributionSet()} field.
+ *
* @param target
* to search for
* @param active
@@ -174,7 +164,7 @@ public interface ActionRepository extends BaseEntityRepository, Jp
/**
* Updates all {@link Action} to inactive for all targets with given ID.
- *
+ *
* @param keySet
* the list of actions to set inactive
* @param targetsIds
@@ -187,6 +177,47 @@ public interface ActionRepository extends BaseEntityRepository, Jp
void setToInactive(@Param("keySet") List keySet, @Param("targetsIds") List targetsIds);
/**
+ * Switches the status of actions from one specific status into another,
+ * only if the actions are in a specific status. This should be a atomar
+ * operation.
+ *
+ * @param statusToSet
+ * the new status the actions should get
+ * @param targetIds
+ * the IDs of the targets of the actions which are affected
+ * @param active
+ * the active flag of the actions which should be affected
+ * @param currentStatus
+ * the current status of the actions which are affected
+ */
+ @Modifying
+ @Transactional
+ @Query("UPDATE Action a SET a.status = :statusToSet WHERE a.target IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false")
+ void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List targetIds,
+ @Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
+
+ /**
+ * Switches the status of actions from one specific status into another,
+ * only if the actions are in a specific status. This should be a atomar
+ * operation.
+ *
+ * @param statusToSet
+ * the new status the actions should get
+ * @param rollout
+ * the rollout of the actions which are affected
+ * @param active
+ * the active flag of the actions which should be affected
+ * @param currentStatus
+ * the current status of the actions which are affected
+ */
+ @Modifying
+ @Transactional
+ @Query("UPDATE Action a SET a.status = :statusToSet WHERE a.rollout = :rollout AND a.active = :active AND a.status = :currentStatus")
+ void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("rollout") Rollout rollout,
+ @Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
+
+ /**
+ *
* Retrieves all {@link Action}s which are active and referring to the given
* target Ids and distribution set required migration step.
*
@@ -202,39 +233,159 @@ public interface ActionRepository extends BaseEntityRepository, Jp
/**
* Counts all {@link Action}s referring to the given target.
- *
+ *
* @param target
* the target to count the {@link Action}s
* @return the count of actions referring to the given target
*/
Long countByTarget(Target target);
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.CrudRepository#save(java.lang.
- * Iterable)
- */
@Override
@CacheEvict(value = "feedbackReceivedOverTime", allEntries = true)
List save(Iterable entities);
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.CrudRepository#save(java.lang.Object)
- */
@Override
@CacheEvict(value = "feedbackReceivedOverTime", allEntries = true)
S save(S entity);
/**
* Counts all {@link Action}s referring to the given DistributionSet.
- *
+ *
* @param distributionSet
* DistributionSet to count the {@link Action}s from
- * @return the count of actions referring to the given target
+ * @return the count of actions referring to the given distributionSet
*/
Long countByDistributionSet(DistributionSet distributionSet);
+
+ /**
+ * Counts all {@link Action}s referring to the given rollout.
+ *
+ * @param rollout
+ * the rollout to count the {@link Action}s from
+ * @return the count of actions referring to the given rollout
+ */
+ Long countByRollout(Rollout rollout);
+
+ /**
+ * Counts all actions referring to a given rollout and rolloutgroup which
+ * are currently not in the given status. An in-clause statement does not
+ * work with the spring-data, so this is specific usecase regarding to the
+ * rollout-management to find out actions which are not in specific states.
+ *
+ * @param rollout
+ * the rollout the actions are belong to
+ * @param rolloutGroup
+ * the rolloutgroup the actions are belong to
+ * @param notStatus1
+ * the status the action should not have
+ * @param notStatus2
+ * the status the action should not have
+ * @param notStatus3
+ * the status the action should not have
+ * @return the count of actions referring the rollout and rolloutgroup and
+ * are not in given states
+ */
+ Long countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(Rollout rollout, RolloutGroup rolloutGroup,
+ Status notStatus1, Status notStatus2, Status notStatus3);
+
+ /**
+ * Counts all actions referring to a given rollout and rolloutgroup.
+ *
+ * @param rollout
+ * the rollout the actions belong to
+ * @param rolloutGroup
+ * the rolloutgroup the actions belong to
+ * @return the count of actions referring to a rollout and rolloutgroup
+ */
+ Long countByRolloutAndRolloutGroup(Rollout rollout, RolloutGroup rolloutGroup);
+
+ /**
+ * Counts all actions referring to a given rollout, rolloutgroup and status.
+ *
+ * @param rolloutId
+ * the ID of rollout the actions belong to
+ * @param rolloutGroupId
+ * the ID rolloutgroup the actions belong to
+ * @param status
+ * the status the actions should have
+ * @return the count of actions referring to a rollout, rolloutgroup and are
+ * in a given status
+ */
+ Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status);
+
+ /**
+ * Retrieving all actions referring to a given rollout with a specific
+ * action as parent reference and a specific status.
+ *
+ * Finding all actions of a specific rolloutgroup parent relation.
+ *
+ * @param rollout
+ * the rollout the actions belong to
+ * @param rolloutGroupParent
+ * the parent rolloutgroup the actions should reference
+ * @param actionStatus
+ * the status the actions have
+ * @return the actions referring a specific rollout and a specific parent
+ * rolloutgroup in a specific status
+ */
+ List findByRolloutAndRolloutGroupParentAndStatus(Rollout rollout, RolloutGroup rolloutGroupParent,
+ Status actionStatus);
+
+ /**
+ * Retrieves all actions for a specific rollout and in a specific status.
+ *
+ * @param rollout
+ * the rollout the actions beglong to
+ * @param actionStatus
+ * the status of the actions
+ * @return the actions referring a specific rollout an in a specific status
+ */
+ List findByRolloutAndStatus(Rollout rollout, Status actionStatus);
+
+ /**
+ * Get list of objects which has details of status and count of targets in
+ * each status in specified rollout.
+ *
+ * @param rolloutId
+ * id of {@link Rollout}
+ * @return list of objects with status and target count
+ */
+ @Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rollout.id IN ?1 GROUP BY a.rollout.id,a.status")
+ List getStatusCountByRolloutId(List rolloutId);
+
+ /**
+ * Get list of objects which has details of status and count of targets in
+ * each status in specified rollout.
+ *
+ * @param rolloutId
+ * id of {@link Rollout}
+ * @return list of objects with status and target count
+ */
+ @Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rollout.id = ?1 GROUP BY a.rollout.id,a.status")
+ List getStatusCountByRolloutId(Long rolloutId);
+
+ /**
+ * Get list of objects which has details of status and count of targets in
+ * each status in specified rollout group.
+ *
+ * @param rolloutGroupId
+ * id of {@link RolloutGroup}
+ * @return list of objects with status and target count
+ */
+ @Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rolloutGroup.id = ?1 GROUP BY a.rolloutGroup.id, a.status")
+ List getStatusCountByRolloutGroupId(Long rolloutGroupId);
+
+ /**
+ * Get list of objects which has details of status and count of targets in
+ * each status in specified rollout group.
+ *
+ * @param rolloutGroupId
+ * list of id of {@link RolloutGroup}
+ * @return list of objects with status and target count
+ */
+ @Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
+ List getStatusCountByRolloutGroupId(List rolloutGroupId);
+
+ // Asha-ends here
+
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java
index 5e2800755..2705b9ac6 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java
@@ -21,45 +21,47 @@ import org.springframework.transaction.annotation.Transactional;
/**
* {@link ActionStatus} repository.
*
- *
- *
- *
*/
@Transactional(readOnly = true)
public interface ActionStatusRepository
extends BaseEntityRepository, JpaSpecificationExecutor {
/**
- * @param target
+ * Counts {@link ActionStatus} entries of given {@link Action} in
+ * repository.
+ *
* @param action
- * @return
+ * to count status entries
+ * @return number of actions in repository
*/
Long countByAction(Action action);
/**
+ * Counts {@link ActionStatus} entries of given {@link Action} with given
+ * {@link Status} in repository.
+ *
* @param action
- * @param retrieved
- * @return
+ * to count status entries
+ * @param status
+ * to filter for
+ * @return number of actions in repository
*/
- Long countByActionAndStatus(Action action, Status retrieved);
+ Long countByActionAndStatus(Action action, Status status);
/**
+ * Retrieves all {@link ActionStatus} entries from repository of given
+ * {@link Action}.
+ *
* @param pageReq
+ * parameters
* @param action
- * @return
+ * of the status entries
+ * @return pages list of {@link ActionStatus} entries
*/
Page findByAction(Pageable pageReq, Action action);
/**
- * @param pageReq
- * @param action
- * @return
- */
- Page findByActionOrderByIdDesc(Pageable pageReq, Action action);
-
- /**
- * Finds all status updates for the defined action and target order by
- * {@link ActionStatus#getId()} desc including
+ * Finds all status updates for the defined action and target including
* {@link ActionStatus#getMessages()}.
*
* @param pageReq
@@ -71,6 +73,6 @@ public interface ActionStatusRepository
* @return Page with found targets
*/
@EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD)
- Page getByActionOrderByIdDesc(Pageable pageReq, Action action);
+ Page getByAction(Pageable pageReq, Action action);
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java
index 844bbd097..7131bbe0a 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java
@@ -15,7 +15,6 @@ import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
@@ -34,13 +33,13 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_;
+import org.eclipse.hawkbit.repository.model.TenantConfiguration;
+import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
+import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.bind.RelaxedPropertyResolver;
-import org.springframework.context.EnvironmentAware;
-import org.springframework.core.env.Environment;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -58,7 +57,7 @@ import org.springframework.validation.annotation.Validated;
@Transactional(readOnly = true)
@Validated
@Service
-public class ControllerManagement implements EnvironmentAware {
+public class ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
@@ -86,9 +85,35 @@ public class ControllerManagement implements EnvironmentAware {
@Autowired
private ActionStatusRepository actionStatusRepository;
- private Integer maxCount = 1000;
+ @Autowired
+ private HawkbitSecurityProperties securityProperties;
- private Integer maxAttributes = 100;
+ @Autowired
+ private TenantConfigurationRepository tenantConfigurationRepository;
+
+ @Autowired
+ private TenantConfigurationManagement tenantConfigurationManagement;
+
+ /**
+ * Retrieves all {@link SoftwareModule}s which are assigned to the given
+ * {@link DistributionSet}.
+ *
+ * @param distributionSet
+ * the distribution set which should be assigned to the returned
+ * {@link SoftwareModule}s
+ * @return a list of {@link SoftwareModule}s assigned to given
+ * {@code distributionSet}
+ */
+ @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
+ public String findPollingTime() {
+ final TenantConfigurationKey configurationKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
+ final Class propertyType = String.class;
+ tenantConfigurationManagement.validateTenantConfigurationDataType(configurationKey, propertyType);
+ final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
+ .findByKey(configurationKey.getKeyName());
+ return tenantConfigurationManagement
+ .buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration).getValue();
+ }
/**
* Refreshes the time of the last time the controller has been connected to
@@ -212,14 +237,9 @@ public class ControllerManagement implements EnvironmentAware {
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
- public Target findOrRegisterTargetIfItDoesNotexist(@NotNull final String targetid, final URI address) {
- final Specification spec = new Specification() {
- @Override
- public Predicate toPredicate(final Root targetRoot, final CriteriaQuery> query,
- final CriteriaBuilder cb) {
- return cb.equal(targetRoot.get(Target_.controllerId), targetid);
- }
- };
+ public Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty final String targetid, final URI address) {
+ final Specification spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId),
+ targetid);
Target target = targetRepository.findOne(spec);
@@ -229,9 +249,9 @@ public class ControllerManagement implements EnvironmentAware {
target.setName(targetid);
return targetManagement.createTarget(target, TargetUpdateStatus.REGISTERED, System.currentTimeMillis(),
address);
- } else {
- return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
+
+ return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
/**
@@ -385,15 +405,16 @@ public class ControllerManagement implements EnvironmentAware {
}
private void checkForToManyStatusEntries(final Action action) {
- if (maxCount > 0) {
+ if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) {
final Long statusCount = actionStatusRepository.countByAction(action);
- if (statusCount >= maxCount) {
+ if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) {
LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
- maxCount);
- throw new ToManyStatusEntriesException(String.valueOf(maxCount));
+ securityProperties.getDos().getMaxStatusEntriesPerAction());
+ throw new ToManyStatusEntriesException(
+ String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
}
}
}
@@ -442,10 +463,12 @@ public class ControllerManagement implements EnvironmentAware {
target.getTargetInfo().getControllerAttributes().putAll(data);
- if (target.getTargetInfo().getControllerAttributes().size() > maxAttributes) {
+ if (target.getTargetInfo().getControllerAttributes().size() > securityProperties.getDos()
+ .getMaxAttributeEntriesPerTarget()) {
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
- maxAttributes);
- throw new ToManyAttributeEntriesException(String.valueOf(maxAttributes));
+ securityProperties.getDos().getMaxAttributeEntriesPerTarget());
+ throw new ToManyAttributeEntriesException(
+ String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget()));
}
target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis());
@@ -453,19 +476,6 @@ public class ControllerManagement implements EnvironmentAware {
return targetRepository.save(target);
}
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.context.EnvironmentAware#setEnvironment(org.
- * springframework.core.env. Environment)
- */
- @Override
- public void setEnvironment(final Environment environment) {
- final RelaxedPropertyResolver env = new RelaxedPropertyResolver(environment, "hawkbit.server.");
- maxCount = env.getProperty("security.dos.maxStatusEntriesPerAction", Integer.class, 1000);
- maxAttributes = env.getProperty("security.dos.maxAttributeEntriesPerTarget", Integer.class, 100);
- }
-
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
index b8b8610cd..77f71bebf 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
@@ -23,7 +23,6 @@ import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
-import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
@@ -46,6 +45,9 @@ import org.eclipse.hawkbit.repository.model.Action_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
+import org.eclipse.hawkbit.repository.model.Rollout;
+import org.eclipse.hawkbit.repository.model.RolloutGroup;
+import org.eclipse.hawkbit.repository.model.Rollout_;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
@@ -228,7 +230,7 @@ public class DeploymentManagement {
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
- return assignDistributionSetToTargets(set, targets);
+ return assignDistributionSetToTargets(set, targets, null, null);
}
/**
@@ -237,8 +239,45 @@ public class DeploymentManagement {
*
* @param dsID
* the ID of the distribution set to assign
- * @param targetsWithActionType
+ * @param targets
* a list of all targets and their action type
+ * @param rollout
+ * the rollout for this assignment
+ * @param rolloutgroup
+ * the rolloutgroup for this assignment
+ * @return the assignment result
+ *
+ * @throw IncompleteDistributionSetException if mandatory
+ * {@link SoftwareModuleType} are not assigned as define by the
+ * {@link DistributionSetType}.
+ */
+ @Modifying
+ @Transactional
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
+ @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
+ public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
+ final List targets, final Rollout rollout, final RolloutGroup rolloutGroup) {
+ final DistributionSet set = distributoinSetRepository.findOne(dsID);
+ if (set == null) {
+ throw new EntityNotFoundException(
+ String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
+ }
+
+ return assignDistributionSetToTargets(set, targets, rollout, rolloutGroup);
+ }
+
+ /**
+ * method assigns the {@link DistributionSet} to all {@link Target}s by
+ * their IDs with a specific {@link ActionType} and {@code forcetime}.
+ *
+ * @param dsID
+ * the ID of the distribution set to assign
+ * @param targets
+ * a list of all targets and their action type
+ * @param rollout
+ * the rollout for this assignment
+ * @param rolloutgroup
+ * the rolloutgroup for this assignment
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
@@ -246,7 +285,8 @@ public class DeploymentManagement {
* {@link DistributionSetType}.
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final DistributionSet set,
- final List targetsWithActionType) {
+ final List targetsWithActionType, final Rollout rollout,
+ final RolloutGroup rolloutGroup) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
@@ -262,13 +302,10 @@ public class DeploymentManagement {
.collect(Collectors.toMap(TargetWithActionType::getTargetId, Function.identity()));
// split tIDs length into max entries in-statement because many database
- // have constraint of
- // max entries in in-statements e.g. Oracle with maximum 1000 elements,
- // so we need to split
- // the entries here and execute multiple statements
- // we take the target only into account if the requested operation is no
- // duplicate of a
- // previous one
+ // have constraint of max entries in in-statements e.g. Oracle with
+ // maximum 1000 elements, so we need to split the entries here and
+ // execute multiple statements we take the target only into account if
+ // the requested operation is no duplicate of a previous one
final List targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
@@ -286,15 +323,16 @@ public class DeploymentManagement {
targets.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT);
// override all active actions and set them into canceling state, we
- // need to remember which
- // one we have been switched to canceling state because for targets
- // which we have changed to
- // canceling we don't want to publish the new action update event.
- final Set targetIdsCancellList = new HashSet();
+ // need to remember which one we have been switched to canceling state
+ // because for targets which we have changed to canceling we don't want
+ // to publish the new action update event.
+ final Set targetIdsCancellList = new HashSet<>();
targetIds.forEach(ids -> targetIdsCancellList.addAll(overrideObsoleteUpdateActions(ids)));
- // check for old assigments that needs cancelation
- // final List canncelledTargetIds =
+ // cancel all scheduled actions which are in-active, these actions were
+ // not active before and the manual assignment which has been done
+ // cancels the
+ targetIds.forEach(tIds -> actionRepository.switchStatus(Status.CANCELED, tIds, false, Status.SCHEDULED));
// set assigned distribution set and TargetUpdateStatus
final String currentUser;
@@ -307,25 +345,15 @@ public class DeploymentManagement {
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSet(set, System.currentTimeMillis(),
currentUser, tIds));
targetIds.forEach(tIds -> targetInfoRepository.setTargetUpdateStatus(TargetUpdateStatus.PENDING, tIds));
+ final Map targetIdsToActions = actionRepository
+ .save(targets.stream().map(t -> createTargetAction(targetsWithActionMap, t, set, rollout, rolloutGroup))
+ .collect(Collectors.toList()))
+ .stream().collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
- final Map targetIdsToActions = actionRepository.save(targets.stream().map(t -> {
- final Action tAction = new Action();
- final TargetWithActionType targetWithActionType = targetsWithActionMap.get(t.getControllerId());
- tAction.setActionType(targetWithActionType.getActionType());
- tAction.setForcedTime(targetWithActionType.getForceTime());
- tAction.setActive(true);
- tAction.setStatus(Status.RUNNING);
- tAction.setTarget(t);
- tAction.setDistributionSet(set);
- return tAction;
- }).collect(Collectors.toList())).stream()
- .collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
-
- // MECS-720 create initial action status when action is created so we
- // remember the initial
- // running status because we will change the status of the action itself
- // and with this action
- // status we have a nicer action history.
+ // create initial action status when action is created so we remember
+ // the initial running status because we will change the status
+ // of the action itself and with this action status we have a nicer
+ // action history.
targetIdsToActions.values().forEach(action -> {
final ActionStatus actionStatus = new ActionStatus();
actionStatus.setAction(action);
@@ -334,11 +362,13 @@ public class DeploymentManagement {
actionStatusRepository.save(actionStatus);
});
- // select updated targets in order to return them
+ // flush to get action IDs
+ entityManager.flush();
+ // collect updated target and actions IDs in order to return them
final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult(
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), targets.size(),
- controllerIDs.size() - targets.size(), Lists.newArrayList(targetIdsToActions.values()),
- targetManagement);
+ controllerIDs.size() - targets.size(),
+ targetIdsToActions.values().stream().map(Action::getId).collect(Collectors.toList()), targetManagement);
LOG.debug("assignDistribution({}) finished {}", set, result);
@@ -347,19 +377,37 @@ public class DeploymentManagement {
// detaching as it is not necessary to persist the set itself
entityManager.detach(set);
- // send distribution set assignment event
+ sendDistributionSetAssignmentEvent(targets, targetIdsCancellList, targetIdsToActions, softwareModules);
+ return result;
+ }
+
+ private void sendDistributionSetAssignmentEvent(final List targets, final Set targetIdsCancellList,
+ final Map targetIdsToActions, final List softwareModules) {
targets.stream().filter(t -> !!!targetIdsCancellList.contains(t.getId()))
.forEach(t -> assignDistributionSetEvent(t, targetIdsToActions.get(t.getControllerId()).getId(),
softwareModules));
+ }
- return result;
+ private Action createTargetAction(final Map targetsWithActionMap, final Target target,
+ final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) {
+ final Action actionForTarget = new Action();
+ final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
+ actionForTarget.setActionType(targetWithActionType.getActionType());
+ actionForTarget.setForcedTime(targetWithActionType.getForceTime());
+ actionForTarget.setActive(true);
+ actionForTarget.setStatus(Status.RUNNING);
+ actionForTarget.setTarget(target);
+ actionForTarget.setDistributionSet(set);
+ actionForTarget.setRollout(rollout);
+ actionForTarget.setRolloutGroup(rolloutGroup);
+ return actionForTarget;
}
/**
* Sends the {@link TargetAssignDistributionSetEvent} for a specific target
* to the {@link EventBus}.
- *
+ *
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
@@ -372,31 +420,32 @@ public class DeploymentManagement {
target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING);
afterCommit.afterCommit(() -> {
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
- eventBus.post(new TargetAssignDistributionSetEvent(target.getControllerId(), actionId, softwareModules,
- target.getTargetInfo().getAddress()));
+ eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
+ target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress()));
});
}
/**
* Removes {@link UpdateAction}s that are no longer necessary and sends
- * cancellations to the controller.
+ * cancelations to the controller.
*
* @param myTarget
* to override {@link UpdateAction}s
*/
private Set overrideObsoleteUpdateActions(final List targetsIds) {
- final Set cancelledTargetIds = new HashSet();
+ final Set cancelledTargetIds = new HashSet<>();
// Figure out if there are potential target/action combinations that
// need to be considered
- // for cancellation
+ // for cancelation
final List activeActions = actionRepository
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
Action.Status.CANCELING);
activeActions.forEach(action -> {
action.setStatus(Status.CANCELING);
// document that the status has been retrieved
+
actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
@@ -404,15 +453,20 @@ public class DeploymentManagement {
cancelledTargetIds.add(action.getTarget().getId());
});
+
actionRepository.save(activeActions);
return cancelledTargetIds;
+
}
private DistributionSetAssignmentResult assignDistributionSetByTargetId(@NotNull final DistributionSet set,
@NotEmpty final List tIDs, final ActionType actionType, final long forcedTime) {
+
return assignDistributionSetToTargets(set, tIDs.stream()
- .map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()));
+ .map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
+ null);
+
}
/**
@@ -475,7 +529,6 @@ public class DeploymentManagement {
actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
final Action saveAction = actionRepository.save(myAction);
-
cancelAssignDistributionSetEvent(target, myAction.getId());
return saveAction;
@@ -488,15 +541,15 @@ public class DeploymentManagement {
/**
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
* the {@link EventBus}.
- *
+ *
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
*/
private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
- afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getControllerId(), actionId,
- target.getTargetInfo().getAddress())));
+ afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getOptLockRevision(),
+ target.getTenant(), target.getControllerId(), actionId, target.getTargetInfo().getAddress())));
}
/**
@@ -518,7 +571,7 @@ public class DeploymentManagement {
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
- public Action forceQuitAction(@NotNull final Action action, @NotNull final Target target) {
+ public Action forceQuitAction(@NotNull final Action action) {
final Action mergedAction = entityManager.merge(action);
if (!mergedAction.isCancelingOrCanceled()) {
@@ -542,6 +595,111 @@ public class DeploymentManagement {
return actionRepository.save(mergedAction);
}
+ /**
+ * Creates an action entry into the action repository. In case of existing
+ * scheduled actions the scheduled actions gets canceled. A scheduled action
+ * is created in-active.
+ *
+ * @param targets
+ * the targets to create scheduled actions for
+ * @param distributionSet
+ * the distribution set for the actions
+ * @param actionType
+ * the action type for the action
+ * @param forcedTime
+ * the forcedTime of the action
+ * @param rollout
+ * the rollout for this action
+ * @param rolloutGroup
+ * the rolloutgroup for this action
+ */
+ @Modifying
+ @Transactional
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
+ public void createScheduledAction(final List targets, final DistributionSet distributionSet,
+ final ActionType actionType, final long forcedTime, final Rollout rollout,
+ final RolloutGroup rolloutGroup) {
+ // cancel all current scheduled actions for this target. E.g. an action
+ // is already scheduled and a next action is created then cancel the
+ // current scheduled action to cancel. E.g. a new scheduled action is
+ // created.
+ final List targetIds = targets.stream().map(t -> t.getId()).collect(Collectors.toList());
+ actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED);
+ targets.forEach(target -> {
+ final Action action = new Action();
+ action.setTarget(target);
+ action.setActive(false);
+ action.setDistributionSet(distributionSet);
+ action.setActionType(actionType);
+ action.setForcedTime(forcedTime);
+ action.setStatus(Status.SCHEDULED);
+ action.setRollout(rollout);
+ action.setRolloutGroup(rolloutGroup);
+ actionRepository.save(action);
+ });
+ }
+
+ /**
+ * Starting an action which is scheduled, e.g. in case of rollout a
+ * scheduled action must be started now.
+ *
+ * @param action
+ * the action to start now.
+ * @return the action which has been started
+ */
+ @Modifying
+ @Transactional
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ + SpringEvalExpressions.IS_SYSTEM_CODE)
+ public Action startScheduledAction(@NotNull final Action action) {
+
+ final Action mergedAction = entityManager.merge(action);
+ final Target mergedTarget = entityManager.merge(action.getTarget());
+
+ // check if we need to override running update actions
+ final Set overrideObsoleteUpdateActions = overrideObsoleteUpdateActions(
+ Collections.singletonList(action.getTarget().getId()));
+
+ final boolean hasDistributionSetAlreadyAssigned = targetRepository
+ .count(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(
+ Collections.singletonList(mergedTarget.getControllerId()),
+ action.getDistributionSet().getId())) == 0;
+ if (hasDistributionSetAlreadyAssigned) {
+ // the target has already the distribution set assigned, we don't
+ // need to start the scheduled action, just finished it.
+ mergedAction.setStatus(Status.FINISHED);
+ mergedAction.setActive(false);
+ return actionRepository.save(mergedAction);
+ }
+
+ mergedAction.setActive(true);
+ mergedAction.setStatus(Status.RUNNING);
+ final Action savedAction = actionRepository.save(mergedAction);
+
+ final ActionStatus actionStatus = new ActionStatus();
+ actionStatus.setAction(action);
+ actionStatus.setOccurredAt(action.getCreatedAt());
+ actionStatus.setStatus(Status.RUNNING);
+ actionStatusRepository.save(actionStatus);
+
+ mergedTarget.setAssignedDistributionSet(action.getDistributionSet());
+ final TargetInfo targetInfo = mergedTarget.getTargetInfo();
+ targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
+ targetRepository.save(mergedTarget);
+ targetInfoRepository.save(targetInfo);
+
+ // in case we canceled an action before for this target, then don't fire
+ // assignment event
+ if (!overrideObsoleteUpdateActions.contains(savedAction.getId())) {
+ final List softwareModules = softwareModuleRepository
+ .findByAssignedTo(action.getDistributionSet());
+ // send distribution set assignment event
+
+ assignDistributionSetEvent(mergedAction.getTarget(), mergedAction.getId(), softwareModules);
+ }
+ return savedAction;
+ }
+
/**
* Get the {@link Action} entity for given actionId.
*
@@ -555,8 +713,8 @@ public class DeploymentManagement {
}
/**
- * Get the {@link Action} entity for given actionId with all lazy
- * attributes.
+ * Get the {@link Action} entity for given actionId with all lazy attributes
+ * (i.e. distributionSet, target, target.assignedDs).
*
* @param actionId
* to be id of the action
@@ -609,18 +767,18 @@ public class DeploymentManagement {
final Root actionRoot = query.from(Action.class);
final ListJoin actionStatusJoin = actionRoot.join(Action_.actionStatus, JoinType.LEFT);
final Join actionDsJoin = actionRoot.join(Action_.distributionSet);
+ final Join actionRolloutJoin = actionRoot.join(Action_.rollout, JoinType.LEFT);
final CriteriaQuery multiselect = query.distinct(true).multiselect(
actionRoot.get(Action_.id), actionRoot.get(Action_.actionType), actionRoot.get(Action_.active),
actionRoot.get(Action_.forcedTime), actionRoot.get(Action_.status), actionRoot.get(Action_.createdAt),
actionRoot.get(Action_.lastModifiedAt), actionDsJoin.get(DistributionSet_.id),
actionDsJoin.get(DistributionSet_.name), actionDsJoin.get(DistributionSet_.version),
- cb.count(actionStatusJoin));
+ cb.count(actionStatusJoin), actionRolloutJoin.get(Rollout_.name));
multiselect.where(cb.equal(actionRoot.get(Action_.target), target));
multiselect.orderBy(cb.desc(actionRoot.get(Action_.id)));
multiselect.groupBy(actionRoot.get(Action_.id));
- final List resultList = entityManager.createQuery(multiselect).getResultList();
- return resultList;
+ return entityManager.createQuery(multiselect).getResultList();
}
/**
@@ -640,25 +798,19 @@ public class DeploymentManagement {
public Slice findActionsByTarget(final Specification specifiction, final Target target,
final Pageable pageable) {
- return actionRepository.findAll(new Specification() {
-
- @Override
- public Predicate toPredicate(final Root root, final CriteriaQuery> query,
- final CriteriaBuilder cb) {
- return cb.and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
- }
- }, pageable);
+ return actionRepository.findAll((Specification) (root, query, cb) -> cb
+ .and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target)), pageable);
}
/**
* Retrieves all {@link Action}s which are referring the given
* {@link Target}.
- *
- * @param pageable
- * page parameters
+ *
* @param foundTarget
- * the target to find assigned actions
- * @return the found {@link Action}s
+ * the target to find actions for
+ * @param pageable
+ * the pageable request to limit, sort the actions
+ * @return a slice of actions found for a specific target
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice findActionsByTarget(final Target foundTarget, final Pageable pageable) {
@@ -744,13 +896,8 @@ public class DeploymentManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Long countActionsByTarget(@NotNull final Specification spec, @NotNull final Target target) {
- return actionRepository.count(new Specification() {
- @Override
- public Predicate toPredicate(final Root root, final CriteriaQuery> query,
- final CriteriaBuilder cb) {
- return cb.and(spec.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
- }
- });
+ return actionRepository.count((root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
+ cb.equal(root.get(Action_.target), target)));
}
/**
@@ -777,7 +924,7 @@ public class DeploymentManagement {
/**
* retrieves all the {@link ActionStatus} entries of the given
- * {@link Action} and {@link Target} in the order latest first.
+ * {@link Action} and {@link Target}.
*
* @param pageReq
* pagination parameter
@@ -789,12 +936,12 @@ public class DeploymentManagement {
* @return the corresponding {@link Page} of {@link ActionStatus}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
- public Page findActionStatusMessagesByActionInDescOrder(final Pageable pageReq, final Action action,
+ public Page findActionStatusByAction(final Pageable pageReq, final Action action,
final boolean withMessages) {
if (withMessages) {
- return actionStatusRepository.getByActionOrderByIdDesc(pageReq, action);
+ return actionStatusRepository.getByAction(pageReq, action);
} else {
- return actionStatusRepository.findByActionOrderByIdDesc(pageReq, action);
+ return actionStatusRepository.findByAction(pageReq, action);
}
}
@@ -802,7 +949,7 @@ public class DeploymentManagement {
* This method is called, when cancellation has been successful. It sets the
* action to canceled, resets the meta data of the target and in case there
* is a new action this action is triggered.
- *
+ *
* @param action
* the action which is set to canceled
*/
@@ -824,4 +971,40 @@ public class DeploymentManagement {
}
targetManagement.updateTarget(target);
}
+
+ /**
+ * Retrieving all actions referring to a given rollout with a specific
+ * action as parent reference and a specific status.
+ *
+ * Finding all actions of a specific rolloutgroup parent relation.
+ *
+ * @param rollout
+ * the rollout the actions belong to
+ * @param rolloutGroupParent
+ * the parent rolloutgroup the actions should reference
+ * @param actionStatus
+ * the status the actions have
+ * @return the actions referring a specific rollout and a specific parent
+ * rolloutgroup in a specific status
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ + SpringEvalExpressions.IS_SYSTEM_CODE)
+ public List findActionsByRolloutGroupParentAndStatus(final Rollout rollout,
+ final RolloutGroup rolloutGroupParent, final Action.Status actionStatus) {
+ return actionRepository.findByRolloutAndRolloutGroupParentAndStatus(rollout, rolloutGroupParent, actionStatus);
+ }
+
+ /**
+ * Retrieves all actions for a specific rollout and in a specific status.
+ *
+ * @param rollout
+ * the rollout the actions beglong to
+ * @param actionStatus
+ * the status of the actions
+ * @return the actions referring a specific rollout an in a specific status
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
+ public List findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
+ return actionRepository.findByRolloutAndStatus(rollout, actionStatus);
+ }
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java
index ac562131d..3e61e4499 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java
@@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository;
import java.util.List;
-import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.AssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
@@ -19,14 +18,11 @@ import org.eclipse.hawkbit.repository.model.Target;
* information of an assignment and how much of the assignment has been done and
* how much of the assignments had already been existed.
*
- *
- *
- *
*/
public class DistributionSetAssignmentResult extends AssignmentResult {
private final List assignedTargets;
- private final List actions;
+ private final List actions;
private final TargetManagement targetManagement;
@@ -48,7 +44,7 @@ public class DistributionSetAssignmentResult extends AssignmentResult {
*
*/
public DistributionSetAssignmentResult(final List assignedTargets, final int assigned,
- final int alreadyAssigned, final List actions, final TargetManagement targetManagement) {
+ final int alreadyAssigned, final List actions, final TargetManagement targetManagement) {
super(assigned, alreadyAssigned);
this.assignedTargets = assignedTargets;
this.actions = actions;
@@ -63,9 +59,9 @@ public class DistributionSetAssignmentResult extends AssignmentResult {
}
/**
- * @return the actionId
+ * @return the actionIds
*/
- public List getActions() {
+ public List getActions() {
return actions;
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java
index 0a3838354..8b07c9bf8 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java
@@ -22,10 +22,6 @@ import java.util.stream.Collectors;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagAssigmentResultEvent;
@@ -53,13 +49,13 @@ import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.specifications.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.specifications.DistributionSetTypeSpecification;
+import org.eclipse.hawkbit.repository.specifications.SpecificationsBuilder;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.data.jpa.domain.Specifications;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@@ -203,7 +199,8 @@ public class DistributionSetManagement {
}
final DistributionSetTagAssigmentResult resultAssignment = result;
- afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagAssigmentResultEvent(resultAssignment)));
+ afterCommit
+ .afterCommit(() -> eventBus.post(new DistributionSetTagAssigmentResultEvent(resultAssignment)));
// no reason to persist the tag
entityManager.detach(myTag);
@@ -263,7 +260,7 @@ public class DistributionSetManagement {
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteDistributionSet(@NotNull final DistributionSet set) {
- this.deleteDistributionSet(set.getId());
+ deleteDistributionSet(set.getId());
}
/**
@@ -297,8 +294,7 @@ public class DistributionSetManagement {
// hard delete the rest if exixts
if (!toHardDelete.isEmpty()) {
// don't give the delete statement an empty list, JPA/Oracle cannot
- // handle the empty list,
- // see MECS-403
+ // handle the empty list
distributionSetRepository.deleteByIdIn(toHardDelete);
}
}
@@ -321,14 +317,10 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public DistributionSet createDistributionSet(@NotNull final DistributionSet dSet) {
prepareDsSave(dSet);
-
if (dSet.getType() == null) {
dSet.setType(systemManagement.getTenantMetadata().getDefaultDsType());
}
-
- final DistributionSet result = distributionSetRepository.save(dSet);
-
- return result;
+ return distributionSetRepository.save(dSet);
}
private void prepareDsSave(final DistributionSet dSet) {
@@ -400,7 +392,7 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds,
final SoftwareModule softwareModule) {
- final Set softwareModules = new HashSet();
+ final Set softwareModules = new HashSet<>();
softwareModules.add(softwareModule);
ds.removeModule(softwareModule);
checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules);
@@ -491,20 +483,11 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
private DistributionSet findDistributionSetsByFiltersAndInstalledOrAssignedTarget(
final DistributionSetFilter distributionSetFilter) {
-
final List> specList = buildDistributionSetSpecifications(distributionSetFilter);
-
- Specifications specs = null;
- if (!specList.isEmpty()) {
- specs = Specifications.where(specList.get(0));
+ if (specList == null || specList.isEmpty()) {
+ return null;
}
- if (specList.size() > 1) {
- specList.remove(0);
- for (final Specification s : specList) {
- specs = specs.and(s);
- }
- }
- return distributionSetRepository.findOne(specs);
+ return distributionSetRepository.findOne(SpecificationsBuilder.combineWithAnd(specList));
}
/**
@@ -531,7 +514,7 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page findDistributionSetsAll(@NotNull final Pageable pageReq, final Boolean deleted,
final Boolean complete) {
- final List> specList = new ArrayList>();
+ final List> specList = new ArrayList<>();
if (deleted != null) {
final Specification spec = DistributionSetSpecification.isDeleted(deleted);
@@ -563,7 +546,7 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page findDistributionSetsAll(@NotNull final Specification spec,
@NotNull final Pageable pageReq, final Boolean deleted) {
- final List> specList = new ArrayList>();
+ final List> specList = new ArrayList<>();
if (deleted != null) {
specList.add(DistributionSetSpecification.isDeleted(deleted));
}
@@ -635,17 +618,6 @@ public class DistributionSetManagement {
return new PageImpl<>(resultSet, pageable, findDistributionSetsByFilters.getTotalElements());
}
- private Long countDistributionSetByCriteriaAPI(@NotEmpty final List> specList) {
- Specifications specs = Specifications.where(specList.get(0));
- if (specList.size() > 1) {
- for (final Specification s : specList.subList(1, specList.size())) {
- specs = specs.and(s);
- }
- }
-
- return distributionSetRepository.count(specs);
- }
-
/**
* Find distribution set by name and version.
*
@@ -689,12 +661,12 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Long countDistributionSetsAll() {
- final List> specList = new ArrayList>();
+ final List> specList = new ArrayList<>();
final Specification spec = DistributionSetSpecification.isDeleted(Boolean.FALSE);
specList.add(spec);
- return countDistributionSetByCriteriaAPI(specList);
+ return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(specList));
}
/**
@@ -869,17 +841,10 @@ public class DistributionSetManagement {
public Page findDistributionSetMetadataByDistributionSetId(
@NotNull final Long distributionSetId, @NotNull final Pageable pageable) {
- return distributionSetMetadataRepository.findAll(new Specification() {
- @Override
- public Predicate toPredicate(final Root root, final CriteriaQuery> query,
- final CriteriaBuilder cb) {
-
- final Predicate predicate = cb.equal(
- root.get(DistributionSetMetadata_.distributionSet).get(DistributionSet_.id), distributionSetId);
-
- return predicate;
- }
- }, pageable);
+ return distributionSetMetadataRepository.findAll(
+ (Specification) (root, query, cb) -> cb.equal(
+ root.get(DistributionSetMetadata_.distributionSet).get(DistributionSet_.id), distributionSetId),
+ pageable);
}
@@ -899,14 +864,14 @@ public class DistributionSetManagement {
public Page findDistributionSetMetadataByDistributionSetId(
@NotNull final Long distributionSetId, @NotNull final Specification spec,
@NotNull final Pageable pageable) {
- return distributionSetMetadataRepository.findAll(new Specification() {
- @Override
- public Predicate toPredicate(final Root root, final CriteriaQuery> query,
- final CriteriaBuilder cb) {
- return cb.and(cb.equal(root.get(DistributionSetMetadata_.distributionSet).get(DistributionSet_.id),
- distributionSetId), spec.toPredicate(root, query, cb));
- }
- }, pageable);
+ return distributionSetMetadataRepository
+ .findAll(
+ (Specification) (root, query,
+ cb) -> cb.and(
+ cb.equal(root.get(DistributionSetMetadata_.distributionSet)
+ .get(DistributionSet_.id), distributionSetId),
+ spec.toPredicate(root, query, cb)),
+ pageable);
}
/**
@@ -956,7 +921,7 @@ public class DistributionSetManagement {
/**
* Checking Distribution Set is already using while assign Software module.
- *
+ *
* @param distributionSet
* @param softwareModules
*/
@@ -969,9 +934,9 @@ public class DistributionSetManagement {
private List> buildDistributionSetSpecifications(
final DistributionSetFilter distributionSetFilter) {
- final List> specList = new ArrayList>();
+ final List> specList = new ArrayList<>();
- Specification spec = null;
+ Specification spec;
if (null != distributionSetFilter.getIsComplete()) {
spec = DistributionSetSpecification.isCompleted(distributionSetFilter.getIsComplete());
@@ -1053,21 +1018,12 @@ public class DistributionSetManagement {
*/
private Page findByCriteriaAPI(@NotNull final Pageable pageable,
final List> specList) {
- Specifications specs = null;
- if (!specList.isEmpty()) {
- specs = Specifications.where(specList.get(0));
- }
- if (specList.size() > 1) {
- for (final Specification s : specList.subList(1, specList.size())) {
- specs = specs.and(s);
- }
+
+ if (specList == null || specList.isEmpty()) {
+ return distributionSetRepository.findAll(pageable);
}
- if (specs == null) {
- return distributionSetRepository.findAll(pageable);
- } else {
- return distributionSetRepository.findAll(specs, pageable);
- }
+ return distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable);
}
private void checkAndThrowAlreadyIfDistributionSetMetadataExists(final DsMetadataCompositeKey metadataId) {
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java
index 4290607fd..79f54a3f0 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java
@@ -57,7 +57,7 @@ public class NoCountPagingRepository {
*/
public Slice findAll(final Specification spec, final Pageable pageable,
final Class domainClass) {
- final SimpleJpaNoCountRepository noCountDao = new SimpleJpaNoCountRepository(domainClass, em);
+ final SimpleJpaNoCountRepository noCountDao = new SimpleJpaNoCountRepository<>(domainClass, em);
return noCountDao.findAll(spec, pageable);
}
@@ -76,7 +76,7 @@ public class NoCountPagingRepository {
* org.springframework.data.domain.Pageable)
*/
public Slice findAll(final Pageable pageable, final Class domainClass) {
- final SimpleJpaNoCountRepository noCountDao = new SimpleJpaNoCountRepository(domainClass, em);
+ final SimpleJpaNoCountRepository noCountDao = new SimpleJpaNoCountRepository<>(domainClass, em);
return noCountDao.findAll(pageable);
}
@@ -120,7 +120,7 @@ public class NoCountPagingRepository {
final List content = query.getResultList();
- return new PageImpl(content, pageable, content.size());
+ return new PageImpl<>(content, pageable, content.size());
}
}
}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/OffsetBasedPageRequest.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/OffsetBasedPageRequest.java
new file mode 100644
index 000000000..753ec6963
--- /dev/null
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/OffsetBasedPageRequest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.repository;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+
+/**
+ * An implementation of the {@link PageRequest} which is offset based by means
+ * the offset is given and not the page number as in the original
+ * {@link PageRequest} implemntation where the offset is generated. Due that the
+ * REST-API is working with {@code offset} and {@code limit} parameter we need
+ * an offset based page request for JPA.
+ *
+ * @author Michael Hirsch
+ * @since 0.2.2
+ *
+ */
+public final class OffsetBasedPageRequest extends PageRequest {
+
+ private static final long serialVersionUID = 1L;
+ private final int offset;
+
+ /**
+ * Creates a new {@link OffsetBasedPageRequest}. Offsets are zero indexed,
+ * thus providing 0 for {@code offset} will return the first entry.
+ *
+ * @param offset
+ * zero-based offset index.
+ * @param limit
+ * the limit of the page to be returned.
+ */
+ public OffsetBasedPageRequest(final int offset, final int limit) {
+ this(offset, limit, null);
+ }
+
+ /**
+ * Creates a new {@link OffsetBasedPageRequest}. Offsets are zero indexed,
+ * thus providing 0 for {@code offset} will return the first entry.
+ *
+ * @param offset
+ * zero-based offset index.
+ * @param limit
+ * the limit of the page to be returned.
+ * @param sort
+ * sort can be {@literal null}.
+ */
+ public OffsetBasedPageRequest(final int offset, final int limit, final Sort sort) {
+ super(0, limit, sort);
+ this.offset = offset;
+ }
+
+ @Override
+ public int getOffset() {
+ return offset;
+ }
+
+ @Override
+ public String toString() {
+ return "OffsetBasedPageRequest [offset=" + offset + ", getPageSize()=" + getPageSize() + ", getPageNumber()="
+ + getPageNumber() + "]";
+ }
+}
diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java
index 801f6c80d..eab926b4b 100644
--- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java
+++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java
@@ -46,7 +46,6 @@ import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -55,14 +54,10 @@ import org.springframework.validation.annotation.Validated;
/**
* Service layer for generating SP reportings.
*
- *
- *
- *
*/
@Transactional(readOnly = true)
@Validated
@Service
-@ConfigurationProperties
public class ReportManagement {
@Value("${spring.jpa.database}")
@@ -213,8 +208,8 @@ public class ReportManagement {
* count
*/
@Cacheable("targetsCreatedOverPeriod")
- public DataReportSeries targetsCreatedOverPeriod(final DateType dateType, final LocalDateTime from,
- final LocalDateTime to) {
+ public DataReportSeries targetsCreatedOverPeriod(final DateType dateType,
+ final LocalDateTime from, final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getTargetsCreatedQueryTemplate(dateType, from, to));
final List