Merge branch 'master' into Feature/Add_Rest_Api_with_Java_client

Conflicts:
	examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/Start.java
	examples/hawkbit-mgmt-api-client/pom.xml
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetBuilder.java
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/DistributionSetTypeBuilder.java
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/RolloutBuilder.java
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleBuilder.java
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TagBuilder.java
	examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/TargetBuilder.java
	hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java
	hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/DataConversionHelper.java
	hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEventProvider.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateUpdateSoftwareTypeLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadHandler.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractTableDetailsLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTableHeader.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/ProxyTarget.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/components/SPUIComponentProvider.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/CreateUpdateDistSetTypeLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/ManageDistBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CustomTargetBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterQueryValidation.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/event/CustomFilterUIEvent.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstag/CreateUpdateDistributionTagLayoutWindow.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstag/DistributionTagButtons.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/tag/CreateUpdateTagLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetAddUpdateWindowLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/CreateUpdateTargetTagLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetTagFilterButtons.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/DistributionBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/ProxyRollout.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/ProxyRolloutGroup.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupListGrid.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupsListHeader.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgrouptargets/RolloutGroupTargetsBeanQuery.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/AuthenticationConfigurationView.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIComponetIdProvider.java
	hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/common.scss
	hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/others.scss
	hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/rollout.scss
	hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/target-filter-query.scss
	hawkbit-ui/src/main/resources/messages.properties
	hawkbit-ui/src/main/resources/messages_de.properties
	hawkbit-ui/src/main/resources/messages_en.properties


Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>
This commit is contained in:
SirWayne
2016-05-17 11:16:18 +02:00
166 changed files with 2700 additions and 1284 deletions

View File

@@ -22,14 +22,17 @@ import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import({ RepositoryApplicationConfiguration.class })
@EnableHawkbitManagedSecurityConfiguration
// Exception squid:S1118 - Spring boot standard behavior
@SuppressWarnings({ "squid:S1118" })
public class Start {
/**
* Main method to start the spring-boot application.
*
* @param args
* the VM arguments.
*/
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) {
SpringApplication.run(Start.class, args);
}

View File

@@ -1,4 +1,3 @@
<?xml version="1.0"?>
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
@@ -9,7 +8,8 @@
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">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -133,13 +133,17 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>7.6.3</version>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

View File

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

View File

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

View File

@@ -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 {

View File

@@ -21,8 +21,6 @@ import com.vaadin.spring.annotation.EnableVaadin;
/**
* The main-method to start the Spring-Boot application.
*
*
*
*/
@SpringBootApplication
@EnableVaadin
@@ -46,6 +44,8 @@ public class DeviceSimulator {
* @param args
* the args
*/
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) {
SpringApplication.run(DeviceSimulator.class, args);
}

View File

@@ -8,27 +8,56 @@
*/
package org.eclipse.hawkbit.simulator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.http.HttpHeaders;
import org.springframework.http.HttpStatus;
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 +83,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,14 +103,23 @@ public class DeviceSimulatorUpdater {
}
device.setProgress(0.0);
device.setSwversion(swVersion);
if (modules == null || modules.isEmpty()) {
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 {
private static final int MINIMUM_TOKENLENGTH_FOR_HINT = 6;
private static final Random rndSleep = new SecureRandom();
private final AbstractSimulatedDevice device;
@@ -85,38 +127,205 @@ 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 (isErrorResponse(device.getUpdateStatus())) {
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 (isErrorResponse(download)) {
result.setResponseStatus(ResponseStatus.ERROR);
}
});
LOGGER.info("Download simulations complete for {}", device.getId());
return result;
}
private boolean isErrorResponse(final UpdateStatus status) {
if (status == null) {
return false;
}
return ResponseStatus.ERROR.equals(status.getResponseStatus());
}
private static void handleArtifacts(final String targetToken, final List<UpdateStatus> status,
final Artifact artifact) {
artifact.getUrls().entrySet().forEach(entry -> {
switch (entry.getKey()) {
case HTTP:
case HTTPS:
status.add(downloadUrl(entry.getValue(), targetToken, artifact.getHashes().getSha1(),
artifact.getSize()));
break;
default:
// not supported yet
break;
}
});
}
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
final long size) {
LOGGER.debug("Downloading {} with token {}, expected sha1 hash {} and size {}", url,
hideTokenDetails(targetToken), sha1Hash, size);
long overallread = 0;
try {
final CloseableHttpClient httpclient = createHttpClientThatAcceptsAllServerCerts();
final HttpGet request = new HttpGet(url);
request.addHeader(HttpHeaders.AUTHORIZATION, "TargetToken " + targetToken);
final String sha1HashResult;
try (final CloseableHttpResponse response = httpclient.execute(request)) {
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
final String message = wrongStatusCode(url, response);
return new UpdateStatus(ResponseStatus.ERROR, message);
}
if (response.getEntity().getContentLength() != size) {
final String message = wrongContentLength(url, size, response);
return new UpdateStatus(ResponseStatus.ERROR, message);
}
final File tempFile = File.createTempFile("uploadFile", null);
final MessageDigest md = MessageDigest.getInstance("SHA-1");
try (final DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(tempFile), md)) {
try (final BufferedOutputStream bdos = new BufferedOutputStream(dos)) {
try (BufferedInputStream bis = new BufferedInputStream(response.getEntity().getContent())) {
overallread = ByteStreams.copy(bis, bdos);
}
}
} finally {
if (tempFile != null && !tempFile.delete()) {
LOGGER.error("Could not delete temporary file: {}", tempFile);
}
}
if (overallread != size) {
final String message = incompleteRead(url, size, overallread);
return new UpdateStatus(ResponseStatus.ERROR, message);
}
sha1HashResult = BaseEncoding.base16().lowerCase().encode(md.digest());
}
if (!sha1Hash.equalsIgnoreCase(sha1HashResult)) {
final String message = wrongHash(url, sha1Hash, overallread, sha1HashResult);
return new UpdateStatus(ResponseStatus.ERROR, message);
}
} catch (IOException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
LOGGER.error("Failed to download {} with {}", url, e.getMessage());
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 String hideTokenDetails(final String targetToken) {
if (targetToken.isEmpty()) {
return "<EMTPTY!>";
}
if (targetToken.length() <= MINIMUM_TOKENLENGTH_FOR_HINT) {
return "***";
}
return targetToken.substring(0, 2) + "***"
+ targetToken.substring(targetToken.length() - 2, targetToken.length());
}
private static String wrongHash(final String url, final String sha1Hash, final long overallread,
final String sha1HashResult) {
final String message = "Download " + url + " failed with SHA1 hash missmatch (Expected: " + sha1Hash
+ " but got: " + sha1HashResult + ") (" + overallread + " bytes)";
LOGGER.error(message);
return message;
}
private static String incompleteRead(final String url, final long size, final long overallread) {
final String message = "Download " + url + " is incomplete (Expected: " + size + " but got: " + overallread
+ ")";
LOGGER.error(message);
return message;
}
private static String wrongContentLength(final String url, final long size,
final CloseableHttpResponse response) {
final String message = "Download " + url + " has wrong content length (Expected: " + size + " but got: "
+ response.getEntity().getContentLength() + ")";
LOGGER.error(message);
return message;
}
private static String wrongStatusCode(final String url, final CloseableHttpResponse response) {
final String message = "Download " + url + " failed (" + response.getStatusLine().getStatusCode() + ")";
LOGGER.error(message);
return 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 back to the hawkBit update server.
*/
@FunctionalInterface
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
* 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));

View File

@@ -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");
}

View File

@@ -86,7 +86,10 @@ public class SimulationController {
final String deviceId = name + i;
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint),
gatewayToken));
spSenderService.createOrUpdateThing(tenant, deviceId);
if (protocol == Protocol.DMF_AMQP) {
spSenderService.createOrUpdateThing(tenant, deviceId);
}
}
return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");

View File

@@ -11,10 +11,12 @@ package org.eclipse.hawkbit.simulator;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@@ -24,6 +26,7 @@ import org.springframework.stereotype.Component;
*
*/
@Component
@ConditionalOnProperty(prefix = "hawkbit.device.simulator", name = "autostart", matchIfMissing = true)
public class SimulatorStartup implements ApplicationListener<ContextRefreshedEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorStartup.class);
@@ -52,7 +55,9 @@ public class SimulatorStartup implements ApplicationListener<ContextRefreshedEve
LOGGER.error("Creation of simulated device at startup failed.", e);
}
spSenderService.createOrUpdateThing(autostart.getTenant(), deviceId);
if (autostart.getApi() == Protocol.DMF_AMQP) {
spSenderService.createOrUpdateThing(autostart.getTenant(), deviceId);
}
}
});
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator;
import java.util.ArrayList;
import java.util.List;
/**
* Update status of the simulated update.
*
*/
public class UpdateStatus {
private ResponseStatus responseStatus;
private List<String> statusMessages;
/**
* Constructor.
*
* @param responseStatus
* of the update
*/
public UpdateStatus(final ResponseStatus responseStatus) {
this.responseStatus = responseStatus;
}
/**
* Constructor including status message.
*
* @param responseStatus
* of the update
* @param message
* of the update status
*/
public UpdateStatus(final ResponseStatus responseStatus, final String message) {
this(responseStatus);
statusMessages = new ArrayList<>();
statusMessages.add(message);
}
public ResponseStatus getResponseStatus() {
return responseStatus;
}
public void setResponseStatus(final ResponseStatus responseStatus) {
this.responseStatus = responseStatus;
}
public List<String> getStatusMessages() {
if (statusMessages == null) {
statusMessages = new ArrayList<>();
}
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 {
/**
* Update has been successful and response the successful update.
*/
SUCCESSFUL,
/**
* Update has been not successful and response the error update.
*/
ERROR;
}
}

View File

@@ -39,6 +39,12 @@ public class AmqpProperties {
*/
private String deadLetterExchange = "simulator.deadletter";
/**
* Message time to live (ttl) for the deadletter queue. Default ttl is 1
* hour.
*/
private int deadLetterTtl = 60_000;
public String getReceiverConnectorQueueFromSp() {
return receiverConnectorQueueFromSp;
}
@@ -70,4 +76,12 @@ public class AmqpProperties {
public void setSenderForSpExchange(final String senderForSpExchange) {
this.senderForSpExchange = senderForSpExchange;
}
public int getDeadLetterTtl() {
return deadLetterTtl;
}
public void setDeadLetterTtl(final int deadLetterTtl) {
this.deadLetterTtl = deadLetterTtl;
}
}

View File

@@ -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.
*
@@ -88,6 +90,8 @@ public class SpReceiverService extends ReceiverService {
if (eventHeader == null) {
logAndThrowMessageError(message, "Event Topic is not set");
}
// Exception squid:S2259 - Checked before
@SuppressWarnings({ "squid:S2259" })
final EventTopic eventTopic = EventTopic.valueOf(eventHeader.toString());
switch (eventTopic) {
case DOWNLOAD_AND_INSTALL:
@@ -109,7 +113,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 +124,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;

View File

@@ -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());
}
}

View File

@@ -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 {

View File

@@ -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,13 +50,27 @@ 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 = "")
public class SimulatorView extends VerticalLayout implements View {
private static final String NEXT_POLL_COUNTER_SEC_COL = "nextPollCounterSec";
private static final String RESPONSE_STATUS_COL = "responseStatus";
private static final String PROTOCOL_COL = "protocol";
private static final String TENANT_COL = "tenant";
private static final String PROGRESS_COL = "progress";
private static final String SWVERSION_COL = "swversion";
private static final String STATUS_COL = "status";
private static final String ID_COL = "id";
private static final long serialVersionUID = 1L;
@Autowired
@@ -79,6 +91,7 @@ public class SimulatorView extends VerticalLayout implements View {
private BeanContainer<String, AbstractSimulatedDevice> beanContainer;
@SuppressWarnings("unchecked")
@Override
public void enter(final ViewChangeEvent event) {
eventbus.register(this);
@@ -91,7 +104,7 @@ public class SimulatorView extends VerticalLayout implements View {
createToolbar();
beanContainer = new BeanContainer<>(AbstractSimulatedDevice.class);
beanContainer.setBeanIdProperty("id");
beanContainer.setBeanIdProperty(ID_COL);
grid.setSizeFull();
grid.setCellStyleGenerator(new CellStyleGenerator() {
@@ -100,28 +113,28 @@ public class SimulatorView extends VerticalLayout implements View {
@Override
public String getStyle(final CellReference cellReference) {
return cellReference.getPropertyId().equals("status") ? "centeralign" : null;
return cellReference.getPropertyId().equals(STATUS_COL) ? "centeralign" : null;
}
});
grid.setSelectionMode(SelectionMode.NONE);
grid.setContainerDataSource(beanContainer);
grid.appendHeaderRow().getCell("responseStatus").setComponent(responseComboBox);
grid.setColumnOrder("id", "status", "swversion", "progress", "tenant", "protocol", "responseStatus",
"nextPollCounterSec");
grid.appendHeaderRow().getCell(RESPONSE_STATUS_COL).setComponent(responseComboBox);
grid.setColumnOrder(ID_COL, STATUS_COL, SWVERSION_COL, PROGRESS_COL, TENANT_COL, PROTOCOL_COL,
RESPONSE_STATUS_COL, NEXT_POLL_COUNTER_SEC_COL);
// header widths
grid.getColumn("status").setMaximumWidth(80);
grid.getColumn("protocol").setMaximumWidth(180);
grid.getColumn("responseStatus").setMaximumWidth(240);
grid.getColumn("nextPollCounterSec").setMaximumWidth(210);
grid.getColumn(STATUS_COL).setMaximumWidth(80);
grid.getColumn(PROTOCOL_COL).setMaximumWidth(180);
grid.getColumn(RESPONSE_STATUS_COL).setMaximumWidth(240);
grid.getColumn(NEXT_POLL_COUNTER_SEC_COL).setMaximumWidth(210);
grid.getColumn("nextPollCounterSec").setHeaderCaption("Next Poll in (sec)");
grid.getColumn("swversion").setHeaderCaption("SW Version");
grid.getColumn("responseStatus").setHeaderCaption("Response Update Status");
grid.getColumn("progress").setRenderer(new ProgressBarRenderer());
grid.getColumn("protocol").setConverter(createProtocolConverter());
grid.getColumn("status").setRenderer(new HtmlRenderer(), createStatusConverter());
grid.removeColumn("tenant");
grid.getColumn(NEXT_POLL_COUNTER_SEC_COL).setHeaderCaption("Next Poll in (sec)");
grid.getColumn(SWVERSION_COL).setHeaderCaption("SW Version");
grid.getColumn(RESPONSE_STATUS_COL).setHeaderCaption("Response Update Status");
grid.getColumn(PROGRESS_COL).setRenderer(new ProgressBarRenderer());
grid.getColumn(PROTOCOL_COL).setConverter(createProtocolConverter());
grid.getColumn(STATUS_COL).setRenderer(new HtmlRenderer(), createStatusConverter());
grid.removeColumn(TENANT_COL);
// grid combobox
responseComboBox.setItemIcon(ResponseStatus.SUCCESSFUL, FontAwesome.CHECK_CIRCLE);
@@ -129,8 +142,8 @@ public class SimulatorView extends VerticalLayout implements View {
responseComboBox.setNullSelectionAllowed(false);
responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
responseComboBox.addValueChangeListener(valueChangeEvent -> {
beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus")
.setValue(valueChangeEvent.getProperty().getValue()));
beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId)
.getItemProperty(RESPONSE_STATUS_COL).setValue(valueChangeEvent.getProperty().getValue()));
});
// add all components
@@ -141,7 +154,7 @@ public class SimulatorView extends VerticalLayout implements View {
setExpandRatio(grid, 1.0F);
// load beans
repository.getAll().forEach(device -> beanContainer.addBean(device));
repository.getAll().forEach(beanContainer::addBean);
}
@Override
@@ -150,21 +163,16 @@ public class SimulatorView extends VerticalLayout implements View {
eventbus.unregister(this);
}
@SuppressWarnings("unchecked")
@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(NEXT_POLL_COUNTER_SEC_COL).setValue(device.getNextPollCounterSec());
}
});
}));
}
/**
@@ -173,21 +181,19 @@ public class SimulatorView extends VerticalLayout implements View {
* @param update
* the update event posted on the event bus
*/
@SuppressWarnings("unchecked")
@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) {
return;
}
item.getItemProperty(PROGRESS_COL).setValue(device.getProgress());
item.getItemProperty(STATUS_COL).setValue(Status.PEDNING);
item.getItemProperty(SWVERSION_COL).setValue(device.getSwversion());
});
}
@@ -197,35 +203,37 @@ public class SimulatorView extends VerticalLayout implements View {
* @param update
* the update event posted on the event bus
*/
@SuppressWarnings("unchecked")
@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_COL).setValue(device.getProgress());
setStatusColumn(device, item);
}
});
}
@SuppressWarnings("unchecked")
private void setStatusColumn(final AbstractSimulatedDevice device, final BeanItem<AbstractSimulatedDevice> item) {
if (device.getProgress() >= 1) {
switch (device.getUpdateStatus().getResponseStatus()) {
case SUCCESSFUL:
item.getItemProperty(STATUS_COL).setValue(Status.FINISH);
break;
case ERROR:
item.getItemProperty(STATUS_COL).setValue(Status.ERROR);
break;
default:
item.getItemProperty(STATUS_COL).setValue(Status.UNKNWON);
}
} else {
item.getItemProperty(STATUS_COL).setValue(Status.PEDNING);
}
}
private void createToolbar() {
final Button createDevicesButton = new Button("generate...");
createDevicesButton.setIcon(FontAwesome.GEARS);
@@ -246,18 +254,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() {

View File

@@ -4,16 +4,20 @@ The hawkBit example application is a standalone spring-boot application with an
We have have described several options for you to get access to the example.
## Try out the example application in our hawkBit sandbox on Bluemix
- try out Management UI https://hawkbit.eu-gb.mybluemix.net/UI
- try out Management API https://hawkbit.eu-gb.mybluemix.net/rest/v1/targets (don't forget basic auth header)
- try out DDI API https://hawkbit.eu-gb.mybluemix.net/DEFAULT/controller/v1/MYTESTDEVICE
- try out Management UI https://hawkbit.eu-gb.mybluemix.net/UI (username: admin, passwd: admin)
- try out Management API https://hawkbit.eu-gb.mybluemix.net/rest/v1/targets (don't forget basic auth header; username: admin, passwd: admin)
- try out DDI API https://hawkbit.eu-gb.mybluemix.net/DEFAULT/controller/v1/MYTESTDEVICE (authentication disabled)
## On your own workstation
### Run
```
java -jar examples/hawkbit-example-app/target/hawkbit-example-app-*-SNAPSHOT.jar
```
_(Note: you have to add the JDBC driver also to your class path if you intend to use another database than H2.)_
Or:
```
run org eclipse.hawkbit.app.Start
```

View File

@@ -20,11 +20,11 @@ import com.vaadin.spring.annotation.SpringUI;
* login path. The easiest way to get an hawkBit login UI running is to extend
* the {@link HawkbitLoginUI} and to annotated it with {@link SpringUI} as in
* this example to the defined {@link HawkbitTheme#LOGIN_UI_PATH}.
*
*
*
*/
@SpringUI(path = HawkbitTheme.LOGIN_UI_PATH)
// Exception squid:MaximumInheritanceDepth - Most of the inheritance comes from
// Vaadin.
@SuppressWarnings({ "squid:MaximumInheritanceDepth" })
public class MyLoginUI extends HawkbitLoginUI {
private static final long serialVersionUID = 1L;

View File

@@ -29,6 +29,9 @@ import com.vaadin.spring.annotation.SpringUI;
*/
@SpringUI
@Push(value = PushMode.AUTOMATIC, transport = Transport.WEBSOCKET)
// Exception squid:MaximumInheritanceDepth - Most of the inheritance comes from
// Vaadin.
@SuppressWarnings({ "squid:MaximumInheritanceDepth" })
public class MyUI extends HawkbitUI {
private static final long serialVersionUID = 1L;

View File

@@ -38,6 +38,8 @@ public class Start {
* @param args
* the VM arguments.
*/
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) {
SpringApplication.run(Start.class, args);
}

View File

@@ -8,3 +8,9 @@
#
vaadin.servlet.productionMode=true
hawkbit.artifact.url.coap.enabled=false
hawkbit.artifact.url.http.enabled=false
hawkbit.artifact.url.https.enabled=true
hawkbit.artifact.url.https.pattern={protocol}://{hostname}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
hawkbit.artifact.url.https.hostname=hawkbit.eu-gb.mybluemix.net

View File

@@ -0,0 +1,32 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# This profile adds basic configurations for a MySQL DB usage.
# Keep in mind that you need the MariaDB driver in your classpath on compile.
# see https://github.com/eclipse/hawkbit/wiki/Run-hawkBit
spring.jpa.database=MYSQL
spring.datasource.url=jdbc:mysql://localhost:3306/hawkbit
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.max-active=100
spring.datasource.max-idle=10
spring.datasource.min-idle=10
spring.datasource.initial-size=10
spring.datasource.validation-query=select 1 from dual
spring.datasource.validation-interval=30000
spring.datasource.test-on-borrow=true
spring.datasource.test-on-return=false
spring.datasource.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=30000
spring.datasource.min-evictable-idle-time-millis=60000
spring.datasource.max-wait=10000
spring.datasource.jmx-enabled=true

View File

@@ -7,21 +7,28 @@
# http://www.eclipse.org/legal/epl-v10.html
#
# DDI authentication configuration
hawkbit.server.ddi.security.authentication.anonymous.enabled=true
hawkbit.server.ddi.security.authentication.targettoken.enabled=false
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
hawkbit.server.ddi.security.authentication.targettoken.enabled=true
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true
spring.profiles.active=amqp
# Download URL generation config
hawkbit.artifact.url.coap.enabled=false
hawkbit.artifact.url.http.enabled=true
hawkbit.artifact.url.http.port=8080
hawkbit.artifact.url.https.enabled=false
## Vaadin configuration
vaadin.servlet.productionMode=false
## Configuration for RabbitMQ integration
## Configuration for DMF/RabbitMQ integration
spring.profiles.active=amqp
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtualHost=/
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver