Merge remote-tracking branch 'eclipse/master'
This commit is contained in:
@@ -20,6 +20,25 @@ The simulator has user authentication enabled by default. Default credentials:
|
|||||||
This can be configured/disabled by spring boot properties
|
This can be configured/disabled by spring boot properties
|
||||||
|
|
||||||
## Usage
|
## 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`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### REST API
|
||||||
The device simulator exposes an REST-API which can be used to trigger device creation.
|
The device simulator exposes an REST-API which can be used to trigger device creation.
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
|||||||
@@ -80,10 +80,52 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||||
</dependency>
|
</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>
|
</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>
|
</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 |
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,8 +8,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator;
|
package org.eclipse.hawkbit.simulator;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
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.
|
* The main-method to start the Spring-Boot application.
|
||||||
@@ -18,12 +25,21 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableVaadin
|
||||||
public class DeviceSimulator {
|
public class DeviceSimulator {
|
||||||
|
|
||||||
private DeviceSimulator() {
|
public DeviceSimulator() {
|
||||||
// utility class
|
// 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.
|
* Start the Spring Boot Application.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator;
|
package org.eclipse.hawkbit.simulator;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -26,6 +27,12 @@ public class SimulationController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SpSenderService spSenderService;
|
private SpSenderService spSenderService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DeviceSimulatorRepository repository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SimulatedDeviceFactory deviceFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The start resource to start a device creation.
|
* The start resource to start a device creation.
|
||||||
*
|
*
|
||||||
@@ -43,7 +50,9 @@ public class SimulationController {
|
|||||||
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
|
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
|
||||||
|
|
||||||
for (int i = 0; i < amount; i++) {
|
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!";
|
return "Updated " + amount + " DMF connected targets!";
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ public class AmqpConfiguration {
|
|||||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
||||||
containerFactory.setDefaultRequeueRejected(false);
|
containerFactory.setDefaultRequeueRejected(false);
|
||||||
containerFactory.setConnectionFactory(connectionFactory);
|
containerFactory.setConnectionFactory(connectionFactory);
|
||||||
|
containerFactory.setConcurrentConsumers(20);
|
||||||
|
containerFactory.setMaxConcurrentConsumers(20);
|
||||||
|
containerFactory.setPrefetchCount(20);
|
||||||
return containerFactory;
|
return containerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.amqp.api.MessageType;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
|
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
@@ -40,6 +43,8 @@ public class SpReceiverService extends ReceiverService {
|
|||||||
|
|
||||||
private final SpSenderService spSenderService;
|
private final SpSenderService spSenderService;
|
||||||
|
|
||||||
|
private final DeviceSimulatorUpdater deviceUpdater;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
@@ -51,12 +56,15 @@ public class SpReceiverService extends ReceiverService {
|
|||||||
* the lwm2mSenderService
|
* the lwm2mSenderService
|
||||||
* @param spSenderService
|
* @param spSenderService
|
||||||
* the spSenderService
|
* the spSenderService
|
||||||
|
* @param deviceUpdater
|
||||||
|
* the updater service to simulate update process
|
||||||
*/
|
*/
|
||||||
@Autowired
|
@Autowired
|
||||||
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
|
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
|
||||||
final SpSenderService spSenderService) {
|
final SpSenderService spSenderService, final DeviceSimulatorUpdater deviceUpdater) {
|
||||||
super(rabbitTemplate, amqpProperties);
|
super(rabbitTemplate, amqpProperties);
|
||||||
this.spSenderService = spSenderService;
|
this.spSenderService = spSenderService;
|
||||||
|
this.deviceUpdater = deviceUpdater;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,16 +147,23 @@ public class SpReceiverService extends ReceiverService {
|
|||||||
|
|
||||||
spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
|
spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
|
||||||
"device Simulator retrieved update request. proceeding with simulation.", actionId);
|
"device Simulator retrieved update request. proceeding with simulation.", actionId);
|
||||||
|
deviceUpdater.startUpdate(tenant, thingId, actionId, downloadAndUpdateRequest.getSoftwareModules().get(0)
|
||||||
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
|
.getModuleVersion(), new UpdaterCallback() {
|
||||||
|
@Override
|
||||||
try {
|
public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
|
||||||
Thread.sleep(1_000);
|
switch (device.getResponseStatus()) {
|
||||||
} catch (final InterruptedException e) {
|
case SUCCESSFUL:
|
||||||
LOGGER.error("Sleep interrupted", e);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
spSenderService.finishUpdateProcess(update, "Simulation complete!");
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ spring.rabbitmq.virtualHost=/
|
|||||||
spring.rabbitmq.host=localhost
|
spring.rabbitmq.host=localhost
|
||||||
spring.rabbitmq.port=5672
|
spring.rabbitmq.port=5672
|
||||||
spring.rabbitmq.dynamic=true
|
spring.rabbitmq.dynamic=true
|
||||||
|
spring.rabbitmq.listener.prefetch=100
|
||||||
|
|
||||||
# SECURITY (SecurityProperties)
|
# SECURITY (SecurityProperties)
|
||||||
security.user.name=${BASIC_USERNAME:admin}
|
security.user.name=${BASIC_USERNAME:admin}
|
||||||
@@ -44,6 +44,6 @@ security.headers.frame=false
|
|||||||
security.headers.content-type=false
|
security.headers.content-type=false
|
||||||
security.headers.hsts=all
|
security.headers.hsts=all
|
||||||
security.sessions=stateless
|
security.sessions=stateless
|
||||||
security.ignored=
|
security.ignored=/VAADIN/**
|
||||||
|
|
||||||
server.port=8083
|
server.port=8083
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ import feign.RequestLine;
|
|||||||
/**
|
/**
|
||||||
* Client binding for the Distribution resource of the management API.
|
* Client binding for the Distribution resource of the management API.
|
||||||
*/
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
public interface DistributionSetResource {
|
public interface DistributionSetResource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a list of distrbution sets.
|
* Creates a list of distribution sets.
|
||||||
*
|
*
|
||||||
* @param sets
|
* @param sets
|
||||||
* the request body java bean containing the necessary attributes
|
* the request body java bean containing the necessary attributes
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
|||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private MongoClientOptions options;
|
private MongoClientOptions options;
|
||||||
|
|
||||||
private Mongo mongo;
|
private Mongo mongoConnection;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDatabaseName() {
|
public String getDatabaseName() {
|
||||||
@@ -59,12 +59,12 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes mongo client when destroyd.
|
* Closes mongo client when destroyed.
|
||||||
*/
|
*/
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void close() {
|
public void close() {
|
||||||
if (this.mongo != null) {
|
if (this.mongoConnection != null) {
|
||||||
this.mongo.close();
|
this.mongoConnection.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,14 +76,14 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
|||||||
|
|
||||||
if (properties.getPort() != null) {
|
if (properties.getPort() != null) {
|
||||||
LOG.debug("Create mongo by properties (host: {}, port: {})", uri.getHosts().get(0), properties.getPort());
|
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())),
|
this.mongoConnection = new MongoClient(
|
||||||
uri.getOptions());
|
Arrays.asList(new ServerAddress(uri.getHosts().get(0), properties.getPort())), uri.getOptions());
|
||||||
} else {
|
} else {
|
||||||
LOG.debug("Create mongo by URI : {}", uri);
|
LOG.debug("Create mongo by URI : {}", uri);
|
||||||
this.mongo = new MongoClient(uri);
|
this.mongoConnection = new MongoClient(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.mongo;
|
return this.mongoConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
|||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() { // NOSONAR - as this is generated
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + ((actionType == null) ? 0 : actionType.hashCode());
|
result = prime * result + ((actionType == null) ? 0 : actionType.hashCode());
|
||||||
@@ -306,7 +306,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||||
// code
|
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class DistributionSetTag extends Tag {
|
|||||||
* @see java.lang.Object#equals(java.lang.Object)
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class ExternalArtifact extends Artifact {
|
|||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() { // NOSONAR - as this is generated
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + this.getClass().getName().hashCode();
|
result = prime * result + this.getClass().getName().hashCode();
|
||||||
@@ -146,7 +146,7 @@ public class ExternalArtifact extends Artifact {
|
|||||||
* @see java.lang.Object#equals(java.lang.Object)
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class ExternalArtifactProvider extends NamedEntity {
|
|||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() { // NOSONAR - as this is generated
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + this.getClass().getName().hashCode();
|
result = prime * result + this.getClass().getName().hashCode();
|
||||||
@@ -109,7 +109,7 @@ public class ExternalArtifactProvider extends NamedEntity {
|
|||||||
* @see java.lang.Object#equals(java.lang.Object)
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class LocalArtifact extends Artifact {
|
|||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() { // NOSONAR - as this is generated
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + this.getClass().getName().hashCode();
|
result = prime * result + this.getClass().getName().hashCode();
|
||||||
@@ -92,7 +92,7 @@ public class LocalArtifact extends Artifact {
|
|||||||
* @see java.lang.Object#equals(java.lang.Object)
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ public class SoftwareModule extends NamedVersionedEntity {
|
|||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() { // NOSONAR - as this is generated
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + this.getClass().getName().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)
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.vaadin.spring.boot.internal.VaadinServletConfigurationProperties;
|
|||||||
@Import(VaadinServletConfiguration.class)
|
@Import(VaadinServletConfiguration.class)
|
||||||
public class AsyncVaadinServletConfiguration extends VaadinServletConfiguration {
|
public class AsyncVaadinServletConfiguration extends VaadinServletConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean
|
@Bean
|
||||||
protected ServletRegistrationBean vaadinServletRegistration() {
|
protected ServletRegistrationBean vaadinServletRegistration() {
|
||||||
return createServletRegistrationBean();
|
return createServletRegistrationBean();
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ public class CustomFile implements Serializable {
|
|||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() { // NOSONAR - as this is generated
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + (fileName == null ? 0 : fileName.hashCode());
|
result = prime * result + (fileName == null ? 0 : fileName.hashCode());
|
||||||
|
|||||||
@@ -8,9 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.management.dstable;
|
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.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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
|
* Simple implementation of generics bean query which dynamically loads a batch
|
||||||
* of beans.
|
* of beans.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
||||||
|
|
||||||
@@ -93,7 +88,8 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
|
|||||||
/**
|
/**
|
||||||
* Load all the Distribution set.
|
* Load all the Distribution set.
|
||||||
*
|
*
|
||||||
* @parm startIndex as page start
|
* @param startIndex
|
||||||
|
* as page start
|
||||||
* @param count
|
* @param count
|
||||||
* as total data
|
* as total data
|
||||||
*/
|
*/
|
||||||
@@ -193,15 +189,4 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
|
|||||||
return distributionSetManagement;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -63,14 +64,12 @@ import com.vaadin.ui.Upload.SucceededListener;
|
|||||||
/**
|
/**
|
||||||
* Bulk target upload handler.
|
* Bulk target upload handler.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class BulkUploadHandler extends CustomComponent
|
public class BulkUploadHandler extends CustomComponent
|
||||||
implements SucceededListener, FailedListener, Receiver, StartedListener {
|
implements SucceededListener, FailedListener, Receiver, StartedListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* *
|
*
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -1273494705754674501L;
|
private static final long serialVersionUID = -1273494705754674501L;
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(BulkUploadHandler.class);
|
private static final Logger LOG = LoggerFactory.getLogger(BulkUploadHandler.class);
|
||||||
@@ -208,15 +207,18 @@ public class BulkUploadHandler extends CustomComponent
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
BufferedReader reader = null;
|
|
||||||
long innerCounter = 0;
|
long innerCounter = 0;
|
||||||
String line;
|
String line;
|
||||||
if (tempFile != null) {
|
if (tempFile == null) {
|
||||||
try {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream tempStream = new FileInputStream(tempFile)) {
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(tempStream, Charset.defaultCharset()))) {
|
||||||
LOG.info("Bulk file upload started");
|
LOG.info("Bulk file upload started");
|
||||||
final double totalFileSize = getTotalNumberOfLines();
|
final double totalFileSize = getTotalNumberOfLines();
|
||||||
reader = new BufferedReader(
|
|
||||||
new InputStreamReader(new FileInputStream(tempFile), Charset.defaultCharset()));
|
|
||||||
/**
|
/**
|
||||||
* Once control is in upload succeeded method automatically
|
* Once control is in upload succeeded method automatically
|
||||||
* upload button is re-enabled. To disable the button firing
|
* upload button is re-enabled. To disable the button firing
|
||||||
@@ -232,21 +234,16 @@ public class BulkUploadHandler extends CustomComponent
|
|||||||
|
|
||||||
// Clearing after assignments are done
|
// Clearing after assignments are done
|
||||||
managementUIState.getTargetTableFilters().getBulkUpload().getTargetsCreated().clear();
|
managementUIState.getTargetTableFilters().getBulkUpload().getTargetsCreated().clear();
|
||||||
} catch (final FileNotFoundException e) {
|
|
||||||
LOG.error("File not found with name {}", tempFile.getName(), e);
|
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
LOG.error("Error reading file {}", tempFile.getName(), e);
|
LOG.error("Error reading file {}", tempFile.getName(), e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
|
||||||
if (null != reader) {
|
|
||||||
reader.close();
|
|
||||||
resetCounts();
|
resetCounts();
|
||||||
deleteFile();
|
deleteFile();
|
||||||
}
|
}
|
||||||
|
} catch (final FileNotFoundException e) {
|
||||||
|
LOG.error("Temporary file not found with name {}", tempFile.getName(), e);
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
LOG.error("Error while reading file ", e);
|
LOG.error("Error while opening temorary file ", e);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -307,24 +304,19 @@ public class BulkUploadHandler extends CustomComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
private double getTotalNumberOfLines() {
|
private double getTotalNumberOfLines() {
|
||||||
InputStreamReader inputStreamReader;
|
|
||||||
BufferedReader readerForSize = null;
|
|
||||||
double totalFileSize = 0;
|
double totalFileSize = 0;
|
||||||
try {
|
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(tempFile),
|
||||||
inputStreamReader = new InputStreamReader(new FileInputStream(tempFile), Charset.defaultCharset());
|
Charset.defaultCharset())) {
|
||||||
readerForSize = new BufferedReader(inputStreamReader);
|
try (BufferedReader readerForSize = new BufferedReader(inputStreamReader)) {
|
||||||
totalFileSize = readerForSize.lines().count();
|
totalFileSize = readerForSize.lines().count();
|
||||||
|
}
|
||||||
} catch (final FileNotFoundException e) {
|
} catch (final FileNotFoundException e) {
|
||||||
LOG.error("Error reading file {}", tempFile.getName(), e);
|
LOG.error("Error reading file {}", tempFile.getName(), e);
|
||||||
} finally {
|
|
||||||
if (readerForSize != null) {
|
|
||||||
try {
|
|
||||||
readerForSize.close();
|
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
LOG.error("Error while closing reader of file {}", tempFile.getName(), e);
|
LOG.error("Error while closing reader of file {}", tempFile.getName(), e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return totalFileSize;
|
return totalFileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user