diff --git a/examples/hawkbit-device-simulator/README.md b/examples/hawkbit-device-simulator/README.md index b5db3d7ef..610a1d256 100644 --- a/examples/hawkbit-device-simulator/README.md +++ b/examples/hawkbit-device-simulator/README.md @@ -20,6 +20,25 @@ The simulator has user authentication enabled by default. Default credentials: This can be configured/disabled by spring boot properties ## Usage + +### Graphical User Interface +The device simulator comes with a graphical user interface which makes it very easy to generate dummy devices handled by the device simulator. +The status and the update progress of the simulated device are shown in the UI. +The UI can be accessed via the URL: +``` +http://localhost:8083 +``` + +`Basic Authentication Credentials are admin / admin` + + ![](src/main/images/generateScreenshot.png) + + ![](src/main/images/updateProcessScreenshot.png) + + ![](src/main/images/updateResultOverviewScreenshot.png) + + +### REST API The device simulator exposes an REST-API which can be used to trigger device creation. Optional parameters: diff --git a/examples/hawkbit-device-simulator/pom.xml b/examples/hawkbit-device-simulator/pom.xml index 9e9fc86dc..eaec9b91e 100644 --- a/examples/hawkbit-device-simulator/pom.xml +++ b/examples/hawkbit-device-simulator/pom.xml @@ -80,10 +80,52 @@ org.springframework.boot spring-boot-starter-log4j2 + + com.vaadin + vaadin-spring-boot-starter + 1.0.0 + + + com.vaadin + vaadin-push + org.springframework.boot spring-boot-autoconfigure + + org.springframework.boot + spring-boot-autoconfigure + + + com.google.guava + guava + 19.0 + + + com.netflix.feign + feign-jackson + 8.14.1 + + + com.netflix.feign + feign-core + 8.12.1 + + + com.jayway.jsonpath + json-path + - + + + + com.vaadin + vaadin-bom + 7.5.5 + pom + import + + + diff --git a/examples/hawkbit-device-simulator/src/main/images/generateScreenshot.png b/examples/hawkbit-device-simulator/src/main/images/generateScreenshot.png new file mode 100644 index 000000000..6c31c2d7d Binary files /dev/null and b/examples/hawkbit-device-simulator/src/main/images/generateScreenshot.png differ diff --git a/examples/hawkbit-device-simulator/src/main/images/updateProcessScreenshot.png b/examples/hawkbit-device-simulator/src/main/images/updateProcessScreenshot.png new file mode 100644 index 000000000..dd3652790 Binary files /dev/null and b/examples/hawkbit-device-simulator/src/main/images/updateProcessScreenshot.png differ diff --git a/examples/hawkbit-device-simulator/src/main/images/updateResultOverviewScreenshot.png b/examples/hawkbit-device-simulator/src/main/images/updateResultOverviewScreenshot.png new file mode 100644 index 000000000..78e16758f Binary files /dev/null and b/examples/hawkbit-device-simulator/src/main/images/updateResultOverviewScreenshot.png differ diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java new file mode 100644 index 000000000..474acb6c4 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/AbstractSimulatedDevice.java @@ -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; + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java new file mode 100644 index 000000000..1417c3153 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DDISimulatedDevice.java @@ -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."); + } + + } + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java new file mode 100644 index 000000000..b9fdc827c --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DMFSimulatedDevice.java @@ -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); + } + +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulator.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulator.java index 37812a19d..944ba1d07 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulator.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulator.java @@ -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. * diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorRepository.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorRepository.java new file mode 100644 index 000000000..68db9df45 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorRepository.java @@ -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 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 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; + } + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java new file mode 100644 index 000000000..6e93b7d04 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java @@ -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); + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java new file mode 100644 index 000000000..81acf897e --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/NextPollTimeController.java @@ -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 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)); + } + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java new file mode 100644 index 000000000..d3e080806 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulatedDeviceFactory.java @@ -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"); + } + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java index ab8847cb1..6f94bd319 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/SimulationController.java @@ -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!"; diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java index c4968c849..492bb3857 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java @@ -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; } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java index 5884a2645..6f0ac732e 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java @@ -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; + } + } + }); } - } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/InitUpdate.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/InitUpdate.java new file mode 100644 index 000000000..1cf6dbbda --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/InitUpdate.java @@ -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; + } + +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/NextPollCounterUpdate.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/NextPollCounterUpdate.java new file mode 100644 index 000000000..b9d7b9027 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/NextPollCounterUpdate.java @@ -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 devices; + + /** + * Creates poll timer update event. + * + * @param devices + * the devices which progress has been updated + */ + public NextPollCounterUpdate(final List devices) { + this.devices = devices; + } + + /** + * @return the devices of the event + */ + public List getDevices() { + return devices; + } + +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java new file mode 100644 index 000000000..3e34a0fa1 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/event/ProgressUpdate.java @@ -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; + } + +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/ControllerResource.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/ControllerResource.java new file mode 100644 index 000000000..1dac4c80b --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/ControllerResource.java @@ -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); +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/GatewayTokenInterceptor.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/GatewayTokenInterceptor.java new file mode 100644 index 000000000..3381481de --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/http/GatewayTokenInterceptor.java @@ -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); + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java new file mode 100644 index 000000000..1400ec0e1 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/GenerateDialog.java @@ -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(10)); + tf2.setIcon(FontAwesome.GEAR); + tf2.setRequired(true); + tf2.addValidator(new RangeValidator("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(10)); + tf4.setIcon(FontAwesome.CLOCK_O); + tf4.setRequired(true); + tf4.setVisible(false); + tf4.addValidator(new RangeValidator("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); + } +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorUI.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorUI.java new file mode 100644 index 000000000..1c09a702d --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorUI.java @@ -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); + } + +} diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java new file mode 100644 index 000000000..25498cea7 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/ui/SimulatorView.java @@ -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 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() { + @Override + public Protocol convertToModel(final String value, final Class targetType, + final Locale locale) { + return null; + } + + @Override + public String convertToPresentation(final Protocol value, final Class 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 getModelType() { + return Protocol.class; + } + + @Override + public Class getPresentationType() { + return String.class; + } + }); + grid.getColumn("status").setRenderer(new HtmlRenderer(), new Converter() { + private static final long serialVersionUID = 1L; + + @Override + public Status convertToModel(final String value, final Class targetType, + final Locale locale) { + return null; + } + + @Override + public String convertToPresentation(final Status value, final Class targetType, + final Locale locale) { + String style = null; + switch (value) { + case UNKNWON: + style = "&#x" + + Integer.toHexString(FontAwesome.QUESTION_CIRCLE.getCodepoint()) + ";"; + break; + case PEDNING: + style = "&#x" + Integer.toHexString(FontAwesome.REFRESH.getCodepoint()) + + ";"; + break; + case FINISH: + style = "&#x" + + Integer.toHexString(FontAwesome.CHECK_CIRCLE.getCodepoint()) + ";"; + break; + case ERROR: + style = "&#x" + + Integer.toHexString(FontAwesome.EXCLAMATION_CIRCLE.getCodepoint()) + ";"; + break; + default: + throw new IllegalStateException("unknown value"); + } + return style; + } + + @Override + public Class getModelType() { + return Status.class; + } + + @Override + public Class 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 devices = update.getDevices(); + this.getUI().access(new Runnable() { + @Override + public void run() { + devices.forEach(device -> { + final BeanItem 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 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 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); + } + } + })); + } +} diff --git a/examples/hawkbit-device-simulator/src/main/resources/VAADIN/themes/simulator/styles.scss b/examples/hawkbit-device-simulator/src/main/resources/VAADIN/themes/simulator/styles.scss new file mode 100644 index 000000000..56ed48c34 --- /dev/null +++ b/examples/hawkbit-device-simulator/src/main/resources/VAADIN/themes/simulator/styles.scss @@ -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; + } +} \ No newline at end of file diff --git a/examples/hawkbit-device-simulator/src/main/resources/application.properties b/examples/hawkbit-device-simulator/src/main/resources/application.properties index 19dd2cb77..402f71bfe 100644 --- a/examples/hawkbit-device-simulator/src/main/resources/application.properties +++ b/examples/hawkbit-device-simulator/src/main/resources/application.properties @@ -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 diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java index 747432cd0..62c987ae8 100644 --- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java +++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/api/client/DistributionSetResource.java @@ -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 diff --git a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoConfiguration.java b/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoConfiguration.java index 3f203f94f..02fb22725 100644 --- a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoConfiguration.java +++ b/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoConfiguration.java @@ -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; } /* diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Action.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Action.java index eb70f235b..80624054a 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Action.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Action.java @@ -233,7 +233,7 @@ public class Action extends BaseEntity implements Comparable { * 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 { /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -284,11 +284,11 @@ public class Action extends BaseEntity implements Comparable { /* * (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 { /* * (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 { /** * The action type for this action relation. - * + * * * * diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java index 3028be775..63a858a7d 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java @@ -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; } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifact.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifact.java index d6664ac59..35e0c4e99 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifact.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifact.java @@ -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; } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifactProvider.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifactProvider.java index f6ed40e79..56d92c8e1 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifactProvider.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/ExternalArtifactProvider.java @@ -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; } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/LocalArtifact.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/LocalArtifact.java index a5ad493b9..baa4ee1f0 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/LocalArtifact.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/LocalArtifact.java @@ -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; } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java index a3685df66..49e73f749 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java @@ -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; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/push/AsyncVaadinServletConfiguration.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/push/AsyncVaadinServletConfiguration.java index 6eb6761e6..89ff3413e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/push/AsyncVaadinServletConfiguration.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/push/AsyncVaadinServletConfiguration.java @@ -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(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/CustomFile.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/CustomFile.java index 9b29c780a..ff350511a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/CustomFile.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/CustomFile.java @@ -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 diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionBeanQuery.java index fa7130d23..f3b68bce6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionBeanQuery.java @@ -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 { @@ -55,7 +50,7 @@ public class DistributionBeanQuery extends AbstractBeanQuery /** * Bean query for retrieving beans/objects of type. - * + * * @param definition * query definition * @param queryConfig @@ -92,8 +87,9 @@ public class DistributionBeanQuery extends AbstractBeanQuery /** * 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 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) in.readObject(); - } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java index 8ba60ec51..fa4022a3e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/BulkUploadHandler.java @@ -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)