From fc74fad8a3d997b1bfd24e7914e324908c2ddc4a Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:35:57 -0700 Subject: [PATCH] updates with API connection and kafka testing --- .../lib/jpo-ode-common-3.0.0-SNAPSHOT.jar | Bin 38120 -> 38120 bytes .../lib/jpo-ode-core-3.0.0-SNAPSHOT.jar | Bin 140630 -> 141750 bytes .../lib/jpo-ode-plugins-3.0.0-SNAPSHOT.jar | Bin 446317 -> 446317 bytes jpo-s3-depositor/pom.xml | 15 ++ .../ode/s3/depositor/gen/FullOdeBsmData.java | 40 +++++ .../ode/s3/depositor/gen/GenOdeSchemas.java | 30 ++++ .../s3/depositor/imp/ImpDepositorService.java | 55 ++++--- .../ode/s3/depositor/imp/ImpMqttService.java | 79 ++++++++++ .../ode/s3/depositor/imp/ImpRegistration.java | 24 +-- .../its/jpo/ode/s3/depositor/imp/ImpUtil.java | 27 ++-- .../s3/depositor/models/imp/ConfigData.java | 49 +++--- .../threads/ServiceThreadController.java | 22 ++- .../ode/s3/depositor/utils/CommonUtils.java | 52 +++++++ .../src/main/resources/application.yaml | 4 +- mongo-connector/Dockerfile | 11 -- mongo-connector/connect_start.sh | 123 --------------- mongo-connector/connect_wait.sh | 20 --- mongo-connector/create_indexes.js | 142 ------------------ 18 files changed, 311 insertions(+), 382 deletions(-) create mode 100644 jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/FullOdeBsmData.java create mode 100644 jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/GenOdeSchemas.java create mode 100644 jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpMqttService.java delete mode 100644 mongo-connector/Dockerfile delete mode 100755 mongo-connector/connect_start.sh delete mode 100755 mongo-connector/connect_wait.sh delete mode 100755 mongo-connector/create_indexes.js diff --git a/jpo-s3-depositor/lib/jpo-ode-common-3.0.0-SNAPSHOT.jar b/jpo-s3-depositor/lib/jpo-ode-common-3.0.0-SNAPSHOT.jar index e402245958575567da0eab7333763fbe319d08a8..db4dc6d611fa5240f69a266640246d98e19eb15f 100644 GIT binary patch delta 755 zcmY+AQAkr!9LDcC=W5&JTF^tQY(_I5gf!YRDY3*^17EZxwRK=1e8OHaS=`=9T8|9ekyP8R2+(cPtV zm!>&1?a*-4GU}8K|65hFgP$H7o|3fvSvuwMGhzK$M*EB;?W zBpd7@50g6 zu^eY+eI*@}r!6I(%(U%O)jPYw#zV)#NvUb;irT#%+Dtq1Kibc8(ec1L-n!BGl_y_< z^VKN2*Y}N&`oo`ja`gHPH?FC|l|Mq$Ug;znJ@mnHA zI|o=qM-9%5+%G!R?-6hQ+N0+ZZ==U%!CcG_uEx&Vcd6Fom+v-Dz>k^D;F5V7ESYU! zYaDM&kK-Nig?Iqmo9IB_l6jDBCu*Um5%52ZQ3nC4!0hIu#BgWO6c3i>R*Z(4)!>lXJ0vRC1A a*$Ai;_YTky{3q(MACue@1Ng7eiQ#R2-2M*$ delta 755 zcmaE{lIg`tCf)#VW)?061`Y-W8Go6HyeiBf%48F>G?02J2cjl(F)D#+eMU7f9l@vt zrdt{H!1Ojob1?mZ(HKmtF_}R5Dw7)`Sy8Q+Jef%gWXI$KOcr4JH>X0d_cP*d7C-5nKCgjyk(y3s4O@6LqF%{=ZQdL1Hi(qkPT z*r2NL`w*#^$YT%@)r{E?6CX04iTcUNz_5yCGNYc{RRFncCRZ%$}g$OH}>miGBzdRe;(n7-R?57IkXz5^2S z0UbtQertyrnBLc61*U&@*nw%APH@;xZtHXg^Urj;fN9|_XRvwuI;1C;bjgF+)4Dvs z^uw-juo~;`2rxaZI~YvA>5c)@-aV0EdU;O}nEnIhNAyC}t%K4meIa1^=st+L4Siu? zK4(9~{N#R!`knm{b0sD~=<*4HV0EV_q=ISVi4grA6O+LFw-X`ex=e}(^OsGE2GcB) bA?C(SP6qRjOb!GE=46Q}abSN{PRR!VcNyDd diff --git a/jpo-s3-depositor/lib/jpo-ode-core-3.0.0-SNAPSHOT.jar b/jpo-s3-depositor/lib/jpo-ode-core-3.0.0-SNAPSHOT.jar index ead2de08706a37f859bf42b2ebf19b8ae151939a..6516c556f9b920bdfc80526fb34e83716d6496f5 100644 GIT binary patch delta 23659 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 Hs(AhnQK!`g delta 22674 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 diff --git a/jpo-s3-depositor/lib/jpo-ode-plugins-3.0.0-SNAPSHOT.jar b/jpo-s3-depositor/lib/jpo-ode-plugins-3.0.0-SNAPSHOT.jar index 354a3abd22bff1093b2022e1497ae9b0c6c97630..0b5eef2e7e844e277e74898c583a2058e005a030 100644 GIT binary patch delta 9716 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 diff --git a/jpo-s3-depositor/pom.xml b/jpo-s3-depositor/pom.xml index b4dfe03..aac2c9f 100644 --- a/jpo-s3-depositor/pom.xml +++ b/jpo-s3-depositor/pom.xml @@ -71,6 +71,16 @@ org.springframework.kafka spring-kafka + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + + + io.github.hakky54 + sslcontext-kickstart-for-pem + 8.3.7 + org.springdoc @@ -124,6 +134,11 @@ protobuf-java-util ${protobuf.version} + diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/FullOdeBsmData.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/FullOdeBsmData.java new file mode 100644 index 0000000..3d34738 --- /dev/null +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/FullOdeBsmData.java @@ -0,0 +1,40 @@ +package us.dot.its.jpo.ode.s3.depositor.gen; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +import us.dot.its.jpo.ode.model.OdeBsmData; +import us.dot.its.jpo.ode.model.OdeBsmMetadata; +import us.dot.its.jpo.ode.model.OdeBsmPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; +import us.dot.its.jpo.ode.model.OdeMsgPayload; + +import static com.fasterxml.jackson.annotation.JsonTypeInfo.*; + +public class FullOdeBsmData extends OdeData { + + private static final long serialVersionUID = 4944935387116447760L; + + public FullOdeBsmData() { + super(); + } + + public FullOdeBsmData(OdeBsmMetadata metadata, OdeBsmPayload payload) { + super(metadata, payload); + } + + @Override + @JsonTypeInfo(use = Id.CLASS, include = As.EXISTING_PROPERTY, defaultImpl = OdeBsmMetadata.class) + public void setMetadata(OdeMsgMetadata metadata) { + super.setMetadata(metadata); + } + + @Override + @JsonTypeInfo(use = Id.CLASS, include = As.EXISTING_PROPERTY, defaultImpl = OdeBsmPayload.class) + public void setPayload(OdeMsgPayload payload) { + super.setPayload(payload); + } + +} diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/GenOdeSchemas.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/GenOdeSchemas.java new file mode 100644 index 0000000..c3d9594 --- /dev/null +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/gen/GenOdeSchemas.java @@ -0,0 +1,30 @@ +package us.dot.its.jpo.ode.s3.depositor.gen; + +import org.springframework.stereotype.Controller; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.annotation.PostConstruct; +import us.dot.its.jpo.ode.model.OdeBsmData; + +@Controller +public class GenOdeSchemas { + + // @PostConstruct + // public void generateSchema() { + // HyperSchemaFactoryWrapper bsm = new HyperSchemaFactoryWrapper(); + // ObjectMapper mapper = new ObjectMapper(); + // try { + // mapper.acceptJsonFormatVisitor(FullOdeBsmData.class, bsm); + // JsonSchema bsmSchema = bsm.finalSchema(); + // String schemaJson = + // mapper.writerWithDefaultPrettyPrinter().writeValueAsString(bsmSchema); + // System.out.println(schemaJson); + // } catch (JsonMappingException e) { + // e.printStackTrace(); + // } catch (Exception e) { + // e.printStackTrace(); + // } + // } +} diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpDepositorService.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpDepositorService.java index 148797d..d52223a 100644 --- a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpDepositorService.java +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpDepositorService.java @@ -6,9 +6,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import us.dot.its.jpo.ode.depositor.GeoRoutedMsg; -import us.dot.its.jpo.ode.depositor.GeoRoutedMsgOrBuilder; -import us.dot.its.jpo.ode.depositor.GeoRoutedMsg_PB2; +// import us.dot.its.jpo.ode.depositor.GeoRoutedMsg; +// import us.dot.its.jpo.ode.depositor.GeoRoutedMsgOrBuilder; +// import us.dot.its.jpo.ode.depositor.GeoRoutedMsg_PB2; import us.dot.its.jpo.ode.model.OdeBsmData; import us.dot.its.jpo.ode.model.OdeBsmMetadata; import us.dot.its.jpo.ode.model.OdeBsmPayload; @@ -17,34 +17,49 @@ import us.dot.its.jpo.ode.model.OdeTimPayload; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage; import us.dot.its.jpo.ode.s3.depositor.DateJsonMapper; +import us.dot.its.jpo.ode.s3.depositor.DepositorProperties; import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + import us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy; -// @Service -public class ImpDepositorService { +@Service +public class ImpDepositorService implements Runnable { private static final Logger logger = LoggerFactory.getLogger(ImpDepositorService.class); - private final ObjectMapper mapper = DateJsonMapper.getInstance(); + private final ImpMqttService mqttService; + private final DepositorProperties properties; - // TODO: Use the filtered TMC topic instead of the mixed TIM topic - @KafkaListener(topics = "topic.OdeTimJson", groupId = "jpo-s3-depositor", concurrency = "${listen.concurrency:3}") - public void listenTopic1(String message) { - try { - OdeTimData timMsg = mapper.readValue(message, OdeTimData.class); - OdeTimMetadata timMetadata = (OdeTimMetadata) timMsg.getMetadata(); - - if (timMetadata.getRecordGeneratedBy().equals(GeneratedBy.T)) { + public ImpDepositorService(DepositorProperties properties, ImpMqttService mqttService) { + this.mqttService = mqttService; + this.properties = properties; + } - } + @Override + public void run() { + // Logic to start the service + logger.info("ImpDepositorService is running"); + // You can add any initialization logic here if needed + } - OdeTimPayload timPayload = (OdeTimPayload) timMsg.getPayload(); - OdeTravelerInformationMessage timData = (OdeTravelerInformationMessage) timPayload.getData(); - timData.get + // TODO: Use the filtered TMC topic instead of the mixed TIM topic + // @KafkaListener(topics = "topic.OdeTimJsonTMCFiltered", groupId = + // "jpo-s3-depositor", concurrency = "${listen.concurrency:1}") + // public void tmcTimListener(String message) { + // try { + // OdeTimData timMsg = mapper.readValue(message, OdeTimData.class); + // OdeTimMetadata timMetadata = (OdeTimMetadata) timMsg.getMetadata(); + // } catch (Exception e) { + // // Handle exception + // } + // } - timPayload.getTim().getRecord().get(0).getFrameType(); + @KafkaListener(topics = "topic.OdeBsmJson", groupId = "jpo-s3-depositor", concurrency = "${listen.concurrency:1}") + public void bsmListener(String message) { + try { + OdeBsmData msg = mapper.readValue(message, OdeBsmData.class); - logger.info("Received message: " + pojo1.getMetadata().getAsn1()); } catch (Exception e) { // Handle exception } diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpMqttService.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpMqttService.java new file mode 100644 index 0000000..4422462 --- /dev/null +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpMqttService.java @@ -0,0 +1,79 @@ +package us.dot.its.jpo.ode.s3.depositor.imp; + +import java.io.File; +import java.nio.file.Paths; + +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509ExtendedTrustManager; + +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import nl.altindag.ssl.SSLFactory; +import nl.altindag.ssl.pem.util.PemUtils; +import us.dot.its.jpo.ode.s3.depositor.DepositorProperties; +import us.dot.its.jpo.ode.s3.depositor.models.imp.ConfigData; +import us.dot.its.jpo.ode.s3.depositor.utils.CommonUtils; + +@Service +public class ImpMqttService { + private static final Logger logger = LoggerFactory.getLogger(ImpMqttService.class); + private MqttClient client; + + public ImpMqttService(DepositorProperties properties) throws MqttException { + ConfigData impConfig = CommonUtils.readConfigFile(properties.getImpCertPath() + "/config.json"); + + String uri = impConfig.getImpMqttUri().toString().replace("mqtt://", "ssl://"); + + client = new MqttClient(uri, impConfig.getDeviceID()); + MqttConnectOptions options = new MqttConnectOptions(); + options.setCleanSession(true); + options.setSocketFactory(createSocketFactory(impConfig.getCaCertPath(), impConfig.getClientCertPath(), + impConfig.getKeyFilePath())); + client.connect(options); + } + + public static SSLSocketFactory createSocketFactory(String caCertPath, String clientCertPath, + String privateKeyPath) { + // Convert to absolute paths + String absoluteCaCertPath = Paths.get(caCertPath).toAbsolutePath().toString(); + String absoluteClientCertPath = Paths.get(clientCertPath).toAbsolutePath().toString(); + String absolutePrivateKeyPath = Paths.get(privateKeyPath).toAbsolutePath().toString(); + + logger.info("CA Cert Path: {}", absoluteCaCertPath); + logger.info("Client Cert Path: {}", absoluteClientCertPath); + logger.info("Private Key Path: {}", absolutePrivateKeyPath); + + // Check if files exist + if (!new File(absoluteCaCertPath).exists()) { + throw new IllegalArgumentException("CA Certificate file not found at path: " + absoluteCaCertPath); + } + if (!new File(absoluteClientCertPath).exists()) { + throw new IllegalArgumentException("Client Certificate file not found at path: " + absoluteClientCertPath); + } + if (!new File(absolutePrivateKeyPath).exists()) { + throw new IllegalArgumentException("Private Key file not found at path: " + absolutePrivateKeyPath); + } + + X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial(Paths.get(absoluteClientCertPath), + Paths.get(absolutePrivateKeyPath)); + X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial(Paths.get(absoluteCaCertPath)); + + var sslFactory = SSLFactory.builder().withIdentityMaterial(keyManager).withTrustMaterial(trustManager).build(); + + var sslSocketFactory = sslFactory.getSslSocketFactory(); + return sslSocketFactory; + } + + public void publish(String topic, String messageContent) throws MqttException { + MqttMessage message = new MqttMessage(); + message.setPayload(messageContent.getBytes()); + client.publish(topic, message); + } +} \ No newline at end of file diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpRegistration.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpRegistration.java index 282f527..d375ea3 100644 --- a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpRegistration.java +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpRegistration.java @@ -2,6 +2,7 @@ import us.dot.its.jpo.ode.s3.depositor.DateJsonMapper; import us.dot.its.jpo.ode.s3.depositor.DepositorProperties; +import us.dot.its.jpo.ode.s3.depositor.utils.CommonUtils; import us.dot.its.jpo.ode.s3.depositor.models.imp.AuthToken; import us.dot.its.jpo.ode.s3.depositor.models.imp.AuthTokenRequest; import us.dot.its.jpo.ode.s3.depositor.models.imp.ClientCompleteResponse; @@ -65,9 +66,9 @@ public ConfigData registerClientPartner() { ClientRegistrationResponse registrationResponse = register(token); - writeToFile(caCertPath, registrationResponse.getCertificate().getCaPem()); - writeToFile(certPath, registrationResponse.getCertificate().getCertPem()); - writeToFile(keyPath, registrationResponse.getCertificate().getKeyPem()); + CommonUtils.writeToFile(caCertPath, registrationResponse.getCertificate().getCaPem()); + CommonUtils.writeToFile(certPath, registrationResponse.getCertificate().getCertPem()); + CommonUtils.writeToFile(keyPath, registrationResponse.getCertificate().getKeyPem()); deviceID = registrationResponse.getDeviceID(); } else { @@ -80,14 +81,12 @@ public ConfigData registerClientPartner() { ClientConnectionResponse connectionResponse = connection(token, deviceID); URI uri = new URI(connectionResponse.getMqttURL()); - String host = uri.getHost(); - int port = uri.getPort(); configData = new ConfigData(configPath, caCertPath, certPath, keyPath, properties.getImpVendor(), - properties.getImpNetworkType(), host, port, deviceID); + properties.getImpNetworkType(), uri, deviceID); String configDataJson = objectMapper.writeValueAsString(configData); - writeToFile(configPath, configDataJson); + CommonUtils.writeToFile(configPath, configDataJson); return configData; } catch (Exception e) { @@ -150,17 +149,6 @@ private ClientConnectionResponse connection(String token, String deviceID) { } } - private void writeToFile(String filePath, String content) { - try { - Files.createDirectories(Paths.get(filePath).getParent()); - try (FileWriter writer = new FileWriter(filePath)) { - writer.write(content); - } - } catch (IOException e) { - logger.error("writeToFile error: " + e.getStackTrace()); - } - } - private boolean validRegistration(String configPath) { boolean valid = false; File file = new File(configPath); diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpUtil.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpUtil.java index 4b27c40..d924ced 100644 --- a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpUtil.java +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/imp/ImpUtil.java @@ -1,14 +1,21 @@ -package us.dot.its.jpo.ode.s3.depositor.imp; +// package us.dot.its.jpo.ode.s3.depositor.imp; -import us.dot.its.jpo.ode.depositor.GeoRoutedMsg; +// import com.google.protobuf.ByteString; -public class ImpUtil { +// import us.dot.its.jpo.ode.depositor.GeoRoutedMsg; +// import us.dot.its.jpo.ode.depositor.GeoRoutedMsgOrBuilder; - public GeoRoutedMsg getGeoRoutedMsg(String asn1String, String odeReceivedAt, ) { - - byte[] asn1 = pojo1.getMetadata().getAsn1().getBytes(); - ByteString asn1ByteString = ByteString.copyFrom(asn1); +// public class ImpUtil { - GeoRoutedMsgOrBuilder geoRoutedMsg = GeoRoutedMsg.newBuilder().setMsgBytes(asn1ByteString).set; - } -} +// public GeoRoutedMsg getGeoRoutedMsg(String asn1String, String odeReceivedAt) +// { + +// // byte[] asn1 = pojo1.getMetadata().getAsn1().getBytes(); +// ByteString asn1ByteString = ByteString.copyFrom(asn1String.getBytes()); + +// GeoRoutedMsg geoRoutedMsg = +// GeoRoutedMsg.newBuilder().setMsgBytes(asn1ByteString).build(); + +// return geoRoutedMsg; +// } +// } diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/models/imp/ConfigData.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/models/imp/ConfigData.java index b89b51b..635c8fe 100644 --- a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/models/imp/ConfigData.java +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/models/imp/ConfigData.java @@ -1,9 +1,11 @@ package us.dot.its.jpo.ode.s3.depositor.models.imp; +import java.io.Serializable; import java.math.BigDecimal; +import java.net.URI; import java.util.Objects; -public class ConfigData { +public class ConfigData implements Serializable { private String configFilePath; private String caCertPath; @@ -11,23 +13,21 @@ public class ConfigData { private String keyFilePath; private String impVendor; private NetworkType networkType; - private String impIpHost; - private int impPort; + private URI impMqttUri; private String deviceID; public ConfigData() { } public ConfigData(String configFilePath, String caCertPath, String clientCertPath, String keyFilePath, - String impVendor, NetworkType networkType, String impIpHost, int impPort, String deviceID) { + String impVendor, NetworkType networkType, URI impMqttUri, String deviceID) { this.configFilePath = configFilePath; this.caCertPath = caCertPath; this.clientCertPath = clientCertPath; this.keyFilePath = keyFilePath; this.impVendor = impVendor; this.networkType = networkType; - this.impIpHost = impIpHost; - this.impPort = impPort; + this.impMqttUri = impMqttUri; this.deviceID = deviceID; } @@ -79,20 +79,12 @@ public void setNetworkType(NetworkType networkType) { this.networkType = networkType; } - public String getImpIpHost() { - return this.impIpHost; + public URI getImpMqttUri() { + return this.impMqttUri; } - public void setImpIpHost(String impIpHost) { - this.impIpHost = impIpHost; - } - - public int getImpPort() { - return this.impPort; - } - - public void setImpPort(int impPort) { - this.impPort = impPort; + public void setImpMqttUri(URI impMqttUri) { + this.impMqttUri = impMqttUri; } public String getDeviceID() { @@ -133,13 +125,8 @@ public ConfigData networkType(NetworkType networkType) { return this; } - public ConfigData impIpHost(String impIpHost) { - setImpIpHost(impIpHost); - return this; - } - - public ConfigData impPort(int impPort) { - setImpPort(impPort); + public ConfigData impMqttUri(URI impMqttUri) { + setImpMqttUri(impMqttUri); return this; } @@ -162,23 +149,21 @@ public boolean equals(Object o) { && Objects.equals(keyFilePath, configData.keyFilePath) && Objects.equals(impVendor, configData.impVendor) && Objects.equals(networkType, configData.networkType) - && Objects.equals(impIpHost, configData.impIpHost) && impPort == configData.impPort - && Objects.equals(deviceID, configData.deviceID); + && Objects.equals(impMqttUri, configData.impMqttUri) && Objects.equals(deviceID, configData.deviceID); } @Override public int hashCode() { - return Objects.hash(configFilePath, caCertPath, clientCertPath, keyFilePath, impVendor, networkType, impIpHost, - impPort, deviceID); + return Objects.hash(configFilePath, caCertPath, clientCertPath, keyFilePath, impVendor, networkType, impMqttUri, + deviceID); } @Override public String toString() { return "{" + " configFilePath='" + getConfigFilePath() + "'" + ", caCertPath='" + getCaCertPath() + "'" + ", clientCertPath='" + getClientCertPath() + "'" + ", keyFilePath='" + getKeyFilePath() + "'" - + ", impVendor='" + getImpVendor() + "'" + ", networkType='" + getNetworkType() + "'" + ", impIpHost='" - + getImpIpHost() + "'" + ", impPort='" + getImpPort() + "'" + ", deviceID='" + getDeviceID() + "'" - + "}"; + + ", impVendor='" + getImpVendor() + "'" + ", networkType='" + getNetworkType() + "'" + ", impMqttUri='" + + getImpMqttUri() + "'" + ", deviceID='" + getDeviceID() + "'" + "}"; } } \ No newline at end of file diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/threads/ServiceThreadController.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/threads/ServiceThreadController.java index 213c57f..6a15b09 100644 --- a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/threads/ServiceThreadController.java +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/threads/ServiceThreadController.java @@ -4,13 +4,15 @@ import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.Serde; import org.apache.kafka.common.serialization.Serdes; - +import org.eclipse.paho.client.mqttv3.MqttException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import us.dot.its.jpo.ode.s3.depositor.DepositorProperties; +import us.dot.its.jpo.ode.s3.depositor.imp.ImpDepositorService; +import us.dot.its.jpo.ode.s3.depositor.imp.ImpMqttService; import us.dot.its.jpo.ode.s3.depositor.imp.ImpRegistration; import java.util.HashMap; @@ -41,12 +43,24 @@ public ServiceThreadController(DepositorProperties depositorProperties) { public void startImpServices(DepositorProperties depositorProperties) { logger.info("Starting IMP services"); - // sm.startServices(); - var impRegistrationService = new ImpRegistration(depositorProperties); var response = impRegistrationService.registerClientPartner(); - var sm = new ServiceManager(new ServiceThreadFactory("ServiceManager")); + if (response != null) { + try { + var impMqttService = new ImpMqttService(depositorProperties); + var impDepositorService = new ImpDepositorService(depositorProperties, impMqttService); + + var sm = new ServiceManager(new ServiceThreadFactory("ImpServiceManager")); + sm.submit(impDepositorService); + + logger.info("IMP services started successfully"); + } catch (MqttException e) { + logger.error("Failed to start IMP services", e); + } + } else { + logger.error("IMP registration failed, services will not be started"); + } } diff --git a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/utils/CommonUtils.java b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/utils/CommonUtils.java index 507382b..a6f273f 100644 --- a/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/utils/CommonUtils.java +++ b/jpo-s3-depositor/src/main/java/us/dot/its/jpo/ode/s3/depositor/utils/CommonUtils.java @@ -1,6 +1,35 @@ package us.dot.its.jpo.ode.s3.depositor.utils; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import us.dot.its.jpo.ode.s3.depositor.models.imp.ConfigData; + public class CommonUtils { + private static final Logger logger = LoggerFactory.getLogger(CommonUtils.class); + private static ObjectMapper objectMapper = new ObjectMapper(); + public static String getEnvironmentVariable(String variableName) { String value = System.getenv(variableName); return value; @@ -15,4 +44,27 @@ public static String getEnvironmentVariable(String variableName, String defaultV } return value; } + + public static void writeToFile(String filePath, String content) { + try { + Files.createDirectories(Paths.get(filePath).getParent()); + try (FileWriter writer = new FileWriter(filePath)) { + writer.write(content); + } + } catch (IOException e) { + logger.error("writeToFile IOException: " + e.getStackTrace()); + } + } + + public static ConfigData readConfigFile(String filePath) { + try { + String fileContent = new String(Files.readAllBytes(Paths.get(filePath))); + ConfigData configData = objectMapper.readValue(fileContent, ConfigData.class); + + return configData; + } catch (IOException e) { + logger.error("writeToFile IOException: " + e.getStackTrace()); + return null; + } + } } diff --git a/jpo-s3-depositor/src/main/resources/application.yaml b/jpo-s3-depositor/src/main/resources/application.yaml index 73e744b..50b2dcd 100644 --- a/jpo-s3-depositor/src/main/resources/application.yaml +++ b/jpo-s3-depositor/src/main/resources/application.yaml @@ -6,8 +6,8 @@ version: ^project.version^ server.port: 8082 # Kafka properties -spring.kafka.bootstrap-servers: ${KAFKA_BROKER_IP:localhost}:9092 -spring.kafka.consumer.group-id: ^project.artifactId^ +spring.kafka.bootstrap-servers: ${BOOTSTRAP_SERVER:localhost:9092} +spring.kafka.consumer.group-id: jpo-s3-base-consumer-group # logging.level.org.apache.kafka: INFO logging.level: diff --git a/mongo-connector/Dockerfile b/mongo-connector/Dockerfile deleted file mode 100644 index 902101a..0000000 --- a/mongo-connector/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -# Note that this image requires a version of Mongodb of 3.6 or later -FROM confluentinc/cp-kafka-connect:6.1.9 - -COPY connect_wait.sh /scripts/connect_wait.sh - -# Docs: https://www.mongodb.com/docs/kafka-connector/current/ -RUN confluent-hub install --no-prompt mongodb/kafka-connect-mongodb:1.11.1 -# Docs: https://docs.confluent.io/platform/current/connect/transforms/overview.html -RUN confluent-hub install --no-prompt confluentinc/connect-transforms:1.4.3 - -CMD ["bash", "-c", "/scripts/connect_wait.sh"] \ No newline at end of file diff --git a/mongo-connector/connect_start.sh b/mongo-connector/connect_start.sh deleted file mode 100755 index f5d0c30..0000000 --- a/mongo-connector/connect_start.sh +++ /dev/null @@ -1,123 +0,0 @@ -# bin/bash -echo "------------------------------------------" -echo "Kafka connector creation started." -echo "------------------------------------------" - -declare -A OdeRawEncodedBSMJson=([name]="topic.OdeRawEncodedBSMJson" [collection]="OdeRawEncodedBSMJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) -declare -A OdeBsmJson=([name]="topic.OdeBsmJson" [collection]="OdeBsmJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) - -declare -A OdeRawEncodedMAPJson=([name]="topic.OdeRawEncodedMAPJson" [collection]="OdeRawEncodedMAPJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) -declare -A OdeMapJson=([name]="topic.OdeMapJson" [collection]="OdeMapJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) - -declare -A OdeRawEncodedSPATJson=([name]="topic.OdeRawEncodedSPATJson" [collection]="OdeRawEncodedSPATJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) -declare -A OdeSpatJson=([name]="topic.OdeSpatJson" [collection]="OdeSpatJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) - -declare -A OdeRawEncodedTIMJson=([name]="topic.OdeRawEncodedTIMJson" [collection]="OdeRawEncodedTIMJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) -declare -A OdeTimJson=([name]="topic.OdeTimJson" [collection]="OdeTimJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) - -declare -A OdeRawEncodedPsmJson=([name]="topic.OdeRawEncodedPsmJson" [collection]="OdeRawEncodedPsmJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) -declare -A OdePsmJson=([name]="topic.OdePsmJson" [collection]="OdePsmJson" - [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) - -function createSink() { - local -n topic=$1 - local name=${topic[name]} - local collection=${topic[collection]} - local timefield=${topic[timefield]} - local convert_timestamp=${topic[convert_timestamp]} - local use_key=${topic[use_key]} - local key=${topic[key]} - local add_timestamp=${topic[add_timestamp]} - - echo "Creating sink connector with parameters:" - echo "name=$name" - echo "collection=$collection" - echo "timefield=$timefield" - echo "convert_timestamp=$convert_timestamp" - echo "use_key=$use_key" - echo "key=$key" - echo "add_timestamp=$add_timestamp" - - local connectConfig=' { - "group.id":"connector-consumer", - "connector.class":"com.mongodb.kafka.connect.MongoSinkConnector", - "tasks.max":3, - "topics":"'$name'", - "connection.uri":"'$MONGO_URI'", - "database":"'$MONGO_DB_NAME'", - "collection":"'$collection'", - "key.converter":"org.apache.kafka.connect.storage.StringConverter", - "key.converter.schemas.enable":false, - "value.converter":"org.apache.kafka.connect.json.JsonConverter", - "value.converter.schemas.enable":false, - "errors.tolerance": "all", - "mongo.errors.tolerance": "all", - "errors.deadletterqueue.topic.name": "", - "errors.log.enable": false, - "errors.log.include.messages": false, - "errors.deadletterqueue.topic.replication.factor": 0' - - - if [ "$convert_timestamp" == true ] - then - local connectConfig=''$connectConfig', - "transforms": "TimestampConverter", - "transforms.TimestampConverter.field": "'$timefield'", - "transforms.TimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value", - "transforms.TimestampConverter.target.type": "Timestamp"' - fi - - if [ "$add_timestamp" == true ] - then - local connectConfig=''$connectConfig', - "transforms": "AddTimestamp,AddedTimestampConverter", - "transforms.AddTimestamp.type": "org.apache.kafka.connect.transforms.InsertField$Value", - "transforms.AddTimestamp.timestamp.field": "recordGeneratedAt", - "transforms.AddedTimestampConverter.field": "recordGeneratedAt", - "transforms.AddedTimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value", - "transforms.AddedTimestampConverter.target.type": "Timestamp"' - fi - - if [ "$use_key" == true ] - then - local connectConfig=''$connectConfig', - "document.id.strategy": "com.mongodb.kafka.connect.sink.processor.id.strategy.PartialValueStrategy", - "document.id.strategy.partial.value.projection.list": "'$key'", - "document.id.strategy.partial.value.projection.type": "AllowList", - "document.id.strategy.overwrite.existing": true' - fi - - local connectConfig=''$connectConfig' }' - - echo " Creating connector with Config : $connectConfig" - - curl -X PUT http://localhost:8083/connectors/MongoSink.${name}/config -H "Content-Type: application/json" -d "$connectConfig" -} - -createSink OdeRawEncodedBSMJson -createSink OdeBsmJson - -createSink OdeRawEncodedMAPJson -createSink OdeMapJson - -createSink OdeRawEncodedSPATJson -createSink OdeSpatJson - -createSink OdeRawEncodedTIMJson -createSink OdeTimJson - -createSink OdeRawEncodedPsmJson -createSink OdePsmJson - -echo "----------------------------------" -echo "ODE Kafka connector creation complete!" -echo "----------------------------------" \ No newline at end of file diff --git a/mongo-connector/connect_wait.sh b/mongo-connector/connect_wait.sh deleted file mode 100755 index 421faef..0000000 --- a/mongo-connector/connect_wait.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -/etc/confluent/docker/run & -echo "Waiting for Kafka Connect to start listening on kafka-connect" -while [ $(curl -s -o /dev/null -w %{http_code} http://localhost:8083/connectors) -eq 000 ] ; do - echo -e $(date) " Kafka Connect listener HTTP state: " $(curl -s -o /dev/null -w %{http_code} http://localhost:8083/connectors) " (waiting for 200)" - sleep 5 -done -sleep 10 -echo -e "\n--\n+> Creating Kafka Connect MongoDB sink" - -# Check if connect_start.sh exists -if [ ! -f /scripts/connect_start.sh ]; then - echo "Error: connect_start.sh does not exist, starting without any connectors." -else - echo "Connect_start.sh exists, starting with connectors." - bash /scripts/connect_start.sh -fi - -sleep infinity \ No newline at end of file diff --git a/mongo-connector/create_indexes.js b/mongo-connector/create_indexes.js deleted file mode 100755 index 0f4e207..0000000 --- a/mongo-connector/create_indexes.js +++ /dev/null @@ -1,142 +0,0 @@ -// Create indexes on all collections - -/* -This is the second script responsible for configuring mongoDB automatically on startup. -This script is responsible for creating collections, adding indexes and TTLs -*/ - -console.log(""); -console.log("Running create_indexes.js"); - -const ttlInDays = process.env.MONGO_COLLECTION_TTL; // TTL in days -const dbName = process.env.MONGO_DB_NAME; // TTL in days - -const expire_seconds = ttlInDays * 24 * 60 * 60; -const retry_milliseconds = 10000; - -// name -> collection name -// ttlField -> field to perform ttl on -// timeField -> field to index for time queries - -const collections = [ - {name: "OdeBsmJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"}, - {name: "OdeRawEncodedBSMJson", ttlField: "recordGeneratedAt", timeField: "none"}, - - {name: "OdeMapJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"}, - {name: "OdeRawEncodedMAPJson", ttlField: "recordGeneratedAt", timeField: "none"}, - - {name: "OdeSpatJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"}, - {name: "OdeRawEncodedSPATJson", ttlField: "recordGeneratedAt", timeField: "none"}, - - {name: "OdeTimJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"}, - {name: "OdeRawEncodedTIMJson", ttlField: "recordGeneratedAt", timeField: "none"}, - - {name: "OdePsmJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"}, - {name: "OdeRawEncodedPsmJson", ttlField: "recordGeneratedAt", timeField: "none"}, -]; - -try{ - db = db.getSiblingDB(dbName); - print("Connected to the MongoDB instance."); -} catch (error) { - print("Error connecting to the MongoDB instance: " + error); -} - - -// Wait for the collections to exist in mongo before trying to create indexes on them -let missing_collection_count; -do { - print(""); - try { - missing_collection_count = 0; - const collection_names = db.getCollectionNames(); - for (collection of collections) { - console.log("Creating Indexes for Collection" + collection); - // Create Collection if It doesn't exist - let created = false; - if(!collection_names.includes(collection.name)){ - created = createCollection(collection); - // created = true; - }else{ - created = true; - } - - if(created){ - if (collection.hasOwnProperty('ttlField') && collection.ttlField !== 'none') { - createTTLIndex(collection); - } - - - }else{ - missing_collection_count++; - console.log("Collection " + collection.name + " does not exist yet"); - } - } - if (missing_collection_count > 0) { - print("Waiting on " + missing_collection_count + " collections to be created...will try again in " + retry_milliseconds + " ms"); - sleep(retry_milliseconds); - } - } catch (err) { - console.log("Error while setting up TTL indexes in collections"); - console.log(rs.status()); - console.error(err); - sleep(retry_milliseconds); - } -} while (missing_collection_count > 0); - -console.log("Finished Creating All TTL indexes"); - - -function createCollection(collection){ - try { - db.createCollection(collection.name); - return true; - } catch (err) { - console.log("Unable to Create Collection: " + collection.name); - console.log(err); - return false; - } -} - -// Create TTL Indexes -function createTTLIndex(collection) { - if (ttlIndexExists(collection)) { - console.log("TTL index already exists for " + collection.name); - return; - } - - const collection_name = collection.name; - const timeField = collection.ttlField; - - console.log( - "Creating TTL index for " + collection_name + " to remove documents after " + - expire_seconds + - " seconds" - ); - - try { - var index_json = {}; - index_json[timeField] = 1; - db[collection_name].createIndex(index_json, - {expireAfterSeconds: expire_seconds} - ); - console.log("Created TTL index for " + collection_name + " using the field: " + timeField + " as the timestamp"); - } catch (err) { - var pattern_json = {}; - pattern_json[timeField] = 1; - db.runCommand({ - "collMod": collection_name, - "index": { - keyPattern: pattern_json, - expireAfterSeconds: expire_seconds - } - }); - console.log("Updated TTL index for " + collection_name + " using the field: " + timeField + " as the timestamp"); - } - -} - - -function ttlIndexExists(collection) { - return db[collection.name].getIndexes().find((idx) => idx.hasOwnProperty('expireAfterSeconds')) !== undefined; -} \ No newline at end of file