Teached simulator to download artifacts including SHA1 hash check and

targetToken usage.

Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2016-05-03 16:16:36 +02:00
parent f32f612e83
commit 7e941cf64d
12 changed files with 333 additions and 186 deletions

View File

@@ -1,4 +1,3 @@
<?xml version="1.0"?>
<!-- <!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others. Copyright (c) 2015 Bosch Software Innovations GmbH and others.
@@ -9,7 +8,8 @@
http://www.eclipse.org/legal/epl-v10.html http://www.eclipse.org/legal/epl-v10.html
--> -->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" <project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
@@ -133,6 +133,15 @@
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.10.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@@ -8,6 +8,8 @@
*/ */
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
/** /**
* The bean of a simulated device which can be stored in the * The bean of a simulated device which can be stored in the
* {@link DeviceSimulatorRepository} or shown in the UI. * {@link DeviceSimulatorRepository} or shown in the UI.
@@ -22,16 +24,15 @@ public abstract class AbstractSimulatedDevice {
private Status status; private Status status;
private double progress; private double progress;
private String swversion = "unknown"; 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 Protocol protocol = Protocol.DMF_AMQP;
private String targetSecurityToken;
private int nextPollCounterSec; private int nextPollCounterSec;
/** /**
* Enum definition of the protocol to be used for the simulated device. * Enum definition of the protocol to be used for the simulated device.
* *
* @author Michael Hirsch
*
*/ */
public enum Protocol { public enum Protocol {
/** /**
@@ -69,24 +70,6 @@ public abstract class AbstractSimulatedDevice {
ERROR; 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. * empty constructor.
*/ */
@@ -158,12 +141,12 @@ public abstract class AbstractSimulatedDevice {
this.swversion = swversion; this.swversion = swversion;
} }
public ResponseStatus getResponseStatus() { public UpdateStatus getUpdateStatus() {
return responseStatus; return updateStatus;
} }
public void setResponseStatus(final ResponseStatus responseStatus) { public void setUpdateStatus(final UpdateStatus updateStatus) {
this.responseStatus = responseStatus; this.updateStatus = updateStatus;
} }
public Protocol getProtocol() { public Protocol getProtocol() {
@@ -177,4 +160,13 @@ public abstract class AbstractSimulatedDevice {
public void setNextPollCounterSec(final int nextPollDelayInSec) { public void setNextPollCounterSec(final int nextPollDelayInSec) {
this.nextPollCounterSec = nextPollDelayInSec; this.nextPollCounterSec = nextPollDelayInSec;
} }
public String getTargetSecurityToken() {
return targetSecurityToken;
}
public void setTargetSecurityToken(final String targetSecurityToken) {
this.targetSecurityToken = targetSecurityToken;
}
} }

View File

@@ -8,8 +8,6 @@
*/ */
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.hawkbit.simulator.http.ControllerResource; import org.eclipse.hawkbit.simulator.http.ControllerResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -18,7 +16,7 @@ import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException; 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 { public class DDISimulatedDevice extends AbstractSimulatedDevice {
@@ -26,12 +24,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class); private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class);
private final int pollDelaySec; private final int pollDelaySec;
private final ScheduledExecutorService pollthreadpool;
private final ControllerResource controllerResource; private final ControllerResource controllerResource;
private final DeviceSimulatorUpdater deviceUpdater;
private volatile boolean removed; private volatile boolean removed;
private volatile Long currentActionId; private volatile Long currentActionId;
private final DeviceSimulatorUpdater deviceUpdater;
/** /**
* @param id * @param id
@@ -42,18 +40,14 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
* the delay of the poll interval in sec * the delay of the poll interval in sec
* @param controllerResource * @param controllerResource
* the http controller resource * the http controller resource
* @param pollthreadpool
* the threadpool for polling endpoint
* @param deviceUpdater * @param deviceUpdater
* the service to update devices * the service to update devices
*/ */
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec, public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
final ControllerResource controllerResource, final ScheduledExecutorService pollthreadpool, final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) {
final DeviceSimulatorUpdater deviceUpdater) {
super(id, tenant, Protocol.DDI_HTTP); super(id, tenant, Protocol.DDI_HTTP);
this.pollDelaySec = pollDelaySec; this.pollDelaySec = pollDelaySec;
this.controllerResource = controllerResource; this.controllerResource = controllerResource;
this.pollthreadpool = pollthreadpool;
this.deviceUpdater = deviceUpdater; this.deviceUpdater = deviceUpdater;
setNextPollCounterSec(pollDelaySec); setNextPollCounterSec(pollDelaySec);
} }
@@ -76,27 +70,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
final String basePollJson = controllerResource.get(getTenant(), getId()); final String basePollJson = controllerResource.get(getTenant(), getId());
try { try {
final String href = JsonPath.parse(basePollJson).read("_links.deploymentBase.href"); 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) { if (currentActionId == null) {
final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId); final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId);
final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version"); final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version");
currentActionId = actionId; currentActionId = actionId;
deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, (device, actionId1) -> { startDdiUpdate(actionId, swVersion);
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) { } catch (final PathNotFoundException e) {
// href might not be in the json response, so ignore // 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;
});
}
} }

View File

@@ -9,10 +9,7 @@
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
/** /**
* An simulated device using the DMF API of the hawkbit update server. * A simulated device using the DMF API of the hawkBit update server.
*
* @author Michael Hirsch
*
*/ */
public class DMFSimulatedDevice extends AbstractSimulatedDevice { public class DMFSimulatedDevice extends AbstractSimulatedDevice {

View File

@@ -8,27 +8,52 @@
*/ */
package org.eclipse.hawkbit.simulator; 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.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; 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.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService; import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate; import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus; 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 @Service
public class DeviceSimulatorUpdater { public class DeviceSimulatorUpdater {
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class);
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4); private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
@@ -54,14 +79,18 @@ public class DeviceSimulatorUpdater {
* @param actionId * @param actionId
* the actionId from the hawkbit update server to start the * the actionId from the hawkbit update server to start the
* update. * update.
* @param swVersion * @param modules
* the software module version from the hawkbit update server * 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 * @param callback
* the callback which gets called when the simulated update * the callback which gets called when the simulated update
* process has been finished * process has been finished
*/ */
public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion, public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion,
final UpdaterCallback callback) { final List<SoftwareModule> modules, final String targetSecurityToken, final UpdaterCallback callback) {
AbstractSimulatedDevice device = repository.get(tenant, id); AbstractSimulatedDevice device = repository.get(tenant, id);
// plug and play - non existing device will be auto created // plug and play - non existing device will be auto created
@@ -70,11 +99,18 @@ public class DeviceSimulatorUpdater {
} }
device.setProgress(0.0); device.setProgress(0.0);
if (modules == null) {
device.setSwversion(swVersion); device.setSwversion(swVersion);
} else {
device.setSwversion(modules.stream().map(sm -> sm.getModuleVersion()).collect(Collectors.joining(", ")));
}
device.setTargetSecurityToken(targetSecurityToken);
eventbus.post(new InitUpdate(device)); eventbus.post(new InitUpdate(device));
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback), threadPool.schedule(
2_000, TimeUnit.MILLISECONDS); new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules), 2_000,
TimeUnit.MILLISECONDS);
} }
private static final class DeviceSimulatorUpdateThread implements Runnable { private static final class DeviceSimulatorUpdateThread implements Runnable {
@@ -85,38 +121,130 @@ public class DeviceSimulatorUpdater {
private final long actionId; private final long actionId;
private final EventBus eventbus; private final EventBus eventbus;
private final UpdaterCallback callback; private final UpdaterCallback callback;
private final List<SoftwareModule> modules;
private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device, final SpSenderService spSenderService, 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<SoftwareModule> modules) {
this.device = device; this.device = device;
this.spSenderService = spSenderService; this.spSenderService = spSenderService;
this.actionId = actionId; this.actionId = actionId;
this.eventbus = eventbus; this.eventbus = eventbus;
this.callback = callback; this.callback = callback;
this.modules = modules;
} }
@Override @Override
public void run() { 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; final double newProgress = device.getProgress() + 0.2;
device.setProgress(newProgress); device.setProgress(newProgress);
if (newProgress < 1.0) { if (newProgress < 1.0) {
threadPool.schedule( threadPool.schedule(
new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback), new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules),
rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS); rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS);
} else { } else {
callback.updateFinished(device, actionId); callback.updateFinished(device, actionId);
} }
eventbus.post(new ProgressUpdate(device)); eventbus.post(new ProgressUpdate(device));
} }
private UpdateStatus simulateDownloads(final String targetToken) {
final List<UpdateStatus> 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<UpdateStatus> 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 * Callback interface which is called when the simulated update process has
* been finished and the caller of starting the simulated update process can * been finished and the caller of starting the simulated update process can
* send the result to the hawkbit update server back. * send the result to the hawkBit update server back. *
*
* @author Michael Hirsch
*
*/ */
@FunctionalInterface @FunctionalInterface
public interface UpdaterCallback { public interface UpdaterCallback {

View File

@@ -26,9 +26,6 @@ import com.google.common.eventbus.EventBus;
/** /**
* Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every * Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every
* second. * second.
*
* @author Michael Hirsch
*
*/ */
@Component @Component
public class NextPollTimeController { public class NextPollTimeController {
@@ -59,8 +56,7 @@ public class NextPollTimeController {
devices.forEach(device -> { devices.forEach(device -> {
int nextCounter = device.getNextPollCounterSec() - 1; int nextCounter = device.getNextPollCounterSec() - 1;
if (nextCounter < 0) { if (nextCounter < 0 && device instanceof DDISimulatedDevice) {
if (device instanceof DDISimulatedDevice) {
try { try {
pollService.submit(() -> ((DDISimulatedDevice) device).poll()); pollService.submit(() -> ((DDISimulatedDevice) device).poll());
} catch (final IllegalStateException e) { } catch (final IllegalStateException e) {
@@ -68,7 +64,7 @@ public class NextPollTimeController {
} }
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec(); nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
} }
}
device.setNextPollCounterSec(nextCounter); device.setNextPollCounterSec(nextCounter);
}); });
eventBus.post(new NextPollCounterUpdate(devices)); eventBus.post(new NextPollCounterUpdate(devices));

View File

@@ -9,8 +9,6 @@
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
import java.net.URL; 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.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.http.ControllerResource; import org.eclipse.hawkbit.simulator.http.ControllerResource;
@@ -24,15 +22,9 @@ import feign.Logger;
/** /**
* The simulated device factory to create either {@link DMFSimulatedDevice} or * The simulated device factory to create either {@link DMFSimulatedDevice} or
* {@link DDISimulatedDevice#}. * {@link DDISimulatedDevice#}.
*
* @author Michael Hirsch
*
*/ */
@Service @Service
public class SimulatedDeviceFactory { public class SimulatedDeviceFactory {
private static final ScheduledExecutorService pollThreadPool = Executors.newScheduledThreadPool(4);
@Autowired @Autowired
private DeviceSimulatorUpdater deviceUpdater; private DeviceSimulatorUpdater deviceUpdater;
@@ -47,7 +39,8 @@ public class SimulatedDeviceFactory {
* the protocol of the device * the protocol of the device
* @return the created simulated 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); return createSimulatedDevice(id, tenant, protocol, 30, null, null);
} }
@@ -80,7 +73,7 @@ public class SimulatedDeviceFactory {
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger()) final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
.requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC) .requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC)
.target(ControllerResource.class, baseEndpoint.toString()); .target(ControllerResource.class, baseEndpoint.toString());
return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, pollThreadPool, deviceUpdater); return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, deviceUpdater);
default: default:
throw new IllegalArgumentException("Protocol " + protocol + " unknown"); throw new IllegalArgumentException("Protocol " + protocol + " unknown");
} }

View File

@@ -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<String> 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<String> 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;
}
}

View File

@@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
/** /**
* Handle all incoming Messages from hawkBit update server. * 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 Long actionId = convertMessage(message, Long.class);
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId); 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) { private void handleUpdateProcess(final Message message, final String thingId) {
@@ -120,19 +122,20 @@ public class SpReceiverService extends ReceiverService {
final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message, final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message,
DownloadAndUpdateRequest.class); DownloadAndUpdateRequest.class);
final Long actionId = downloadAndUpdateRequest.getActionId(); final Long actionId = downloadAndUpdateRequest.getActionId();
final String targetSecurityToken = downloadAndUpdateRequest.getTargetSecurityToken();
deviceUpdater.startUpdate(tenant, thingId, actionId, deviceUpdater.startUpdate(tenant, thingId, actionId, null, downloadAndUpdateRequest.getSoftwareModules(),
downloadAndUpdateRequest.getSoftwareModules().get(0).getModuleVersion(), (device, actionId1) -> { targetSecurityToken, (device, actionId1) -> {
switch (device.getResponseStatus()) { switch (device.getUpdateStatus().getResponseStatus()) {
case SUCCESSFUL: case SUCCESSFUL:
spSenderService.finishUpdateProcess( spSenderService.finishUpdateProcess(
new SimulatedUpdate(device.getTenant(), device.getId(), actionId1), new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
"Simulation complete!"); device.getUpdateStatus().getStatusMessages());
break; break;
case ERROR: case ERROR:
spSenderService.finishUpdateProcessWithError( spSenderService.finishUpdateProcessWithError(
new SimulatedUpdate(device.getTenant(), device.getId(), actionId1), new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
"Simulation complete with error!"); device.getUpdateStatus().getStatusMessages());
break; break;
default: default:
break; break;

View File

@@ -8,6 +8,7 @@
*/ */
package org.eclipse.hawkbit.simulator.amqp; package org.eclipse.hawkbit.simulator.amqp;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; 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.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; 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 @Service
public class SpSenderService extends SenderService { public class SpSenderService extends SenderService {
@@ -59,8 +56,9 @@ public class SpSenderService extends SenderService {
* @param description * @param description
* a description according the update process * a description according the update process
*/ */
public void finishUpdateProcess(final SimulatedUpdate update, final String description) { public void finishUpdateProcess(final SimulatedUpdate update, final List<String> updateResultMessages) {
final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED, description); final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED,
updateResultMessages);
sendMessage(spExchange, updateResultMessage); sendMessage(spExchange, updateResultMessage);
} }
@@ -72,9 +70,9 @@ public class SpSenderService extends SenderService {
* @param messageDescription * @param messageDescription
* a description according the update process * a description according the update process
*/ */
public void finishUpdateProcessWithError(final SimulatedUpdate update, final String messageDescription) { public void finishUpdateProcessWithError(final SimulatedUpdate update, final List<String> updateResultMessages) {
sendErrorgMessage(update, messageDescription); sendErrorgMessage(update, updateResultMessages);
LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", messageDescription, LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", updateResultMessages,
update.getThingId()); update.getThingId());
} }
@@ -88,8 +86,8 @@ public class SpSenderService extends SenderService {
* @param actionId * @param actionId
* the ID of the action for the error message * the ID of the action for the error message
*/ */
public void sendErrorMessage(final String tenant, final String messageDescription, final Long actionId) { public void sendErrorMessage(final String tenant, final List<String> updateResultMessages, final Long actionId) {
final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, messageDescription, actionId); final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, updateResultMessages, actionId);
sendMessage(spExchange, message); sendMessage(spExchange, message);
} }
@@ -101,8 +99,8 @@ public class SpSenderService extends SenderService {
* @param warningMessage * @param warningMessage
* a warning description * a warning description
*/ */
public void sendWarningMessage(final SimulatedUpdate update, final String warningMessage) { public void sendWarningMessage(final SimulatedUpdate update, final List<String> updateResultMessages) {
final Message message = createActionStatusMessage(update, warningMessage, ActionStatus.WARNING); final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.WARNING);
sendMessage(spExchange, message); sendMessage(spExchange, message);
} }
@@ -119,8 +117,8 @@ public class SpSenderService extends SenderService {
* the cached value * the cached value
*/ */
public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus, public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus,
final String actionMessage, final Long actionId) { final List<String> updateResultMessages, final Long actionId) {
final Message message = createActionStatusMessage(tenant, actionStatus, actionMessage, actionId); final Message message = createActionStatusMessage(tenant, actionStatus, updateResultMessages, actionId);
sendMessage(message); sendMessage(message);
} }
@@ -162,11 +160,11 @@ public class SpSenderService extends SenderService {
* *
* @param context * @param context
* the current context * the current context
* @param messageDescription * @param updateResultMessages
* a description according the update process * a list of descriptions according the update process
*/ */
private void sendErrorgMessage(final SimulatedUpdate update, final String messageDescription) { private void sendErrorgMessage(final SimulatedUpdate update, final List<String> updateResultMessages) {
final Message message = createActionStatusMessage(update, messageDescription, ActionStatus.ERROR); final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.ERROR);
sendMessage(spExchange, message); sendMessage(spExchange, message);
} }
@@ -183,7 +181,7 @@ public class SpSenderService extends SenderService {
* the cacheValue value * the cacheValue value
*/ */
private Message createActionStatusMessage(final String tenant, final ActionStatus actionStatus, private Message createActionStatusMessage(final String tenant, final ActionStatus actionStatus,
final String actionMessage, final Long actionId) { final List<String> updateResultMessages, final Long actionId) {
final MessageProperties messageProperties = new MessageProperties(); final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders(); final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
@@ -192,15 +190,14 @@ public class SpSenderService extends SenderService {
headers.put(MessageHeaderKey.TENANT, tenant); headers.put(MessageHeaderKey.TENANT, tenant);
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
if (!StringUtils.isEmpty(actionMessage)) { actionUpdateStatus.getMessage().addAll(updateResultMessages);
actionUpdateStatus.getMessage().add(actionMessage);
}
actionUpdateStatus.setActionId(actionId); actionUpdateStatus.setActionId(actionId);
return convertMessage(actionUpdateStatus, messageProperties); return convertMessage(actionUpdateStatus, messageProperties);
} }
private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus, private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus,
final String updateResultMessage) { final List<String> updateResultMessages) {
final MessageProperties messageProperties = new MessageProperties(); final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders(); final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
@@ -209,14 +206,14 @@ public class SpSenderService extends SenderService {
headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant()); headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant());
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
actionUpdateStatus.getMessage().add(updateResultMessage); actionUpdateStatus.getMessage().addAll(updateResultMessages);
actionUpdateStatus.setActionId(cacheValue.getActionId()); actionUpdateStatus.setActionId(cacheValue.getActionId());
return convertMessage(actionUpdateStatus, messageProperties); return convertMessage(actionUpdateStatus, messageProperties);
} }
private Message createActionStatusMessage(final SimulatedUpdate update, final String messageDescription, private Message createActionStatusMessage(final SimulatedUpdate update, final List<String> updateResultMessages,
final ActionStatus status) { final ActionStatus status) {
return createActionStatusMessage(update.getTenant(), status, messageDescription, update.getActionId()); return createActionStatusMessage(update.getTenant(), status, updateResultMessages, update.getActionId());
} }
} }

View File

@@ -14,8 +14,6 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
* Event definition object which is published if the simulated device updated * Event definition object which is published if the simulated device updated
* its update progress. * its update progress.
* *
* @author Michael Hirsch
*
*/ */
public class ProgressUpdate { public class ProgressUpdate {

View File

@@ -8,21 +8,19 @@
*/ */
package org.eclipse.hawkbit.simulator.ui; package org.eclipse.hawkbit.simulator.ui;
import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; 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.AbstractSimulatedDevice.Status;
import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository; import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository;
import org.eclipse.hawkbit.simulator.SimulatedDeviceFactory; 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.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate; import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate; import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate; import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
import org.eclipse.hawkbit.simulator.ui.GenerateDialog.GenerateDialogCallback;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -53,8 +51,6 @@ import com.vaadin.ui.renderers.ProgressBarRenderer;
* current simulated devices in a grid with their current status and update * current simulated devices in a grid with their current status and update
* progress. * progress.
* *
* @author Michael Hirsch
*
*/ */
@SpringView(name = "") @SpringView(name = "")
public class SimulatorView extends VerticalLayout implements View { public class SimulatorView extends VerticalLayout implements View {
@@ -153,18 +149,12 @@ public class SimulatorView extends VerticalLayout implements View {
@Subscribe @Subscribe
public void pollCounterUpdate(final NextPollCounterUpdate update) { public void pollCounterUpdate(final NextPollCounterUpdate update) {
final List<AbstractSimulatedDevice> devices = update.getDevices(); final List<AbstractSimulatedDevice> devices = update.getDevices();
this.getUI().access(new Runnable() { this.getUI().access(() -> devices.forEach(device -> {
@Override
public void run() {
devices.forEach(device -> {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId()); final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) { if (item != null) {
item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec()); item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec());
} }
}); }));
}
});
} }
/** /**
@@ -176,10 +166,7 @@ public class SimulatorView extends VerticalLayout implements View {
@Subscribe @Subscribe
public void initUpdate(final InitUpdate update) { public void initUpdate(final InitUpdate update) {
final AbstractSimulatedDevice device = update.getDevice(); final AbstractSimulatedDevice device = update.getDevice();
this.getUI().access(new Runnable() { this.getUI().access(() -> {
@Override
public void run() {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId()); final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) { if (item != null) {
item.getItemProperty("progress").setValue(device.getProgress()); item.getItemProperty("progress").setValue(device.getProgress());
@@ -187,7 +174,6 @@ public class SimulatorView extends VerticalLayout implements View {
item.getItemProperty("swversion").setValue(device.getSwversion()); item.getItemProperty("swversion").setValue(device.getSwversion());
} }
}
}); });
} }
@@ -200,14 +186,12 @@ public class SimulatorView extends VerticalLayout implements View {
@Subscribe @Subscribe
public void progessUpdate(final ProgressUpdate update) { public void progessUpdate(final ProgressUpdate update) {
final AbstractSimulatedDevice device = update.getDevice(); final AbstractSimulatedDevice device = update.getDevice();
this.getUI().access(new Runnable() { this.getUI().access(() -> {
@Override
public void run() {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId()); final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) { if (item != null) {
item.getItemProperty("progress").setValue(device.getProgress()); item.getItemProperty("progress").setValue(device.getProgress());
if (device.getProgress() >= 1) { if (device.getProgress() >= 1) {
switch (device.getResponseStatus()) { switch (device.getUpdateStatus().getResponseStatus()) {
case SUCCESSFUL: case SUCCESSFUL:
item.getItemProperty("status").setValue(Status.FINISH); item.getItemProperty("status").setValue(Status.FINISH);
break; break;
@@ -222,7 +206,6 @@ public class SimulatorView extends VerticalLayout implements View {
} }
} }
}
}); });
} }
@@ -246,17 +229,14 @@ public class SimulatorView extends VerticalLayout implements View {
} }
private void openGenerateDialog() { private void openGenerateDialog() {
UI.getCurrent().addWindow(new GenerateDialog(new GenerateDialogCallback() { UI.getCurrent().addWindow(
@Override new GenerateDialog((namePrefix, tenant, amount, pollDelay, basePollUrl, gatewayToken, protocol) -> {
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++) { for (int index = 0; index < amount; index++) {
final String deviceId = namePrefix + index; final String deviceId = namePrefix + index;
beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId, beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId,
tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken))); tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken)));
spSenderService.createOrUpdateThing(tenant, deviceId); spSenderService.createOrUpdateThing(tenant, deviceId);
} }
}
})); }));
} }