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
|
||||
|
||||
## 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.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@@ -80,10 +80,52 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vaadin</groupId>
|
||||
<artifactId>vaadin-spring-boot-starter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vaadin</groupId>
|
||||
<artifactId>vaadin-push</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-jackson</artifactId>
|
||||
<version>8.14.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<version>8.12.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.vaadin</groupId>
|
||||
<artifactId>vaadin-bom</artifactId>
|
||||
<version>7.5.5</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
@@ -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;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.vaadin.spring.annotation.EnableVaadin;
|
||||
|
||||
/**
|
||||
* The main-method to start the Spring-Boot application.
|
||||
@@ -18,12 +25,21 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableVaadin
|
||||
public class DeviceSimulator {
|
||||
|
||||
private DeviceSimulator() {
|
||||
public DeviceSimulator() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an asynchronous event bus to publish and retrieve events.
|
||||
*/
|
||||
@Bean
|
||||
public EventBus eventBus() {
|
||||
return new AsyncEventBus(Executors.newFixedThreadPool(4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the Spring Boot Application.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -26,6 +27,12 @@ public class SimulationController {
|
||||
@Autowired
|
||||
private SpSenderService spSenderService;
|
||||
|
||||
@Autowired
|
||||
private DeviceSimulatorRepository repository;
|
||||
|
||||
@Autowired
|
||||
private SimulatedDeviceFactory deviceFactory;
|
||||
|
||||
/**
|
||||
* The start resource to start a device creation.
|
||||
*
|
||||
@@ -43,7 +50,9 @@ public class SimulationController {
|
||||
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
spSenderService.createOrUpdateThing(tenant, name + i);
|
||||
final String deviceId = name + i;
|
||||
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, Protocol.DMF_AMQP));
|
||||
spSenderService.createOrUpdateThing(tenant, deviceId);
|
||||
}
|
||||
|
||||
return "Updated " + amount + " DMF connected targets!";
|
||||
|
||||
@@ -132,6 +132,9 @@ public class AmqpConfiguration {
|
||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
||||
containerFactory.setDefaultRequeueRejected(false);
|
||||
containerFactory.setConnectionFactory(connectionFactory);
|
||||
containerFactory.setConcurrentConsumers(20);
|
||||
containerFactory.setMaxConcurrentConsumers(20);
|
||||
containerFactory.setPrefetchCount(20);
|
||||
return containerFactory;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater;
|
||||
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Message;
|
||||
@@ -40,6 +43,8 @@ public class SpReceiverService extends ReceiverService {
|
||||
|
||||
private final SpSenderService spSenderService;
|
||||
|
||||
private final DeviceSimulatorUpdater deviceUpdater;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -51,12 +56,15 @@ public class SpReceiverService extends ReceiverService {
|
||||
* the lwm2mSenderService
|
||||
* @param spSenderService
|
||||
* the spSenderService
|
||||
* @param deviceUpdater
|
||||
* the updater service to simulate update process
|
||||
*/
|
||||
@Autowired
|
||||
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
|
||||
final SpSenderService spSenderService) {
|
||||
final SpSenderService spSenderService, final DeviceSimulatorUpdater deviceUpdater) {
|
||||
super(rabbitTemplate, amqpProperties);
|
||||
this.spSenderService = spSenderService;
|
||||
this.deviceUpdater = deviceUpdater;
|
||||
|
||||
}
|
||||
|
||||
@@ -139,16 +147,23 @@ public class SpReceiverService extends ReceiverService {
|
||||
|
||||
spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
|
||||
"device Simulator retrieved update request. proceeding with simulation.", actionId);
|
||||
|
||||
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
|
||||
|
||||
try {
|
||||
Thread.sleep(1_000);
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.error("Sleep interrupted", e);
|
||||
}
|
||||
|
||||
spSenderService.finishUpdateProcess(update, "Simulation complete!");
|
||||
deviceUpdater.startUpdate(tenant, thingId, actionId, downloadAndUpdateRequest.getSoftwareModules().get(0)
|
||||
.getModuleVersion(), new UpdaterCallback() {
|
||||
@Override
|
||||
public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
|
||||
switch (device.getResponseStatus()) {
|
||||
case SUCCESSFUL:
|
||||
spSenderService.finishUpdateProcess(new SimulatedUpdate(device.getTenant(), device.getId(),
|
||||
actionId), "Simulation complete!");
|
||||
break;
|
||||
case ERROR:
|
||||
spSenderService.finishUpdateProcessWithError(new SimulatedUpdate(device.getTenant(),
|
||||
device.getId(), actionId), "Simulation complete with error!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.port=5672
|
||||
spring.rabbitmq.dynamic=true
|
||||
|
||||
spring.rabbitmq.listener.prefetch=100
|
||||
|
||||
# SECURITY (SecurityProperties)
|
||||
security.user.name=${BASIC_USERNAME:admin}
|
||||
@@ -44,6 +44,6 @@ security.headers.frame=false
|
||||
security.headers.content-type=false
|
||||
security.headers.hsts=all
|
||||
security.sessions=stateless
|
||||
security.ignored=
|
||||
security.ignored=/VAADIN/**
|
||||
|
||||
server.port=8083
|
||||
|
||||
@@ -20,10 +20,11 @@ import feign.RequestLine;
|
||||
/**
|
||||
* Client binding for the Distribution resource of the management API.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DistributionSetResource {
|
||||
|
||||
/**
|
||||
* Creates a list of distrbution sets.
|
||||
* Creates a list of distribution sets.
|
||||
*
|
||||
* @param sets
|
||||
* the request body java bean containing the necessary attributes
|
||||
|
||||
@@ -51,7 +51,7 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
||||
@Autowired(required = false)
|
||||
private MongoClientOptions options;
|
||||
|
||||
private Mongo mongo;
|
||||
private Mongo mongoConnection;
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
@@ -59,12 +59,12 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes mongo client when destroyd.
|
||||
* Closes mongo client when destroyed.
|
||||
*/
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (this.mongo != null) {
|
||||
this.mongo.close();
|
||||
if (this.mongoConnection != null) {
|
||||
this.mongoConnection.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,14 +76,14 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
||||
|
||||
if (properties.getPort() != null) {
|
||||
LOG.debug("Create mongo by properties (host: {}, port: {})", uri.getHosts().get(0), properties.getPort());
|
||||
this.mongo = new MongoClient(Arrays.asList(new ServerAddress(uri.getHosts().get(0), properties.getPort())),
|
||||
uri.getOptions());
|
||||
this.mongoConnection = new MongoClient(
|
||||
Arrays.asList(new ServerAddress(uri.getHosts().get(0), properties.getPort())), uri.getOptions());
|
||||
} else {
|
||||
LOG.debug("Create mongo by URI : {}", uri);
|
||||
this.mongo = new MongoClient(uri);
|
||||
this.mongoConnection = new MongoClient(uri);
|
||||
}
|
||||
|
||||
return this.mongo;
|
||||
return this.mongoConnection;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -233,7 +233,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
* checks if the {@link #forcedTime} is hit by the given
|
||||
* {@code hitTimeMillis}, by means if the given milliseconds are greater
|
||||
* than the forcedTime.
|
||||
*
|
||||
*
|
||||
* @param hitTimeMillis
|
||||
* the milliseconds, mostly the
|
||||
* {@link System#currentTimeMillis()}
|
||||
@@ -274,7 +274,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
@@ -284,11 +284,11 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((actionType == null) ? 0 : actionType.hashCode());
|
||||
@@ -301,12 +301,12 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
// code
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
@@ -384,7 +384,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
|
||||
/**
|
||||
* The action type for this action relation.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
|
||||
@@ -71,7 +71,7 @@ public class DistributionSetTag extends Tag {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
@@ -84,11 +84,11 @@ public class DistributionSetTag extends Tag {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -129,11 +129,11 @@ public class ExternalArtifact extends Artifact {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
@@ -142,11 +142,11 @@ public class ExternalArtifact extends Artifact {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,11 +92,11 @@ public class ExternalArtifactProvider extends NamedEntity {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
@@ -105,11 +105,11 @@ public class ExternalArtifactProvider extends NamedEntity {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -75,11 +75,11 @@ public class LocalArtifact extends Artifact {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
@@ -88,11 +88,11 @@ public class LocalArtifact extends Artifact {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ public class SoftwareModule extends NamedVersionedEntity {
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
@@ -270,7 +270,7 @@ public class SoftwareModule extends NamedVersionedEntity {
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.vaadin.spring.boot.internal.VaadinServletConfigurationProperties;
|
||||
@Import(VaadinServletConfiguration.class)
|
||||
public class AsyncVaadinServletConfiguration extends VaadinServletConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
protected ServletRegistrationBean vaadinServletRegistration() {
|
||||
return createServletRegistrationBean();
|
||||
|
||||
@@ -72,7 +72,7 @@ public class CustomFile implements Serializable {
|
||||
|
||||
/**
|
||||
* Initialize details.
|
||||
*
|
||||
*
|
||||
* @param fileName
|
||||
* uploaded file name
|
||||
* @param baseSoftwareModuleName
|
||||
@@ -138,7 +138,7 @@ public class CustomFile implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return the isValid
|
||||
*/
|
||||
public Boolean getIsValid() {
|
||||
@@ -170,11 +170,11 @@ public class CustomFile implements Serializable {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (fileName == null ? 0 : fileName.hashCode());
|
||||
@@ -183,7 +183,7 @@ public class CustomFile implements Serializable {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.management.dstable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -39,8 +36,6 @@ import com.google.common.base.Strings;
|
||||
* Simple implementation of generics bean query which dynamically loads a batch
|
||||
* of beans.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
|
||||
|
||||
@@ -55,7 +50,7 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
|
||||
|
||||
/**
|
||||
* Bean query for retrieving beans/objects of type.
|
||||
*
|
||||
*
|
||||
* @param definition
|
||||
* query definition
|
||||
* @param queryConfig
|
||||
@@ -92,8 +87,9 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
|
||||
|
||||
/**
|
||||
* Load all the Distribution set.
|
||||
*
|
||||
* @parm startIndex as page start
|
||||
*
|
||||
* @param startIndex
|
||||
* as page start
|
||||
* @param count
|
||||
* as total data
|
||||
*/
|
||||
@@ -193,15 +189,4 @@ public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution>
|
||||
return distributionSetManagement;
|
||||
}
|
||||
|
||||
private void writeObject(final ObjectOutputStream out) throws IOException {
|
||||
out.defaultWriteObject();
|
||||
out.writeObject(firstPageDistributionSets);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
firstPageDistributionSets = (Page<DistributionSet>) in.readObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -62,15 +63,13 @@ import com.vaadin.ui.Upload.SucceededListener;
|
||||
|
||||
/**
|
||||
* Bulk target upload handler.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class BulkUploadHandler extends CustomComponent
|
||||
implements SucceededListener, FailedListener, Receiver, StartedListener {
|
||||
|
||||
/**
|
||||
* *
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -1273494705754674501L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BulkUploadHandler.class);
|
||||
@@ -103,7 +102,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
final TargetBulkUpdateWindowLayout targetBulkUpdateWindowLayout;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param targetBulkUpdateWindowLayout
|
||||
* @param targetManagement
|
||||
* @param managementUIState
|
||||
@@ -152,7 +151,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see com.vaadin.ui.Upload.Receiver#receiveUpload(java.lang.String,
|
||||
* java.lang.String)
|
||||
*/
|
||||
@@ -173,7 +172,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* com.vaadin.ui.Upload.FailedListener#uploadFailed(com.vaadin.ui.Upload.
|
||||
* FailedEvent)
|
||||
@@ -185,7 +184,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* com.vaadin.ui.Upload.SucceededListener#uploadSucceeded(com.vaadin.ui.
|
||||
* Upload.SucceededEvent)
|
||||
@@ -199,7 +198,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
final SucceededEvent event;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
public UploadAsync(final SucceededEvent event) {
|
||||
@@ -208,15 +207,18 @@ public class BulkUploadHandler extends CustomComponent
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
BufferedReader reader = null;
|
||||
long innerCounter = 0;
|
||||
String line;
|
||||
if (tempFile != null) {
|
||||
try {
|
||||
if (tempFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream tempStream = new FileInputStream(tempFile)) {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(tempStream, Charset.defaultCharset()))) {
|
||||
LOG.info("Bulk file upload started");
|
||||
final double totalFileSize = getTotalNumberOfLines();
|
||||
reader = new BufferedReader(
|
||||
new InputStreamReader(new FileInputStream(tempFile), Charset.defaultCharset()));
|
||||
|
||||
/**
|
||||
* Once control is in upload succeeded method automatically
|
||||
* upload button is re-enabled. To disable the button firing
|
||||
@@ -232,21 +234,16 @@ public class BulkUploadHandler extends CustomComponent
|
||||
|
||||
// Clearing after assignments are done
|
||||
managementUIState.getTargetTableFilters().getBulkUpload().getTargetsCreated().clear();
|
||||
} catch (final FileNotFoundException e) {
|
||||
LOG.error("File not found with name {}", tempFile.getName(), e);
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Error reading file {}", tempFile.getName(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (null != reader) {
|
||||
reader.close();
|
||||
resetCounts();
|
||||
deleteFile();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Error while reading file ", e);
|
||||
}
|
||||
resetCounts();
|
||||
deleteFile();
|
||||
}
|
||||
} catch (final FileNotFoundException e) {
|
||||
LOG.error("Temporary file not found with name {}", tempFile.getName(), e);
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Error while opening temorary file ", e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -307,24 +304,19 @@ public class BulkUploadHandler extends CustomComponent
|
||||
}
|
||||
|
||||
private double getTotalNumberOfLines() {
|
||||
InputStreamReader inputStreamReader;
|
||||
BufferedReader readerForSize = null;
|
||||
|
||||
double totalFileSize = 0;
|
||||
try {
|
||||
inputStreamReader = new InputStreamReader(new FileInputStream(tempFile), Charset.defaultCharset());
|
||||
readerForSize = new BufferedReader(inputStreamReader);
|
||||
totalFileSize = readerForSize.lines().count();
|
||||
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(tempFile),
|
||||
Charset.defaultCharset())) {
|
||||
try (BufferedReader readerForSize = new BufferedReader(inputStreamReader)) {
|
||||
totalFileSize = readerForSize.lines().count();
|
||||
}
|
||||
} catch (final FileNotFoundException e) {
|
||||
LOG.error("Error reading file {}", tempFile.getName(), e);
|
||||
} finally {
|
||||
if (readerForSize != null) {
|
||||
try {
|
||||
readerForSize.close();
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Error while closing reader of file {}", tempFile.getName(), e);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Error while closing reader of file {}", tempFile.getName(), e);
|
||||
}
|
||||
|
||||
return totalFileSize;
|
||||
}
|
||||
|
||||
@@ -449,7 +441,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
private static class NullOutputStream extends OutputStream {
|
||||
/**
|
||||
* null output stream.
|
||||
*
|
||||
*
|
||||
* @param i
|
||||
* byte
|
||||
*/
|
||||
@@ -468,7 +460,7 @@ public class BulkUploadHandler extends CustomComponent
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* com.vaadin.ui.Upload.StartedListener#uploadStarted(com.vaadin.ui.Upload
|
||||
* .StartedEvent)
|
||||
|
||||
Reference in New Issue
Block a user