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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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<SoftwareModule> 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<SoftwareModule> 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<SoftwareModule> 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<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
|
||||
* 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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> updateResultMessages, final Long actionId) {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
final Map<String, Object> 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<String> updateResultMessages) {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
final Map<String, Object> 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<String> updateResultMessages,
|
||||
final ActionStatus status) {
|
||||
return createActionStatusMessage(update.getTenant(), status, messageDescription, update.getActionId());
|
||||
return createActionStatusMessage(update.getTenant(), status, updateResultMessages, update.getActionId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<AbstractSimulatedDevice> devices = update.getDevices();
|
||||
this.getUI().access(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
devices.forEach(device -> {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec());
|
||||
}
|
||||
});
|
||||
this.getUI().access(() -> devices.forEach(device -> {
|
||||
final BeanItem<AbstractSimulatedDevice> 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<AbstractSimulatedDevice> 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<AbstractSimulatedDevice> 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<AbstractSimulatedDevice> 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<AbstractSimulatedDevice> 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<String, Protocol> createProtocolConverter() {
|
||||
|
||||
Reference in New Issue
Block a user