From 7e941cf64d9ba2c2204b751c01b62c7d431ad2f2 Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Tue, 3 May 2016 16:16:36 +0200 Subject: [PATCH] Teached simulator to download artifacts including SHA1 hash check and targetToken usage. Signed-off-by: Kai Zimmermann --- examples/hawkbit-device-simulator/pom.xml | 13 +- .../simulator/AbstractSimulatedDevice.java | 42 ++--- .../hawkbit/simulator/DDISimulatedDevice.java | 50 +++--- .../hawkbit/simulator/DMFSimulatedDevice.java | 5 +- .../simulator/DeviceSimulatorUpdater.java | 152 ++++++++++++++++-- .../simulator/NextPollTimeController.java | 18 +-- .../simulator/SimulatedDeviceFactory.java | 13 +- .../hawkbit/simulator/UpdateStatus.java | 58 +++++++ .../simulator/amqp/SpReceiverService.java | 15 +- .../simulator/amqp/SpSenderService.java | 53 +++--- .../simulator/event/ProgressUpdate.java | 2 - .../hawkbit/simulator/ui/SimulatorView.java | 98 +++++------ 12 files changed, 333 insertions(+), 186 deletions(-) create mode 100644 examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/UpdateStatus.java diff --git a/examples/hawkbit-device-simulator/pom.xml b/examples/hawkbit-device-simulator/pom.xml index c74eace3e..33d165584 100644 --- a/examples/hawkbit-device-simulator/pom.xml +++ b/examples/hawkbit-device-simulator/pom.xml @@ -1,4 +1,3 @@ - - 4.0.0 @@ -133,6 +133,15 @@ spring-boot-configuration-processor true + + org.scala-lang + scala-library + 2.10.4 + + + org.apache.httpcomponents + httpclient + diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java index 474acb6c4..890f43367 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.simulator; +import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus; + /** * The bean of a simulated device which can be stored in the * {@link DeviceSimulatorRepository} or shown in the UI. @@ -22,16 +24,15 @@ public abstract class AbstractSimulatedDevice { private Status status; private double progress; private String swversion = "unknown"; - private ResponseStatus responseStatus = ResponseStatus.SUCCESSFUL; + private UpdateStatus updateStatus = new UpdateStatus(ResponseStatus.SUCCESSFUL, "Simulation complete!"); private Protocol protocol = Protocol.DMF_AMQP; + private String targetSecurityToken; private int nextPollCounterSec; /** * Enum definition of the protocol to be used for the simulated device. * - * @author Michael Hirsch - * */ public enum Protocol { /** @@ -69,24 +70,6 @@ public abstract class AbstractSimulatedDevice { ERROR; } - /** - * The status to response to the hawkbit update server if an simulated - * update process should be respond with successful or failure update. - * - * @author Michael Hirsch - * - */ - public enum ResponseStatus { - /** - * updated has been successful and response the successful update. - */ - SUCCESSFUL, - /** - * updated has been not successful and response the error update. - */ - ERROR; - } - /** * empty constructor. */ @@ -158,12 +141,12 @@ public abstract class AbstractSimulatedDevice { this.swversion = swversion; } - public ResponseStatus getResponseStatus() { - return responseStatus; + public UpdateStatus getUpdateStatus() { + return updateStatus; } - public void setResponseStatus(final ResponseStatus responseStatus) { - this.responseStatus = responseStatus; + public void setUpdateStatus(final UpdateStatus updateStatus) { + this.updateStatus = updateStatus; } public Protocol getProtocol() { @@ -177,4 +160,13 @@ public abstract class AbstractSimulatedDevice { public void setNextPollCounterSec(final int nextPollDelayInSec) { this.nextPollCounterSec = nextPollDelayInSec; } + + public String getTargetSecurityToken() { + return targetSecurityToken; + } + + public void setTargetSecurityToken(final String targetSecurityToken) { + this.targetSecurityToken = targetSecurityToken; + } + } 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 b58d3a413..26e613dd9 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 @@ -8,8 +8,6 @@ */ package org.eclipse.hawkbit.simulator; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.hawkbit.simulator.http.ControllerResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +16,7 @@ import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathNotFoundException; /** - * @author Michael Hirsch + * A simulated device using the DDI API of the hawkBit update server. * */ public class DDISimulatedDevice extends AbstractSimulatedDevice { @@ -26,12 +24,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice { private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class); private final int pollDelaySec; - private final ScheduledExecutorService pollthreadpool; private final ControllerResource controllerResource; + private final DeviceSimulatorUpdater deviceUpdater; + private volatile boolean removed; private volatile Long currentActionId; - private final DeviceSimulatorUpdater deviceUpdater; /** * @param id @@ -42,18 +40,14 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice { * the delay of the poll interval in sec * @param controllerResource * the http controller resource - * @param pollthreadpool - * the threadpool for polling endpoint * @param deviceUpdater * the service to update devices */ public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec, - final ControllerResource controllerResource, final ScheduledExecutorService pollthreadpool, - final DeviceSimulatorUpdater deviceUpdater) { + final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) { super(id, tenant, Protocol.DDI_HTTP); this.pollDelaySec = pollDelaySec; this.controllerResource = controllerResource; - this.pollthreadpool = pollthreadpool; this.deviceUpdater = deviceUpdater; setNextPollCounterSec(pollDelaySec); } @@ -76,27 +70,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice { final String basePollJson = controllerResource.get(getTenant(), getId()); try { final String href = JsonPath.parse(basePollJson).read("_links.deploymentBase.href"); - final long actionId = Long.parseLong(href.substring(href.lastIndexOf("/") + 1, href.indexOf("?"))); + final long actionId = Long.parseLong(href.substring(href.lastIndexOf('/') + 1, href.indexOf('?'))); if (currentActionId == null) { 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, (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; - }); + startDdiUpdate(actionId, swVersion); } } catch (final PathNotFoundException e) { // href might not be in the json response, so ignore @@ -106,4 +85,21 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice { } } + + private void startDdiUpdate(final long actionId, final String swVersion) { + deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, null, null, (device, actionId1) -> { + switch (device.getUpdateStatus().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.getUpdateStatus().getResponseStatus()); + } + currentActionId = null; + }); + } } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java index b9fdc827c..6b79a85b8 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java @@ -9,10 +9,7 @@ package org.eclipse.hawkbit.simulator; /** - * An simulated device using the DMF API of the hawkbit update server. - * - * @author Michael Hirsch - * + * A simulated device using the DMF API of the hawkBit update server. */ public class DMFSimulatedDevice extends AbstractSimulatedDevice { 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 afc3a2569..12ff7dbd2 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,27 +8,52 @@ */ package org.eclipse.hawkbit.simulator; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.DigestOutputStream; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.eclipse.hawkbit.dmf.json.model.Artifact; +import org.eclipse.hawkbit.dmf.json.model.SoftwareModule; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; +import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus; import org.eclipse.hawkbit.simulator.amqp.SpSenderService; import org.eclipse.hawkbit.simulator.event.InitUpdate; import org.eclipse.hawkbit.simulator.event.ProgressUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.google.common.eventbus.EventBus; +import com.google.common.io.BaseEncoding; +import com.google.common.io.ByteStreams; /** - * @author Michael Hirsch + * Update simulation handler. * */ @Service public class DeviceSimulatorUpdater { + private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class); private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4); @@ -54,14 +79,18 @@ public class DeviceSimulatorUpdater { * @param actionId * the actionId from the hawkbit update server to start the * update. - * @param swVersion + * @param modules * the software module version from the hawkbit update server + * @param swVersion + * the software version as static value in case modules is null + * @param targetSecurityToken + * the target security token for download authentication * @param callback * the callback which gets called when the simulated update * process has been finished */ public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion, - final UpdaterCallback callback) { + final List modules, final String targetSecurityToken, final UpdaterCallback callback) { AbstractSimulatedDevice device = repository.get(tenant, id); // plug and play - non existing device will be auto created @@ -70,11 +99,18 @@ public class DeviceSimulatorUpdater { } device.setProgress(0.0); - device.setSwversion(swVersion); + + if (modules == null) { + device.setSwversion(swVersion); + } else { + device.setSwversion(modules.stream().map(sm -> sm.getModuleVersion()).collect(Collectors.joining(", "))); + } + device.setTargetSecurityToken(targetSecurityToken); eventbus.post(new InitUpdate(device)); - threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback), - 2_000, TimeUnit.MILLISECONDS); + threadPool.schedule( + new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules), 2_000, + TimeUnit.MILLISECONDS); } private static final class DeviceSimulatorUpdateThread implements Runnable { @@ -85,38 +121,130 @@ public class DeviceSimulatorUpdater { private final long actionId; private final EventBus eventbus; private final UpdaterCallback callback; + private final List modules; private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device, final SpSenderService spSenderService, - final long actionId, final EventBus eventbus, final UpdaterCallback callback) { + final long actionId, final EventBus eventbus, final UpdaterCallback callback, + final List modules) { this.device = device; this.spSenderService = spSenderService; this.actionId = actionId; this.eventbus = eventbus; this.callback = callback; + this.modules = modules; } @Override public void run() { + if (device.getProgress() <= 0 && modules != null) { + device.setUpdateStatus(simulateDownloads(device.getTargetSecurityToken())); + if (device.getUpdateStatus().getResponseStatus().equals(ResponseStatus.ERROR)) { + callback.updateFinished(device, actionId); + eventbus.post(new ProgressUpdate(device)); + return; + } + } + final double newProgress = device.getProgress() + 0.2; device.setProgress(newProgress); if (newProgress < 1.0) { threadPool.schedule( - new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback), + new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules), rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS); } else { callback.updateFinished(device, actionId); } eventbus.post(new ProgressUpdate(device)); } + + private UpdateStatus simulateDownloads(final String targetToken) { + final List status = new ArrayList<>(); + + LOGGER.info("Simulate downloads for {}", device.getId()); + + modules.forEach(module -> module.getArtifacts() + .forEach(artifact -> handleArtifacts(targetToken, status, artifact))); + + final UpdateStatus result = new UpdateStatus(ResponseStatus.SUCCESSFUL); + result.getStatusMessages().add("Simulation complete!"); + status.forEach(download -> { + result.getStatusMessages().addAll(download.getStatusMessages()); + if (download.getResponseStatus().equals(ResponseStatus.ERROR)) { + result.setResponseStatus(ResponseStatus.ERROR); + } + }); + + LOGGER.info("Download simulations complete for {}", device.getId()); + + return result; + } + + private static void handleArtifacts(final String targetToken, final List status, + final Artifact artifact) { + artifact.getUrls().entrySet().forEach(entry -> { + switch (entry.getKey()) { + case HTTPS: + status.add(downloadUrl(entry.getValue(), targetToken, artifact.getHashes().getSha1())); + break; + default: + // not supported yet + break; + } + }); + } + + private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash) { + LOGGER.debug("Downloading " + url); + + long overallread = 0; + try { + final CloseableHttpClient httpclient = createHttpClientThatAcceptsAllServerCerts(); + final HttpGet request = new HttpGet(url); + request.addHeader("TargetToken", targetToken); + + final String sha1HashResult; + try (final CloseableHttpResponse response = httpclient.execute(request)) { + final File tempFile = File.createTempFile("uploadFile", null); + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + + try (final DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(tempFile), md)) { + overallread = ByteStreams.copy(response.getEntity().getContent(), dos); + sha1HashResult = BaseEncoding.base16().lowerCase().encode(md.digest()); + } finally { + tempFile.delete(); + } + } + + if (!sha1Hash.equals(sha1HashResult)) { + final String message = "Download " + url + " failed with SHA1 hash missmatch (Expected: " + sha1Hash + + " but got: " + sha1HashResult + ")"; + LOGGER.debug(message); + return new UpdateStatus(ResponseStatus.ERROR, message); + } + + } catch (IOException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { + LOGGER.error("Failed to download {}", url, e); + return new UpdateStatus(ResponseStatus.ERROR, "Failed to download " + url + ": " + e.getMessage()); + } + + final String message = "Downloaded " + url + " (" + overallread + " bytes)"; + LOGGER.debug(message); + return new UpdateStatus(ResponseStatus.SUCCESSFUL, message); + } + + private static CloseableHttpClient createHttpClientThatAcceptsAllServerCerts() + throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + final SSLContextBuilder builder = new SSLContextBuilder(); + builder.loadTrustMaterial(null, (chain, authType) -> true); + final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); + return HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } } /** * 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 - * + * send the result to the hawkBit update server back. * */ @FunctionalInterface public interface UpdaterCallback { 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 9c78ec65a..956d6d36a 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 @@ -26,9 +26,6 @@ import com.google.common.eventbus.EventBus; /** * Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every * second. - * - * @author Michael Hirsch - * */ @Component public class NextPollTimeController { @@ -59,16 +56,15 @@ public class NextPollTimeController { devices.forEach(device -> { int nextCounter = device.getNextPollCounterSec() - 1; - if (nextCounter < 0) { - if (device instanceof DDISimulatedDevice) { - try { - pollService.submit(() -> ((DDISimulatedDevice) device).poll()); - } catch (final IllegalStateException e) { - LOGGER.trace("Device could not be polled", e); - } - nextCounter = ((DDISimulatedDevice) device).getPollDelaySec(); + if (nextCounter < 0 && device instanceof DDISimulatedDevice) { + try { + pollService.submit(() -> ((DDISimulatedDevice) device).poll()); + } catch (final IllegalStateException e) { + LOGGER.trace("Device could not be polled", e); } + nextCounter = ((DDISimulatedDevice) device).getPollDelaySec(); } + device.setNextPollCounterSec(nextCounter); }); eventBus.post(new NextPollCounterUpdate(devices)); diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java index d3e080806..f29aad001 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java @@ -9,8 +9,6 @@ package org.eclipse.hawkbit.simulator; import java.net.URL; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; import org.eclipse.hawkbit.simulator.http.ControllerResource; @@ -24,15 +22,9 @@ import feign.Logger; /** * The simulated device factory to create either {@link DMFSimulatedDevice} or * {@link DDISimulatedDevice#}. - * - * @author Michael Hirsch - * */ @Service public class SimulatedDeviceFactory { - - private static final ScheduledExecutorService pollThreadPool = Executors.newScheduledThreadPool(4); - @Autowired private DeviceSimulatorUpdater deviceUpdater; @@ -47,7 +39,8 @@ public class SimulatedDeviceFactory { * the protocol of the device * @return the created simulated device */ - public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol) { + public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, + final Protocol protocol) { return createSimulatedDevice(id, tenant, protocol, 30, null, null); } @@ -80,7 +73,7 @@ public class SimulatedDeviceFactory { final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger()) .requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC) .target(ControllerResource.class, baseEndpoint.toString()); - return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, pollThreadPool, deviceUpdater); + return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, deviceUpdater); default: throw new IllegalArgumentException("Protocol " + protocol + " unknown"); } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/UpdateStatus.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/UpdateStatus.java new file mode 100644 index 000000000..49e9947d7 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/UpdateStatus.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.simulator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Update status of the simulated update. + * + */ +public class UpdateStatus { + private ResponseStatus responseStatus = ResponseStatus.SUCCESSFUL; + private final List statusMessages = new ArrayList<>(); + + public UpdateStatus(final ResponseStatus responseStatus) { + this.responseStatus = responseStatus; + } + + public UpdateStatus(final ResponseStatus responseStatus, final String message) { + this(responseStatus); + statusMessages.add(message); + } + + public ResponseStatus getResponseStatus() { + return responseStatus; + } + + public void setResponseStatus(final ResponseStatus responseStatus) { + this.responseStatus = responseStatus; + } + + public List getStatusMessages() { + return statusMessages; + } + + /** + * The status to response to the hawkBit update server if an simulated + * update process should be respond with successful or failure update. + */ + public enum ResponseStatus { + /** + * updated has been successful and response the successful update. + */ + SUCCESSFUL, + /** + * updated has been not successful and response the error update. + */ + ERROR; + } + +} \ No newline at end of file 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 f22839422..12e540fc9 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 @@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; +import com.google.common.collect.Lists; + /** * Handle all incoming Messages from hawkBit update server. * @@ -109,7 +111,7 @@ public class SpReceiverService extends ReceiverService { final Long actionId = convertMessage(message, Long.class); final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId); - spSenderService.finishUpdateProcess(update, "Simulation canceled"); + spSenderService.finishUpdateProcess(update, Lists.newArrayList("Simulation canceled")); } private void handleUpdateProcess(final Message message, final String thingId) { @@ -120,19 +122,20 @@ public class SpReceiverService extends ReceiverService { final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message, DownloadAndUpdateRequest.class); final Long actionId = downloadAndUpdateRequest.getActionId(); + final String targetSecurityToken = downloadAndUpdateRequest.getTargetSecurityToken(); - deviceUpdater.startUpdate(tenant, thingId, actionId, - downloadAndUpdateRequest.getSoftwareModules().get(0).getModuleVersion(), (device, actionId1) -> { - switch (device.getResponseStatus()) { + deviceUpdater.startUpdate(tenant, thingId, actionId, null, downloadAndUpdateRequest.getSoftwareModules(), + targetSecurityToken, (device, actionId1) -> { + switch (device.getUpdateStatus().getResponseStatus()) { case SUCCESSFUL: spSenderService.finishUpdateProcess( new SimulatedUpdate(device.getTenant(), device.getId(), actionId1), - "Simulation complete!"); + device.getUpdateStatus().getStatusMessages()); break; case ERROR: spSenderService.finishUpdateProcessWithError( new SimulatedUpdate(device.getTenant(), device.getId(), actionId1), - "Simulation complete with error!"); + device.getUpdateStatus().getStatusMessages()); break; default: break; diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java index 2358bf013..1ced8c2fd 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpSenderService.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.simulator.amqp; +import java.util.List; import java.util.Map; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; @@ -23,13 +24,9 @@ import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; /** - * Sender service to send message to SP. - * - * - * + * Sender service to send messages to update server. */ @Service public class SpSenderService extends SenderService { @@ -59,8 +56,9 @@ public class SpSenderService extends SenderService { * @param description * a description according the update process */ - public void finishUpdateProcess(final SimulatedUpdate update, final String description) { - final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED, description); + public void finishUpdateProcess(final SimulatedUpdate update, final List updateResultMessages) { + final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED, + updateResultMessages); sendMessage(spExchange, updateResultMessage); } @@ -72,9 +70,9 @@ public class SpSenderService extends SenderService { * @param messageDescription * a description according the update process */ - public void finishUpdateProcessWithError(final SimulatedUpdate update, final String messageDescription) { - sendErrorgMessage(update, messageDescription); - LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", messageDescription, + public void finishUpdateProcessWithError(final SimulatedUpdate update, final List updateResultMessages) { + sendErrorgMessage(update, updateResultMessages); + LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", updateResultMessages, update.getThingId()); } @@ -88,8 +86,8 @@ public class SpSenderService extends SenderService { * @param actionId * the ID of the action for the error message */ - public void sendErrorMessage(final String tenant, final String messageDescription, final Long actionId) { - final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, messageDescription, actionId); + public void sendErrorMessage(final String tenant, final List updateResultMessages, final Long actionId) { + final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, updateResultMessages, actionId); sendMessage(spExchange, message); } @@ -101,8 +99,8 @@ public class SpSenderService extends SenderService { * @param warningMessage * a warning description */ - public void sendWarningMessage(final SimulatedUpdate update, final String warningMessage) { - final Message message = createActionStatusMessage(update, warningMessage, ActionStatus.WARNING); + public void sendWarningMessage(final SimulatedUpdate update, final List updateResultMessages) { + final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.WARNING); sendMessage(spExchange, message); } @@ -119,8 +117,8 @@ public class SpSenderService extends SenderService { * the cached value */ public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus, - final String actionMessage, final Long actionId) { - final Message message = createActionStatusMessage(tenant, actionStatus, actionMessage, actionId); + final List updateResultMessages, final Long actionId) { + final Message message = createActionStatusMessage(tenant, actionStatus, updateResultMessages, actionId); sendMessage(message); } @@ -162,11 +160,11 @@ public class SpSenderService extends SenderService { * * @param context * the current context - * @param messageDescription - * a description according the update process + * @param updateResultMessages + * a list of descriptions according the update process */ - private void sendErrorgMessage(final SimulatedUpdate update, final String messageDescription) { - final Message message = createActionStatusMessage(update, messageDescription, ActionStatus.ERROR); + private void sendErrorgMessage(final SimulatedUpdate update, final List updateResultMessages) { + final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.ERROR); sendMessage(spExchange, message); } @@ -183,7 +181,7 @@ public class SpSenderService extends SenderService { * the cacheValue value */ private Message createActionStatusMessage(final String tenant, final ActionStatus actionStatus, - final String actionMessage, final Long actionId) { + final List updateResultMessages, final Long actionId) { final MessageProperties messageProperties = new MessageProperties(); final Map headers = messageProperties.getHeaders(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); @@ -192,15 +190,14 @@ public class SpSenderService extends SenderService { headers.put(MessageHeaderKey.TENANT, tenant); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); - if (!StringUtils.isEmpty(actionMessage)) { - actionUpdateStatus.getMessage().add(actionMessage); - } + actionUpdateStatus.getMessage().addAll(updateResultMessages); + actionUpdateStatus.setActionId(actionId); return convertMessage(actionUpdateStatus, messageProperties); } private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus, - final String updateResultMessage) { + final List updateResultMessages) { final MessageProperties messageProperties = new MessageProperties(); final Map headers = messageProperties.getHeaders(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); @@ -209,14 +206,14 @@ public class SpSenderService extends SenderService { headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); - actionUpdateStatus.getMessage().add(updateResultMessage); + actionUpdateStatus.getMessage().addAll(updateResultMessages); actionUpdateStatus.setActionId(cacheValue.getActionId()); return convertMessage(actionUpdateStatus, messageProperties); } - private Message createActionStatusMessage(final SimulatedUpdate update, final String messageDescription, + private Message createActionStatusMessage(final SimulatedUpdate update, final List updateResultMessages, final ActionStatus status) { - return createActionStatusMessage(update.getTenant(), status, messageDescription, update.getActionId()); + return createActionStatusMessage(update.getTenant(), status, updateResultMessages, update.getActionId()); } } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java index 3e34a0fa1..6c18e61ac 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java @@ -13,8 +13,6 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice; /** * Event definition object which is published if the simulated device updated * its update progress. - * - * @author Michael Hirsch * */ public class ProgressUpdate { 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 28ad0eaa9..455702501 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 @@ -8,21 +8,19 @@ */ package org.eclipse.hawkbit.simulator.ui; -import java.net.URL; import java.util.List; import java.util.Locale; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; -import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.ResponseStatus; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Status; import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository; import org.eclipse.hawkbit.simulator.SimulatedDeviceFactory; +import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus; import org.eclipse.hawkbit.simulator.amqp.SpSenderService; import org.eclipse.hawkbit.simulator.event.InitUpdate; import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate; import org.eclipse.hawkbit.simulator.event.ProgressUpdate; -import org.eclipse.hawkbit.simulator.ui.GenerateDialog.GenerateDialogCallback; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.collect.Lists; @@ -52,8 +50,6 @@ import com.vaadin.ui.renderers.ProgressBarRenderer; * Vaadin view which allows to generate devices through the DMF API and show the * current simulated devices in a grid with their current status and update * progress. - * - * @author Michael Hirsch * */ @SpringView(name = "") @@ -153,18 +149,12 @@ public class SimulatorView extends VerticalLayout implements View { @Subscribe public void pollCounterUpdate(final NextPollCounterUpdate update) { final List devices = update.getDevices(); - this.getUI().access(new Runnable() { - - @Override - public void run() { - devices.forEach(device -> { - final BeanItem item = beanContainer.getItem(device.getId()); - if (item != null) { - item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec()); - } - }); + this.getUI().access(() -> devices.forEach(device -> { + final BeanItem item = beanContainer.getItem(device.getId()); + if (item != null) { + item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec()); } - }); + })); } /** @@ -176,18 +166,14 @@ public class SimulatorView extends VerticalLayout implements View { @Subscribe public void initUpdate(final InitUpdate update) { final AbstractSimulatedDevice device = update.getDevice(); - this.getUI().access(new Runnable() { - - @Override - public void run() { - final BeanItem item = beanContainer.getItem(device.getId()); - if (item != null) { - item.getItemProperty("progress").setValue(device.getProgress()); - item.getItemProperty("status").setValue(Status.PEDNING); - item.getItemProperty("swversion").setValue(device.getSwversion()); - } - + this.getUI().access(() -> { + final BeanItem item = beanContainer.getItem(device.getId()); + if (item != null) { + item.getItemProperty("progress").setValue(device.getProgress()); + item.getItemProperty("status").setValue(Status.PEDNING); + item.getItemProperty("swversion").setValue(device.getSwversion()); } + }); } @@ -200,29 +186,26 @@ public class SimulatorView extends VerticalLayout implements View { @Subscribe public void progessUpdate(final ProgressUpdate update) { final AbstractSimulatedDevice device = update.getDevice(); - this.getUI().access(new Runnable() { - @Override - public void run() { - final BeanItem item = beanContainer.getItem(device.getId()); - if (item != null) { - item.getItemProperty("progress").setValue(device.getProgress()); - if (device.getProgress() >= 1) { - switch (device.getResponseStatus()) { - case SUCCESSFUL: - item.getItemProperty("status").setValue(Status.FINISH); - break; - case ERROR: - item.getItemProperty("status").setValue(Status.ERROR); - break; - default: - item.getItemProperty("status").setValue(Status.UNKNWON); - } - } else { - item.getItemProperty("status").setValue(Status.PEDNING); + this.getUI().access(() -> { + final BeanItem item = beanContainer.getItem(device.getId()); + if (item != null) { + item.getItemProperty("progress").setValue(device.getProgress()); + if (device.getProgress() >= 1) { + switch (device.getUpdateStatus().getResponseStatus()) { + case SUCCESSFUL: + item.getItemProperty("status").setValue(Status.FINISH); + break; + case ERROR: + item.getItemProperty("status").setValue(Status.ERROR); + break; + default: + item.getItemProperty("status").setValue(Status.UNKNWON); } + } else { + item.getItemProperty("status").setValue(Status.PEDNING); } - } + }); } @@ -246,18 +229,15 @@ public class SimulatorView extends VerticalLayout implements View { } private void openGenerateDialog() { - UI.getCurrent().addWindow(new GenerateDialog(new GenerateDialogCallback() { - @Override - public void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay, - final URL basePollUrl, final String gatewayToken, final Protocol protocol) { - for (int index = 0; index < amount; index++) { - final String deviceId = namePrefix + index; - beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId, - tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken))); - spSenderService.createOrUpdateThing(tenant, deviceId); - } - } - })); + UI.getCurrent().addWindow( + new GenerateDialog((namePrefix, tenant, amount, pollDelay, basePollUrl, gatewayToken, protocol) -> { + for (int index = 0; index < amount; index++) { + final String deviceId = namePrefix + index; + beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId, + tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken))); + spSenderService.createOrUpdateThing(tenant, deviceId); + } + })); } private Converter createProtocolConverter() {