Propose SDK Refactor (#1821)

* Propose SDK Refactor

* Added ExecutorService for DMF Devices

* After review, Created MgmtApi inside sdk-mgmt

* Removed direct dependency to halkbit-mgmt-api all mgmt related calls now goes through hawkbit-sdk-mgmt

* Added copyright header

* Removed redundant paramters for deleteController

* Fixed File Copyright Headers
This commit is contained in:
Vasil Ilchev
2024-08-19 15:34:29 +03:00
committed by GitHub
parent d958d8e82c
commit ac34b952d9
15 changed files with 215 additions and 131 deletions

View File

@@ -23,10 +23,7 @@
<name>hawkBit :: SDK :: Commons</name> <name>hawkBit :: SDK :: Commons</name>
<description>SDK commons</description> <description>SDK commons</description>
<properties>
<spring-cloud-starter-openfeign.version>4.1.0</spring-cloud-starter-openfeign.version>
<openfeign-hc5.version>13.2.1</openfeign-hc5.version>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -39,7 +36,6 @@
<artifactId>feign-hc5</artifactId> <artifactId>feign-hc5</artifactId>
<version>${openfeign-hc5.version}</version> <version>${openfeign-hc5.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId> <artifactId>spring-boot-starter-hateoas</artifactId>

View File

@@ -42,7 +42,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.hawkbit</groupId> <groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-mgmt-api</artifactId> <artifactId>hawkbit-sdk-mgmt</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>

View File

@@ -15,25 +15,22 @@ import feign.codec.Decoder;
import feign.codec.Encoder; import feign.codec.Encoder;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.sdk.Controller; import org.eclipse.hawkbit.sdk.Controller;
import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.HawkbitClient; import org.eclipse.hawkbit.sdk.HawkbitClient;
import org.eclipse.hawkbit.sdk.HawkbitSDKConfigurtion; import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.Tenant; import org.eclipse.hawkbit.sdk.Tenant;
import org.eclipse.hawkbit.sdk.demo.SetupHelper;
import org.eclipse.hawkbit.sdk.device.DdiController; import org.eclipse.hawkbit.sdk.device.DdiController;
import org.eclipse.hawkbit.sdk.device.DdiTenant;
import org.eclipse.hawkbit.sdk.device.UpdateHandler; import org.eclipse.hawkbit.sdk.device.UpdateHandler;
import org.springframework.beans.factory.annotation.Value; import org.eclipse.hawkbit.sdk.mgmt.MgmtApi;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod; import org.springframework.shell.standard.ShellMethod;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/** /**
* Abstract class representing DDI device connecting directly to hawkVit. * Abstract class representing DDI device connecting directly to hawkVit.
@@ -54,49 +51,49 @@ public class DeviceApp {
} }
@Bean @Bean
DdiController device( DdiTenant ddiTenant(final Tenant defaultTenant,
@Value("${hawkbit.device:controller-default}") final String controllerId, final HawkbitClient hawkbitClient) {
@Value("${hawkbit.device.securityToken:}") final String securityToken, return new DdiTenant(defaultTenant, hawkbitClient);
final Tenant defaultTenant, }
final Optional<UpdateHandler> updateHandler,
final HawkbitClient hawkbitClient) { @Bean
return new DdiController( MgmtApi mgmtApi(final Tenant tenant, final HawkbitClient hawkbitClient) {
defaultTenant, return new MgmtApi(tenant, hawkbitClient);
Controller.builder()
.controllerId(controllerId)
.securityToken(ObjectUtils.isEmpty(securityToken) ?
(ObjectUtils.isEmpty(defaultTenant.getGatewayToken()) ? SetupHelper.randomToken() : securityToken) :
securityToken)
.build(),
updateHandler.orElse(null),
hawkbitClient).setOverridePollMillis(10_000);
} }
@ShellComponent @ShellComponent
public static class Shell { public static class Shell {
private final Tenant tenant; private final DdiTenant ddiTenant;
private final DdiController device; private final DdiController device;
private final HawkbitClient hawkbitClient; private final MgmtApi mgmtApi;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
Shell(final Tenant tenant, final DdiController device, final HawkbitClient hawkbitClient) { Shell(final DdiTenant ddiTenant, final MgmtApi mgmtApi, final Optional<UpdateHandler> updateHandler) {
this.tenant = tenant; this.ddiTenant = ddiTenant;
this.device = device; this.mgmtApi = mgmtApi;
this.hawkbitClient = hawkbitClient; String controllerId = System.getProperty("demo.controller.id");
String securityToken = System.getProperty("demo.controller.securityToken");
this.device = this.ddiTenant.createController(Controller.builder()
.controllerId(controllerId)
.securityToken(ObjectUtils.isEmpty(securityToken) ?
(ObjectUtils.isEmpty(ddiTenant.getTenant().getGatewayToken()) ? MgmtApi.randomToken() : securityToken) :
securityToken)
.build(),
updateHandler.orElse(null)).setOverridePollMillis(10_000);
} }
@ShellMethod(key = "setup") @ShellMethod(key = "setup")
public void setup() { public void setup() {
SetupHelper.setupTargetAuthentication(hawkbitClient, tenant); mgmtApi.setupTargetAuthentication();
SetupHelper.setupTargetToken( mgmtApi.setupTargetToken(device.getControllerId(),device.getTargetSecurityToken());
device.getControllerId(), device.getTargetSecurityToken(), hawkbitClient, tenant);
} }
@ShellMethod(key = "start") @ShellMethod(key = "start")
public void start() { public void start() {
device.start(scheduler); device.start(Executors.newSingleThreadScheduledExecutor());
} }
@ShellMethod(key = "stop") @ShellMethod(key = "stop")

View File

@@ -27,6 +27,7 @@ import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption; import org.springframework.shell.standard.ShellOption;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.Executors;
/** /**
* Abstract class representing DDI device connecting directly to hawkVit. * Abstract class representing DDI device connecting directly to hawkVit.
@@ -45,33 +46,35 @@ public class DmfApp {
return new Amqp(rabbitProperties, amqpProperties); return new Amqp(rabbitProperties, amqpProperties);
} }
@Bean
DmfTenant dmfTenant(Tenant tenant, Amqp amqp) {
return new DmfTenant(tenant, amqp);
}
@ShellComponent @ShellComponent
public static class Shell { public static class Shell {
private final UpdateHandler updateHandler;
private final DmfTenant dmfTenant; private final DmfTenant dmfTenant;
private final UpdateHandler updateHandler;
Shell(final Tenant tenant, final Optional<UpdateHandler> updateHandler, final Amqp amqp) { Shell(final DmfTenant dmfTenant, final Optional<UpdateHandler> updateHandler) {
this.dmfTenant = dmfTenant;
this.updateHandler = updateHandler.orElse(null); this.updateHandler = updateHandler.orElse(null);
dmfTenant = new DmfTenant(tenant, amqp);
} }
@ShellMethod(key = "start-one") @ShellMethod(key = "start-one")
public void startOne(@ShellOption("--id") final String controllerId) { public void startOne(@ShellOption("--id") final String controllerId) {
if (dmfTenant.getController(controllerId).isEmpty()) { dmfTenant.getController(controllerId).ifPresentOrElse(
dmfTenant.create( dmfController -> dmfController.start(Executors.newSingleThreadScheduledExecutor()),
Controller.builder().controllerId(controllerId).build(), () -> dmfTenant.createController(Controller.builder().controllerId(controllerId).build(), updateHandler)
updateHandler).connect(); .start(Executors.newSingleThreadScheduledExecutor()));
}
} }
@ShellMethod(key = "stop-one") @ShellMethod(key = "stop-one")
public void stopOne(@ShellOption("--id") final String controllerId) { public void stopOne(@ShellOption("--id") final String controllerId) {
dmfTenant.getController(controllerId).ifPresentOrElse( dmfTenant.getController(controllerId).ifPresentOrElse(
DmfController::stop, DmfController::stop,
() -> { () -> log.error("Controller with id " + controllerId + " not found!"));
throw new IllegalArgumentException("Controller with id " + controllerId + " not found!");
});
} }
@ShellMethod(key = "start") @ShellMethod(key = "start")

View File

@@ -15,26 +15,22 @@ import feign.codec.Decoder;
import feign.codec.Encoder; import feign.codec.Encoder;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.sdk.Controller; import org.eclipse.hawkbit.sdk.Controller;
import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.HawkbitClient; import org.eclipse.hawkbit.sdk.HawkbitClient;
import org.eclipse.hawkbit.sdk.HawkbitSDKConfigurtion; import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.Tenant; import org.eclipse.hawkbit.sdk.Tenant;
import org.eclipse.hawkbit.sdk.demo.SetupHelper;
import org.eclipse.hawkbit.sdk.device.DdiController; import org.eclipse.hawkbit.sdk.device.DdiController;
import org.eclipse.hawkbit.sdk.device.DdiTenant;
import org.eclipse.hawkbit.sdk.device.UpdateHandler; import org.eclipse.hawkbit.sdk.device.UpdateHandler;
import org.eclipse.hawkbit.sdk.mgmt.MgmtApi;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod; import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption; import org.springframework.shell.standard.ShellOption;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/** /**
* Abstract class representing DDI device connecting directly to hawkVit. * Abstract class representing DDI device connecting directly to hawkVit.
@@ -54,66 +50,66 @@ public class MultiDeviceApp {
return new HawkbitClient(hawkBitServer, client, encoder, decoder, contract); return new HawkbitClient(hawkBitServer, client, encoder, decoder, contract);
} }
@Bean
DdiTenant ddiTenant(final Tenant defaultTenant,
final HawkbitClient hawkbitClient) {
return new DdiTenant(defaultTenant, hawkbitClient);
}
@Bean
MgmtApi mgmtApi(final Tenant defaultTenant, final HawkbitClient hawkbitClient) {
return new MgmtApi(defaultTenant, hawkbitClient);
}
@ShellComponent @ShellComponent
public static class Shell { public static class Shell {
private final Tenant tenant; private final DdiTenant ddiTenant;
private final MgmtApi mgmtApi;
private final UpdateHandler updateHandler; private final UpdateHandler updateHandler;
private final HawkbitClient hawkbitClient;
private final Map<String, DdiController> devices = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private boolean setup; private boolean setup;
Shell(final Tenant tenant, final Optional<UpdateHandler> updateHandler, final HawkbitClient hawkbitClient) { Shell(final DdiTenant ddiTenant, final MgmtApi mgmtApi, final Optional<UpdateHandler> updateHandler) {
this.tenant = tenant; this.ddiTenant = ddiTenant;
this.mgmtApi = mgmtApi;
this.updateHandler = updateHandler.orElse(null); this.updateHandler = updateHandler.orElse(null);
this.hawkbitClient = hawkbitClient;
} }
@ShellMethod(key = "setup") @ShellMethod(key = "setup")
public void setup() { public void setup() {
SetupHelper.setupTargetAuthentication(hawkbitClient, tenant); mgmtApi.setupTargetAuthentication();
setup = true; setup = true;
} }
@ShellMethod(key = "start-one") @ShellMethod(key = "start-one")
public void startOne(@ShellOption("--id") final String controllerId) { public void startOne(@ShellOption("--id") final String controllerId) {
DdiController device = devices.get(controllerId);
final String securityTargetToken; final String securityTargetToken;
if (setup) { if (setup) {
securityTargetToken = SetupHelper.setupTargetToken( securityTargetToken = mgmtApi.setupTargetToken(controllerId,null);
controllerId, null, hawkbitClient, tenant);
} else { } else {
securityTargetToken = null; securityTargetToken = null;
} }
if (device == null) { // Create device with security token if not yet registered in this execution
device = new DdiController( // if already created in this execution of app, just start the poll
tenant, // for each new device - separate ThreadScheduler
Controller.builder() ddiTenant.getController(controllerId).ifPresentOrElse(
ddiController -> ddiController.start(Executors.newSingleThreadScheduledExecutor()),
() -> ddiTenant.createController(Controller.builder()
.controllerId(controllerId) .controllerId(controllerId)
.securityToken(securityTargetToken) .securityToken(securityTargetToken)
.build(), .build(),updateHandler)
updateHandler, .setOverridePollMillis(10_000)
hawkbitClient).setOverridePollMillis(10_000); .start(Executors.newSingleThreadScheduledExecutor())
final DdiController oldDevice = devices.putIfAbsent(controllerId, device); );
if (oldDevice != null) {
device = oldDevice; // reuse existing
}
}
device.start(scheduler);
} }
@ShellMethod(key = "stop-one") @ShellMethod(key = "stop-one")
public void stopOne(@ShellOption("--id") final String controllerId) { public void stopOne(@ShellOption("--id") final String controllerId) {
final DdiController device = devices.get(controllerId); ddiTenant.getController(controllerId).ifPresentOrElse(
if (device == null) { DdiController::stop,
throw new IllegalArgumentException("Controller with id " + controllerId + " not found!"); () -> log.error("Controller with id " + controllerId + " not found!"));
} else {
device.stop();
}
} }
@ShellMethod(key = "start") @ShellMethod(key = "start")

View File

@@ -35,5 +35,6 @@
<artifactId>hawkbit-ddi-api</artifactId> <artifactId>hawkbit-ddi-api</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -9,17 +9,6 @@
*/ */
package org.eclipse.hawkbit.sdk.device; package org.eclipse.hawkbit.sdk.device;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@@ -39,6 +28,17 @@ import org.springframework.hateoas.Link;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/** /**
* Class representing DDI device connecting directly to hawkBit. * Class representing DDI device connecting directly to hawkBit.
*/ */
@@ -84,7 +84,7 @@ public class DdiController {
* for communication to hawkBit * for communication to hawkBit
*/ */
public DdiController(final Tenant tenant, final Controller controller, public DdiController(final Tenant tenant, final Controller controller,
final UpdateHandler updateHandler, final HawkbitClient hawkbitClient) { final UpdateHandler updateHandler, final HawkbitClient hawkbitClient) {
this.tenantId = tenant.getTenantId(); this.tenantId = tenant.getTenantId();
gatewayToken = tenant.getGatewayToken(); gatewayToken = tenant.getGatewayToken();
downloadAuthenticationEnabled = tenant.isDownloadAuthenticationEnabled(); downloadAuthenticationEnabled = tenant.isDownloadAuthenticationEnabled();
@@ -96,13 +96,17 @@ public class DdiController {
// expects single threaded {@link java.util.concurrent.ScheduledExecutorService} // expects single threaded {@link java.util.concurrent.ScheduledExecutorService}
public void start(final ScheduledExecutorService executorService) { public void start(final ScheduledExecutorService executorService) {
Objects.requireNonNull(executorService, "Require non null executor!"); stop();
Objects.requireNonNull(executorService, "Require non null executor!");
this.executorService = executorService; this.executorService = executorService;
executorService.submit(this::poll); executorService.submit(this::poll);
} }
public void stop() { public void stop() {
if (executorService != null) {
executorService.shutdown();
}
executorService = null; executorService = null;
lastActionId = null; lastActionId = null;
currentActionId = null; currentActionId = null;

View File

@@ -19,7 +19,7 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* An in-memory simulated DMF Tenant to hold the controller twins in * An in-memory simulated DDI Tenant to hold the controller twins in
* memory and be able to retrieve them again. * memory and be able to retrieve them again.
*/ */
public class DdiTenant { public class DdiTenant {
@@ -27,8 +27,9 @@ public class DdiTenant {
@Getter @Getter
private final Tenant tenant; private final Tenant tenant;
private final Map<String, DdiController> controllers = new ConcurrentHashMap<>(); @Getter
private final HawkbitClient hawkbitClient; private final HawkbitClient hawkbitClient;
private final Map<String, DdiController> controllers = new ConcurrentHashMap<>();
public DdiTenant(final Tenant tenant, final HawkbitClient hawkbitClient) { public DdiTenant(final Tenant tenant, final HawkbitClient hawkbitClient) {
this.tenant = tenant; this.tenant = tenant;
@@ -40,17 +41,14 @@ public class DdiTenant {
controllers.clear(); controllers.clear();
} }
public DdiController create(final Controller controller, final UpdateHandler updateHandler) { public DdiController createController(final Controller controller, final UpdateHandler updateHandler) {
final DdiController ddiController = new DdiController(tenant, controller, updateHandler, hawkbitClient); final DdiController ddiController = new DdiController(tenant, controller, updateHandler, hawkbitClient);
controllers.put(controller.getControllerId(), ddiController); controllers.put(controller.getControllerId(), ddiController);
return ddiController; return ddiController;
} }
public void remove(final String controllerId) {
Optional.ofNullable(controllers.remove(controllerId)).ifPresent(DdiController::stop);
}
public Optional<DdiController> getController(final String controllerId) { public Optional<DdiController> getController(final String controllerId) {
return Optional.ofNullable(controllers.get(controllerId)); return Optional.ofNullable(controllers.get(controllerId));
} }
} }

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.sdk.dmf.amqp.DmfSender;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
/** /**
* Class representing DMF device twin connecting to hawkBit via DMF. * Class representing DMF device twin connecting to hawkBit via DMF.
@@ -50,6 +51,9 @@ public class DmfController {
private volatile Long lastActionId; private volatile Long lastActionId;
@SuppressWarnings("java:S3077") // volatile used only for the reference as expected
private volatile ScheduledExecutorService executorService;
/** /**
* Creates a new device instance. * Creates a new device instance.
* *
@@ -67,26 +71,32 @@ public class DmfController {
this.dmfSender = dmfSender; this.dmfSender = dmfSender;
} }
public void connect() { public void start(ScheduledExecutorService executorService) {
stop();
this.executorService = executorService;
log.debug(LOG_PREFIX + "Connecting/Polling ...", getTenantId(), getControllerId()); log.debug(LOG_PREFIX + "Connecting/Polling ...", getTenantId(), getControllerId());
dmfSender.createOrUpdateThing(getTenantId(), getControllerId()); dmfSender.createOrUpdateThing(getTenantId(), getControllerId());
log.debug(LOG_PREFIX + "Done. Create thing sent.", getTenantId(), getControllerId()); log.debug(LOG_PREFIX + "Done. Create thing sent.", getTenantId(), getControllerId());
} }
public void poll() { public void unregisterThing() {
log.debug(LOG_PREFIX + "Polling ..", getTenantId(), getControllerId()); log.debug(LOG_PREFIX + "Removing Controller...", getTenantId(), getControllerId());
dmfSender.createOrUpdateThing(getTenantId(), getControllerId()); dmfSender.removeThing(getTenantId(), getControllerId());
log.debug(LOG_PREFIX + "Done. Create thing sent.", getTenantId(), getControllerId()); log.debug(LOG_PREFIX + "Done. Create thing sent.", getTenantId(), getControllerId());
} }
public void stop() { public void stop() {
if (executorService != null) {
executorService.shutdown();
}
executorService = null;
lastActionId = null; lastActionId = null;
currentActionId = null; currentActionId = null;
} }
public void processUpdate(final EventTopic actionType, final DmfDownloadAndUpdateRequest updateRequest) { public void processUpdate(final EventTopic actionType, final DmfDownloadAndUpdateRequest updateRequest) {
log.info(LOG_PREFIX + "Processing update for action {} .", getTenantId(), controllerId, updateRequest.getActionId()); log.info(LOG_PREFIX + "Processing update for action {} .", getTenantId(), controllerId, updateRequest.getActionId());
updateHandler.getUpdateProcessor(this, actionType, updateRequest).run(); executorService.submit(updateHandler.getUpdateProcessor(this, actionType, updateRequest));
} }
public void sendFeedback(final UpdateStatus updateStatus) { public void sendFeedback(final UpdateStatus updateStatus) {

View File

@@ -46,17 +46,21 @@ public class DmfTenant {
} }
public void destroy() { public void destroy() {
controllers.values().forEach(DmfController::stop); controllers.values().forEach(DmfController::unregisterThing);
controllers.clear(); controllers.clear();
} }
public DmfController create(final Controller controller, final UpdateHandler updateHandler) { public DmfController createController(final Controller controller, final UpdateHandler updateHandler) {
final DmfController dmfController = new DmfController(tenant, controller, updateHandler, vHost); final DmfController dmfController = new DmfController(tenant, controller, updateHandler, vHost);
controllers.put(controller.getControllerId(), dmfController); controllers.put(controller.getControllerId(), dmfController);
return dmfController; return dmfController;
} }
public void remove(final String controllerId) { public void removeController(final String controllerId) {
Optional.ofNullable(controllers.remove(controllerId)).ifPresent(DmfController::unregisterThing);
}
public void handleThingDeleted(final String controllerId) {
Optional.ofNullable(controllers.remove(controllerId)).ifPresent(DmfController::stop); Optional.ofNullable(controllers.remove(controllerId)).ifPresent(DmfController::stop);
} }

View File

@@ -51,8 +51,16 @@ public class DmfSender {
} }
public void createOrUpdateThing(final String tenant, final String controllerId) { public void createOrUpdateThing(final String tenant, final String controllerId) {
sendThingMessage(tenant, controllerId, MessageType.THING_CREATED.name());
}
public void removeThing(final String tenant, final String controllerId) {
sendThingMessage(tenant, controllerId, MessageType.THING_REMOVED.name());
}
public void sendThingMessage(final String tenant, final String controllerId, String thingStatusChange) {
final MessageProperties messagePropertiesForSP = new MessageProperties(); final MessageProperties messagePropertiesForSP = new MessageProperties();
messagePropertiesForSP.setHeader(MessageHeaderKey.TYPE, MessageType.THING_CREATED.name()); messagePropertiesForSP.setHeader(MessageHeaderKey.TYPE, thingStatusChange);
messagePropertiesForSP.setHeader(MessageHeaderKey.TENANT, tenant); messagePropertiesForSP.setHeader(MessageHeaderKey.TENANT, tenant);
messagePropertiesForSP.setHeader(MessageHeaderKey.THING_ID, controllerId); messagePropertiesForSP.setHeader(MessageHeaderKey.THING_ID, controllerId);
messagePropertiesForSP.setHeader(MessageHeaderKey.SENDER, "hawkBit-sdk"); messagePropertiesForSP.setHeader(MessageHeaderKey.SENDER, "hawkBit-sdk");

View File

@@ -103,7 +103,7 @@ public class VHost extends DmfSender implements MessageListener {
final String controllerId = (String)message.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID); final String controllerId = (String)message.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID);
final String type = (String)message.getMessageProperties().getHeaders().get(MessageHeaderKey.TYPE); final String type = (String)message.getMessageProperties().getHeaders().get(MessageHeaderKey.TYPE);
log.info("Message received for target {}, value : {}", controllerId, message.toString()); log.info("Message received for target {}, value : {}", controllerId, message);
switch (MessageType.valueOf(type)) { switch (MessageType.valueOf(type)) {
case EVENT: { case EVENT: {
checkContentTypeJson(message); checkContentTypeJson(message);
@@ -112,7 +112,7 @@ public class VHost extends DmfSender implements MessageListener {
} }
case THING_DELETED: { case THING_DELETED: {
checkContentTypeJson(message); checkContentTypeJson(message);
Optional.ofNullable(dmfTenants.get(tenantId)).ifPresent(dmfTenant -> dmfTenant.remove(controllerId)); Optional.ofNullable(dmfTenants.get(tenantId)).ifPresent(dmfTenant -> dmfTenant.handleThingDeleted(controllerId));
break; break;
} }
case PING_RESPONSE: { case PING_RESPONSE: {
@@ -237,7 +237,7 @@ public class VHost extends DmfSender implements MessageListener {
final String tenant = getTenant(message); final String tenant = getTenant(message);
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message, final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message,
DmfDownloadAndUpdateRequest.class); DmfDownloadAndUpdateRequest.class);
dmfTenants.get(tenant).getController(controllerId).get().setCurrentActionId(downloadAndUpdateRequest.getActionId()); dmfTenants.get(tenant).getController(controllerId).ifPresent(controller -> controller.setCurrentActionId(downloadAndUpdateRequest.getActionId()));
processUpdate(tenant, controllerId, actionType, downloadAndUpdateRequest); processUpdate(tenant, controllerId, actionType, downloadAndUpdateRequest);
} }

View File

@@ -0,0 +1,51 @@
<!--
Copyright (c) 2023 Bosch.IO GmbH and others
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-sdk</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hawkbit-sdk-mgmt</artifactId>
<name>hawkBit :: SDK :: Management Api SDK</name>
<description>Management Api SDK</description>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-sdk-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-mgmt-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring-cloud-starter-openfeign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
<version>${openfeign-hc5.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -7,11 +7,12 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.sdk.demo; package org.eclipse.hawkbit.sdk.mgmt;
import feign.FeignException; import feign.FeignException;
import lombok.AccessLevel; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor; import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
@@ -29,21 +30,28 @@ import java.util.Objects;
import java.util.Random; import java.util.Random;
/** /**
* Abstract class representing DDI device connecting directly to hawkVit. * Management Api Interface
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j @Slf4j
public class SetupHelper { @AllArgsConstructor
public class MgmtApi {
private static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY = "authentication.gatewaytoken.key"; private static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY = "authentication.gatewaytoken.key";
private static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED = "authentication.gatewaytoken.enabled"; private static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED = "authentication.gatewaytoken.enabled";
private static final String AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED = "authentication.targettoken.enabled"; private static final String AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED = "authentication.targettoken.enabled";
@NonNull
private final Tenant tenant;
@NonNull
private final HawkbitClient hawkbitClient;
// if gateway toke is configured then the gateway auth is enabled key is set // if gateway toke is configured then the gateway auth is enabled key is set
// so all devices use gateway token authentication // so all devices use gateway token authentication
// otherwise target token authentication is enabled. Then all devices shall be registerd // otherwise target token authentication is enabled. Then all devices shall be registerd
// and the target token shall be set to the one from the DDI controller instance // and the target token shall be set to the one from the DDI controller instance
public static void setupTargetAuthentication(final HawkbitClient hawkbitClient, final Tenant tenant) { public void setupTargetAuthentication() {
final MgmtTenantManagementRestApi mgmtTenantManagementRestApi = final MgmtTenantManagementRestApi mgmtTenantManagementRestApi =
hawkbitClient.mgmtService(MgmtTenantManagementRestApi.class, tenant); hawkbitClient.mgmtService(MgmtTenantManagementRestApi.class, tenant);
final String gatewayToken = tenant.getGatewayToken(); final String gatewayToken = tenant.getGatewayToken();
@@ -75,9 +83,7 @@ public class SetupHelper {
} }
// returns target token // returns target token
public static String setupTargetToken( public String setupTargetToken(final String controllerId, String securityTargetToken) {
final String controllerId, String securityTargetToken,
final HawkbitClient hawkbitClient, final Tenant tenant) {
if (ObjectUtils.isEmpty(tenant.getGatewayToken())) { if (ObjectUtils.isEmpty(tenant.getGatewayToken())) {
final MgmtTargetRestApi mgmtTargetRestApi = hawkbitClient.mgmtService(MgmtTargetRestApi.class, tenant); final MgmtTargetRestApi mgmtTargetRestApi = hawkbitClient.mgmtService(MgmtTargetRestApi.class, tenant);
try { try {
@@ -112,6 +118,10 @@ public class SetupHelper {
return securityTargetToken; return securityTargetToken;
} }
public void deleteController(final String controllerId) {
hawkbitClient.mgmtService(MgmtTargetRestApi.class, tenant).deleteTarget(controllerId);
}
private static final Random RND = new SecureRandom(); private static final Random RND = new SecureRandom();
public static String randomToken() { public static String randomToken() {
final byte[] rnd = new byte[24]; final byte[] rnd = new byte[24];

View File

@@ -22,8 +22,14 @@
<artifactId>hawkbit-sdk</artifactId> <artifactId>hawkbit-sdk</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties>
<spring-cloud-starter-openfeign.version>4.1.0</spring-cloud-starter-openfeign.version>
<openfeign-hc5.version>13.2.1</openfeign-hc5.version>
</properties>
<modules> <modules>
<module>hawkbit-sdk-commons</module> <module>hawkbit-sdk-commons</module>
<module>hawkbit-sdk-mgmt</module>
<module>hawkbit-sdk-device</module> <module>hawkbit-sdk-device</module>
<module>hawkbit-sdk-dmf</module> <module>hawkbit-sdk-dmf</module>
<module>hawkbit-sdk-demo</module> <module>hawkbit-sdk-demo</module>