Merge remote-tracking branch 'eclipse/master'

This commit is contained in:
Kai Zimmermann
2016-01-28 13:52:52 +01:00
38 changed files with 1843 additions and 114 deletions

View File

@@ -20,6 +20,25 @@ The simulator has user authentication enabled by default. Default credentials:
This can be configured/disabled by spring boot properties
## Usage
### Graphical User Interface
The device simulator comes with a graphical user interface which makes it very easy to generate dummy devices handled by the device simulator.
The status and the update progress of the simulated device are shown in the UI.
The UI can be accessed via the URL:
```
http://localhost:8083
```
`Basic Authentication Credentials are admin / admin`
![](src/main/images/generateScreenshot.png)
![](src/main/images/updateProcessScreenshot.png)
![](src/main/images/updateResultOverviewScreenshot.png)
### REST API
The device simulator exposes an REST-API which can be used to trigger device creation.
Optional parameters:

View File

@@ -80,10 +80,52 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-push</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jackson</artifactId>
<version>8.14.1</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.12.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>7.5.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -0,0 +1,180 @@
/**
* 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;
/**
* The bean of a simulated device which can be stored in the
* {@link DeviceSimulatorRepository} or shown in the UI.
*
* @author Michael Hirsch
*
*/
public abstract class AbstractSimulatedDevice {
private String id;
private String tenant;
private Status status;
private double progress;
private String swversion = "unknown";
private ResponseStatus responseStatus = ResponseStatus.SUCCESSFUL;
private Protocol protocol = Protocol.DMF_AMQP;
private int nextPollCounterSec;
/**
* Enum definition of the protocol to be used for the simulated device.
*
* @author Michael Hirsch
*
*/
public enum Protocol {
/**
* Device Management Federation API via AMQP, push mechanism.
*/
DMF_AMQP,
/**
* Direct Device Interface via HTTP, poll mechanism.
*/
DDI_HTTP;
}
/**
* The current status of the simulated device.
*
* @author Michael Hirsch
*
*/
public enum Status {
/**
* device is in status unknown.
*/
UNKNWON,
/**
* device is in status pending which represents is updating software.
*/
PEDNING,
/**
* device has been updated successfully.
*/
FINISH,
/**
* device has been updated with an error.
*/
ERROR;
}
/**
* The status to response to the hawkbit update server if an simulated
* update process should be respond with successful or failure update.
*
* @author Michael Hirsch
*
*/
public enum ResponseStatus {
/**
* updated has been successful and response the successful update.
*/
SUCCESSFUL,
/**
* updated has been not successful and response the error update.
*/
ERROR;
}
/**
* empty constructor.
*/
AbstractSimulatedDevice() {
}
/**
* Creates a new simulated device.
*
* @param id
* the ID of the simulated device
* @param tenant
* the tenant of the simulated device
*/
AbstractSimulatedDevice(final String id, final String tenant, final Protocol protocol) {
this.id = id;
this.tenant = tenant;
this.status = Status.UNKNWON;
this.progress = 0.0;
this.protocol = protocol;
}
/**
* Method to clean-up resource e.g. when the simulated device has been
* removed from the repository.
*/
public void clean() {
}
public String getId() {
return id;
}
public Status getStatus() {
return status;
}
public double getProgress() {
return progress;
}
public String getTenant() {
return tenant;
}
public void setId(final String id) {
this.id = id;
}
public void setTenant(final String tenant) {
this.tenant = tenant;
}
public void setStatus(final Status status) {
this.status = status;
}
public void setProgress(final double progress) {
this.progress = progress;
}
public String getSwversion() {
return swversion;
}
public void setSwversion(final String swversion) {
this.swversion = swversion;
}
public ResponseStatus getResponseStatus() {
return responseStatus;
}
public void setResponseStatus(final ResponseStatus responseStatus) {
this.responseStatus = responseStatus;
}
public Protocol getProtocol() {
return protocol;
}
public int getNextPollCounterSec() {
return nextPollCounterSec;
}
public void setNextPollCounterSec(final int nextPollDelayInSec) {
this.nextPollCounterSec = nextPollDelayInSec;
}
}

View File

@@ -0,0 +1,111 @@
/**
* 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.concurrent.ScheduledExecutorService;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
import org.eclipse.hawkbit.simulator.http.ControllerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
/**
* @author Michael Hirsch
*
*/
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 volatile boolean removed;
private volatile Long currentActionId;
private final DeviceSimulatorUpdater deviceUpdater;
/**
* @param id
* the ID of the device
* @param tenant
* the tenant of the simulated device
* @param pollDelaySec
* 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) {
super(id, tenant, Protocol.DDI_HTTP);
this.pollDelaySec = pollDelaySec;
this.controllerResource = controllerResource;
this.pollthreadpool = pollthreadpool;
this.deviceUpdater = deviceUpdater;
setNextPollCounterSec(pollDelaySec);
}
@Override
public void clean() {
super.clean();
removed = true;
}
public int getPollDelaySec() {
return pollDelaySec;
}
/**
* Polls the base URL for the DDI API interface.
*/
public void poll() {
if (!removed) {
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("?")));
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, new UpdaterCallback() {
@Override
public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
switch (device.getResponseStatus()) {
case SUCCESSFUL:
controllerResource.postSuccessFeedback(getTenant(), getId(), actionId);
break;
case ERROR:
controllerResource.postErrorFeedback(getTenant(), getId(), actionId);
break;
default:
throw new IllegalStateException("simulated device has an unknown response status + "
+ device.getResponseStatus());
}
currentActionId = null;
}
});
}
} catch (final PathNotFoundException e) {
// href might not be in the json response, so ignore
// exception here.
LOGGER.trace("Response does not contain a deploymentbase href link, ignoring.");
}
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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;
/**
* An simulated device using the DMF API of the hawkbit update server.
*
* @author Michael Hirsch
*
*/
public class DMFSimulatedDevice extends AbstractSimulatedDevice {
/**
* @param id
* the ID of the device
* @param tenant
* the tenant of the simulated device
*/
public DMFSimulatedDevice(final String id, final String tenant) {
super(id, tenant, Protocol.DMF_AMQP);
}
}

View File

@@ -8,8 +8,15 @@
*/
package org.eclipse.hawkbit.simulator;
import java.util.concurrent.Executors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.vaadin.spring.annotation.EnableVaadin;
/**
* The main-method to start the Spring-Boot application.
@@ -18,12 +25,21 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
*
*/
@SpringBootApplication
@EnableVaadin
public class DeviceSimulator {
private DeviceSimulator() {
public DeviceSimulator() {
// utility class
}
/**
* @return an asynchronous event bus to publish and retrieve events.
*/
@Bean
public EventBus eventBus() {
return new AsyncEventBus(Executors.newFixedThreadPool(4));
}
/**
* Start the Spring Boot Application.
*

View File

@@ -0,0 +1,123 @@
/**
* 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.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* An in-memory simulated device repository to hold the simulated device in
* memory and be able to retrieve them again.
*
* @author Michael Hirsch
*
*/
@Service
public class DeviceSimulatorRepository {
private final Map<DeviceKey, AbstractSimulatedDevice> devices = new LinkedHashMap<>();
@Autowired
private SimulatedDeviceFactory deviceFactory;
/**
* Adds a simulated device to the repository.
*
* @param simulatedDevice
* the device to add
* @return the device which has been added to the repository
*/
public AbstractSimulatedDevice add(final AbstractSimulatedDevice simulatedDevice) {
devices.put(new DeviceKey(simulatedDevice.getTenant().toLowerCase(), simulatedDevice.getId()), simulatedDevice);
return simulatedDevice;
}
/**
* @return all simulated devices
*/
public Collection<AbstractSimulatedDevice> getAll() {
return devices.values();
}
/**
* Retrieves a single simulated devices or {@code null} if device does not
* exists.
*
* @param tenant
* the tenant of the simulated device
* @param id
* the ID of the device
* @return a simulated device from the repository or {@code null} if device
* does not exixts.
*/
public AbstractSimulatedDevice get(final String tenant, final String id) {
return devices.get(new DeviceKey(tenant.toLowerCase(), id));
}
/**
* Clears all stored devices.
*/
public void clear() {
devices.values().forEach(device -> device.clean());
devices.clear();
}
private static final class DeviceKey {
private final String tenant;
private final String id;
private DeviceKey(final String tenant, final String id) {
this.tenant = tenant;
this.id = id;
}
@Override
public int hashCode() {// NOSONAR - as this is generated
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((tenant == null) ? 0 : tenant.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {// NOSONAR - as this is
// generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DeviceKey other = (DeviceKey) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (tenant == null) {
if (other.tenant != null) {
return false;
}
} else if (!tenant.equals(other.tenant)) {
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,122 @@
/**
* 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.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
/**
* @author Michael Hirsch
*
*/
@Service
public class DeviceSimulatorUpdater {
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
@Autowired
private SpSenderService spSenderService;
@Autowired
private EventBus eventbus;
@Autowired
private DeviceSimulatorRepository repository;
/**
* Starting an simulated update process of an simulated device.
*
* @param tenant
* the tenant of the device
* @param id
* the ID of the simulated device
* @param actionId
* the actionId from the hawkbit update server to start the
* update.
* @param swVersion
* the software module version from the hawkbit update server
* @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 AbstractSimulatedDevice device = repository.get(tenant, id);
device.setProgress(0.0);
device.setSwversion(swVersion);
eventbus.post(new InitUpdate(device));
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback),
2000, TimeUnit.MILLISECONDS);
}
private static final class DeviceSimulatorUpdateThread implements Runnable {
private static final Random rndSleep = new Random();
private final AbstractSimulatedDevice device;
private final SpSenderService spSenderService;
private final long actionId;
private final EventBus eventbus;
private final UpdaterCallback callback;
private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device,
final SpSenderService spSenderService, final long actionId, final EventBus eventbus,
final UpdaterCallback callback) {
this.device = device;
this.spSenderService = spSenderService;
this.actionId = actionId;
this.eventbus = eventbus;
this.callback = callback;
}
@Override
public void run() {
final double newProgress = device.getProgress() + 0.2;
device.setProgress(newProgress);
if (newProgress < 1.0) {
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus,
callback), rndSleep.nextInt(3000), TimeUnit.MILLISECONDS);
} else {
callback.updateFinished(device, actionId);
}
eventbus.post(new ProgressUpdate(device));
}
}
/**
* 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
*
*/
@FunctionalInterface
public interface UpdaterCallback {
/**
* Callback method to indicate that the simulated update process has
* been finished.
*
* @param device
* the device which has been updated
* @param actionId
* the ID of the action from the hawkbit update server
*/
void updateFinished(AbstractSimulatedDevice device, final Long actionId);
}
}

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.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.common.eventbus.EventBus;
/**
* Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every
* second.
*
* @author Michael Hirsch
*
*/
@Component
public class NextPollTimeController {
private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private static final ExecutorService pollService = Executors.newFixedThreadPool(1);
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private EventBus eventBus;
/**
* Constructor which schedules the poll trigger runnable every second.
*/
public NextPollTimeController() {
executorService.scheduleWithFixedDelay(new NextPollUpdaterRunnable(), 1, 1, TimeUnit.SECONDS);
}
private class NextPollUpdaterRunnable implements Runnable {
@Override
public void run() {
final List<AbstractSimulatedDevice> devices = repository.getAll().stream()
.filter(device -> device instanceof DDISimulatedDevice).collect(Collectors.toList());
devices.forEach(device -> {
int nextCounter = device.getNextPollCounterSec() - 1;
if (nextCounter < 0) {
if (device instanceof DDISimulatedDevice) {
try {
pollService.submit(new Runnable() {
@Override
public void run() {
((DDISimulatedDevice) device).poll();
}
});
} catch (final Exception e) {
}
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
}
}
device.setNextPollCounterSec(nextCounter);
});
eventBus.post(new NextPollCounterUpdate(devices));
}
}
}

View File

@@ -0,0 +1,88 @@
/**
* 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.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;
import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import feign.Feign;
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;
/**
* Creating a simulated devices.
*
* @param id
* the ID of the simulated device
* @param tenant
* the tenant of the simulated device
* @param protocol
* the protocol of the device
* @return the created simulated device
*/
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol) {
return createSimulatedDevice(id, tenant, protocol, 30, null, null);
}
/**
* Creating a simulated device.
*
* @param id
* the ID of the simulated device
* @param tenant
* the tenant of the simulated device
* @param protocol
* the protocol which should be used be the simulated device
* @param pollDelaySec
* the poll delay time in seconds which should be used for
* {@link DDISimulatedDevice}s
* @param baseEndpoint
* the http base endpoint which should be used for
* {@link DDISimulatedDevice}s
* @param gatewayToken
* the gatewayToken to be used to authenticate
* {@link DDISimulatedDevice}s at the endpoint
* @return the created simulated device
*/
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol,
final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) {
switch (protocol) {
case DMF_AMQP:
return new DMFSimulatedDevice(id, tenant);
case DDI_HTTP:
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);
default:
throw new IllegalArgumentException("Protocol " + protocol + " unknown");
}
}
}

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.simulator;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,6 +27,12 @@ public class SimulationController {
@Autowired
private SpSenderService spSenderService;
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private SimulatedDeviceFactory deviceFactory;
/**
* The start resource to start a device creation.
*
@@ -43,7 +50,9 @@ public class SimulationController {
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
for (int i = 0; i < amount; i++) {
spSenderService.createOrUpdateThing(tenant, name + i);
final String deviceId = name + i;
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, Protocol.DMF_AMQP));
spSenderService.createOrUpdateThing(tenant, deviceId);
}
return "Updated " + amount + " DMF connected targets!";

View File

@@ -132,6 +132,9 @@ public class AmqpConfiguration {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory);
containerFactory.setConcurrentConsumers(20);
containerFactory.setMaxConcurrentConsumers(20);
containerFactory.setPrefetchCount(20);
return containerFactory;
}

View File

@@ -15,6 +15,9 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
@@ -40,6 +43,8 @@ public class SpReceiverService extends ReceiverService {
private final SpSenderService spSenderService;
private final DeviceSimulatorUpdater deviceUpdater;
/**
* Constructor.
*
@@ -51,12 +56,15 @@ public class SpReceiverService extends ReceiverService {
* the lwm2mSenderService
* @param spSenderService
* the spSenderService
* @param deviceUpdater
* the updater service to simulate update process
*/
@Autowired
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
final SpSenderService spSenderService) {
final SpSenderService spSenderService, final DeviceSimulatorUpdater deviceUpdater) {
super(rabbitTemplate, amqpProperties);
this.spSenderService = spSenderService;
this.deviceUpdater = deviceUpdater;
}
@@ -139,16 +147,23 @@ public class SpReceiverService extends ReceiverService {
spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
"device Simulator retrieved update request. proceeding with simulation.", actionId);
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
try {
Thread.sleep(1_000);
} catch (final InterruptedException e) {
LOGGER.error("Sleep interrupted", e);
}
spSenderService.finishUpdateProcess(update, "Simulation complete!");
deviceUpdater.startUpdate(tenant, thingId, actionId, downloadAndUpdateRequest.getSoftwareModules().get(0)
.getModuleVersion(), new UpdaterCallback() {
@Override
public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
switch (device.getResponseStatus()) {
case SUCCESSFUL:
spSenderService.finishUpdateProcess(new SimulatedUpdate(device.getTenant(), device.getId(),
actionId), "Simulation complete!");
break;
case ERROR:
spSenderService.finishUpdateProcessWithError(new SimulatedUpdate(device.getTenant(),
device.getId(), actionId), "Simulation complete with error!");
break;
default:
break;
}
}
});
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.event;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
/**
* Event description which indicates the initialization of an update.
*
* @author Michael Hirsch
*
*/
public class InitUpdate {
private final AbstractSimulatedDevice device;
/**
* Creates new progress update event.
*
* @param device
* the device which progress has been updated
*/
public InitUpdate(final AbstractSimulatedDevice device) {
this.device = device;
}
/**
* @return the device of the event
*/
public AbstractSimulatedDevice getDevice() {
return device;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.simulator.event;
import java.util.List;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
/**
* Event description which indicates an poll time update.
*
* @author Michael Hirsch
*
*/
public class NextPollCounterUpdate {
private final List<AbstractSimulatedDevice> devices;
/**
* Creates poll timer update event.
*
* @param devices
* the devices which progress has been updated
*/
public NextPollCounterUpdate(final List<AbstractSimulatedDevice> devices) {
this.devices = devices;
}
/**
* @return the devices of the event
*/
public List<AbstractSimulatedDevice> getDevices() {
return devices;
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.event;
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 {
private final AbstractSimulatedDevice device;
/**
* Creates new progress update event.
*
* @param device
* the device which progress has been updated
*/
public ProgressUpdate(final AbstractSimulatedDevice device) {
this.device = device;
}
/**
* @return the device of the event
*/
public AbstractSimulatedDevice getDevice() {
return device;
}
}

View File

@@ -0,0 +1,88 @@
/**
* 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.http;
import org.eclipse.hawkbit.simulator.DDISimulatedDevice;
import feign.Body;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
/**
* A feign based controller resource interface declaration for
* {@link DDISimulatedDevice}s using over HTTP.
*
* @author Michael Hirsch
*
*/
public interface ControllerResource {
/**
* The base poll URL for the devices to retrieve if there is an update
* available.
*
* @param tenant
* the tenant of the device
* @param controllerId
* the ID of the device
* @return the plain json response of the http request
*/
@RequestLine("GET /{tenant}/controller/v1/{controllerId}")
@Headers({ "Content-Type: application/json" })
String get(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId);
/**
* Retrieving the deployment job response from the hawkbit update server.
*
* @param tenant
* the tenant for the simulated device
* @param controllerId
* the ID of the device
* @param actionId
* the ID of the action to retrieve
* @return the json response of the http request
*/
@RequestLine("GET /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}")
@Headers({ "Content-Type: application/json" })
String getDeployment(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId,
@Param("actionId") final long actionId);
/**
* Post a success update feedback to the hawkbit update server
*
* @param tenant
* the tenant of the device
* @param controllerId
* the ID of the device
* @param actionId
* the ID of the action to post feedback back
*/
@RequestLine("POST /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback")
@Headers("Content-Type: application/json")
@Body("%7B\"id\":{actionId},\"time\":\"20140511T121314\",\"status\":%7B\"execution\":\"closed\",\"result\":%7B\"finished\":\"success\",\"progress\":%7B%7D%7D%7D%7D")
void postSuccessFeedback(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId,
@Param("actionId") final long actionId);
/**
* Post a failure update feedback to the hawkbit update server
*
* @param tenant
* the tenant of the device
* @param controllerId
* the ID of the device
* @param actionId
* the ID of the action to post feedback back
*/
@RequestLine("POST /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback")
@Headers("Content-Type: application/json")
@Body("%7B\"id\":{actionId},\"time\":\"20140511T121314\",\"status\":%7B\"execution\":\"closed\",\"result\":%7B\"finished\":\"failure\",\"progress\":%7B%7D%7D%7D%7D")
void postErrorFeedback(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId,
@Param("actionId") final long actionId);
}

View File

@@ -0,0 +1,36 @@
/**
* 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.http;
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* A feign interceptor to apply the gateway-token header to each http-request.
*
* @author Michael Hirsch
*
*/
public class GatewayTokenInterceptor implements RequestInterceptor {
private final String gatewayToken;
/**
* @param gatewayToken
* the gatwway token to be used in the http-header
*/
public GatewayTokenInterceptor(final String gatewayToken) {
this.gatewayToken = gatewayToken;
}
@Override
public void apply(final RequestTemplate template) {
template.header("Authorization", "GatewayToken " + gatewayToken);
}
}

View File

@@ -0,0 +1,233 @@
/**
* 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.ui;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.validator.NullValidator;
import com.vaadin.data.validator.RangeValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Window;
/**
* Popup dialog window for setting the values of generating the simulated
* devices, e.g. the amount.
*
* @author Michael Hirsch
*
*/
public class GenerateDialog extends Window {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(GenerateDialog.class);
private final FormLayout formLayout = new FormLayout();
/**
* Creates a new pop window for setting the configuration of simulating
* devices.
*
* @param callback
* the callback which is called when the dialog has been
* successfully confirmed.
*/
public GenerateDialog(final GenerateDialogCallback callback) {
formLayout.setSpacing(true);
formLayout.setMargin(true);
final TextField tf1 = new TextField("name prefix", "dmfSimulated");
tf1.setIcon(FontAwesome.INFO);
tf1.setRequired(true);
tf1.addValidator(new NullValidator("Must be given", false));
final TextField tf2 = new TextField("amount", new ObjectProperty<Integer>(10));
tf2.setIcon(FontAwesome.GEAR);
tf2.setRequired(true);
tf2.addValidator(new RangeValidator<Integer>("Must be between 1 and 30000", Integer.class, 1, 30000));
final TextField tf3 = new TextField("tenant", "default");
tf3.setIcon(FontAwesome.USER);
tf3.setRequired(true);
tf3.addValidator(new NullValidator("Must be given", false));
final TextField tf4 = new TextField("poll delay (sec)", new ObjectProperty<Integer>(10));
tf4.setIcon(FontAwesome.CLOCK_O);
tf4.setRequired(true);
tf4.setVisible(false);
tf4.addValidator(new RangeValidator<Integer>("Must be between 1 and 60", Integer.class, 1, 60));
final TextField tf5 = new TextField("base poll URL endpoint", "http://localhost:8080");
tf5.setColumns(50);
tf5.setIcon(FontAwesome.FLAG_O);
tf5.setRequired(true);
tf5.setVisible(false);
tf5.addValidator(new RegexpValidator(
"^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "is not an URL"));
final TextField tf6 = new TextField("gateway token", "");
tf6.setColumns(50);
tf6.setIcon(FontAwesome.FLAG_O);
tf6.setRequired(true);
tf6.setVisible(false);
final OptionGroup protocolGroup = new OptionGroup("Simulated Device Protocol");
protocolGroup.addItem(Protocol.DMF_AMQP);
protocolGroup.addItem(Protocol.DDI_HTTP);
protocolGroup.setItemCaption(Protocol.DMF_AMQP, "Device Management Federation API (AMQP push)");
protocolGroup.setItemCaption(Protocol.DDI_HTTP, "Direct Device Interface (HTTP poll)");
protocolGroup.setNullSelectionAllowed(false);
protocolGroup.select(Protocol.DMF_AMQP);
protocolGroup.addValueChangeListener(new ValueChangeListener() {
private static final long serialVersionUID = 1L;
@Override
public void valueChange(final ValueChangeEvent event) {
if (event.getProperty().getValue().equals(Protocol.DDI_HTTP)) {
tf4.setVisible(true);
tf5.setVisible(true);
tf6.setVisible(true);
} else {
tf4.setVisible(false);
tf5.setVisible(false);
tf6.setVisible(false);
}
}
});
final Button buttonOk = new Button("generate");
buttonOk.setImmediate(true);
buttonOk.setIcon(FontAwesome.GEARS);
buttonOk.addClickListener(new ClickListener() {
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(final ClickEvent event) {
try {
callback.okButton(tf1.getValue(), tf3.getValue(), Integer.valueOf(tf2.getValue().replace(".", "")),
Integer.valueOf(tf4.getValue().replace(".", "")), new URL(tf5.getValue()), tf6.getValue(),
(Protocol) protocolGroup.getValue());
} catch (final NumberFormatException e) {
LOGGER.info(e.getMessage(), e);
} catch (final MalformedURLException e) {
LOGGER.info(e.getMessage(), e);
}
GenerateDialog.this.close();
}
});
tf1.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
tf2.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
tf3.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
formLayout.addComponent(tf1);
formLayout.addComponent(tf2);
formLayout.addComponent(tf3);
formLayout.addComponent(protocolGroup);
formLayout.addComponent(tf4);
formLayout.addComponent(tf5);
formLayout.addComponent(tf6);
formLayout.addComponent(buttonOk);
setCaption("Simulate Devices");
setContent(formLayout);
setResizable(false);
center();
}
private void checkValid(final TextField tf1, final TextField tf2, final TextField tf3, final TextField tf4,
final Button buttonOk) {
if (tf1.isValid() && tf2.isValid() && tf3.isValid() && tf4.isValid()) {
buttonOk.setEnabled(true);
} else {
buttonOk.setEnabled(false);
}
}
@Override
public int hashCode() {// NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((formLayout == null) ? 0 : formLayout.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {// NOSONAR - as this is generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final GenerateDialog other = (GenerateDialog) obj;
if (formLayout == null) {
if (other.formLayout != null) {
return false;
}
} else if (!formLayout.equals(other.formLayout)) {
return false;
}
return true;
}
/**
* Callback interface to retrieve the result from the dialog window.
*
* @author Michael Hirsch
*
*/
@FunctionalInterface
interface GenerateDialogCallback {
/**
* Callback method which is called when dialog closes with the OK
* button.
*
* @param namePrefix
* the parameter for name prefix for the simulated devices
* @param tenant
* the tenant for the simulated devices
* @param amount
* the number of simulated devices to be created
* @param pollDelay
* the delay poll time in seconds for DDI devices
* @param basePollURL
* the base http URL endpoint for DDI devices
* @param gatewayToken
* the gateway token header for authentication for DDI
* devices
* @param protocol
* the protocol to be used for the simulated devices to be
* generated
*/
void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay,
final URL basePollURL, final String gatewayToken, final Protocol protocol);
}
}

View File

@@ -0,0 +1,62 @@
/**
* 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.ui;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.navigator.Navigator;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.spring.navigator.SpringViewProvider;
import com.vaadin.ui.Panel;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
* The vaadin simulator UI which allows to generate simulated devices and show
* their current status and update progress.
*
* @author Michael Hirsch
*
*/
@SpringUI(path = "")
@Title("hawkBit Device Simulator")
@Theme(value = "simulator")
@Push(value = PushMode.AUTOMATIC, transport = Transport.WEBSOCKET)
public class SimulatorUI extends UI {
private static final long serialVersionUID = 1L;
private final VerticalLayout rootLayout = new VerticalLayout();
@Autowired
private SpringViewProvider viewProvider;
@Override
protected void init(final VaadinRequest request) {
rootLayout.setSizeFull();
final Panel viewContainer = new Panel();
viewContainer.setSizeFull();
rootLayout.addComponent(viewContainer);
rootLayout.setExpandRatio(viewContainer, 1.0F);
final Navigator navigator = new Navigator(this, viewContainer);
navigator.addProvider(viewProvider);
setContent(rootLayout);
}
}

View File

@@ -0,0 +1,341 @@
/**
* 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.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.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;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanContainer;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.FontAwesome;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.CellReference;
import com.vaadin.ui.Grid.CellStyleGenerator;
import com.vaadin.ui.Grid.SelectionMode;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.renderers.HtmlRenderer;
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 long serialVersionUID = 1L;
@Autowired
private transient SpSenderService spSenderService;
@Autowired
private transient DeviceSimulatorRepository repository;
@Autowired
private transient SimulatedDeviceFactory deviceFactory;
@Autowired
private transient EventBus eventbus;
private final Label caption = new Label("DMF/DDI Simulated Devices");
private final HorizontalLayout toolbar = new HorizontalLayout();
private final Grid grid = new Grid();
private final ComboBox responseComboBox = new ComboBox("", Lists.newArrayList(ResponseStatus.SUCCESSFUL,
ResponseStatus.ERROR));
private BeanContainer<String, AbstractSimulatedDevice> beanContainer;
@Override
public void enter(final ViewChangeEvent event) {
eventbus.register(this);
setSizeFull();
// caption
caption.addStyleName("h2");
// toolbar
createToolbar();
beanContainer = new BeanContainer<>(AbstractSimulatedDevice.class);
beanContainer.setBeanIdProperty("id");
grid.setSizeFull();
grid.setCellStyleGenerator(new CellStyleGenerator() {
@Override
public String getStyle(final CellReference cellReference) {
return cellReference.getPropertyId().equals("status") ? "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");
// header widths
grid.getColumn("status").setMaximumWidth(80);
grid.getColumn("protocol").setMaximumWidth(180);
grid.getColumn("responseStatus").setMaximumWidth(240);
grid.getColumn("nextPollCounterSec").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(new Converter<String, Protocol>() {
@Override
public Protocol convertToModel(final String value, final Class<? extends Protocol> targetType,
final Locale locale) {
return null;
}
@Override
public String convertToPresentation(final Protocol value, final Class<? extends String> targetType,
final Locale locale) {
switch (value) {
case DDI_HTTP:
return "DDI API (http)";
case DMF_AMQP:
return "DMF API (amqp)";
default:
return "unknown";
}
}
@Override
public Class<Protocol> getModelType() {
return Protocol.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
});
grid.getColumn("status").setRenderer(new HtmlRenderer(), new Converter<String, Status>() {
private static final long serialVersionUID = 1L;
@Override
public Status convertToModel(final String value, final Class<? extends Status> targetType,
final Locale locale) {
return null;
}
@Override
public String convertToPresentation(final Status value, final Class<? extends String> targetType,
final Locale locale) {
String style = null;
switch (value) {
case UNKNWON:
style = "<span class=\"v-icon grayicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"gray\";\">&#x"
+ Integer.toHexString(FontAwesome.QUESTION_CIRCLE.getCodepoint()) + ";</span>";
break;
case PEDNING:
style = "<span class=\"v-icon yellowicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"yellow\";\">&#x" + Integer.toHexString(FontAwesome.REFRESH.getCodepoint())
+ ";</span>";
break;
case FINISH:
style = "<span class=\"v-icon greenicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"green\";\">&#x"
+ Integer.toHexString(FontAwesome.CHECK_CIRCLE.getCodepoint()) + ";</span>";
break;
case ERROR:
style = "<span class=\"v-icon redicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"red\";\">&#x"
+ Integer.toHexString(FontAwesome.EXCLAMATION_CIRCLE.getCodepoint()) + ";</span>";
break;
default:
throw new IllegalStateException("unknown value");
}
return style;
}
@Override
public Class<Status> getModelType() {
return Status.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
});
grid.removeColumn("tenant");
// grid combobox
responseComboBox.setItemIcon(ResponseStatus.SUCCESSFUL, FontAwesome.CHECK_CIRCLE);
responseComboBox.setItemIcon(ResponseStatus.ERROR, FontAwesome.EXCLAMATION_CIRCLE);
responseComboBox.setNullSelectionAllowed(false);
responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
responseComboBox.addValueChangeListener(new ValueChangeListener() {
@Override
public void valueChange(final ValueChangeEvent event) {
beanContainer.getItemIds().forEach(
itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus")
.setValue(event.getProperty().getValue()));
}
});
// add all components
addComponent(caption);
addComponent(toolbar);
addComponent(grid);
setExpandRatio(grid, 1.0F);
// load beans
repository.getAll().forEach(device -> beanContainer.addBean(device));
}
@Override
public void detach() {
super.detach();
eventbus.unregister(this);
}
@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());
}
});
}
});
}
/**
* Method to retrieve {@link InitUpdate} events from the event bus.
*
* @param update
* the update event posted on the event bus
*/
@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());
}
}
});
}
/**
* Method to retrieve {@link ProgressUpdate} events from the event bus.
*
* @param update
* the update event posted on the event bus
*/
@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);
}
}
}
});
}
private void createToolbar() {
final Button createDevicesButton = new Button("generate...");
createDevicesButton.setIcon(FontAwesome.GEARS);
createDevicesButton.addClickListener(event -> openGenerateDialog());
final Button clearDevicesButton = new Button("clear");
clearDevicesButton.setIcon(FontAwesome.ERASER);
clearDevicesButton.addClickListener(event -> clearSimulatedDevices());
toolbar.addComponent(createDevicesButton);
toolbar.addComponent(clearDevicesButton);
toolbar.setSpacing(true);
}
private void clearSimulatedDevices() {
repository.clear();
beanContainer.removeAllItems();
}
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);
}
}
}));
}
}

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
*/
// Import valo after setting the parameters
@import "../valo/valo";
.simulator{
@include valo;
.yellowicon {
color: orange;
}
.greenicon {
color: green;
}
.grayicon {
color: gray;
}
.redicon {
color: red;
}
.v-grid-cell.centeralign {
text-align: center;
}
}

View File

@@ -25,7 +25,7 @@ spring.rabbitmq.virtualHost=/
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.dynamic=true
spring.rabbitmq.listener.prefetch=100
# SECURITY (SecurityProperties)
security.user.name=${BASIC_USERNAME:admin}
@@ -44,6 +44,6 @@ security.headers.frame=false
security.headers.content-type=false
security.headers.hsts=all
security.sessions=stateless
security.ignored=
security.ignored=/VAADIN/**
server.port=8083

View File

@@ -20,10 +20,11 @@ import feign.RequestLine;
/**
* Client binding for the Distribution resource of the management API.
*/
@FunctionalInterface
public interface DistributionSetResource {
/**
* Creates a list of distrbution sets.
* Creates a list of distribution sets.
*
* @param sets
* the request body java bean containing the necessary attributes

View File

@@ -51,7 +51,7 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
@Autowired(required = false)
private MongoClientOptions options;
private Mongo mongo;
private Mongo mongoConnection;
@Override
public String getDatabaseName() {
@@ -59,12 +59,12 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
}
/**
* Closes mongo client when destroyd.
* Closes mongo client when destroyed.
*/
@PreDestroy
public void close() {
if (this.mongo != null) {
this.mongo.close();
if (this.mongoConnection != null) {
this.mongoConnection.close();
}
}
@@ -76,14 +76,14 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
if (properties.getPort() != null) {
LOG.debug("Create mongo by properties (host: {}, port: {})", uri.getHosts().get(0), properties.getPort());
this.mongo = new MongoClient(Arrays.asList(new ServerAddress(uri.getHosts().get(0), properties.getPort())),
uri.getOptions());
this.mongoConnection = new MongoClient(
Arrays.asList(new ServerAddress(uri.getHosts().get(0), properties.getPort())), uri.getOptions());
} else {
LOG.debug("Create mongo by URI : {}", uri);
this.mongo = new MongoClient(uri);
this.mongoConnection = new MongoClient(uri);
}
return this.mongo;
return this.mongoConnection;
}
/*

View File

@@ -233,7 +233,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
* checks if the {@link #forcedTime} is hit by the given
* {@code hitTimeMillis}, by means if the given milliseconds are greater
* than the forcedTime.
*
*
* @param hitTimeMillis
* the milliseconds, mostly the
* {@link System#currentTimeMillis()}
@@ -274,7 +274,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#toString()
*/
@Override
@@ -284,11 +284,11 @@ public class Action extends BaseEntity implements Comparable<Action> {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((actionType == null) ? 0 : actionType.hashCode());
@@ -301,12 +301,12 @@ public class Action extends BaseEntity implements Comparable<Action> {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is generated
// code
if (this == obj) {
return true;
}
@@ -384,7 +384,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
/**
* The action type for this action relation.
*
*
*
*
*

View File

@@ -71,7 +71,7 @@ public class DistributionSetTag extends Tag {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
@@ -84,11 +84,11 @@ public class DistributionSetTag extends Tag {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
public boolean equals(final Object obj) { // NOSONAR - as this is generated
if (this == obj) {
return true;
}

View File

@@ -129,11 +129,11 @@ public class ExternalArtifact extends Artifact {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
@@ -142,11 +142,11 @@ public class ExternalArtifact extends Artifact {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
public boolean equals(final Object obj) { // NOSONAR - as this is generated
if (this == obj) {
return true;
}

View File

@@ -92,11 +92,11 @@ public class ExternalArtifactProvider extends NamedEntity {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
@@ -105,11 +105,11 @@ public class ExternalArtifactProvider extends NamedEntity {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
public boolean equals(final Object obj) { // NOSONAR - as this is generated
if (this == obj) {
return true;
}

View File

@@ -75,11 +75,11 @@ public class LocalArtifact extends Artifact {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
@@ -88,11 +88,11 @@ public class LocalArtifact extends Artifact {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
public boolean equals(final Object obj) { // NOSONAR - as this is generated
if (this == obj) {
return true;
}

View File

@@ -257,7 +257,7 @@ public class SoftwareModule extends NamedVersionedEntity {
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
@@ -270,7 +270,7 @@ public class SoftwareModule extends NamedVersionedEntity {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
public boolean equals(final Object obj) { // NOSONAR - as this is generated
if (this == obj) {
return true;
}

View File

@@ -32,6 +32,7 @@ import com.vaadin.spring.boot.internal.VaadinServletConfigurationProperties;
@Import(VaadinServletConfiguration.class)
public class AsyncVaadinServletConfiguration extends VaadinServletConfiguration {
@Override
@Bean
protected ServletRegistrationBean vaadinServletRegistration() {
return createServletRegistrationBean();

View File

@@ -72,7 +72,7 @@ public class CustomFile implements Serializable {
/**
* Initialize details.
*
*
* @param fileName
* uploaded file name
* @param baseSoftwareModuleName
@@ -138,7 +138,7 @@ public class CustomFile implements Serializable {
}
/**
*
*
* @return the isValid
*/
public Boolean getIsValid() {
@@ -170,11 +170,11 @@ public class CustomFile implements Serializable {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = 1;
result = prime * result + (fileName == null ? 0 : fileName.hashCode());
@@ -183,7 +183,7 @@ public class CustomFile implements Serializable {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override

View File

@@ -8,9 +8,6 @@
*/
package org.eclipse.hawkbit.ui.management.dstable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -39,8 +36,6 @@ import com.google.common.base.Strings;
* Simple implementation of generics bean query which dynamically loads a batch
* of beans.
*
*
*
*/
public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
@@ -55,7 +50,7 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
/**
* Bean query for retrieving beans/objects of type.
*
*
* @param definition
* query definition
* @param queryConfig
@@ -92,8 +87,9 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
/**
* Load all the Distribution set.
*
* @parm startIndex as page start
*
* @param startIndex
* as page start
* @param count
* as total data
*/
@@ -193,15 +189,4 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
return distributionSetManagement;
}
private void writeObject(final ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(firstPageDistributionSets);
}
@SuppressWarnings("unchecked")
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
firstPageDistributionSets = (Page<DistributionSet>) in.readObject();
}
}

View File

@@ -14,6 +14,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
@@ -62,15 +63,13 @@ import com.vaadin.ui.Upload.SucceededListener;
/**
* Bulk target upload handler.
*
*
*
*/
public class BulkUploadHandler extends CustomComponent
implements SucceededListener, FailedListener, Receiver, StartedListener {
/**
* *
*
*/
private static final long serialVersionUID = -1273494705754674501L;
private static final Logger LOG = LoggerFactory.getLogger(BulkUploadHandler.class);
@@ -103,7 +102,7 @@ public class BulkUploadHandler extends CustomComponent
final TargetBulkUpdateWindowLayout targetBulkUpdateWindowLayout;
/**
*
*
* @param targetBulkUpdateWindowLayout
* @param targetManagement
* @param managementUIState
@@ -152,7 +151,7 @@ public class BulkUploadHandler extends CustomComponent
/*
* (non-Javadoc)
*
*
* @see com.vaadin.ui.Upload.Receiver#receiveUpload(java.lang.String,
* java.lang.String)
*/
@@ -173,7 +172,7 @@ public class BulkUploadHandler extends CustomComponent
/*
* (non-Javadoc)
*
*
* @see
* com.vaadin.ui.Upload.FailedListener#uploadFailed(com.vaadin.ui.Upload.
* FailedEvent)
@@ -185,7 +184,7 @@ public class BulkUploadHandler extends CustomComponent
/*
* (non-Javadoc)
*
*
* @see
* com.vaadin.ui.Upload.SucceededListener#uploadSucceeded(com.vaadin.ui.
* Upload.SucceededEvent)
@@ -199,7 +198,7 @@ public class BulkUploadHandler extends CustomComponent
final SucceededEvent event;
/**
*
*
* @param event
*/
public UploadAsync(final SucceededEvent event) {
@@ -208,15 +207,18 @@ public class BulkUploadHandler extends CustomComponent
@Override
public void run() {
BufferedReader reader = null;
long innerCounter = 0;
String line;
if (tempFile != null) {
try {
if (tempFile == null) {
return;
}
try (InputStream tempStream = new FileInputStream(tempFile)) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(tempStream, Charset.defaultCharset()))) {
LOG.info("Bulk file upload started");
final double totalFileSize = getTotalNumberOfLines();
reader = new BufferedReader(
new InputStreamReader(new FileInputStream(tempFile), Charset.defaultCharset()));
/**
* Once control is in upload succeeded method automatically
* upload button is re-enabled. To disable the button firing
@@ -232,21 +234,16 @@ public class BulkUploadHandler extends CustomComponent
// Clearing after assignments are done
managementUIState.getTargetTableFilters().getBulkUpload().getTargetsCreated().clear();
} catch (final FileNotFoundException e) {
LOG.error("File not found with name {}", tempFile.getName(), e);
} catch (final IOException e) {
LOG.error("Error reading file {}", tempFile.getName(), e);
} finally {
try {
if (null != reader) {
reader.close();
resetCounts();
deleteFile();
}
} catch (final IOException e) {
LOG.error("Error while reading file ", e);
}
resetCounts();
deleteFile();
}
} catch (final FileNotFoundException e) {
LOG.error("Temporary file not found with name {}", tempFile.getName(), e);
} catch (final IOException e) {
LOG.error("Error while opening temorary file ", e);
}
}
@@ -307,24 +304,19 @@ public class BulkUploadHandler extends CustomComponent
}
private double getTotalNumberOfLines() {
InputStreamReader inputStreamReader;
BufferedReader readerForSize = null;
double totalFileSize = 0;
try {
inputStreamReader = new InputStreamReader(new FileInputStream(tempFile), Charset.defaultCharset());
readerForSize = new BufferedReader(inputStreamReader);
totalFileSize = readerForSize.lines().count();
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(tempFile),
Charset.defaultCharset())) {
try (BufferedReader readerForSize = new BufferedReader(inputStreamReader)) {
totalFileSize = readerForSize.lines().count();
}
} catch (final FileNotFoundException e) {
LOG.error("Error reading file {}", tempFile.getName(), e);
} finally {
if (readerForSize != null) {
try {
readerForSize.close();
} catch (final IOException e) {
LOG.error("Error while closing reader of file {}", tempFile.getName(), e);
}
}
} catch (final IOException e) {
LOG.error("Error while closing reader of file {}", tempFile.getName(), e);
}
return totalFileSize;
}
@@ -449,7 +441,7 @@ public class BulkUploadHandler extends CustomComponent
private static class NullOutputStream extends OutputStream {
/**
* null output stream.
*
*
* @param i
* byte
*/
@@ -468,7 +460,7 @@ public class BulkUploadHandler extends CustomComponent
/*
* (non-Javadoc)
*
*
* @see
* com.vaadin.ui.Upload.StartedListener#uploadStarted(com.vaadin.ui.Upload
* .StartedEvent)