From e8664f26ab063e8749f4c125e0ca291e06f39ec1 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sat, 3 Feb 2024 22:14:48 -0800 Subject: [PATCH 01/13] Add profile interval sec --- schema/access_iot.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/schema/access_iot.json b/schema/access_iot.json index 38397f4d79..10003743e0 100644 --- a/schema/access_iot.json +++ b/schema/access_iot.json @@ -10,6 +10,9 @@ "project_id": { "type": "string" }, + "profile_sec": { + "type": "integer" + }, "options": { "type": "string" } From 4f740dd39e5558286a31139a38551950ce99116b Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sat, 3 Feb 2024 22:16:16 -0800 Subject: [PATCH 02/13] Gencode --- .gencode_hash.txt | 6 +-- gencode/docs/configuration_pod.html | 47 ++++++++++++++++++++++++ gencode/java/udmi/schema/IotAccess.java | 6 ++- gencode/python/udmi/schema/access_iot.py | 4 ++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/.gencode_hash.txt b/.gencode_hash.txt index 0a82a6f7a3..6dc0c3dc64 100644 --- a/.gencode_hash.txt +++ b/.gencode_hash.txt @@ -4,7 +4,7 @@ e5ae5dd058ce298448741a75a6ac67166a9f197b32061b9cbfd47655861864e2 gencode/docs/c 6e853ce072dde0bccd7cdeb31845301ba681841d7cb21a8ff90d023bc9597729 gencode/docs/config_mapping.html 08583688b20f892c0b453f41787ac01a46ac601663736bcd6ed6f57be0758e79 gencode/docs/configuration_endpoint.html 6cf94d6cb600c75cde32a64bd78acb3ed3b54adfad08dbf6bb159b467e8925c9 gencode/docs/configuration_execution.html -6f8a3766b840e96881e3573d594e28864917efb288e068d6c82de55deb576694 gencode/docs/configuration_pod.html +aba6934ec7e80caa26d428203a1535b588ded41a64a3b97455a3287248773374 gencode/docs/configuration_pod.html dc77a21a05f9f98a23403a1db0c3b468ef50d028e4c4934734d08e15f36b1d57 gencode/docs/configuration_pubber.html a5454f8dc6a843115823d1122ce34e36a5e9058cd3f6ea42c3482c8b4b5adf72 gencode/docs/event.html f7268ffd426cd03007e85a7f73347888c569f2ae92f10ec5569885d1a6c5e807 gencode/docs/event_discovery.html @@ -68,7 +68,7 @@ d3fdb2d8b485c1e61786dfde45cc8be08294339bb31a1b8f02180485789a3ab2 gencode/java/u 60a8115ae1acae7c199b63180823198d38ec50d57b48dd85aca1ccc865058f85 gencode/java/udmi/schema/GatewayConfig.java 56b46f4914ef1f4baa59bf597186ff7901b7c8b607720ec798f4e4e6ad59aa08 gencode/java/udmi/schema/GatewayModel.java 9d606a8e0a3787fc79c3c89db5a0a3aaa58cf88972ecff7e4052e2ce0f78d3de gencode/java/udmi/schema/GatewayState.java -58881d02bf7047ec9b29d7c80459b0db331e878af8eb9ea978361d855144acd4 gencode/java/udmi/schema/IotAccess.java +ba03843303bd2436fc7151da3a4fbc23963dd2d86843cf17c1e245ff42d06378 gencode/java/udmi/schema/IotAccess.java b0d4bff14a65ebddc2dd253c996708a4cce99592e82978f057f32a6d9c7768a1 gencode/java/udmi/schema/Jwt.java a5e5adfc187709e8646a11c92e804acfb67743f9d72149008aaca954df3177f6 gencode/java/udmi/schema/Level.java 07fd4911363437b274c19b024759b04b116152176702da8d4203c4ff4cb55b7f gencode/java/udmi/schema/LocalnetConfig.java @@ -124,7 +124,7 @@ d54631f8bada01ffecf34361891ee52d41786b1289ce56a9edb696b6ad2d3ace gencode/java/u e007ddd1ceeae3603c85110c33e1bb4a418ff9c7a791ca0df25b7ea3caeafd36 gencode/java/udmi/schema/ValidationSummary.java b77d953fd22e655c0f10ae32deeaa222769d971f8c38b3379eba45720fb910cc gencode/java/udmi/schema/VirtualEquipmentLinks.java cf0da75640384f1033a0ac43c758f3ec2f2814878d9a84bd222a4a17d06e2f4d gencode/python/udmi/schema/__init__.py -4550b708782ad9328d9d94c6d2d21c8079b4b40697547e6d7df3f45a596a9960 gencode/python/udmi/schema/access_iot.py +21a1521cef58f195ef6c44f2b63b820bf981408806f58d3370eeeea07cc5436c gencode/python/udmi/schema/access_iot.py 4b25dd95f863059b761269f93adcae7049507924a1c6e74d6856849203c179db gencode/python/udmi/schema/ancillary_properties.py dab4f5fca272ec48c2881bca2b6bc43786ada47fa1f6dd935c35f7ce0eb6b0f6 gencode/python/udmi/schema/building_translation.py 470b688984b89b25fcdfa8e08bd95b0c5d8c551d53a6ab5512503ee39419e6fa gencode/python/udmi/schema/category.py diff --git a/gencode/docs/configuration_pod.html b/gencode/docs/configuration_pod.html index 22c2bd2b53..9a988b4951 100644 --- a/gencode/docs/configuration_pod.html +++ b/gencode/docs/configuration_pod.html @@ -4420,6 +4420,53 @@

+ + + + +
+
+
+

+ +

+
+ +
+
+ + Type: integer
+ + + + + + +
diff --git a/gencode/java/udmi/schema/IotAccess.java b/gencode/java/udmi/schema/IotAccess.java index ad2368b7c1..6788ae36dd 100644 --- a/gencode/java/udmi/schema/IotAccess.java +++ b/gencode/java/udmi/schema/IotAccess.java @@ -21,6 +21,7 @@ @JsonPropertyOrder({ "provider", "project_id", + "profile_sec", "options" }) @Generated("jsonschema2pojo") @@ -36,6 +37,8 @@ public class IotAccess { public IotAccess.IotProvider provider; @JsonProperty("project_id") public String project_id; + @JsonProperty("profile_sec") + public Integer profile_sec; @JsonProperty("options") public String options; @@ -45,6 +48,7 @@ public int hashCode() { result = ((result* 31)+((this.options == null)? 0 :this.options.hashCode())); result = ((result* 31)+((this.provider == null)? 0 :this.provider.hashCode())); result = ((result* 31)+((this.project_id == null)? 0 :this.project_id.hashCode())); + result = ((result* 31)+((this.profile_sec == null)? 0 :this.profile_sec.hashCode())); return result; } @@ -57,7 +61,7 @@ public boolean equals(Object other) { return false; } IotAccess rhs = ((IotAccess) other); - return ((((this.options == rhs.options)||((this.options!= null)&&this.options.equals(rhs.options)))&&((this.provider == rhs.provider)||((this.provider!= null)&&this.provider.equals(rhs.provider))))&&((this.project_id == rhs.project_id)||((this.project_id!= null)&&this.project_id.equals(rhs.project_id)))); + return (((((this.options == rhs.options)||((this.options!= null)&&this.options.equals(rhs.options)))&&((this.provider == rhs.provider)||((this.provider!= null)&&this.provider.equals(rhs.provider))))&&((this.project_id == rhs.project_id)||((this.project_id!= null)&&this.project_id.equals(rhs.project_id))))&&((this.profile_sec == rhs.profile_sec)||((this.profile_sec!= null)&&this.profile_sec.equals(rhs.profile_sec)))); } diff --git a/gencode/python/udmi/schema/access_iot.py b/gencode/python/udmi/schema/access_iot.py index 92cc9ea20e..4f2a969e6e 100644 --- a/gencode/python/udmi/schema/access_iot.py +++ b/gencode/python/udmi/schema/access_iot.py @@ -7,6 +7,7 @@ class IotAccess: def __init__(self): self.provider = None self.project_id = None + self.profile_sec = None self.options = None @staticmethod @@ -16,6 +17,7 @@ def from_dict(source): result = IotAccess() result.provider = source.get('provider') result.project_id = source.get('project_id') + result.profile_sec = source.get('profile_sec') result.options = source.get('options') return result @@ -41,6 +43,8 @@ def to_dict(self): result['provider'] = self.provider # 5 if self.project_id: result['project_id'] = self.project_id # 5 + if self.profile_sec: + result['profile_sec'] = self.profile_sec # 5 if self.options: result['options'] = self.options # 5 return result From c0925fe82b6568e1e74a3ab5f8447601c4ecf192 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sat, 3 Feb 2024 22:32:16 -0800 Subject: [PATCH 03/13] Refactor IotAccessProvider --- .../access/ClearBladeIotAccessProvider.java | 6 +- .../access/DynamicIotAccessProvider.java | 6 +- .../service/access/GcpIotAccessProvider.java | 6 +- .../udmi/service/access/IotAccessBase.java | 47 +-------------- .../service/access/IotAccessProvider.java | 60 +++++++++++++++++++ .../access/LocalIotAccessProvider.java | 8 +-- .../access/PubSubIotAccessProvider.java | 12 ++-- .../bos/udmi/service/pod/ContainerBase.java | 8 +-- .../udmi/service/pod/ContainerProvider.java | 10 ++++ .../bos/udmi/service/pod/UdmiServicePod.java | 16 ++--- 10 files changed, 102 insertions(+), 77 deletions(-) create mode 100644 udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java create mode 100644 udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index c5e6170834..fdd7b7c654 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -181,7 +181,7 @@ protected DeviceManagerClient getDeviceManagerClient() { } @NotNull - protected Set getRegistriesForRegion(String region) { + public Set getRegistriesForRegion(String region) { if (region == null) { return CLOUD_REGIONS; } @@ -206,11 +206,11 @@ protected Set getRegistriesForRegion(String region) { } @Override - protected boolean isEnabled() { + public boolean isEnabled() { return !isNullOrEmpty(projectId); } - protected String updateConfig(String registryId, String deviceId, String config, Long version) { + public String updateConfig(String registryId, String deviceId, String config, Long version) { try { DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); ByteString binaryData = new ByteString(encodeBase64(config)); diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java index d71f5ddf84..91d0e7c744 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java @@ -49,17 +49,17 @@ protected Map fetchRegistryRegions() { } @Override - protected Set getRegistriesForRegion(String region) { + public Set getRegistriesForRegion(String region) { throw new RuntimeException("Should not be called!"); } @Override - protected boolean isEnabled() { + public boolean isEnabled() { return true; } @Override - protected String updateConfig(String registryId, String deviceId, String config, Long version) { + public String updateConfig(String registryId, String deviceId, String config, Long version) { throw new RuntimeException("Shouldn't be called for dynamic provider"); } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java index e7864c5a17..cddab36f47 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java @@ -127,7 +127,7 @@ protected CloudIot createCloudIotService() { } } - protected String updateConfig(String registryId, String deviceId, String config, Long version) { + public String updateConfig(String registryId, String deviceId, String config, Long version) { try { String useConfig = ofNullable(config).orElse(""); registries.devices().modifyCloudToDeviceConfig( @@ -237,7 +237,7 @@ private String getProjectPath() { } @NotNull - protected Set getRegistriesForRegion(String region) { + public Set getRegistriesForRegion(String region) { if (region == null) { return CLOUD_REGIONS; } @@ -322,7 +322,7 @@ private void unbindGatewayDevices(String registryId, String deviceId) { } @Override - protected boolean isEnabled() { + public boolean isEnabled() { return projectId != null; } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java index 73390c9c41..1d57b72ca6 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java @@ -30,17 +30,15 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import udmi.schema.CloudModel; import udmi.schema.Envelope; import udmi.schema.Envelope.SubFolder; import udmi.schema.IotAccess; -import udmi.schema.IotAccess.IotProvider; import udmi.schema.UdmiState; /** * Generic interface for accessing iot device management. */ -public abstract class IotAccessBase extends ContainerBase { +public abstract class IotAccessBase extends ContainerBase implements IotAccessProvider { public static final int MAX_CONFIG_LENGTH = 262144; public static final TemporalAmount REGION_RETRY_BACKOFF = Duration.ofSeconds(30); @@ -49,13 +47,6 @@ public abstract class IotAccessBase extends ContainerBase { private static final Map BACKOFF_MAP = new ConcurrentHashMap<>(); private static final long CONFIG_UPDATE_BACKOFF_MS = 1000; private static final int CONFIG_UPDATE_MAX_RETRIES = 10; - private static final Map> PROVIDERS = ImmutableMap.of( - IotProvider.DYNAMIC, DynamicIotAccessProvider.class, - IotProvider.CLEARBLADE, ClearBladeIotAccessProvider.class, - IotProvider.GCP, GcpIotAccessProvider.class, - IotProvider.PUBSUB, PubSubIotAccessProvider.class, - IotProvider.LOCAL, LocalIotAccessProvider.class - ); final Map options; private CompletableFuture> registryRegions; private Instant regionRetry = Instant.now(); @@ -65,19 +56,6 @@ public IotAccessBase(IotAccess iotAccess) { options = parseOptions(iotAccess); } - /** - * Factory constructor for new instances. - */ - public static IotAccessBase from(IotAccess iotAccess) { - try { - return PROVIDERS.get(iotAccess.provider).getDeclaredConstructor(IotAccess.class) - .newInstance(iotAccess); - } catch (Exception e) { - throw new RuntimeException( - format("While instantiating access provider type %s", iotAccess.provider), e); - } - } - private static Instant getBackoff(String registryId, String deviceId) { return BACKOFF_MAP.get(getBackoffKey(registryId, deviceId)); } @@ -86,8 +64,6 @@ private static String getBackoffKey(String registryId, String deviceId) { return format("%s/%s", registryId, deviceId); } - protected abstract Entry fetchConfig(String registryId, String deviceId); - /** * Update the cached registry regions with any incremental updates. */ @@ -121,8 +97,6 @@ private Set getRegistriesForRegionLog(String region) { return getRegistriesForRegion(region); } - protected abstract Set getRegistriesForRegion(String region); - @NotNull protected String getRegistryRegion(String registryId) { String region = ofNullable(getCompletedRegistryRegion(registryId)).orElseGet( @@ -130,8 +104,6 @@ protected String getRegistryRegion(String registryId) { return requireNonNull(region, "unknown region for registry " + registryId); } - protected abstract boolean isEnabled(); - protected synchronized String populateRegistryRegions(String registryId) { if (regionRetry.isBefore(Instant.now())) { regionRetry = Instant.now().plus(REGION_RETRY_BACKOFF); @@ -145,12 +117,6 @@ protected synchronized String populateRegistryRegions(String registryId) { return getCompletedRegistryRegion(registryId); } - protected abstract void sendCommandBase(String registryId, String deviceId, SubFolder folder, - String message); - - protected abstract String updateConfig(String registryId, String deviceId, String config, - Long version); - private String checkedUpdate(String registryId, String deviceId, Long version, String updated) { int configLength = updated.length(); if (configLength > MAX_CONFIG_LENGTH) { @@ -230,15 +196,6 @@ public void activate() { } } - public abstract CloudModel fetchDevice(String deviceRegistryId, String deviceId); - - public abstract String fetchState(String deviceRegistryId, String deviceId); - - public abstract CloudModel listDevices(String deviceRegistryId); - - public abstract CloudModel modelResource(String deviceRegistryId, String deviceId, - CloudModel cloudModel); - /** * Modify a device configuration. Return the full/complete update that was actually written. */ @@ -323,6 +280,4 @@ Map parseOptions(IotAccess iotAccess) { .collect(Collectors.toMap(x -> x[0], x -> x.length > 1 ? x[1] : true)); } - abstract String fetchRegistryMetadata(String registryId, String metadataKey); - } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java new file mode 100644 index 0000000000..326c8e3582 --- /dev/null +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java @@ -0,0 +1,60 @@ +package com.google.bos.udmi.service.access; + +import static java.lang.String.format; + +import com.google.bos.udmi.service.pod.ContainerProvider; +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import udmi.schema.CloudModel; +import udmi.schema.Envelope.SubFolder; +import udmi.schema.IotAccess; +import udmi.schema.IotAccess.IotProvider; + +public interface IotAccessProvider extends ContainerProvider { + + Map> PROVIDERS = ImmutableMap.of( + IotProvider.DYNAMIC, DynamicIotAccessProvider.class, + IotProvider.CLEARBLADE, ClearBladeIotAccessProvider.class, + IotProvider.GCP, GcpIotAccessProvider.class, + IotProvider.PUBSUB, PubSubIotAccessProvider.class, + IotProvider.LOCAL, LocalIotAccessProvider.class + ); + + /** + * Factory constructor for new instances. + */ + static IotAccessProvider from(IotAccess iotAccess) { + try { + return PROVIDERS.get(iotAccess.provider).getDeclaredConstructor(IotAccess.class) + .newInstance(iotAccess); + } catch (Exception e) { + throw new RuntimeException( + format("While instantiating access provider type %s", iotAccess.provider), e); + } + } + + Entry fetchConfig(String registryId, String deviceId); + + Set getRegistriesForRegion(String region); + + boolean isEnabled(); + + void sendCommandBase(String registryId, String deviceId, SubFolder folder, + String message); + + String updateConfig(String registryId, String deviceId, String config, + Long version); + + CloudModel fetchDevice(String deviceRegistryId, String deviceId); + + String fetchState(String deviceRegistryId, String deviceId); + + CloudModel listDevices(String deviceRegistryId); + + CloudModel modelResource(String deviceRegistryId, String deviceId, + CloudModel cloudModel); + + String fetchRegistryMetadata(String registryId, String metadataKey); +} diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java index 066fb6ff21..7857b1acc2 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java @@ -39,7 +39,7 @@ public LocalIotAccessProvider(IotAccess iotAccess) { } @Override - protected String updateConfig(String registryId, String deviceId, String config, Long version) { + public String updateConfig(String registryId, String deviceId, String config, Long version) { Entry entry = DEVICE_CONFIGS.get(deviceId); if (version != null && !entry.getKey().equals(version)) { throw new IllegalStateException("Config version mismatch"); @@ -50,12 +50,12 @@ protected String updateConfig(String registryId, String deviceId, String config, } @Override - protected boolean isEnabled() { + public boolean isEnabled() { return true; } @Override - protected Set getRegistriesForRegion(String region) { + public Set getRegistriesForRegion(String region) { return ImmutableSet.of(); } @@ -111,7 +111,7 @@ public void shutdown() { } @Override - String fetchRegistryMetadata(String registryId, String metadataKey) { + public String fetchRegistryMetadata(String registryId, String metadataKey) { throw new RuntimeException("Not yet implemented"); } } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/PubSubIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/PubSubIotAccessProvider.java index f158217375..366ce8f34b 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/PubSubIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/PubSubIotAccessProvider.java @@ -47,29 +47,29 @@ public class PubSubIotAccessProvider extends IotAccessBase { } @Override - protected Entry fetchConfig(String registryId, String deviceId) { + public Entry fetchConfig(String registryId, String deviceId) { // Sticky config isn't supported (nor required) for PubSub reflector, so always return empty. return new SimpleEntry<>(1L, "{}"); } @Override - protected Set getRegistriesForRegion(String region) { + public Set getRegistriesForRegion(String region) { return null; } @Override - protected boolean isEnabled() { + public boolean isEnabled() { return true; } @Override - protected void sendCommandBase(String registryId, String deviceId, SubFolder folder, + public void sendCommandBase(String registryId, String deviceId, SubFolder folder, String message) { publish(registryId, deviceId, COMMANDS_CATEGORY, folder, message); } @Override - protected String updateConfig(String registryId, String deviceId, String config, Long version) { + public String updateConfig(String registryId, String deviceId, String config, Long version) { publish(registryId, deviceId, CONFIG_CATEGORY, null, config); return config; } @@ -131,7 +131,7 @@ public CloudModel modelResource(String deviceRegistryId, String deviceId, CloudM } @Override - String fetchRegistryMetadata(String registryId, String metadataKey) { + public String fetchRegistryMetadata(String registryId, String metadataKey) { // Metadata is not supported by PubSub, so just pretend there is none. return null; } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerBase.java b/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerBase.java index 13916ad150..4bb8a67027 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerBase.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerBase.java @@ -7,17 +7,14 @@ import static java.util.stream.Collectors.toSet; import com.google.bos.udmi.service.core.ComponentName; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.udmi.util.JsonUtil; import java.io.PrintStream; import java.util.Arrays; -import java.util.List; import java.util.Set; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; import udmi.schema.BasePodConfiguration; @@ -29,7 +26,7 @@ * convenience and abstraction to keep the main component code more clear. * TODO: Implement facilities for other loggers, including structured-to-cloud. */ -public abstract class ContainerBase { +public abstract class ContainerBase implements ContainerProvider { public static final String INITIAL_EXECUTION_CONTEXT = "xxxxxxxx"; public static final Integer FUNCTIONS_VERSION_MIN = 11; @@ -167,9 +164,11 @@ private void output(Level level, String message) { printStream.flush(); } + @Override public void activate() { } + @Override public void debug(String format, Object... args) { debug(format(format, args)); } @@ -198,6 +197,7 @@ public void notice(String message) { output(Level.NOTICE, message); } + @Override public void shutdown() { } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java new file mode 100644 index 0000000000..5841a9a421 --- /dev/null +++ b/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java @@ -0,0 +1,10 @@ +package com.google.bos.udmi.service.pod; + +public interface ContainerProvider { + + void activate(); + + void shutdown(); + + void debug(String format, Object... args); +} diff --git a/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java b/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java index 41f01bd798..796203cf4a 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java @@ -11,7 +11,7 @@ import static java.lang.String.format; import static java.util.Objects.requireNonNull; -import com.google.bos.udmi.service.access.IotAccessBase; +import com.google.bos.udmi.service.access.IotAccessProvider; import com.google.bos.udmi.service.core.BridgeProcessor; import com.google.bos.udmi.service.core.DistributorPipe; import com.google.bos.udmi.service.core.ProcessorBase; @@ -45,7 +45,7 @@ public class UdmiServicePod extends ContainerBase { public static final String UDMI_VERSION = requireNonNull(getDeployedConfig().udmi_version); public static final int FATAL_ERROR_CODE = -1; static final File READY_INDICATOR = new File("/tmp/pod_ready.txt"); - private static final Map COMPONENT_MAP = new ConcurrentHashMap<>(); + private static final Map COMPONENT_MAP = new ConcurrentHashMap<>(); private static final Set> PROCESSOR_CLASSES = ImmutableSet.of( TargetProcessor.class, ReflectProcessor.class, StateProcessor.class); private static final Map> PROCESSORS = @@ -71,7 +71,7 @@ public UdmiServicePod(String[] args) { /** * Loop through all the registered components and apply the given action. */ - public static void forAllComponents(Consumer action) { + public static void forAllComponents(Consumer action) { COMPONENT_MAP.forEach((key, value) -> { try { action.accept(value); @@ -133,9 +133,9 @@ public static T maybeGetComponent(String name) { /** * Put this component into the central component registry. */ - public static void putComponent(String componentName, Supplier component) { + public static void putComponent(String componentName, Supplier component) { try { - ContainerBase container = component.get(); + ContainerProvider container = component.get(); ifNotNullThen(COMPONENT_MAP.put(componentName, container), replaced -> { throw new IllegalStateException( @@ -155,7 +155,7 @@ public static void resetForTest() { } private void createAccess(String name, IotAccess config) { - putComponent(name, () -> IotAccessBase.from(config)); + putComponent(name, () -> IotAccessProvider.from(config)); } private void createBridge(String name, BridgePodConfiguration config) { @@ -193,7 +193,7 @@ public void activate() { notice("Starting activation of container components"); String absolutePath = READY_INDICATOR.getAbsolutePath(); try { - forAllComponents(ContainerBase::activate); + forAllComponents(ContainerProvider::activate); checkState(READY_INDICATOR.createNewFile(), "ready file already exists: " + absolutePath); READY_INDICATOR.deleteOnExit(); } catch (Exception e) { @@ -212,7 +212,7 @@ public PodConfiguration getPodConfiguration() { @Override public void shutdown() { notice("Starting shutdown of container components"); - forAllComponents(ContainerBase::shutdown); + forAllComponents(ContainerProvider::shutdown); notice("Finished shutdown of container components"); super.shutdown(); } From ebe2f6c11689c4d1ca8752c42981cc10f288c605 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sun, 4 Feb 2024 05:51:06 -0800 Subject: [PATCH 04/13] Linty things --- udmis/etc/prod_pod.json | 1 + .../access/ClearBladeIotAccessProvider.java | 2 + .../access/DynamicIotAccessProvider.java | 10 ++-- .../service/access/GcpIotAccessProvider.java | 2 + .../udmi/service/access/IotAccessBase.java | 2 + .../service/access/IotAccessProvider.java | 19 ++++++- .../udmi/service/access/ProfilingProxy.java | 50 +++++++++++++++++++ .../udmi/service/pod/ContainerProvider.java | 3 ++ 8 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java diff --git a/udmis/etc/prod_pod.json b/udmis/etc/prod_pod.json index b04b691a76..6f5c3afb5b 100644 --- a/udmis/etc/prod_pod.json +++ b/udmis/etc/prod_pod.json @@ -28,6 +28,7 @@ "clearblade-iot-core": { "provider": "clearblade", "project_id": "${CLEARBLADE_PROJECT}", + "profile_sec": 10, "options": "distributor=stately" }, "gcp-iot-core": { diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index fdd7b7c654..d3da43368c 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -181,6 +181,7 @@ protected DeviceManagerClient getDeviceManagerClient() { } @NotNull + @Override public Set getRegistriesForRegion(String region) { if (region == null) { return CLOUD_REGIONS; @@ -210,6 +211,7 @@ public boolean isEnabled() { return !isNullOrEmpty(projectId); } + @Override public String updateConfig(String registryId, String deviceId, String config, Long version) { try { DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java index 91d0e7c744..b884da1750 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java @@ -32,7 +32,7 @@ public class DynamicIotAccessProvider extends IotAccessBase { private static final long INDEX_ORDERING_MULTIPLIER_MS = 10000L; private final Map registryProviders = new ConcurrentHashMap<>(); private final List providerList; - private final Map providers = new HashMap<>(); + private final Map providers = new HashMap<>(); /** * Create a new instance for interfacing with multiple providers. @@ -71,15 +71,15 @@ private String determineProvider(String registryId) { return providerId; } - private IotAccessBase getProviderFor(String registryId) { - IotAccessBase provider = + private IotAccessProvider getProviderFor(String registryId) { + IotAccessProvider provider = providers.get(registryProviders.computeIfAbsent(registryId, this::determineProvider)); return requireNonNull( provider, "could not determine provider for " + registryId); } - private String registryPriority(String registryId, Entry provider) { + private String registryPriority(String registryId, Entry provider) { int providerIndex = providerList.size() - providerList.indexOf(provider.getKey()); String provisionedAt = ofNullable( provider.getValue().fetchRegistryMetadata(registryId, "udmi_provisioned")).orElse( @@ -94,7 +94,7 @@ public void activate() { super.activate(); providerList.forEach( providerId -> { - IotAccessBase component = getComponent(providerId); + IotAccessProvider component = getComponent(providerId); ifTrueThen(component.isEnabled(), () -> providers.put(providerId, component)); }); if (providerList.isEmpty()) { diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java index cddab36f47..6ee0932d93 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/GcpIotAccessProvider.java @@ -127,6 +127,7 @@ protected CloudIot createCloudIotService() { } } + @Override public String updateConfig(String registryId, String deviceId, String config, Long version) { try { String useConfig = ofNullable(config).orElse(""); @@ -237,6 +238,7 @@ private String getProjectPath() { } @NotNull + @Override public Set getRegistriesForRegion(String region) { if (region == null) { return CLOUD_REGIONS; diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java index 1d57b72ca6..c66e94c3a9 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java @@ -67,6 +67,7 @@ private static String getBackoffKey(String registryId, String deviceId) { /** * Update the cached registry regions with any incremental updates. */ + @Override public void updateRegistryRegions(Map regions) { try { registryRegions.get().putAll(regions); @@ -199,6 +200,7 @@ public void activate() { /** * Modify a device configuration. Return the full/complete update that was actually written. */ + @Override public String modifyConfig(String registryId, String deviceId, Function munger) { int retryCount = CONFIG_UPDATE_MAX_RETRIES; try { diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java index 326c8e3582..5b03def7ef 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java @@ -1,17 +1,22 @@ package com.google.bos.udmi.service.access; import static java.lang.String.format; +import static java.util.Optional.ofNullable; import com.google.bos.udmi.service.pod.ContainerProvider; import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Function; import udmi.schema.CloudModel; import udmi.schema.Envelope.SubFolder; import udmi.schema.IotAccess; import udmi.schema.IotAccess.IotProvider; +/** + * Interface for things that provide for iot-access controls for connecting to devices. + */ public interface IotAccessProvider extends ContainerProvider { Map> PROVIDERS = ImmutableMap.of( @@ -27,8 +32,10 @@ public interface IotAccessProvider extends ContainerProvider { */ static IotAccessProvider from(IotAccess iotAccess) { try { - return PROVIDERS.get(iotAccess.provider).getDeclaredConstructor(IotAccess.class) - .newInstance(iotAccess); + IotAccessProvider provider = PROVIDERS.get(iotAccess.provider) + .getDeclaredConstructor(IotAccess.class).newInstance(iotAccess); + boolean createProxy = ofNullable(iotAccess.profile_sec).orElse(0) > 0; + return createProxy ? ProfilingProxy.create(provider, iotAccess.profile_sec) : provider; } catch (Exception e) { throw new RuntimeException( format("While instantiating access provider type %s", iotAccess.provider), e); @@ -37,6 +44,10 @@ static IotAccessProvider from(IotAccess iotAccess) { Entry fetchConfig(String registryId, String deviceId); + /** + * Get all the registries that exist in a given region. If region is null, then return + * all available regions. + */ Set getRegistriesForRegion(String region); boolean isEnabled(); @@ -57,4 +68,8 @@ CloudModel modelResource(String deviceRegistryId, String deviceId, CloudModel cloudModel); String fetchRegistryMetadata(String registryId, String metadataKey); + + void updateRegistryRegions(Map regions); + + String modifyConfig(String registryId, String deviceId, Function munger); } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java new file mode 100644 index 0000000000..4635b93922 --- /dev/null +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java @@ -0,0 +1,50 @@ +package com.google.bos.udmi.service.access; + +import static com.google.udmi.util.GeneralUtils.ifNotNullThen; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Class to handling profiling execution through a use of a java proxy. + */ +public class ProfilingProxy implements InvocationHandler { + + private final T provider; + + private ProfilingProxy(T provider) { + this.provider = provider; + } + + /** + * Create a new profiling instance for the given actual provider object. + */ + public static T create(T provider, int profileSec) { + Object[] objects = getAllInterfaces(provider.getClass()).toArray(); + Class[] interfaces = Arrays.copyOf(objects, objects.length, Class[].class); + //noinspection unchecked + return (T) Proxy.newProxyInstance(provider.getClass().getClassLoader(), + interfaces, new ProfilingProxy(provider)); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable { + return method.invoke(provider, objects); + } + + private static Set> getAllInterfaces(Class clazz) { + Class[] interfaces = clazz.getInterfaces(); + + Set> result = new HashSet<>(Arrays.asList(interfaces)); + + Arrays.asList(interfaces).forEach(iface -> result.addAll(getAllInterfaces(iface))); + ifNotNullThen(clazz.getSuperclass(), sclass -> result.addAll(getAllInterfaces(sclass))); + + return result; + } + +} diff --git a/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java index 5841a9a421..dd24fc084c 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/pod/ContainerProvider.java @@ -1,5 +1,8 @@ package com.google.bos.udmi.service.pod; +/** + * Simple interface for representing all containers. + */ public interface ContainerProvider { void activate(); From b0400b33b8bda17a4569fe1805f90cd2b314d9ad Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sun, 4 Feb 2024 08:24:26 -0800 Subject: [PATCH 05/13] Light refactoring --- .../access/ClearBladeIotAccessProvider.java | 9 ++--- .../service/access/IotAccessProvider.java | 37 +++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index d3da43368c..9650a07c99 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -71,7 +71,6 @@ import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -458,7 +457,7 @@ private CloudModel createRegistry(String registryId, Device device) { cloudModel.num_id = registryId; try { String location = getRegistryLocation(reflectRegistry); - DeviceManagerClient client = new DeviceManagerClient(); + DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); DeviceRegistry.Builder registry = DeviceRegistry.newBuilder() .setId(registryId) .setEventNotificationConfigs(ImmutableList.of(eventNotificationConfig())) @@ -467,7 +466,7 @@ private CloudModel createRegistry(String registryId, Device device) { CreateDeviceRegistryRequest request = CreateDeviceRegistryRequest.Builder.newBuilder() .setParent(LocationName.of(projectId, location).toString()) .setDeviceRegistry(registry.build()).build(); - client.createDeviceRegistry(request); + deviceManagerClient.createDeviceRegistry(request); } catch (ApplicationException applicationException) { if (!applicationException.getMessage().contains("ALREADY_EXISTS")) { throw applicationException; @@ -593,7 +592,7 @@ public void activate() { @Override public Entry fetchConfig(String registryId, String deviceId) { try { - DeviceManagerClient deviceManagerClient = new DeviceManagerClient(); + DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); String location = getRegistryLocation(registryId); ListDeviceConfigVersionsRequest request = ListDeviceConfigVersionsRequest.Builder.newBuilder() .setName(DeviceName.of(projectId, location, registryId, deviceId) @@ -648,7 +647,7 @@ public String fetchRegistryMetadata(String registryId, String metadataKey) { public String fetchState(String deviceRegistryId, String deviceId) { String devicePath = getDeviceName(deviceRegistryId, deviceId); try { - DeviceManagerClient deviceManagerClient = new DeviceManagerClient(); + DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); String location = getRegistryLocation(deviceRegistryId); DeviceName name = DeviceName.of(projectId, location, deviceRegistryId, deviceId); diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java index 5b03def7ef..24bd316eb1 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java @@ -1,7 +1,6 @@ package com.google.bos.udmi.service.access; import static java.lang.String.format; -import static java.util.Optional.ofNullable; import com.google.bos.udmi.service.pod.ContainerProvider; import com.google.common.collect.ImmutableMap; @@ -32,10 +31,8 @@ public interface IotAccessProvider extends ContainerProvider { */ static IotAccessProvider from(IotAccess iotAccess) { try { - IotAccessProvider provider = PROVIDERS.get(iotAccess.provider) - .getDeclaredConstructor(IotAccess.class).newInstance(iotAccess); - boolean createProxy = ofNullable(iotAccess.profile_sec).orElse(0) > 0; - return createProxy ? ProfilingProxy.create(provider, iotAccess.profile_sec) : provider; + return PROVIDERS.get(iotAccess.provider).getDeclaredConstructor(IotAccess.class) + .newInstance(iotAccess); } catch (Exception e) { throw new RuntimeException( format("While instantiating access provider type %s", iotAccess.provider), e); @@ -44,32 +41,32 @@ static IotAccessProvider from(IotAccess iotAccess) { Entry fetchConfig(String registryId, String deviceId); + CloudModel fetchDevice(String deviceRegistryId, String deviceId); + + String fetchRegistryMetadata(String registryId, String metadataKey); + + String fetchState(String deviceRegistryId, String deviceId); + /** - * Get all the registries that exist in a given region. If region is null, then return - * all available regions. + * Get all the registries that exist in a given region. If region is null, then return all + * available regions. */ Set getRegistriesForRegion(String region); boolean isEnabled(); - void sendCommandBase(String registryId, String deviceId, SubFolder folder, - String message); - - String updateConfig(String registryId, String deviceId, String config, - Long version); - - CloudModel fetchDevice(String deviceRegistryId, String deviceId); - - String fetchState(String deviceRegistryId, String deviceId); - CloudModel listDevices(String deviceRegistryId); CloudModel modelResource(String deviceRegistryId, String deviceId, CloudModel cloudModel); - String fetchRegistryMetadata(String registryId, String metadataKey); + String modifyConfig(String registryId, String deviceId, Function munger); - void updateRegistryRegions(Map regions); + void sendCommandBase(String registryId, String deviceId, SubFolder folder, + String message); - String modifyConfig(String registryId, String deviceId, Function munger); + String updateConfig(String registryId, String deviceId, String config, + Long version); + + void updateRegistryRegions(Map regions); } From fbfb82d716652a19eff57c8c424ca6e41f62f029 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sun, 4 Feb 2024 16:10:47 -0800 Subject: [PATCH 06/13] Add extracted interface --- .../access/ClearBladeIotAccessProvider.java | 65 +++++++++--------- .../access/DeviceManagerInterface.java | 67 +++++++++++++++++++ .../access/DeviceWrapperInterface.java | 10 +++ .../ClearBladeIotAccessProviderTest.java | 5 +- 4 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerInterface.java create mode 100644 udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index 9650a07c99..032b69da6c 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -21,7 +21,6 @@ import static udmi.schema.CloudModel.Resource_type.GATEWAY; import static udmi.schema.CloudModel.Resource_type.REGISTRY; -import com.clearblade.cloud.iot.v1.DeviceManagerClient; import com.clearblade.cloud.iot.v1.binddevicetogateway.BindDeviceToGatewayRequest; import com.clearblade.cloud.iot.v1.createdevice.CreateDeviceRequest; import com.clearblade.cloud.iot.v1.createdeviceregistry.CreateDeviceRegistryRequest; @@ -175,8 +174,8 @@ private static Resource_type resourceType(Device deviceRaw) { } @VisibleForTesting - protected DeviceManagerClient getDeviceManagerClient() { - return new DeviceManagerClient(); + protected DeviceManagerInterface getDeviceManagerInterface() { + return new DeviceWrapperInterface(); } @NotNull @@ -187,11 +186,11 @@ public Set getRegistriesForRegion(String region) { } try { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String parent = LocationName.of(projectId, region).getLocationFullName() + FIELD_MASK_HACK; ListDeviceRegistriesRequest request = ListDeviceRegistriesRequest.Builder.newBuilder() .setParent(parent).build(); - ListDeviceRegistriesResponse response = deviceManagerClient.listDeviceRegistries(request); + ListDeviceRegistriesResponse response = clearbladeManager.listDeviceRegistries(request); requireNonNull(response, "get registries response is null"); List deviceRegistries = response.getDeviceRegistriesList(); Set registries = @@ -213,7 +212,7 @@ public boolean isEnabled() { @Override public String updateConfig(String registryId, String deviceId, String config, Long version) { try { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); ByteString binaryData = new ByteString(encodeBase64(config)); String updateVersion = ifNotNullGet(version, v -> Long.toString(version)); String location = getRegistryLocation(registryId); @@ -221,7 +220,7 @@ public String updateConfig(String registryId, String deviceId, String config, Lo ModifyCloudToDeviceConfigRequest.Builder.newBuilder() .setName(DeviceName.of(projectId, location, registryId, deviceId).toString()) .setBinaryData(binaryData).setVersionToUpdate(updateVersion).build(); - DeviceConfig response = deviceManagerClient.modifyCloudToDeviceConfig(request); + DeviceConfig response = clearbladeManager.modifyCloudToDeviceConfig(request); debug("Modified %s/%s config version %s", registryId, deviceId, response.getVersion()); return config; } catch (Exception e) { @@ -247,8 +246,8 @@ private CloudModel bindDeviceToGateway(String registryId, String gatewayId, .setDevice(id) .setGateway(gatewayId) .build(); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); - requireNonNull(deviceManagerClient.bindDeviceToGateway(request), + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); + requireNonNull(clearbladeManager.bindDeviceToGateway(request), "binding device to gateway"); } catch (Exception e) { throw new RuntimeException(format("While binding %s to gateway %s", id, gatewayId), e); @@ -308,13 +307,13 @@ private CloudModel createDevice(String registryId, Device device) { CloudModel cloudModel = new CloudModel(); cloudModel.operation = CREATE; try { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); String parent = RegistryName.of(projectId, location, registryId).toString(); CreateDeviceRequest request = CreateDeviceRequest.Builder.newBuilder().setParent(parent).setDevice(device) .build(); - requireNonNull(deviceManagerClient.createDevice(request), + requireNonNull(clearbladeManager.createDevice(request), "create device failed for " + parent); cloudModel.num_id = hashedDeviceId(registryId, device.toBuilder().getId()); return cloudModel; @@ -330,7 +329,7 @@ private CloudModel createDevice(String registryId, Device device) { @NotNull private HashMap fetchDevices(String deviceRegistryId, String gatewayId) { String location = getRegistryLocation(deviceRegistryId); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); GatewayListOptions gatewayListOptions = ifNotNullGet(gatewayId, this::getGatewayListOptions); String registryFullName = RegistryName.of(projectId, location, deviceRegistryId).getRegistryFullName(); @@ -342,7 +341,7 @@ private HashMap fetchDevices(String deviceRegistryId, String .setGatewayListOptions(gatewayListOptions) .setPageToken(pageToken) .build(); - DevicesListResponse response = deviceManagerClient.listDevices(request); + DevicesListResponse response = clearbladeManager.listDevices(request); requireNonNull(response, "DeviceRegistriesList fetch failed"); Map responseMap = response.getDevicesList().stream().map(ClearBladeIotAccessProvider::convertToEntry) @@ -377,7 +376,7 @@ private String getRegistryName(String registryId) { private Entry> listDevicesPage(String deviceRegistryId, String gatewayId, String pageToken) { String location = getRegistryLocation(deviceRegistryId); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); GatewayListOptions gatewayListOptions = ifNotNullGet(gatewayId, this::getGatewayListOptions); String registryFullName = @@ -387,7 +386,7 @@ private Entry> listDevicesPage(String device .setGatewayListOptions(gatewayListOptions) .setPageToken(pageToken) .build(); - DevicesListResponse response = deviceManagerClient.listDevices(request); + DevicesListResponse response = clearbladeManager.listDevices(request); requireNonNull(response, "DeviceRegistriesList fetch failed"); HashMap devices = response.getDevicesList().stream().map(ClearBladeIotAccessProvider::convertToEntry) @@ -457,7 +456,7 @@ private CloudModel createRegistry(String registryId, Device device) { cloudModel.num_id = registryId; try { String location = getRegistryLocation(reflectRegistry); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); DeviceRegistry.Builder registry = DeviceRegistry.newBuilder() .setId(registryId) .setEventNotificationConfigs(ImmutableList.of(eventNotificationConfig())) @@ -466,7 +465,7 @@ private CloudModel createRegistry(String registryId, Device device) { CreateDeviceRegistryRequest request = CreateDeviceRegistryRequest.Builder.newBuilder() .setParent(LocationName.of(projectId, location).toString()) .setDeviceRegistry(registry.build()).build(); - deviceManagerClient.createDeviceRegistry(request); + clearbladeManager.createDeviceRegistry(request); } catch (ApplicationException applicationException) { if (!applicationException.getMessage().contains("ALREADY_EXISTS")) { throw applicationException; @@ -494,12 +493,12 @@ private CloudModel unbindAndDelete(String registryId, Device device) { String deviceId = requireNonNull(device.toBuilder().getId(), "unspecified device id"); try { unbindGatewayDevices(registryId, device); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); DeviceName deviceName = DeviceName.of(projectId, location, registryId, deviceId); DeleteDeviceRequest request = DeleteDeviceRequest.Builder.newBuilder().setName(deviceName).build(); - deviceManagerClient.deleteDevice(request); + clearbladeManager.deleteDevice(request); CloudModel cloudModel = new CloudModel(); cloudModel.operation = DELETE; cloudModel.num_id = hashedDeviceId(registryId, deviceId); @@ -512,12 +511,12 @@ private CloudModel unbindAndDelete(String registryId, Device device) { private void unbindDevice(String registryId, String gatewayId, String proxyId) { try { debug(format("Unbind %s: %s from %s", registryId, proxyId, gatewayId)); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); UnbindDeviceFromGatewayRequest request = UnbindDeviceFromGatewayRequest.Builder.newBuilder() .setParent(RegistryName.of(projectId, location, registryId).getRegistryFullName()) .setGateway(gatewayId).setDevice(proxyId).build(); - requireNonNull(deviceManagerClient.unbindDeviceFromGateway(request), "invalid response"); + requireNonNull(clearbladeManager.unbindDeviceFromGateway(request), "invalid response"); } catch (Exception e) { throw new RuntimeException("While unbinding " + proxyId + " from " + gatewayId, e); } @@ -535,7 +534,7 @@ private void unbindGatewayDevices(String registryId, Device device) { } private CloudModel blockDevice(String registryId, Device device) { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String deviceId = device.toBuilder().getId(); String name = getDeviceName(registryId, deviceId); Device fullDevice = device.toBuilder().setName(name).setBlocked(true).build(); @@ -543,7 +542,7 @@ private CloudModel blockDevice(String registryId, Device device) { UpdateDeviceRequest request = UpdateDeviceRequest.Builder.newBuilder().setDevice(fullDevice).setName(name) .setUpdateMask(BLOCKED_FIELD_MASK).build(); - requireNonNull(deviceManagerClient.updateDevice(request), "Invalid RPC response"); + requireNonNull(clearbladeManager.updateDevice(request), "Invalid RPC response"); CloudModel cloudModel = new CloudModel(); cloudModel.operation = Operation.BLOCK; cloudModel.num_id = hashedDeviceId(registryId, deviceId); @@ -554,7 +553,7 @@ private CloudModel blockDevice(String registryId, Device device) { } private CloudModel updateDevice(String registryId, Device device) { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String deviceId = device.toBuilder().getId(); String name = getDeviceName(registryId, deviceId); Device fullDevice = device.toBuilder().setName(name).build(); @@ -562,7 +561,7 @@ private CloudModel updateDevice(String registryId, Device device) { UpdateDeviceRequest request = UpdateDeviceRequest.Builder.newBuilder().setDevice(fullDevice).setName(name) .setUpdateMask(UPDATE_FIELD_MASK).build(); - requireNonNull(deviceManagerClient.updateDevice(request), "Invalid RPC response"); + requireNonNull(clearbladeManager.updateDevice(request), "Invalid RPC response"); CloudModel cloudModel = new CloudModel(); cloudModel.operation = Operation.UPDATE; cloudModel.num_id = hashedDeviceId(registryId, deviceId); @@ -592,13 +591,13 @@ public void activate() { @Override public Entry fetchConfig(String registryId, String deviceId) { try { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); ListDeviceConfigVersionsRequest request = ListDeviceConfigVersionsRequest.Builder.newBuilder() .setName(DeviceName.of(projectId, location, registryId, deviceId) .toString()).setNumVersions(1).build(); ListDeviceConfigVersionsResponse listDeviceConfigVersionsResponse = - deviceManagerClient.listDeviceConfigVersions(request); + clearbladeManager.listDeviceConfigVersions(request); List deviceConfigs = listDeviceConfigVersionsResponse.getDeviceConfigList(); if (deviceConfigs.isEmpty()) { return new SimpleEntry<>(null, EMPTY_JSON); @@ -617,11 +616,11 @@ public CloudModel fetchDevice(String deviceRegistryId, String deviceId) { String devicePath = getDeviceName(deviceRegistryId, deviceId); try { String location = getRegistryLocation(deviceRegistryId); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); DeviceName name = DeviceName.of(projectId, location, deviceRegistryId, deviceId); GetDeviceRequest request = GetDeviceRequest.Builder.newBuilder().setName(name) .setFieldMask(FieldMask.newBuilder().build()).build(); - Device device = deviceManagerClient.getDevice(request); + Device device = clearbladeManager.getDevice(request); requireNonNull(device, "GetDeviceRequest failed"); CloudModel cloudModel = convert(device, Operation.FETCH); cloudModel.device_ids = listRegistryDevices(deviceRegistryId, deviceId).device_ids; @@ -647,7 +646,7 @@ public String fetchRegistryMetadata(String registryId, String metadataKey) { public String fetchState(String deviceRegistryId, String deviceId) { String devicePath = getDeviceName(deviceRegistryId, deviceId); try { - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(deviceRegistryId); DeviceName name = DeviceName.of(projectId, location, deviceRegistryId, deviceId); @@ -655,7 +654,7 @@ public String fetchState(String deviceRegistryId, String deviceId) { .setName(name.toString()) .setNumStates(1).build(); ListDeviceStatesResponse response = requireNonNull( - deviceManagerClient.listDeviceStates(request), "Null response returned"); + clearbladeManager.listDeviceStates(request), "Null response returned"); List deviceStatesList = response.getDeviceStatesList(); return deviceStatesList.isEmpty() ? null : (String) deviceStatesList.get(0).getBinaryData(); } catch (Exception e) { @@ -683,14 +682,14 @@ public void sendCommandBase(String registryId, String deviceId, SubFolder folder try { ByteString binaryData = new ByteString(encodeBase64(message)); String location = getRegistryLocation(registryId); - DeviceManagerClient deviceManagerClient = getDeviceManagerClient(); + DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String deviceName = DeviceName.of(projectId, location, registryId, deviceId).toString(); SendCommandToDeviceRequest request = SendCommandToDeviceRequest.Builder.newBuilder() .setName(deviceName) .setBinaryData(binaryData) .setSubfolder(subFolder) .build(); - SendCommandToDeviceResponse response = deviceManagerClient.sendCommandToDevice(request); + SendCommandToDeviceResponse response = clearbladeManager.sendCommandToDevice(request); if (response == null) { throw new RuntimeException("SendCommandToDevice execution failed for " + deviceName); } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerInterface.java b/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerInterface.java new file mode 100644 index 0000000000..cfe87f0f17 --- /dev/null +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerInterface.java @@ -0,0 +1,67 @@ +package com.google.bos.udmi.service.access; + +import com.clearblade.cloud.iot.v1.binddevicetogateway.BindDeviceToGatewayRequest; +import com.clearblade.cloud.iot.v1.binddevicetogateway.BindDeviceToGatewayResponse; +import com.clearblade.cloud.iot.v1.createdevice.CreateDeviceRequest; +import com.clearblade.cloud.iot.v1.createdeviceregistry.CreateDeviceRegistryRequest; +import com.clearblade.cloud.iot.v1.deletedevice.DeleteDeviceRequest; +import com.clearblade.cloud.iot.v1.deletedeviceregistry.DeleteDeviceRegistryRequest; +import com.clearblade.cloud.iot.v1.deviceslist.DevicesListRequest; +import com.clearblade.cloud.iot.v1.deviceslist.DevicesListResponse; +import com.clearblade.cloud.iot.v1.devicestateslist.ListDeviceStatesRequest; +import com.clearblade.cloud.iot.v1.devicestateslist.ListDeviceStatesResponse; +import com.clearblade.cloud.iot.v1.devicetypes.Device; +import com.clearblade.cloud.iot.v1.devicetypes.DeviceConfig; +import com.clearblade.cloud.iot.v1.getdevice.GetDeviceRequest; +import com.clearblade.cloud.iot.v1.getdeviceregistry.GetDeviceRegistryRequest; +import com.clearblade.cloud.iot.v1.listdeviceconfigversions.ListDeviceConfigVersionsRequest; +import com.clearblade.cloud.iot.v1.listdeviceconfigversions.ListDeviceConfigVersionsResponse; +import com.clearblade.cloud.iot.v1.listdeviceregistries.ListDeviceRegistriesRequest; +import com.clearblade.cloud.iot.v1.listdeviceregistries.ListDeviceRegistriesResponse; +import com.clearblade.cloud.iot.v1.modifycloudtodeviceconfig.ModifyCloudToDeviceConfigRequest; +import com.clearblade.cloud.iot.v1.registrytypes.DeviceRegistry; +import com.clearblade.cloud.iot.v1.sendcommandtodevice.SendCommandToDeviceRequest; +import com.clearblade.cloud.iot.v1.sendcommandtodevice.SendCommandToDeviceResponse; +import com.clearblade.cloud.iot.v1.unbinddevicefromgateway.UnbindDeviceFromGatewayRequest; +import com.clearblade.cloud.iot.v1.unbinddevicefromgateway.UnbindDeviceFromGatewayResponse; +import com.clearblade.cloud.iot.v1.updatedevice.UpdateDeviceRequest; +import com.clearblade.cloud.iot.v1.updatedeviceregistry.UpdateDeviceRegistryRequest; + +/** + * Manager interface for device management actions. + */ +public interface DeviceManagerInterface { + + Device getDevice(GetDeviceRequest request); + + Device createDevice(CreateDeviceRequest request); + + BindDeviceToGatewayResponse bindDeviceToGateway(BindDeviceToGatewayRequest request); + + UnbindDeviceFromGatewayResponse unbindDeviceFromGateway(UnbindDeviceFromGatewayRequest request); + + void deleteDevice(DeleteDeviceRequest request); + + Device updateDevice(UpdateDeviceRequest request); + + SendCommandToDeviceResponse sendCommandToDevice(SendCommandToDeviceRequest request); + + DevicesListResponse listDevices(DevicesListRequest request); + + DeviceConfig modifyCloudToDeviceConfig(ModifyCloudToDeviceConfigRequest request); + + ListDeviceStatesResponse listDeviceStates(ListDeviceStatesRequest request); + + ListDeviceConfigVersionsResponse listDeviceConfigVersions( + ListDeviceConfigVersionsRequest request); + + DeviceRegistry getDeviceRegistry(GetDeviceRegistryRequest request); + + DeviceRegistry createDeviceRegistry(CreateDeviceRegistryRequest request); + + DeviceRegistry updateDeviceRegistry(UpdateDeviceRegistryRequest request); + + void deleteDeviceRegistry(DeleteDeviceRegistryRequest request); + + ListDeviceRegistriesResponse listDeviceRegistries(ListDeviceRegistriesRequest request); +} diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java b/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java new file mode 100644 index 0000000000..be3d1c5e76 --- /dev/null +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java @@ -0,0 +1,10 @@ +package com.google.bos.udmi.service.access; + +import com.clearblade.cloud.iot.v1.DeviceManagerClient; + +/** + * Shell wrapper class to provide an interface attached (without src access). + */ +public class DeviceWrapperInterface extends DeviceManagerClient implements DeviceManagerInterface { + +} diff --git a/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java b/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java index 675607115a..ce5ff4f90a 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java @@ -5,7 +5,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.clearblade.cloud.iot.v1.DeviceManagerClient; import com.clearblade.cloud.iot.v1.deviceslist.DevicesListRequest; import com.clearblade.cloud.iot.v1.deviceslist.DevicesListResponse; import com.clearblade.cloud.iot.v1.devicetypes.Device; @@ -22,7 +21,7 @@ class ClearBladeIotAccessProviderTest extends MessageTestCore { - private final DeviceManagerClient mockClient = mock(DeviceManagerClient.class); + private final DeviceManagerInterface mockClient = mock(DeviceManagerInterface.class); @NotNull private ClearBladeIotAccessProvider getProvider() { @@ -45,7 +44,7 @@ protected Map fetchRegistryRegions() { } @Override - protected DeviceManagerClient getDeviceManagerClient() { + protected DeviceManagerInterface getDeviceManagerInterface() { return mockClient; } } From 5014b26cf050b1cbc6c675415afebeae37994fc1 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Sun, 4 Feb 2024 16:21:19 -0800 Subject: [PATCH 07/13] Adding profiling proxy --- .../access/ClearBladeIotAccessProvider.java | 51 +++++++------------ ...terface.java => DeviceManagerWrapper.java} | 2 +- .../udmi/service/access/ProfilingProxy.java | 7 +++ .../ClearBladeIotAccessProviderTest.java | 2 +- 4 files changed, 28 insertions(+), 34 deletions(-) rename udmis/src/main/java/com/google/bos/udmi/service/access/{DeviceWrapperInterface.java => DeviceManagerWrapper.java} (65%) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index 032b69da6c..f58cff0987 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -117,12 +117,14 @@ public class ClearBladeIotAccessProvider extends IotAccessBase { private static final String FIELD_MASK_HACK = "&fieldMask=id%2Cname"; private final String projectId; + private final DeviceManagerInterface deviceManager; /** * Create a new instance for interfacing with GCP IoT Core. */ public ClearBladeIotAccessProvider(IotAccess iotAccess) { super(iotAccess); + deviceManager = getDeviceManager(ofNullable(iotAccess.profile_sec).orElse(0)); projectId = getProjectId(iotAccess); info("Fetching registry regions..."); ifTrueThen(isEnabled(), this::fetchRegistryRegions); @@ -174,8 +176,8 @@ private static Resource_type resourceType(Device deviceRaw) { } @VisibleForTesting - protected DeviceManagerInterface getDeviceManagerInterface() { - return new DeviceWrapperInterface(); + protected DeviceManagerInterface getDeviceManager(int monitor_sec) { + return ProfilingProxy.create(new DeviceManagerWrapper(), monitor_sec); } @NotNull @@ -186,11 +188,10 @@ public Set getRegistriesForRegion(String region) { } try { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String parent = LocationName.of(projectId, region).getLocationFullName() + FIELD_MASK_HACK; ListDeviceRegistriesRequest request = ListDeviceRegistriesRequest.Builder.newBuilder() .setParent(parent).build(); - ListDeviceRegistriesResponse response = clearbladeManager.listDeviceRegistries(request); + ListDeviceRegistriesResponse response = deviceManager.listDeviceRegistries(request); requireNonNull(response, "get registries response is null"); List deviceRegistries = response.getDeviceRegistriesList(); Set registries = @@ -212,7 +213,6 @@ public boolean isEnabled() { @Override public String updateConfig(String registryId, String deviceId, String config, Long version) { try { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); ByteString binaryData = new ByteString(encodeBase64(config)); String updateVersion = ifNotNullGet(version, v -> Long.toString(version)); String location = getRegistryLocation(registryId); @@ -220,7 +220,7 @@ public String updateConfig(String registryId, String deviceId, String config, Lo ModifyCloudToDeviceConfigRequest.Builder.newBuilder() .setName(DeviceName.of(projectId, location, registryId, deviceId).toString()) .setBinaryData(binaryData).setVersionToUpdate(updateVersion).build(); - DeviceConfig response = clearbladeManager.modifyCloudToDeviceConfig(request); + DeviceConfig response = deviceManager.modifyCloudToDeviceConfig(request); debug("Modified %s/%s config version %s", registryId, deviceId, response.getVersion()); return config; } catch (Exception e) { @@ -246,8 +246,7 @@ private CloudModel bindDeviceToGateway(String registryId, String gatewayId, .setDevice(id) .setGateway(gatewayId) .build(); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); - requireNonNull(clearbladeManager.bindDeviceToGateway(request), + requireNonNull(deviceManager.bindDeviceToGateway(request), "binding device to gateway"); } catch (Exception e) { throw new RuntimeException(format("While binding %s to gateway %s", id, gatewayId), e); @@ -307,13 +306,12 @@ private CloudModel createDevice(String registryId, Device device) { CloudModel cloudModel = new CloudModel(); cloudModel.operation = CREATE; try { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); String parent = RegistryName.of(projectId, location, registryId).toString(); CreateDeviceRequest request = CreateDeviceRequest.Builder.newBuilder().setParent(parent).setDevice(device) .build(); - requireNonNull(clearbladeManager.createDevice(request), + requireNonNull(deviceManager.createDevice(request), "create device failed for " + parent); cloudModel.num_id = hashedDeviceId(registryId, device.toBuilder().getId()); return cloudModel; @@ -329,7 +327,6 @@ private CloudModel createDevice(String registryId, Device device) { @NotNull private HashMap fetchDevices(String deviceRegistryId, String gatewayId) { String location = getRegistryLocation(deviceRegistryId); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); GatewayListOptions gatewayListOptions = ifNotNullGet(gatewayId, this::getGatewayListOptions); String registryFullName = RegistryName.of(projectId, location, deviceRegistryId).getRegistryFullName(); @@ -341,7 +338,7 @@ private HashMap fetchDevices(String deviceRegistryId, String .setGatewayListOptions(gatewayListOptions) .setPageToken(pageToken) .build(); - DevicesListResponse response = clearbladeManager.listDevices(request); + DevicesListResponse response = deviceManager.listDevices(request); requireNonNull(response, "DeviceRegistriesList fetch failed"); Map responseMap = response.getDevicesList().stream().map(ClearBladeIotAccessProvider::convertToEntry) @@ -376,7 +373,6 @@ private String getRegistryName(String registryId) { private Entry> listDevicesPage(String deviceRegistryId, String gatewayId, String pageToken) { String location = getRegistryLocation(deviceRegistryId); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); GatewayListOptions gatewayListOptions = ifNotNullGet(gatewayId, this::getGatewayListOptions); String registryFullName = @@ -386,7 +382,7 @@ private Entry> listDevicesPage(String device .setGatewayListOptions(gatewayListOptions) .setPageToken(pageToken) .build(); - DevicesListResponse response = clearbladeManager.listDevices(request); + DevicesListResponse response = deviceManager.listDevices(request); requireNonNull(response, "DeviceRegistriesList fetch failed"); HashMap devices = response.getDevicesList().stream().map(ClearBladeIotAccessProvider::convertToEntry) @@ -456,7 +452,6 @@ private CloudModel createRegistry(String registryId, Device device) { cloudModel.num_id = registryId; try { String location = getRegistryLocation(reflectRegistry); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); DeviceRegistry.Builder registry = DeviceRegistry.newBuilder() .setId(registryId) .setEventNotificationConfigs(ImmutableList.of(eventNotificationConfig())) @@ -465,7 +460,7 @@ private CloudModel createRegistry(String registryId, Device device) { CreateDeviceRegistryRequest request = CreateDeviceRegistryRequest.Builder.newBuilder() .setParent(LocationName.of(projectId, location).toString()) .setDeviceRegistry(registry.build()).build(); - clearbladeManager.createDeviceRegistry(request); + deviceManager.createDeviceRegistry(request); } catch (ApplicationException applicationException) { if (!applicationException.getMessage().contains("ALREADY_EXISTS")) { throw applicationException; @@ -493,12 +488,11 @@ private CloudModel unbindAndDelete(String registryId, Device device) { String deviceId = requireNonNull(device.toBuilder().getId(), "unspecified device id"); try { unbindGatewayDevices(registryId, device); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); DeviceName deviceName = DeviceName.of(projectId, location, registryId, deviceId); DeleteDeviceRequest request = DeleteDeviceRequest.Builder.newBuilder().setName(deviceName).build(); - clearbladeManager.deleteDevice(request); + deviceManager.deleteDevice(request); CloudModel cloudModel = new CloudModel(); cloudModel.operation = DELETE; cloudModel.num_id = hashedDeviceId(registryId, deviceId); @@ -511,12 +505,11 @@ private CloudModel unbindAndDelete(String registryId, Device device) { private void unbindDevice(String registryId, String gatewayId, String proxyId) { try { debug(format("Unbind %s: %s from %s", registryId, proxyId, gatewayId)); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); UnbindDeviceFromGatewayRequest request = UnbindDeviceFromGatewayRequest.Builder.newBuilder() .setParent(RegistryName.of(projectId, location, registryId).getRegistryFullName()) .setGateway(gatewayId).setDevice(proxyId).build(); - requireNonNull(clearbladeManager.unbindDeviceFromGateway(request), "invalid response"); + requireNonNull(deviceManager.unbindDeviceFromGateway(request), "invalid response"); } catch (Exception e) { throw new RuntimeException("While unbinding " + proxyId + " from " + gatewayId, e); } @@ -534,7 +527,6 @@ private void unbindGatewayDevices(String registryId, Device device) { } private CloudModel blockDevice(String registryId, Device device) { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String deviceId = device.toBuilder().getId(); String name = getDeviceName(registryId, deviceId); Device fullDevice = device.toBuilder().setName(name).setBlocked(true).build(); @@ -542,7 +534,7 @@ private CloudModel blockDevice(String registryId, Device device) { UpdateDeviceRequest request = UpdateDeviceRequest.Builder.newBuilder().setDevice(fullDevice).setName(name) .setUpdateMask(BLOCKED_FIELD_MASK).build(); - requireNonNull(clearbladeManager.updateDevice(request), "Invalid RPC response"); + requireNonNull(deviceManager.updateDevice(request), "Invalid RPC response"); CloudModel cloudModel = new CloudModel(); cloudModel.operation = Operation.BLOCK; cloudModel.num_id = hashedDeviceId(registryId, deviceId); @@ -553,7 +545,6 @@ private CloudModel blockDevice(String registryId, Device device) { } private CloudModel updateDevice(String registryId, Device device) { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String deviceId = device.toBuilder().getId(); String name = getDeviceName(registryId, deviceId); Device fullDevice = device.toBuilder().setName(name).build(); @@ -561,7 +552,7 @@ private CloudModel updateDevice(String registryId, Device device) { UpdateDeviceRequest request = UpdateDeviceRequest.Builder.newBuilder().setDevice(fullDevice).setName(name) .setUpdateMask(UPDATE_FIELD_MASK).build(); - requireNonNull(clearbladeManager.updateDevice(request), "Invalid RPC response"); + requireNonNull(deviceManager.updateDevice(request), "Invalid RPC response"); CloudModel cloudModel = new CloudModel(); cloudModel.operation = Operation.UPDATE; cloudModel.num_id = hashedDeviceId(registryId, deviceId); @@ -591,13 +582,12 @@ public void activate() { @Override public Entry fetchConfig(String registryId, String deviceId) { try { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(registryId); ListDeviceConfigVersionsRequest request = ListDeviceConfigVersionsRequest.Builder.newBuilder() .setName(DeviceName.of(projectId, location, registryId, deviceId) .toString()).setNumVersions(1).build(); ListDeviceConfigVersionsResponse listDeviceConfigVersionsResponse = - clearbladeManager.listDeviceConfigVersions(request); + deviceManager.listDeviceConfigVersions(request); List deviceConfigs = listDeviceConfigVersionsResponse.getDeviceConfigList(); if (deviceConfigs.isEmpty()) { return new SimpleEntry<>(null, EMPTY_JSON); @@ -616,11 +606,10 @@ public CloudModel fetchDevice(String deviceRegistryId, String deviceId) { String devicePath = getDeviceName(deviceRegistryId, deviceId); try { String location = getRegistryLocation(deviceRegistryId); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); DeviceName name = DeviceName.of(projectId, location, deviceRegistryId, deviceId); GetDeviceRequest request = GetDeviceRequest.Builder.newBuilder().setName(name) .setFieldMask(FieldMask.newBuilder().build()).build(); - Device device = clearbladeManager.getDevice(request); + Device device = deviceManager.getDevice(request); requireNonNull(device, "GetDeviceRequest failed"); CloudModel cloudModel = convert(device, Operation.FETCH); cloudModel.device_ids = listRegistryDevices(deviceRegistryId, deviceId).device_ids; @@ -646,7 +635,6 @@ public String fetchRegistryMetadata(String registryId, String metadataKey) { public String fetchState(String deviceRegistryId, String deviceId) { String devicePath = getDeviceName(deviceRegistryId, deviceId); try { - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String location = getRegistryLocation(deviceRegistryId); DeviceName name = DeviceName.of(projectId, location, deviceRegistryId, deviceId); @@ -654,7 +642,7 @@ public String fetchState(String deviceRegistryId, String deviceId) { .setName(name.toString()) .setNumStates(1).build(); ListDeviceStatesResponse response = requireNonNull( - clearbladeManager.listDeviceStates(request), "Null response returned"); + deviceManager.listDeviceStates(request), "Null response returned"); List deviceStatesList = response.getDeviceStatesList(); return deviceStatesList.isEmpty() ? null : (String) deviceStatesList.get(0).getBinaryData(); } catch (Exception e) { @@ -682,14 +670,13 @@ public void sendCommandBase(String registryId, String deviceId, SubFolder folder try { ByteString binaryData = new ByteString(encodeBase64(message)); String location = getRegistryLocation(registryId); - DeviceManagerInterface clearbladeManager = getDeviceManagerInterface(); String deviceName = DeviceName.of(projectId, location, registryId, deviceId).toString(); SendCommandToDeviceRequest request = SendCommandToDeviceRequest.Builder.newBuilder() .setName(deviceName) .setBinaryData(binaryData) .setSubfolder(subFolder) .build(); - SendCommandToDeviceResponse response = clearbladeManager.sendCommandToDevice(request); + SendCommandToDeviceResponse response = deviceManager.sendCommandToDevice(request); if (response == null) { throw new RuntimeException("SendCommandToDevice execution failed for " + deviceName); } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java b/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerWrapper.java similarity index 65% rename from udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java rename to udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerWrapper.java index be3d1c5e76..a49fb117da 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceWrapperInterface.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/DeviceManagerWrapper.java @@ -5,6 +5,6 @@ /** * Shell wrapper class to provide an interface attached (without src access). */ -public class DeviceWrapperInterface extends DeviceManagerClient implements DeviceManagerInterface { +public class DeviceManagerWrapper extends DeviceManagerClient implements DeviceManagerInterface { } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java index 4635b93922..b9e87c43ae 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java @@ -1,7 +1,9 @@ package com.google.bos.udmi.service.access; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; +import com.google.common.base.Preconditions; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -24,6 +26,11 @@ private ProfilingProxy(T provider) { * Create a new profiling instance for the given actual provider object. */ public static T create(T provider, int profileSec) { + checkArgument(profileSec >= 0, "Illegal profile period " + profileSec); + if (profileSec == 0) { + return provider; + } + Object[] objects = getAllInterfaces(provider.getClass()).toArray(); Class[] interfaces = Arrays.copyOf(objects, objects.length, Class[].class); //noinspection unchecked diff --git a/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java b/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java index ce5ff4f90a..efe132f2cf 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java @@ -44,7 +44,7 @@ protected Map fetchRegistryRegions() { } @Override - protected DeviceManagerInterface getDeviceManagerInterface() { + protected DeviceManagerInterface getDeviceManager(int monitor_sec) { return mockClient; } } From dc71ae87e79d70c78fabc960d8d8334f4f7b722b Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Mon, 5 Feb 2024 07:52:45 -0800 Subject: [PATCH 08/13] Fix linty --- .../bos/udmi/service/access/ClearBladeIotAccessProvider.java | 4 ++-- .../udmi/service/access/ClearBladeIotAccessProviderTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index f58cff0987..737a189fb0 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -176,8 +176,8 @@ private static Resource_type resourceType(Device deviceRaw) { } @VisibleForTesting - protected DeviceManagerInterface getDeviceManager(int monitor_sec) { - return ProfilingProxy.create(new DeviceManagerWrapper(), monitor_sec); + protected DeviceManagerInterface getDeviceManager(int monitorSec) { + return ProfilingProxy.create(new DeviceManagerWrapper(), monitorSec); } @NotNull diff --git a/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java b/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java index efe132f2cf..6398213742 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProviderTest.java @@ -44,7 +44,7 @@ protected Map fetchRegistryRegions() { } @Override - protected DeviceManagerInterface getDeviceManager(int monitor_sec) { + protected DeviceManagerInterface getDeviceManager(int monitorSec) { return mockClient; } } From bcb3e5c76b988425997342aefac97d9f27e0fcfa Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Mon, 5 Feb 2024 09:08:46 -0800 Subject: [PATCH 09/13] Add log-based profiling of methods --- .../access/ClearBladeIotAccessProvider.java | 2 +- .../udmi/service/access/ProfilingProxy.java | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java index 737a189fb0..0159d7f4ee 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ClearBladeIotAccessProvider.java @@ -177,7 +177,7 @@ private static Resource_type resourceType(Device deviceRaw) { @VisibleForTesting protected DeviceManagerInterface getDeviceManager(int monitorSec) { - return ProfilingProxy.create(new DeviceManagerWrapper(), monitorSec); + return ProfilingProxy.create(this, new DeviceManagerWrapper(), monitorSec); } @NotNull diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java index b9e87c43ae..2cb5876bab 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java @@ -3,10 +3,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; -import com.google.common.base.Preconditions; +import com.google.bos.udmi.service.pod.ContainerProvider; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -17,15 +19,19 @@ public class ProfilingProxy implements InvocationHandler { private final T provider; + private final ContainerProvider container; + private String providerName; - private ProfilingProxy(T provider) { + private ProfilingProxy(ContainerProvider container, T provider) { this.provider = provider; + this.container = container; + providerName = provider.getClass().getSimpleName(); } /** * Create a new profiling instance for the given actual provider object. */ - public static T create(T provider, int profileSec) { + public static T create(ContainerProvider container, T provider, int profileSec) { checkArgument(profileSec >= 0, "Illegal profile period " + profileSec); if (profileSec == 0) { return provider; @@ -35,12 +41,18 @@ public static T create(T provider, int profileSec) { Class[] interfaces = Arrays.copyOf(objects, objects.length, Class[].class); //noinspection unchecked return (T) Proxy.newProxyInstance(provider.getClass().getClassLoader(), - interfaces, new ProfilingProxy(provider)); + interfaces, new ProfilingProxy(container, provider)); } @Override public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable { - return method.invoke(provider, objects); + Instant start = Instant.now(); + try { + return method.invoke(provider, objects); + } finally { + double durationSec = Duration.between(start, Instant.now()).toMillis() / 1000.0; + container.debug("Method %s#%s took %.03f", providerName, method.getName(), durationSec); + } } private static Set> getAllInterfaces(Class clazz) { From a7bf8007632ffd43f8adf1c5f9abecb6eaaf52af Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Mon, 5 Feb 2024 13:45:14 -0800 Subject: [PATCH 10/13] Remove stagger --- .github/workflows/testing.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index ffd386b5f5..fc9b1d6037 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -149,8 +149,6 @@ jobs: java-version: '17' - name: base setup run: bin/run_tests install_dependencies - - name: stagger startup - run: sleep $(($MATRIX_SHARD_INDEX * 20 + 20)) - name: registrar clean run: bin/test_regclean $TARGET_PROJECT - name: sequence tests clean From 764b7ae49d1e4a2239db200046568ff8dcb6d48b Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 6 Feb 2024 09:16:25 -0800 Subject: [PATCH 11/13] Increase logging info --- .../udmi/service/access/ProfilingProxy.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java index 2cb5876bab..1d24924243 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ProfilingProxy.java @@ -2,6 +2,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; +import static java.util.Optional.ofNullable; import com.google.bos.udmi.service.pod.ContainerProvider; import java.lang.reflect.InvocationHandler; @@ -20,7 +21,7 @@ public class ProfilingProxy implements InvocationHandler { private final T provider; private final ContainerProvider container; - private String providerName; + private final String providerName; private ProfilingProxy(ContainerProvider container, T provider) { this.provider = provider; @@ -44,17 +45,6 @@ public static T create(ContainerProvider container, T provider, int profileS interfaces, new ProfilingProxy(container, provider)); } - @Override - public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable { - Instant start = Instant.now(); - try { - return method.invoke(provider, objects); - } finally { - double durationSec = Duration.between(start, Instant.now()).toMillis() / 1000.0; - container.debug("Method %s#%s took %.03f", providerName, method.getName(), durationSec); - } - } - private static Set> getAllInterfaces(Class clazz) { Class[] interfaces = clazz.getInterfaces(); @@ -66,4 +56,20 @@ private static Set> getAllInterfaces(Class clazz) { return result; } + @Override + public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable { + Instant start = Instant.now(); + Throwable caught = null; + try { + return method.invoke(provider, objects); + } catch (Throwable throwable) { + caught = throwable; + throw throwable; + } finally { + double durationSec = Duration.between(start, Instant.now()).toMillis() / 1000.0; + String message = ofNullable(caught).map(Throwable::getMessage).orElse("success"); + container.debug("Method %s#%s took %.03f (%s)", providerName, method.getName(), durationSec, + message); + } + } } From a55f86c838c0773e5d22dae544be1e1b190542a9 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 6 Feb 2024 14:30:51 -0800 Subject: [PATCH 12/13] Add config pair debugging --- .../java/com/google/bos/udmi/service/access/IotAccessBase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java index c66e94c3a9..702e421996 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java @@ -207,6 +207,7 @@ public String modifyConfig(String registryId, String deviceId, Function configPair = fetchConfig(registryId, deviceId); + debug("Fetched config version %s for %s", configPair.getKey(), deviceId); Long version = ifNotNullGet(configPair, Entry::getKey); return ifNotNullGet(safeMunge(munger, configPair), updated -> checkedUpdate(registryId, deviceId, version, updated)); From 6b3e7175ce20ea04a8869d788953e627b5566c83 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 6 Feb 2024 14:54:19 -0800 Subject: [PATCH 13/13] Fix test NPE --- .../google/bos/udmi/service/access/LocalIotAccessProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java index 7857b1acc2..f7964ec3fa 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java @@ -67,7 +67,7 @@ public void activate() { @Override public Entry fetchConfig(String registryId, String deviceId) { - return DEVICE_CONFIGS.get(deviceId); + return DEVICE_CONFIGS.getOrDefault(deviceId, new SimpleEntry<>(null, EMPTY_JSON)); } @Override