diff --git a/.vscode/settings.json b/.vscode/settings.json index ee3c8c3bc..72a38025e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,9 @@ "java.test.config": { "name": "Allow Reflection", "vmArgs": ["--add-opens", "java.base/java.time=ALL-UNNAMED"] // allows the ValidateSdx tests, which use reflection, to run - } + }, + "coverage-gutters.coverageFileNames": [ + "jacoco.xml" + ], + "coverage-gutters.coverageBaseDir": "**/target/site/jacoco" } \ No newline at end of file diff --git a/cert-expiration/src/main/java/com/trihydro/certexpiration/app/CertExpirationConsumer.java b/cert-expiration/src/main/java/com/trihydro/certexpiration/app/CertExpirationConsumer.java index 4e2ea58f6..ce6a6533a 100644 --- a/cert-expiration/src/main/java/com/trihydro/certexpiration/app/CertExpirationConsumer.java +++ b/cert-expiration/src/main/java/com/trihydro/certexpiration/app/CertExpirationConsumer.java @@ -8,36 +8,35 @@ import com.trihydro.certexpiration.controller.LoopController; import com.trihydro.library.factory.KafkaFactory; import com.trihydro.library.helpers.EmailHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.TopicDataWrapper; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.producer.ProducerRecord; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class CertExpirationConsumer { - private CertExpirationConfiguration configProperties; - private Utility utility; + private CertExpirationConfiguration configProperties; private EmailHelper emailHelper; private LoopController loopController; private KafkaFactory kafkaFactory; @Autowired - public CertExpirationConsumer(CertExpirationConfiguration configProperties, Utility _utility, - EmailHelper _emailHelper, LoopController _loopController, KafkaFactory _kafkaFactory) + public CertExpirationConsumer(CertExpirationConfiguration configProperties, + EmailHelper _emailHelper, LoopController _loopController, KafkaFactory _kafkaFactory) throws IOException, Exception { this.configProperties = configProperties; - utility = _utility; emailHelper = _emailHelper; loopController = _loopController; kafkaFactory = _kafkaFactory; } public void startKafkaConsumer() throws Exception { - utility.logWithDate("starting.............."); - var stringConsumer = kafkaFactory.createStringConsumer(configProperties.getKafkaHostServer() + ":9092", + log.info("starting.............."); + var stringConsumer = kafkaFactory.createStringConsumer(configProperties.getKafkaHostServer() + ":9092", configProperties.getDepositGroup(), configProperties.getDepositTopic()); var stringProducer = kafkaFactory.createStringProducer(configProperties.getKafkaHostServer() + ":9092"); @@ -51,9 +50,9 @@ public void startKafkaConsumer() throws Exception { for (var record : records) { String logTxt = String.format("Found topic %s, submitting to %s for later consumption", record.topic(), producerTopic); - utility.logWithDate(logTxt); + log.info(logTxt); - TopicDataWrapper tdw = new TopicDataWrapper(); + TopicDataWrapper tdw = new TopicDataWrapper(); tdw.setTopic(record.topic()); tdw.setData(record.value()); ProducerRecord producerRecord = new ProducerRecord(producerTopic, @@ -62,8 +61,8 @@ public void startKafkaConsumer() throws Exception { } } } catch (Exception ex) { - utility.logWithDate(ex.getMessage()); - emailHelper.ContainerRestarted(configProperties.getAlertAddresses(), configProperties.getMailPort(), + log.info(ex.getMessage()); + emailHelper.ContainerRestarted(configProperties.getAlertAddresses(), configProperties.getMailPort(), configProperties.getMailHost(), configProperties.getFromEmail(), "Logger Kafka Consumer"); // Re-throw exception to cause container to exit and restart throw ex; @@ -72,7 +71,7 @@ public void startKafkaConsumer() throws Exception { stringConsumer.close(); stringProducer.close(); } catch (Exception consumerEx) { - consumerEx.printStackTrace(); + log.error("Exception", consumerEx); } } } diff --git a/cert-expiration/src/test/java/com/trihydro/certexpiration/app/CertExpirationConsumerTest.java b/cert-expiration/src/test/java/com/trihydro/certexpiration/app/CertExpirationConsumerTest.java index 9ef341778..7ef76c0d3 100644 --- a/cert-expiration/src/test/java/com/trihydro/certexpiration/app/CertExpirationConsumerTest.java +++ b/cert-expiration/src/test/java/com/trihydro/certexpiration/app/CertExpirationConsumerTest.java @@ -20,6 +20,7 @@ import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.Utility; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.MockConsumer; import org.apache.kafka.clients.consumer.OffsetResetStrategy; @@ -37,6 +38,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) +@Slf4j public class CertExpirationConsumerTest { private static final String TOPIC = "topic"; private static final String PRODUCERTOPIC = "producerTopic"; @@ -52,8 +54,6 @@ public class CertExpirationConsumerTest { @Mock private CertExpirationConfiguration mockConfigProperties; @Mock - private Utility mockUtility; - @Mock private EmailHelper mockEmailHelper; @Mock private LoopController mockLoopController; @@ -115,11 +115,6 @@ public void startKafkaConsumer_SUCCESS() throws Exception { // Assert Assertions.assertEquals(1, mockProducer.history().size()); - - verify(mockUtility).logWithDate("starting.............."); - verify(mockUtility).logWithDate("Found topic topic, submitting to producerTopic for later consumption"); - - verifyNoMoreInteractions(mockUtility); Assertions.assertTrue(mockConsumer.closed()); Assertions.assertTrue(mockProducer.closed()); } @@ -130,16 +125,11 @@ public void startKafkaConsumer_EXCEPTION() throws Exception { configureConsumerException("Network error"); // Act - Exception ex = assertThrows(KafkaException.class, () -> uut.startKafkaConsumer()); + assertThrows(KafkaException.class, () -> uut.startKafkaConsumer()); // Assert - Assertions.assertEquals("Network error", ex.getMessage()); - verify(mockUtility).logWithDate("starting.............."); - - verify(mockUtility).logWithDate("Network error"); verify(mockEmailHelper).ContainerRestarted(any(), any(), any(), any(), any()); - verifyNoMoreInteractions(mockUtility); Assertions.assertTrue(mockConsumer.closed()); Assertions.assertTrue(mockProducer.closed()); } @@ -156,12 +146,8 @@ public void startKafkaConsumer_DOUBLEEXCEPTION() throws Exception { // Assert Assertions.assertEquals("Mail Exception", ex.getMessage()); - verify(mockUtility).logWithDate("starting.............."); - - verify(mockUtility).logWithDate("Network error"); verify(mockEmailHelper).ContainerRestarted(any(), any(), any(), any(), any()); - verifyNoMoreInteractions(mockUtility); Assertions.assertTrue(mockConsumer.closed()); Assertions.assertTrue(mockProducer.closed()); } diff --git a/cv-data-controller/.gitignore b/cv-data-controller/.gitignore index f23b9489f..8d59a882c 100644 --- a/cv-data-controller/.gitignore +++ b/cv-data-controller/.gitignore @@ -1 +1,2 @@ -*.jar \ No newline at end of file +*.jar +*.crt \ No newline at end of file diff --git a/cv-data-controller/pom.xml b/cv-data-controller/pom.xml index c3736e270..aa1a3e9cf 100644 --- a/cv-data-controller/pom.xml +++ b/cv-data-controller/pom.xml @@ -87,7 +87,24 @@ jakarta.xml.bind-api 4.0.0 - + + org.springframework.boot + spring-boot-testcontainers + 3.1.0 + test + + + org.testcontainers + postgresql + 1.19.0 + test + + + org.testcontainers + junit-jupiter + 1.19.0 + test + diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java index a5a35e9be..79354031e 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java @@ -1,5 +1,7 @@ package com.trihydro.cvdatacontroller.controller; +import com.trihydro.library.helpers.DateTimeHelper; +import com.trihydro.library.helpers.DateTimeHelperImpl; import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; @@ -31,6 +33,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -50,20 +53,23 @@ @RequestMapping("active-tim") @ApiIgnore @Slf4j +@Import(DateTimeHelperImpl.class) public class ActiveTimController extends BaseController { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; protected Calendar UTCCalendar; + private DateTimeHelper dateTimeHelper; public ActiveTimController() { UTCCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); } @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { + public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler, DateTimeHelper dateTimeHelper) { timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; + this.dateTimeHelper = dateTimeHelper; } /** @@ -305,21 +311,27 @@ public ResponseEntity> GetActiveTimIndicesByRsu(@PathVariable Stri } @RequestMapping(value = {"/client-id-direction/{clientId}/{timTypeId}", "/client-id-direction/{clientId}/{timTypeId}/{direction}"}, method = RequestMethod.GET) - public ResponseEntity> GetActiveTimsByClientIdDirection(@PathVariable String clientId, @PathVariable Long timTypeId, @PathVariable(required = false) String direction) { + public ResponseEntity> getActiveTimsByClientIdDirection(@PathVariable String clientId, @PathVariable Long timTypeId, @PathVariable(required = false) String direction) { List activeTims = new ArrayList<>(); - // There may be multiple TIMs grouped together by client_id. ex. CLIENTID_1, - // CLIENTID_2 - String query = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " + timTypeId; - + StringBuilder queryBuilder = new StringBuilder( + "SELECT * FROM active_tim WHERE CLIENT_ID = ? AND TIM_TYPE_ID = ?"); if (direction != null) { - query += " and DIRECTION = '" + direction + "'"; + queryBuilder.append(" AND DIRECTION = ?"); } + queryBuilder.append(" AND MARKED_FOR_DELETION = '0'"); // exclude active tims marked for deletion + String query = queryBuilder.toString(); + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement ps = connection.prepareStatement(query)) { + ps.setString(1, clientId); + ps.setLong(2, timTypeId); + if (direction != null) { + ps.setString(3, direction); + } - query += " and MARKED_FOR_DELETION = '0'"; // exclude active tims marked for deletion - - try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { - activeTims = getActiveTimFromRS(rs, false); + log.trace("Executing parameterized query to get active tims by client id and direction: \"{}\"", ps); + try (ResultSet rs = ps.executeQuery()) { + activeTims = getActiveTimFromRS(rs, false); + } } catch (SQLException e) { log.error("Error getting active tims by client id and direction", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); @@ -332,9 +344,17 @@ public ResponseEntity> GetActiveTimsByClientIdDirection(@PathVar public ResponseEntity> GetBufferTimsByClientId(@PathVariable String clientId) { List activeTims = new ArrayList<>(); - String query = "select * from active_tim where CLIENT_ID like '" + clientId + "\\%BUFF_-%' ESCAPE '\\'"; - - try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + // Use database concatenation (||) to append the LIKE pattern to the parameterized clientId. + // This follows best practices by keeping user input fully parameterized and separate from SQL literals. + // The PreparedStatement.setString() method automatically escapes special characters, so even if + // clientId contains malicious input like "'; DROP TABLE active_tim; --", it would be treated as + // a literal string pattern to match against CLIENT_ID values, not as executable SQL code. + String parameterizedQuery = "select * from active_tim where CLIENT_ID like ? || '%BUFF%'"; + try (Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement statement = connection.prepareStatement(parameterizedQuery) + ) { + statement.setString(1, clientId); + var rs = statement.executeQuery(); activeTims = getActiveTimFromRS(rs, false); } catch (SQLException e) { log.error("Error getting buffer tims by client id", e); @@ -394,7 +414,13 @@ public ResponseEntity DeleteActiveTim(@PathVariable Long activeTimId) { } @RequestMapping(value = "/delete-ids", method = RequestMethod.DELETE, headers = "Accept=application/json") - public ResponseEntity DeleteActiveTimsById(@RequestBody List activeTimIds) { + public ResponseEntity deleteActiveTimsById(@RequestBody List activeTimIds) { + // Handle empty list case + if (activeTimIds == null || activeTimIds.isEmpty()) { + log.debug("No active tim IDs provided for deletion"); + return ResponseEntity.ok(true); // Return success as there's nothing to delete + } + boolean deleteActiveTimResult = false; StringBuilder deleteSQL = new StringBuilder("DELETE FROM ACTIVE_TIM WHERE ACTIVE_TIM_ID in ("); @@ -404,7 +430,8 @@ public ResponseEntity DeleteActiveTimsById(@RequestBody List acti deleteSQL = new StringBuilder(deleteSQL.substring(0, deleteSQL.length() - 1)); deleteSQL.append(")"); - try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(deleteSQL.toString())) { + try (Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(deleteSQL.toString())) { for (int i = 0; i < activeTimIds.size(); i++) { preparedStatement.setLong(i + 1, activeTimIds.get(i)); } @@ -415,7 +442,7 @@ public ResponseEntity DeleteActiveTimsById(@RequestBody List acti if (deleteActiveTimResult) { log.info("Active Tims (active_tim_ids {}) are deleted!", activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(","))); } else { - log.warn("Failed to delete Active Tims (active_tim_ids {}). They may not exist.", activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(","))); + log.warn("Failed to delete Active Tims (active_tim_ids {}). They may not exist.", Arrays.toString(activeTimIds.toArray())); } } catch (SQLException e) { @@ -760,12 +787,12 @@ public ResponseEntity InsertActiveTim(@RequestBody ActiveTim activeTim) { } else if (col.equals("DIRECTION")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getDirection()); } else if (col.equals("TIM_START")) { - java.util.Date tim_start_date = utility.convertDate(activeTim.getStartDateTime()); + java.util.Date tim_start_date = dateTimeHelper.convertDate(activeTim.getStartDateTime()); Timestamp tim_start_timestamp = new Timestamp(tim_start_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_start_timestamp); } else if (col.equals("TIM_END")) { if (activeTim.getEndDateTime() != null) { - java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); + java.util.Date tim_end_date = dateTimeHelper.convertDate(activeTim.getEndDateTime()); Timestamp tim_end_timestamp = new Timestamp(tim_end_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_end_timestamp); } else { @@ -773,7 +800,7 @@ public ResponseEntity InsertActiveTim(@RequestBody ActiveTim activeTim) { } } else if (col.equals("EXPIRATION_DATE")) { if (activeTim.getExpirationDateTime() != null) { - java.util.Date tim_exp_date = utility.convertDate(activeTim.getExpirationDateTime()); + java.util.Date tim_exp_date = dateTimeHelper.convertDate(activeTim.getExpirationDateTime()); Timestamp tim_exp_timestamp = new Timestamp(tim_exp_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_exp_timestamp); } else { @@ -955,7 +982,7 @@ public ResponseEntity UpdateExpiration(@PathVariable String packetID, @ updateStatement += ")"; try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(updateStatement)) { - Date date = utility.convertDate(expDate); + Date date = dateTimeHelper.convertDate(expDate); Timestamp expDateTimestamp = new Timestamp(date.getTime()); preparedStatement.setTimestamp(1, expDateTimestamp);// expDate comes in as MST from previously called function // (GetMinExpiration) @@ -993,7 +1020,7 @@ public ResponseEntity GetMinExpiration(@PathVariable String packetID, @P try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { while (rs.next()) { var tmpTs = rs.getTimestamp("MINSTART", UTCCalendar); - minStart = utility.timestampFormat.format(tmpTs); + minStart = utility.getTimestampFormat().format(tmpTs); } } catch (SQLException e) { log.error("Error getting min expiration date for packetID: {}, expDate: {}", packetID, expDate, e); @@ -1024,6 +1051,18 @@ public ResponseEntity MarkForDeletion(@PathVariable Long activeTimId) { return ResponseEntity.ok(success); } + @RequestMapping(value = "/get-active-planned-condition-tims", method = RequestMethod.GET) + public ResponseEntity> getActivePlannedConditionTims() throws SQLException { + String query = "select * from active_tim where client_id like '%planned%'"; + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(query); ResultSet resultSet = preparedStatement.executeQuery()) { + return ResponseEntity.ok(getActiveTimFromRS(resultSet, false)); + } catch(Exception e) { + log.error("Error getting active planned condition TIMs", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + } + private TimUpdateModel buildTimUpdateModelFromResultSet(ResultSet rs) throws SQLException { TimUpdateModel timUpdateModel = new TimUpdateModel(); timUpdateModel.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java index 00a92b26c..f4cb989c9 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java @@ -1,5 +1,7 @@ package com.trihydro.cvdatacontroller.controller; +import com.trihydro.library.helpers.DateStringNotInISO8601FormatException; +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.model.ActiveTimHoldingDeleteModel; import java.sql.Connection; import java.sql.PreparedStatement; @@ -37,16 +39,18 @@ public class ActiveTimHoldingController extends BaseController { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; + private DateTimeHelper dateTimeHelper; @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { + public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler, DateTimeHelper dateTimeHelper) { timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; + this.dateTimeHelper = dateTimeHelper; } @RequestMapping(value = "/add", method = RequestMethod.POST, produces = "application/json") public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding activeTimHolding) { - + log.debug("Inserting active_tim_holding record with client_id = {} and sat_record_id = {}", activeTimHolding.getClientId(), activeTimHolding.getSatRecordId()); Long activeTimHoldingId = 0L; String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim_holding", timDbTables.getActiveTimHoldingTable()); @@ -85,21 +89,52 @@ public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding } else if (col.equals("RSU_INDEX")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTimHolding.getRsuIndex()); } else if (col.equals("DATE_CREATED")) { - java.util.Date dateCreated = utility.convertDate(activeTimHolding.getDateCreated()); + java.util.Date dateCreated = dateTimeHelper.convertDate(activeTimHolding.getDateCreated()); Timestamp dateCreatedTimestamp = new Timestamp(dateCreated.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, dateCreatedTimestamp); } else if (col.equals("PROJECT_KEY")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTimHolding.getProjectKey()); } else if (col.equals("EXPIRATION_DATE")) { if (activeTimHolding.getExpirationDateTime() != null) { - java.util.Date tim_exp_date = utility.convertDate(activeTimHolding.getExpirationDateTime()); - sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, utility.timestampFormat.format(tim_exp_date)); + java.util.Date tim_exp_date = dateTimeHelper.convertDate(activeTimHolding.getExpirationDateTime()); + sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, utility.getTimestampFormat().format(tim_exp_date)); } else { preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); } } else if (col.equals("PACKET_ID")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTimHolding.getPacketId()); } + else if (col.equals("TIM_END")) { // record tim_end for planned conditions with long intervals (or any other TIMs with long intervals) to ensure identity checks work as expected + String desiredEndDateTime = activeTimHolding.getDesiredEndDateTime(); + if (desiredEndDateTime == null) { + preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); + fieldNum++; + continue; + } + log.trace("End date time string is not null. If this is a planned condition with a long interval, the desired end date time will need to be recorded for post-processing."); + + if (dateTimeHelper.isInTableFormat(desiredEndDateTime)) { + log.trace("End date time string is in table format. Attempting to convert to ISO8601 format."); + try { + desiredEndDateTime = dateTimeHelper.convertDateStringFromTableFormatIntoISO8601Format(desiredEndDateTime); + } catch (Exception e) { + log.error("Desired end date time candidate value is in table format but unable to convert to ISO8601 format: {}. Unable to continue.", desiredEndDateTime); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(-2L); + } + } + + Timestamp timEndTimestamp; + try { + log.trace("Converting date string into timestamp object"); + timEndTimestamp = dateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject(desiredEndDateTime); + } + catch (DateStringNotInISO8601FormatException e) { + log.error("Desired end date time candidate value was not in ISO8601 format and failed to convert to timestamp object: {}. Unable to continue.", desiredEndDateTime); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(-3L); + } + log.trace("Converted date string from ISO8601 format into timestamp object. Setting TIM_END value in prepared statement."); + sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, timEndTimestamp); + } fieldNum++; } @@ -131,6 +166,7 @@ public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTimHoldingId); } } + log.trace("Done inserting active_tim_holding record with client_id = {} and sat_record_id = {}", activeTimHolding.getClientId(), activeTimHolding.getSatRecordId()); return ResponseEntity.ok(activeTimHoldingId); } catch (SQLException e) { log.error("Error inserting active tim holding", e); @@ -154,7 +190,7 @@ public ResponseEntity> getActiveTimHoldingForRsu(@PathVar activeTimHolding.setActiveTimHoldingId(rs.getLong("ACTIVE_TIM_HOLDING_ID")); activeTimHolding.setClientId(rs.getString("CLIENT_ID")); activeTimHolding.setDirection(rs.getString("DIRECTION")); - activeTimHolding.setRsuTargetId(rs.getString("RSU_TARGET")); + activeTimHolding.setRsuTarget(rs.getString("RSU_TARGET")); activeTimHolding.setSatRecordId(rs.getString("SAT_RECORD_ID")); activeTimHolding.setStartPoint(new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); activeTimHolding.setEndPoint(new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); @@ -187,7 +223,7 @@ public ResponseEntity> getAllRecords() { activeTimHolding.setActiveTimHoldingId(rs.getLong("ACTIVE_TIM_HOLDING_ID")); activeTimHolding.setClientId(rs.getString("CLIENT_ID")); activeTimHolding.setDirection(rs.getString("DIRECTION")); - activeTimHolding.setRsuTargetId(rs.getString("RSU_TARGET")); + activeTimHolding.setRsuTarget(rs.getString("RSU_TARGET")); activeTimHolding.setSatRecordId(rs.getString("SAT_RECORD_ID")); activeTimHolding.setStartPoint(new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); activeTimHolding.setEndPoint(new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/CategoryController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/CategoryController.java index aa1d60e73..5f5322f10 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/CategoryController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/CategoryController.java @@ -9,6 +9,7 @@ import com.trihydro.library.model.Category; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -20,12 +21,13 @@ @CrossOrigin @RestController +@Slf4j @ApiIgnore @RequestMapping("category") public class CategoryController extends BaseController { - // select all ITIS codes - @RequestMapping(value = "/all", method = RequestMethod.GET, headers = "Accept=application/json") + // select all ITIS codes + @RequestMapping(value = "/all", method = RequestMethod.GET, headers = "Accept=application/json") public ResponseEntity> SelectAllCategories() { List categories = new ArrayList(); Connection connection = null; @@ -48,7 +50,7 @@ public ResponseEntity> SelectAllCategories() { categories.add(category); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(categories); } finally { try { @@ -62,7 +64,7 @@ public ResponseEntity> SelectAllCategories() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(categories); diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java index 2c8c3950b..698f71ef9 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.TimeZone; +import lombok.extern.slf4j.Slf4j; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -32,11 +34,12 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("data-frame") @ApiIgnore public class DataFrameController extends BaseController { - private TimDbTables timDbTables; + private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; @Autowired @@ -75,7 +78,7 @@ public ResponseEntity GetItisCodesForDataFrameId(@PathVariable Integer } return ResponseEntity.ok(itisCodes.toArray(new String[itisCodes.size()])); } catch (Exception e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(itisCodes.toArray(new String[itisCodes.size()])); } finally { @@ -90,7 +93,7 @@ public ResponseEntity GetItisCodesForDataFrameId(@PathVariable Integer if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } @@ -135,7 +138,7 @@ public ResponseEntity AddDataFrame(@RequestBody DataFrame dFrame, @PathVar Date dt = df.parse(dFrame.getStartDateTime()); time = new Timestamp(dt.getTime()); } catch (ParseException ex) { - System.out.println("Unable to parse startdate: " + dFrame.getStartDateTime()); + log.info("Unable to parse startdate: {}", dFrame.getStartDateTime()); } sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, time); } @@ -146,7 +149,7 @@ public ResponseEntity AddDataFrame(@RequestBody DataFrame dFrame, @PathVar Long dataFrameId = dbInteractions.executeAndLog(preparedStatement, "dataframe"); return ResponseEntity.ok(dataFrameId); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); } finally { try { @@ -157,7 +160,7 @@ public ResponseEntity AddDataFrame(@RequestBody DataFrame dFrame, @PathVar if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameItisCodeController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameItisCodeController.java index 7f66a2afa..35951ab61 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameItisCodeController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameItisCodeController.java @@ -7,6 +7,7 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -21,6 +22,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("data-frame-itis-code") @ApiIgnore public class DataFrameItisCodeController extends BaseController { @@ -71,7 +73,7 @@ public ResponseEntity AddDataFrameItisCode(@PathVariable Long dataFrameId, Long dataFrameItisCodeId = dbInteractions.executeAndLog(preparedStatement, "dataFrameItisCode"); return ResponseEntity.ok(dataFrameItisCodeId); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); } finally { try { @@ -82,7 +84,7 @@ public ResponseEntity AddDataFrameItisCode(@PathVariable Long dataFrameId, if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/HttpLoggingController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/HttpLoggingController.java index 960ccd133..37f3249bd 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/HttpLoggingController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/HttpLoggingController.java @@ -8,6 +8,7 @@ import com.trihydro.library.model.HttpLoggingModel; import com.trihydro.library.tables.LoggingTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,6 +22,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("http-logging") @ApiIgnore public class HttpLoggingController extends BaseController { @@ -61,7 +63,7 @@ public ResponseEntity LogHttpRequest(@RequestBody HttpLoggingModel httpLog Long httpLoggingId = dbInteractions.executeAndLog(preparedStatement, "http_logging"); return ResponseEntity.ok(httpLoggingId); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); } finally { try { @@ -72,7 +74,7 @@ public ResponseEntity LogHttpRequest(@RequestBody HttpLoggingModel httpLog if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/IncidentChoiceController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/IncidentChoiceController.java index 756533c66..e08fa7282 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/IncidentChoiceController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/IncidentChoiceController.java @@ -9,6 +9,7 @@ import com.trihydro.library.model.IncidentChoice; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -20,6 +21,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("incident-choice") @ApiIgnore public class IncidentChoiceController extends BaseController { @@ -51,7 +53,7 @@ public ResponseEntity> SelectAllIncidentActions() { } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(incidentActions); } finally { try { @@ -65,7 +67,7 @@ public ResponseEntity> SelectAllIncidentActions() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(incidentActions); @@ -95,7 +97,7 @@ public ResponseEntity> SelectAllIncidentEffects() { } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(incidentEffects); } finally { try { @@ -109,7 +111,7 @@ public ResponseEntity> SelectAllIncidentEffects() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(incidentEffects); @@ -140,7 +142,7 @@ public ResponseEntity> SelectAllIncidentProblems() { } return ResponseEntity.ok(incidentProblems); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(incidentProblems); } finally { try { @@ -154,7 +156,7 @@ public ResponseEntity> SelectAllIncidentProblems() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ItisCodeController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ItisCodeController.java index 028f416a6..ecde52fb7 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ItisCodeController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ItisCodeController.java @@ -10,6 +10,7 @@ import com.trihydro.library.model.ItisCode; import com.trihydro.library.model.TmddItisCode; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -21,9 +22,11 @@ @CrossOrigin @RestController +@Slf4j @ApiIgnore public class ItisCodeController extends BaseController { - @RequestMapping(value = "/itiscodes", method = RequestMethod.GET, headers = "Accept=application/json") + + @RequestMapping(value = "/itiscodes", method = RequestMethod.GET, headers = "Accept=application/json") public ResponseEntity> selectAllItisCodes() { List itisCodes = new ArrayList(); Connection connection = null; @@ -45,7 +48,7 @@ public ResponseEntity> selectAllItisCodes() { } return ResponseEntity.ok(itisCodes); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(itisCodes); } finally { @@ -56,7 +59,7 @@ public ResponseEntity> selectAllItisCodes() { if (connection != null) connection.close(); } catch (SQLException ex) { - ex.printStackTrace(); + log.error("Exception", ex); } } } @@ -85,7 +88,7 @@ public ResponseEntity> selectAllTmddItisCodes() { results.add(result); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(results); } finally { try { @@ -99,7 +102,7 @@ public ResponseEntity> selectAllTmddItisCodes() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java index 5207aa539..c1da71f5e 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java @@ -14,6 +14,9 @@ import com.mapbox.services.commons.geojson.FeatureCollection; import com.mapbox.services.commons.geojson.LineString; import com.mapbox.services.commons.models.Position; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; + import com.trihydro.cvdatacontroller.services.MilepostService; import com.trihydro.library.model.Milepost; import com.trihydro.library.model.MilepostBuffer; @@ -33,10 +36,11 @@ @CrossOrigin @RestController +@Slf4j @ApiIgnore public class MilepostController extends BaseController { - private MilepostService milepostService; + private MilepostService milepostService; @Autowired public void InjectDependencies(MilepostService _milepostService) { @@ -63,7 +67,7 @@ public ResponseEntity> getRoutes() { } return ResponseEntity.ok(routes); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(routes); } finally { try { @@ -77,7 +81,7 @@ public ResponseEntity> getRoutes() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } @@ -122,7 +126,7 @@ public ResponseEntity> getMilepostsCommonName(@PathVariable Strin mileposts.add(milepost); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(mileposts); } finally { try { @@ -136,7 +140,7 @@ public ResponseEntity> getMilepostsCommonName(@PathVariable Strin if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(mileposts); @@ -178,10 +182,10 @@ public ResponseEntity> getMilepostRange(@PathVariable String dire } if (mileposts.size() == 0) { - System.out.println("Unable to find mileposts with query: " + statementStr); + log.info("Unable to find mileposts with query: {}", statementStr); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(mileposts); } finally { try { @@ -195,7 +199,7 @@ public ResponseEntity> getMilepostRange(@PathVariable String dire if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(mileposts); @@ -234,7 +238,7 @@ public ResponseEntity> getMilepostRangeNoDirection(@PathVariable mileposts.add(milepost); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(mileposts); } finally { try { @@ -248,7 +252,7 @@ public ResponseEntity> getMilepostRangeNoDirection(@PathVariable if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(mileposts); @@ -401,7 +405,7 @@ public List getMilepostsTest() { mileposts.add(milepost); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -414,7 +418,7 @@ public List getMilepostsTest() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return mileposts; @@ -463,7 +467,7 @@ public List getMilepostTestRange(@PathVariable String direction, @Path mileposts.add(milepost); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -476,7 +480,7 @@ public List getMilepostTestRange(@PathVariable String direction, @Path if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return mileposts; diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java index 9975e45fd..90ddc45ce 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java @@ -7,6 +7,7 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,6 +22,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("nodexy") @ApiIgnore public class NodeXYController extends BaseController { @@ -76,7 +78,7 @@ else if (col.equals("ATTRIBUTES_DELEVATION")) Long nodeXYId = dbInteractions.executeAndLog(preparedStatement, "nodexy"); return ResponseEntity.ok(nodeXYId); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); } finally { try { @@ -87,7 +89,7 @@ else if (col.equals("ATTRIBUTES_DELEVATION")) if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathController.java index 10fbf4363..2ffc57ff4 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathController.java @@ -7,6 +7,7 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -19,6 +20,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("path") @ApiIgnore public class PathController extends BaseController { @@ -54,7 +56,7 @@ public ResponseEntity InsertPath() { Long pathId = dbInteractions.executeAndLog(preparedStatement, "pathId"); return ResponseEntity.ok(pathId); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -64,7 +66,7 @@ public ResponseEntity InsertPath() { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } // if we got here, its an error diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeLLController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeLLController.java index d86f50dff..f9e9ccfb6 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeLLController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeLLController.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -20,6 +21,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("path-node-ll") @ApiIgnore public class PathNodeLLController extends BaseController { @@ -54,7 +56,7 @@ public ResponseEntity GetNodeLLForPath(@PathVariable int pathId) { } return ResponseEntity.ok(nodeXYs.toArray(new NodeXY[nodeXYs.size()])); } catch (Exception e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(nodeXYs.toArray(new NodeXY[nodeXYs.size()])); } finally { @@ -69,7 +71,7 @@ public ResponseEntity GetNodeLLForPath(@PathVariable int pathId) { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java index 3fa1e6a04..c80348c79 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java @@ -9,8 +9,10 @@ import java.util.List; import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.tables.TimDbTables; - +import com.trihydro.library.tables.TimDbTables; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,15 +23,16 @@ import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; -import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.NodeXY; - -@CrossOrigin -@RestController -@RequestMapping("path-node-xy") -@ApiIgnore -public class PathNodeXYController extends BaseController { - - private TimDbTables timDbTables; +import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.NodeXY; + +@CrossOrigin +@RestController +@Slf4j +@RequestMapping("path-node-xy") +@ApiIgnore +public class PathNodeXYController extends BaseController { + + private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; @Autowired @@ -68,8 +71,8 @@ public ResponseEntity GetNodeXYForPath(@PathVariable int pathId) { nodeXYs.add(nodexy); } return ResponseEntity.ok(nodeXYs.toArray(new NodeXY[nodeXYs.size()])); - } catch (Exception e) { - e.printStackTrace(); + } catch (Exception e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(nodeXYs.toArray(new NodeXY[nodeXYs.size()])); } finally { @@ -83,8 +86,8 @@ public ResponseEntity GetNodeXYForPath(@PathVariable int pathId) { // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } } @@ -114,8 +117,8 @@ else if (col.equals("PATH_ID")) Long pathNodeXYId = dbInteractions.executeAndLog(preparedStatement, "pathnodexyid"); return ResponseEntity.ok(pathNodeXYId); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); } finally { try { @@ -125,8 +128,8 @@ else if (col.equals("PATH_ID")) // return connection back to pool if (connection != null) connection.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RegionController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RegionController.java index 6b7ae3123..0d2792aa7 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RegionController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RegionController.java @@ -8,10 +8,12 @@ import java.util.List; import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.tables.TimDbTables; - +import com.trihydro.library.tables.TimDbTables; + +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -24,15 +26,16 @@ import springfox.documentation.annotations.ApiIgnore; import us.dot.its.jpo.ode.plugin.j2735.OdePosition3D; -import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region; - -@CrossOrigin -@RestController -@RequestMapping("region") -@ApiIgnore -public class RegionController extends BaseController { - - private TimDbTables timDbTables; +import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region; + +@CrossOrigin +@RestController +@Slf4j +@RequestMapping("region") +@ApiIgnore +public class RegionController extends BaseController { + + private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; @Autowired @@ -55,8 +58,8 @@ public ResponseEntity UpdateRegionName(@PathVariable Long regionId, @Pa // execute update statement Boolean success = dbInteractions.updateOrDelete(preparedStatement); return ResponseEntity.ok(success); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } finally { try { // close prepared statement @@ -65,8 +68,8 @@ public ResponseEntity UpdateRegionName(@PathVariable Long regionId, @Pa // return connection back to pool if (connection != null) connection.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); @@ -164,8 +167,8 @@ else if (col.equals("GEOMETRY_DIRECTION")) { // execute insert statement Long regionId = dbInteractions.executeAndLog(preparedStatement, "regionID"); return ResponseEntity.ok(regionId); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } finally { try { // close prepared statement @@ -174,8 +177,8 @@ else if (col.equals("GEOMETRY_DIRECTION")) { // return connection back to pool if (connection != null) connection.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java index c4a9fe3b5..6120f124a 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java @@ -9,8 +9,10 @@ import java.util.List; import com.trihydro.library.model.WydotRsu; -import com.trihydro.library.model.WydotRsuTim; - +import com.trihydro.library.model.WydotRsuTim; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -19,14 +21,15 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import springfox.documentation.annotations.ApiIgnore; - -@CrossOrigin -@RestController -@ApiIgnore -public class RsuController extends BaseController { - - @RequestMapping(value = "/rsus", method = RequestMethod.GET, headers = "Accept=application/json") +import springfox.documentation.annotations.ApiIgnore; + +@CrossOrigin +@RestController +@Slf4j +@ApiIgnore +public class RsuController extends BaseController { + + @RequestMapping(value = "/rsus", method = RequestMethod.GET, headers = "Accept=application/json") public ResponseEntity> SelectAllRsus() { ArrayList rsus = new ArrayList(); Connection connection = null; @@ -52,8 +55,8 @@ public ResponseEntity> SelectAllRsus() { rsus.add(rsu); } - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(rsus); } finally { try { @@ -66,8 +69,8 @@ public ResponseEntity> SelectAllRsus() { // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } @@ -97,8 +100,8 @@ public ResponseEntity> SelectActiveRsus() { rsu.setLongitude(rs.getBigDecimal("LONGITUDE")); rsus.add(rsu); } - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(rsus); } finally { try { @@ -111,8 +114,8 @@ public ResponseEntity> SelectActiveRsus() { // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } return ResponseEntity.ok(rsus); @@ -147,8 +150,8 @@ public ResponseEntity> GetFullRsusTimIsOn(@PathVariable Long t rsus.add(rsu); } } - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(rsus); } finally { try { @@ -161,8 +164,8 @@ public ResponseEntity> GetFullRsusTimIsOn(@PathVariable Long t // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } return ResponseEntity.ok(rsus); @@ -194,8 +197,8 @@ public ResponseEntity> SelectRsusByRoute(@PathVariable Strin rsu.setMilepost(rs.getDouble("MILEPOST")); rsus.add(rsu); } - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(rsus); } finally { try { @@ -208,8 +211,8 @@ public ResponseEntity> SelectRsusByRoute(@PathVariable Strin // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } return ResponseEntity.ok(rsus); @@ -237,8 +240,8 @@ public ResponseEntity> GetActiveRsuTimIndexes(@PathVariable Intege while (rs.next()) { indexes.add(rs.getInt("RSU_INDEX")); } - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(indexes); } finally { try { @@ -251,8 +254,8 @@ public ResponseEntity> GetActiveRsuTimIndexes(@PathVariable Intege // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } return ResponseEntity.ok(indexes); diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/SecurityResultCodeTypeController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/SecurityResultCodeTypeController.java index 180961764..77da26073 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/SecurityResultCodeTypeController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/SecurityResultCodeTypeController.java @@ -7,20 +7,24 @@ import java.util.ArrayList; import java.util.List; -import com.trihydro.library.model.SecurityResultCodeType; - +import com.trihydro.library.model.SecurityResultCodeType; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -@CrossOrigin -@RestController -@RequestMapping("security-result-code-type") -public class SecurityResultCodeTypeController extends BaseController { - @RequestMapping(value = "/get-all", method = RequestMethod.GET, headers = "Accept=application/json") +import org.springframework.web.bind.annotation.RestController; + +@CrossOrigin +@RestController +@Slf4j +@RequestMapping("security-result-code-type") +public class SecurityResultCodeTypeController extends BaseController { + + @RequestMapping(value = "/get-all", method = RequestMethod.GET, headers = "Accept=application/json") public ResponseEntity> GetSecurityResultCodeTypes() { SecurityResultCodeType securityResultCodeType = null; @@ -43,8 +47,8 @@ public ResponseEntity> GetSecurityResultCodeTypes() securityResultCodeTypes.add(securityResultCodeType); } return ResponseEntity.ok(securityResultCodeTypes); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(securityResultCodeTypes); } finally { try { @@ -57,8 +61,8 @@ public ResponseEntity> GetSecurityResultCodeTypes() // close result set if (rs != null) rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } catch (SQLException e) { + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/StatusLogController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/StatusLogController.java index 489b21aa0..7da8baffe 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/StatusLogController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/StatusLogController.java @@ -4,6 +4,8 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -15,6 +17,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("status-log") @ApiIgnore public class StatusLogController extends BaseController { @@ -36,7 +39,7 @@ public ResponseEntity DeleteOldStatusLogs() { // execute delete SQL stetement deleteResult = dbInteractions.updateOrDelete(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); } finally { try { @@ -47,7 +50,7 @@ public ResponseEntity DeleteOldStatusLogs() { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimController.java index 2cbbdc7ea..bbef437f8 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimController.java @@ -1,5 +1,7 @@ package com.trihydro.cvdatacontroller.controller; +import com.trihydro.library.helpers.DateTimeHelper; +import com.trihydro.library.helpers.DateTimeHelperImpl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -15,8 +17,11 @@ import com.trihydro.library.model.WydotOdeTravelerInformationMessage; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -32,18 +37,22 @@ @CrossOrigin @RestController +@Slf4j +@Import(DateTimeHelperImpl.class) public class TimController extends BaseController { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; private SecurityResultCodeTypeController securityResultCodeTypeController; + private DateTimeHelper dateTimeHelper; @Autowired public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler, - SecurityResultCodeTypeController _securityResultCodeTypeController) { + SecurityResultCodeTypeController _securityResultCodeTypeController, DateTimeHelper dateTimeHelper) { timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; securityResultCodeTypeController = _securityResultCodeTypeController; + this.dateTimeHelper = dateTimeHelper; } @RequestMapping(value = "/get-tim/{timId}", method = RequestMethod.GET) @@ -70,7 +79,7 @@ public ResponseEntity GetTim(@PathVariable L } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(tim); } finally { try { @@ -84,7 +93,7 @@ public ResponseEntity GetTim(@PathVariable L if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } @@ -136,7 +145,7 @@ else if (col.equals("TIME_STAMP")) { preparedStatement.setString(fieldNum, null); } else if (col.equals("RECORD_GENERATED_AT")) { if (odeTimMetadata.getRecordGeneratedAt() != null) { - java.util.Date recordGeneratedAtDate = utility.convertDate(odeTimMetadata.getRecordGeneratedAt()); + java.util.Date recordGeneratedAtDate = dateTimeHelper.convertDate(odeTimMetadata.getRecordGeneratedAt()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, new java.sql.Timestamp(recordGeneratedAtDate.getTime())); } else { @@ -153,7 +162,7 @@ else if (col.equals("TIME_STAMP")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, odeTimMetadata.getPayloadType()); } else if (col.equals("ODE_RECEIVED_AT")) { if (odeTimMetadata.getOdeReceivedAt() != null) { - java.util.Date receivedAtDate = utility.convertDate(odeTimMetadata.getOdeReceivedAt()); + java.util.Date receivedAtDate = dateTimeHelper.convertDate(odeTimMetadata.getOdeReceivedAt()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, new java.sql.Timestamp(receivedAtDate.getTime())); } else { @@ -248,7 +257,7 @@ else if (col.equals("LOG_FILE_NAME")) { Long timId = dbInteractions.executeAndLog(preparedStatement, "timID"); return timId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -258,7 +267,7 @@ else if (col.equals("LOG_FILE_NAME")) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return Long.valueOf(0); @@ -277,49 +286,49 @@ public ResponseEntity deleteOldTim() { // 1. delete old tim_rsu records deleteResult = deleteOldTimRsus(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old tim_rsu records"); + log.info("Failed to cleanup old tim_rsu records"); return ResponseEntity.ok(false); } // 2. delete old data_frame_itis_code records deleteResult &= deleteOldDataFrameItisCodes(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old data_frame_itis_code records"); + log.info("Failed to cleanup old data_frame_itis_code records"); return ResponseEntity.ok(false); } // 3. delete old region records deleteResult &= deleteOldRegions(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old region records"); + log.info("Failed to cleanup old region records"); return ResponseEntity.ok(false); } // 4. delete old path_node_ll records deleteResult &= deleteOldPathNodeLL(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old path_node_ll records"); + log.info("Failed to cleanup old path_node_ll records"); return ResponseEntity.ok(false); } // 5. delete old path records deleteResult &= deleteOldPaths(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old path records"); + log.info("Failed to cleanup old path records"); return ResponseEntity.ok(false); } // 6. delete old node_ll records deleteResult &= deleteOldNodeLL(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old node_ll records"); + log.info("Failed to cleanup old node_ll records"); return ResponseEntity.ok(false); } // 7. delete old data_frame records deleteResult &= deleteOldDataFrames(oneMonthPriorTimestamp); if (!deleteResult) { - utility.logWithDate("Failed to cleanup old data_frame records"); + log.info("Failed to cleanup old data_frame records"); return ResponseEntity.ok(false); } @@ -332,7 +341,7 @@ public ResponseEntity deleteOldTim() { // execute delete SQL statement deleteResult &= dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); } finally { try { @@ -343,7 +352,7 @@ public ResponseEntity deleteOldTim() { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } @@ -365,7 +374,7 @@ private boolean deleteOldDataFrames(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -375,12 +384,12 @@ private boolean deleteOldDataFrames(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete data_frame"); + log.info("Failed to delete data_frame"); } return deleteResult; } @@ -401,7 +410,7 @@ private boolean deleteOldDataFrameItisCodes(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -411,12 +420,12 @@ private boolean deleteOldDataFrameItisCodes(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete DATA_FRAME_ITIS_CODE"); + log.info("Failed to delete DATA_FRAME_ITIS_CODE"); } return deleteResult; } @@ -437,7 +446,7 @@ private boolean deleteOldRegions(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -447,12 +456,12 @@ private boolean deleteOldRegions(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete region"); + log.info("Failed to delete region"); } return deleteResult; } @@ -473,7 +482,7 @@ private boolean deleteOldPaths(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -483,12 +492,12 @@ private boolean deleteOldPaths(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete path"); + log.info("Failed to delete path"); } return deleteResult; } @@ -509,7 +518,7 @@ private boolean deleteOldPathNodeLL(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -519,12 +528,12 @@ private boolean deleteOldPathNodeLL(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete path_node_ll"); + log.info("Failed to delete path_node_ll"); } return deleteResult; } @@ -546,7 +555,7 @@ private boolean deleteOldNodeLL(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -556,12 +565,12 @@ private boolean deleteOldNodeLL(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete node_ll"); + log.info("Failed to delete node_ll"); } return deleteResult; } @@ -581,7 +590,7 @@ private boolean deleteOldTimRsus(Timestamp timestamp) { // execute delete SQL stetement deleteResult = dbInteractions.deleteWithPossibleZero(preparedStatement); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -591,12 +600,12 @@ private boolean deleteOldTimRsus(Timestamp timestamp) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } if (!deleteResult) { - utility.logWithDate("Failed to delete tim_rsus"); + log.info("Failed to delete tim_rsus"); } return deleteResult; } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimRsuController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimRsuController.java index 01edd7475..e166e8a14 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimRsuController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimRsuController.java @@ -12,6 +12,8 @@ import com.trihydro.library.model.TimRsu; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -23,6 +25,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("tim-rsu") public class TimRsuController extends BaseController { @@ -62,10 +65,11 @@ else if (col.equals("RSU_INDEX")) return ResponseEntity.ok(timRsuId); } catch (SQLException e) { if (e.getMessage() != null && e.getMessage().contains("duplicate key value violates unique constraint")) { - utility.logWithDate("Record already exists in 'tim_rsu' table for tim_id " + timId + ", rsu_id " + rsuId + ", rsu_index " + rsuIndex, TimRsuController.class); + log.info("{}: " + "Record already exists in 'tim_rsu' table for tim_id {}, rsu_id {}, rsu_index {}", TimRsuController.class.getSimpleName(), timId, rsuId, rsuIndex); } else { - utility.logWithDate("Error adding record to 'tim_rsu' table for tim_id " + timId + ", rsu_id " + rsuId + ", rsu_index " + rsuIndex + ": " + e.getMessage(), TimRsuController.class); + String msg = "Error adding record to 'tim_rsu' table for tim_id " + timId + ", rsu_id " + rsuId + ", rsu_index " + rsuIndex + ": " + e.getMessage(); + log.info("{}: {}", TimRsuController.class.getSimpleName(), msg); } return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Long.valueOf(0)); } finally { @@ -77,7 +81,7 @@ else if (col.equals("RSU_INDEX")) if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } @@ -109,7 +113,7 @@ public ResponseEntity> GetTimRsusByTimId(@PathVariable Long timId) } return ResponseEntity.ok(timRsus); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(timRsus); } finally { try { @@ -123,7 +127,7 @@ public ResponseEntity> GetTimRsusByTimId(@PathVariable Long timId) if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } @@ -151,7 +155,7 @@ public ResponseEntity GetTimRsu(@PathVariable Long timId, @PathVariable } return ResponseEntity.ok(timRsu); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } finally { try { @@ -165,7 +169,7 @@ public ResponseEntity GetTimRsu(@PathVariable Long timId, @PathVariable if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimTypeController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimTypeController.java index b236cb793..b8e724eb1 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimTypeController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/TimTypeController.java @@ -9,6 +9,8 @@ import com.trihydro.library.model.TimType; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; @@ -17,6 +19,7 @@ @CrossOrigin @RestController +@Slf4j @RequestMapping("tim-type") public class TimTypeController extends BaseController { @@ -42,7 +45,7 @@ public ResponseEntity> SelectAll() { timTypes.add(timType); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(timTypes); } finally { try { @@ -56,7 +59,7 @@ public ResponseEntity> SelectAll() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return ResponseEntity.ok(timTypes); diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/model/DataControllerConfigProperties.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/model/DataControllerConfigProperties.java index 10eca26b1..6ffb2b0a3 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/model/DataControllerConfigProperties.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/model/DataControllerConfigProperties.java @@ -1,9 +1,5 @@ package com.trihydro.cvdatacontroller.model; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Import; -import org.springframework.stereotype.Component; - import com.trihydro.library.helpers.DbInteractions; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.JavaMailSenderImplProvider; @@ -13,9 +9,14 @@ import com.trihydro.library.model.EmailProps; import com.trihydro.library.tables.LoggingTables; import com.trihydro.library.tables.TimDbTables; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; @Component @ConfigurationProperties("config") +@Data @Import({ TimDbTables.class, SQLNullHandler.class, Utility.class, EmailHelper.class, JavaMailSenderImplProvider.class, LoggingTables.class, DbInteractions.class }) public class DataControllerConfigProperties implements DbInteractionsProps, EmailProps { diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/tables/DbTables.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/tables/DbTables.java index e0099b15c..d07c25329 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/tables/DbTables.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/tables/DbTables.java @@ -6,8 +6,10 @@ import java.util.Iterator; import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Pair; +@Slf4j public class DbTables { public String buildInsertQueryStatement(String tableName, List table) { @@ -60,8 +62,8 @@ public PreparedStatement buildUpdateStatement(Long id, String tableName, String preparedStatement.setObject(index, id); return preparedStatement; } catch (SQLException ex) { - System.out.println("Error creating update statement"); - ex.printStackTrace(); + log.info("Error creating update statement"); + log.error("Exception", ex); } return null; diff --git a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java index b52ca03fb..ce82d01dc 100644 --- a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java +++ b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java @@ -1,16 +1,20 @@ package com.trihydro.cvdatacontroller.controller; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.trihydro.library.helpers.*; +import com.trihydro.library.model.*; +import com.trihydro.library.tables.TimDbTables; import java.math.BigDecimal; -import java.sql.SQLException; -import java.sql.Timestamp; +import java.sql.*; import java.text.ParseException; import java.time.Instant; import java.util.ArrayList; @@ -18,24 +22,16 @@ import java.util.Date; import java.util.List; -import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.model.ActiveRsuTimQueryModel; -import com.trihydro.library.model.ActiveTim; -import com.trihydro.library.model.ContentEnum; -import com.trihydro.library.model.Coordinate; -import com.trihydro.library.model.TimUpdateModel; -import com.trihydro.library.model.WydotTim; -import com.trihydro.library.tables.TimDbTables; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; - import org.springframework.web.client.HttpServerErrorException; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType; public class ActiveTimControllerTest extends TestBase { @@ -43,10 +39,12 @@ public class ActiveTimControllerTest extends TestBase { private TimDbTables mockTimDbTables = new TimDbTables(); @Mock private SQLNullHandler mockSqlNullHandler; + @Mock + private DateTimeHelper dateTimeHelper; @BeforeEach public void setupSubTest() { - uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler); + uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler, dateTimeHelper); } private void setupPreparedStatement() { @@ -428,21 +426,23 @@ public void GetActiveTimIndicesByRsu_FAIL() throws SQLException { } @Test - public void GetActiveTimsByClientIdDirection_SUCCESS() throws SQLException { + public void getActiveTimsByClientIdDirection_SUCCESS() throws SQLException { // Arrange String clientId = "clientId"; - Long timTypeId = -1l; + long timTypeId = -1L; String direction = "eastward"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " + timTypeId; - selectStatement += " and DIRECTION = '" + direction + "'"; - selectStatement += " and MARKED_FOR_DELETION = '0'"; + String selectStatement = "SELECT * FROM active_tim WHERE CLIENT_ID = ? AND TIM_TYPE_ID = ? AND DIRECTION = ? AND MARKED_FOR_DELETION = '0'"; // Act - ResponseEntity> data = uut.GetActiveTimsByClientIdDirection(clientId, timTypeId, direction); + ResponseEntity> data = uut.getActiveTimsByClientIdDirection(clientId, timTypeId, direction); // Assert Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - verify(mockStatement).executeQuery(selectStatement); + verify(mockConnection).prepareStatement(selectStatement); + verify(mockPreparedStatement).setString(1, clientId); + verify(mockPreparedStatement).setLong(2, timTypeId); + verify(mockPreparedStatement).setString(3, direction); + verify(mockPreparedStatement).executeQuery(); verify(mockRs).getLong("ACTIVE_TIM_ID"); verify(mockRs).getLong("TIM_ID"); verify(mockRs).getString("SAT_RECORD_ID"); @@ -456,29 +456,31 @@ public void GetActiveTimsByClientIdDirection_SUCCESS() throws SQLException { verify(mockRs).getBigDecimal("END_LONGITUDE"); verify(mockRs).getString("ROUTE"); verify(mockRs).getInt("PK"); - verify(mockStatement).close(); + verify(mockPreparedStatement).close(); verify(mockConnection).close(); verify(mockRs).close(); } @Test - public void GetActiveTimsByClientIdDirection_FAIL() throws SQLException { + public void getActiveTimsByClientIdDirection_FAIL() throws SQLException { // Arrange String clientId = "clientId"; - Long timTypeId = -1l; + long timTypeId = -1L; String direction = "eastward"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " + timTypeId; - selectStatement += " and DIRECTION = '" + direction + "'"; - selectStatement += " and MARKED_FOR_DELETION = '0'"; + String selectStatement = "SELECT * FROM active_tim WHERE CLIENT_ID = ? AND TIM_TYPE_ID = ? AND DIRECTION = ? AND MARKED_FOR_DELETION = '0'"; doThrow(new SQLException()).when(mockRs).getLong("ACTIVE_TIM_ID"); // Act - ResponseEntity> data = uut.GetActiveTimsByClientIdDirection(clientId, timTypeId, direction); + ResponseEntity> data = uut.getActiveTimsByClientIdDirection(clientId, timTypeId, direction); // Assert Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); - verify(mockStatement).executeQuery(selectStatement); - verify(mockStatement).close(); + verify(mockConnection).prepareStatement(selectStatement); + verify(mockPreparedStatement).setString(1, clientId); + verify(mockPreparedStatement).setLong(2, timTypeId); + verify(mockPreparedStatement).setString(3, direction); + verify(mockPreparedStatement).executeQuery(); + verify(mockPreparedStatement).close(); verify(mockConnection).close(); verify(mockRs).close(); } @@ -487,14 +489,15 @@ public void GetActiveTimsByClientIdDirection_FAIL() throws SQLException { public void GetBufferTimsByClientId_SUCCESS() throws SQLException { // Arrange String clientId = "clientId"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "\\%BUFF_-%' ESCAPE '\\'"; - // Act ResponseEntity> data = uut.GetBufferTimsByClientId(clientId); // Assert Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - verify(mockStatement).executeQuery(selectStatement); + verify(mockPreparedStatement).setString(1, clientId); + verify(mockPreparedStatement).executeQuery(); + verify(mockPreparedStatement).close(); + verify(mockConnection).close(); verify(mockRs).getLong("ACTIVE_TIM_ID"); verify(mockRs).getLong("TIM_ID"); verify(mockRs).getString("SAT_RECORD_ID"); @@ -508,9 +511,6 @@ public void GetBufferTimsByClientId_SUCCESS() throws SQLException { verify(mockRs).getBigDecimal("END_LONGITUDE"); verify(mockRs).getString("ROUTE"); verify(mockRs).getInt("PK"); - verify(mockStatement).close(); - verify(mockConnection).close(); - verify(mockRs).close(); } @Test @@ -518,8 +518,6 @@ public void GetBufferTimsByClientId_FAIL() throws SQLException { // Arrange String clientId = "clientId"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "\\%BUFF_-%' ESCAPE '\\'"; - doThrow(new SQLException()).when(mockRs).getLong("ACTIVE_TIM_ID"); // Act @@ -527,10 +525,11 @@ public void GetBufferTimsByClientId_FAIL() throws SQLException { // Assert Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); - verify(mockStatement).executeQuery(selectStatement); - verify(mockStatement).close(); + verify(mockPreparedStatement).setString(1, clientId); + verify(mockPreparedStatement).executeQuery(); + verify(mockPreparedStatement).close(); verify(mockConnection).close(); - verify(mockRs).close(); + verify(mockRs).getLong("ACTIVE_TIM_ID"); } @Test @@ -612,13 +611,13 @@ public void DeleteActiveTim_FAIL() throws SQLException { } @Test - public void DeleteActiveTimsById_SUCCESS() throws SQLException { + public void deleteActiveTimsById_SUCCESS() throws SQLException { // Arrange List activeTimIds = new ArrayList<>(); activeTimIds.add(Long.valueOf(-1)); // Act - ResponseEntity data = uut.DeleteActiveTimsById(activeTimIds); + ResponseEntity data = uut.deleteActiveTimsById(activeTimIds); // Assert Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); @@ -630,14 +629,14 @@ public void DeleteActiveTimsById_SUCCESS() throws SQLException { } @Test - public void DeleteActiveTimsById_FAIL() throws SQLException { + public void deleteActiveTimsById_FAIL() throws SQLException { // Arrange List activeTimIds = new ArrayList<>(); activeTimIds.add(Long.valueOf(-1)); doThrow(new SQLException()).when(mockPreparedStatement).setLong(1, -1l); // Act - ResponseEntity data = uut.DeleteActiveTimsById(activeTimIds); + ResponseEntity data = uut.deleteActiveTimsById(activeTimIds); // Assert Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); @@ -647,6 +646,24 @@ public void DeleteActiveTimsById_FAIL() throws SQLException { verify(mockConnection).close(); } + @Test + public void testDeleteActiveTimsById_WithEmptyList_ShouldHandleGracefully() throws SQLException { + // Arrange + List emptyList = new ArrayList<>(); + + // Act + ResponseEntity response = uut.deleteActiveTimsById(emptyList); + + // Assert + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + Assertions.assertTrue(response.getBody()); + + // Verify that the connection pool was not accessed + verify(mockDbInteractions, never()).getConnectionPool(); + // Verify no SQL was executed + verify(mockDbInteractions, never()).updateOrDelete(any(PreparedStatement.class)); + } + @Test public void GetActiveTimsByIds_SUCCESS() throws SQLException { // Arrange @@ -993,7 +1010,7 @@ public void GetMinExpiration_SUCCESS() throws ParseException, SQLException { var minVal = "27-Oct-20 06.21.00.000 PM"; var ts = Timestamp.valueOf("2020-10-27 18:21:00"); doReturn(ts).when(mockRs).getTimestamp("MINSTART", uut.UTCCalendar); - mockUtility.timestampFormat = timestampFormat; + when(mockUtility.getTimestampFormat()).thenReturn(timestampFormat); // Act ResponseEntity response = uut.GetMinExpiration(packetID, expDate); @@ -1048,7 +1065,7 @@ public void UpdateExpiration_SUCCESS() throws SQLException { Timestamp ts = new Timestamp(date.getTime()); doReturn(mockPreparedStatement).when(mockConnection).prepareStatement(any()); doReturn(true).when(mockDbInteractions).updateOrDelete(mockPreparedStatement); - doReturn(date).when(mockUtility).convertDate(expDate); + doReturn(date).when(dateTimeHelper).convertDate(expDate); String updateStatement = "UPDATE ACTIVE_TIM SET EXPIRATION_DATE = ? WHERE ACTIVE_TIM_ID IN ("; updateStatement += "SELECT ACTIVE_TIM_ID FROM ACTIVE_TIM atim"; @@ -1079,7 +1096,7 @@ public void UpdateExpiration_FAIL() throws SQLException { Date date = Date.from(Instant.parse(expDate)); doReturn(mockPreparedStatement).when(mockConnection).prepareStatement(any()); doReturn(false).when(mockDbInteractions).updateOrDelete(mockPreparedStatement); - doReturn(date).when(mockUtility).convertDate(expDate); + doReturn(date).when(dateTimeHelper).convertDate(expDate); String updateStatement = "UPDATE ACTIVE_TIM SET EXPIRATION_DATE = ? WHERE ACTIVE_TIM_ID IN ("; updateStatement += "SELECT ACTIVE_TIM_ID FROM ACTIVE_TIM atim"; @@ -1128,8 +1145,8 @@ public void InsertActiveTim_SUCCESS() throws SQLException { java.util.Date tim_end_date = java.util.Date.from(now.plusSeconds(60)); java.util.Date tim_start_date = java.util.Date.from(now); - doReturn(tim_start_date).when(mockUtility).convertDate(startTime); - doReturn(tim_end_date).when(mockUtility).convertDate(endTime); + doReturn(tim_start_date).when(dateTimeHelper).convertDate(startTime); + doReturn(tim_end_date).when(dateTimeHelper).convertDate(endTime); Timestamp startTimestamp = new Timestamp(tim_start_date.getTime()); Timestamp endTimestamp = new Timestamp(tim_end_date.getTime()); @@ -1251,4 +1268,196 @@ public void ResetExpirationDate_FAIL() throws SQLException { verify(mockPreparedStatement).close(); verify(mockConnection).close(); } + + @Test + void getActivePlannedConditionTims_NoTims_ShouldReturnEmptyList() throws SQLException { + // Arrange + String expectedQuery = "select * from active_tim where client_id like '%planned%'"; + doReturn(mockConnection).when(mockDbInteractions).getConnectionPool(); + doReturn(mockPreparedStatement).when(mockConnection).prepareStatement(expectedQuery); + doReturn(mockRs).when(mockPreparedStatement).executeQuery(); + doReturn(false).when(mockRs).next(); + + // Act + ResponseEntity> response = uut.getActivePlannedConditionTims(); + + // Assert + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + Assertions.assertEquals(0, response.getBody().size()); + verify(mockConnection).prepareStatement(expectedQuery); + verify(mockPreparedStatement).close(); + verify(mockConnection).close(); + } + + @Test + void getActivePlannedConditionTims_OneTim_ShouldReturnNonEmptyList() throws SQLException { + // Arrange + String expectedQuery = "select * from active_tim where client_id like '%planned%'"; + doReturn(mockConnection).when(mockDbInteractions).getConnectionPool(); + doReturn(mockPreparedStatement).when(mockConnection).prepareStatement(expectedQuery); + doReturn(mockRs).when(mockPreparedStatement).executeQuery(); + when(mockRs.next()).thenReturn(true).thenReturn(false); + + // Act + ResponseEntity> response = uut.getActivePlannedConditionTims(); + + // Assert + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + Assertions.assertEquals(1, response.getBody().size()); + verify(mockConnection).prepareStatement(expectedQuery); + verify(mockPreparedStatement).close(); + verify(mockConnection).close(); + } + + @Test + void getActivePlannedConditionTims_SQLException_ShouldReturnInternalServerError() throws SQLException { + // Arrange + String expectedQuery = "select * from active_tim where client_id like '%planned%'"; + doReturn(mockConnection).when(mockDbInteractions).getConnectionPool(); + doReturn(mockPreparedStatement).when(mockConnection).prepareStatement(expectedQuery); + doThrow(new SQLException()).when(mockPreparedStatement).executeQuery(); + + // Act + ResponseEntity> response = uut.getActivePlannedConditionTims(); + + // Assert + Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + Assertions.assertNull(response.getBody(), "ActiveTim list should be null"); + verify(mockConnection).prepareStatement(expectedQuery); + verify(mockPreparedStatement).close(); + verify(mockConnection).close(); + } + + @Nested + @Testcontainers + class ActiveTimControllerIntegrationTest { + private ActiveTimController uut; + + @Container + PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine") + .withDatabaseName("test") + .withUsername("test") + .withPassword("test"); + + @Mock + private TimDbTables timDbTables; + + @Mock + private SQLNullHandler sqlNullHandler; + + @Mock + private DateTimeHelper dateTimeHelper; + + @Mock + private Utility utility; + + @Mock + private DbInteractionsProps dbConfig; + + @Mock + private EmailHelper emailHelper; + + @BeforeEach + void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(dbConfig.getDbUrl()).thenReturn(postgres.getJdbcUrl()); + when(dbConfig.getDbUsername()).thenReturn(postgres.getUsername()); + when(dbConfig.getDbPassword()).thenReturn(postgres.getPassword()); + when(dbConfig.getMaximumPoolSize()).thenReturn(10); + when(dbConfig.getConnectionTimeout()).thenReturn(1000); + + // Initialize the controller + uut = new ActiveTimController(); + + // Manually inject dependencies + DbInteractions dbInteractions = new DbInteractions(dbConfig, emailHelper); + uut.InjectBaseDependencies(dbInteractions, utility); + uut.InjectDependencies(timDbTables, sqlNullHandler, dateTimeHelper); + + // Create the active_tim table + try (Connection connection = DriverManager.getConnection( + postgres.getJdbcUrl(), + postgres.getUsername(), + postgres.getPassword()); + Statement statement = connection.createStatement() + ) { + // note: this is not the standard/accepted practice, this is a one-off instance to get a test + // to prevent a regression of a production bug + statement.execute( + "CREATE TABLE IF NOT EXISTS active_tim (" + + " active_tim_id SERIAL PRIMARY KEY," + + " tim_id BIGINT," + + " client_id VARCHAR(255)," + + " direction VARCHAR(10)," + + " tim_start TIMESTAMP," + + " tim_end TIMESTAMP," + + " expiration_date TIMESTAMP," + + " route VARCHAR(255)," + + " sat_record_id VARCHAR(255)," + + " pk INTEGER," + + " start_latitude DECIMAL," + + " start_longitude DECIMAL," + + " end_latitude DECIMAL," + + " end_longitude DECIMAL," + + " tim_type_id BIGINT," + + " project_key INTEGER," + + " marked_for_deletion CHAR(1) DEFAULT '0'" + + ")" + ); + } + } + + @Test + void testGetBufferTimsByClientId_ShouldFindBufferTimsSuccessfully() throws Exception { + // Arrange + try (Connection connection = DriverManager.getConnection( + postgres.getJdbcUrl(), + postgres.getUsername(), + postgres.getPassword()); + Statement statement = connection.createStatement() + ) { + statement.execute( + "INSERT INTO active_tim (client_id, direction, tim_type_id) " + + "VALUES ('59009%BUFF0', 'I', 1)" + ); + statement.execute( + "INSERT INTO active_tim (client_id, direction, tim_type_id) " + + "VALUES ('59009%BUFF1', 'I', 1)" + ); + // Insert a non-buffer TIM for the same client + statement.execute( + "INSERT INTO active_tim (client_id, direction, tim_type_id) " + + "VALUES ('59009', 'I', 1)" + ); + } + + + // Act + ResponseEntity> response = uut.GetBufferTimsByClientId("59009"); + + // Assert + Assertions.assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + List results = response.getBody(); + Assertions.assertNotNull(results); + assertEquals(2, results.size(), + "Expected to find 2 buffer TIMs matching pattern '59009%BUFF*', but found " + results.size()); + + // Verify that the buffer TIMs were found + boolean found59009BUFF0 = results.stream() + .anyMatch(tim -> "59009%BUFF0".equals(tim.getClientId())); + Assertions.assertTrue(found59009BUFF0, + "Should match '59009%BUFF0'"); + boolean found59009BUFF1 = results.stream() + .anyMatch(tim -> "59009%BUFF1".equals(tim.getClientId())); + Assertions.assertTrue(found59009BUFF1, + "Should match '59009%BUFF1'"); + + // Verify the non-buffer TIM is NOT included + boolean foundNonBuffer = results.stream() + .anyMatch(tim -> "59009".equals(tim.getClientId())); + Assertions.assertFalse(foundNonBuffer, + "Should NOT match '59009' - this is the anchor TIM, not a buffer TIM"); + } + } } \ No newline at end of file diff --git a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java index 4b7598675..53ab19455 100644 --- a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java +++ b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java @@ -1,14 +1,18 @@ package com.trihydro.cvdatacontroller.controller; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; +import com.trihydro.library.helpers.DateStringNotInISO8601FormatException; +import com.trihydro.library.helpers.DateTimeHelper; +import com.trihydro.library.helpers.DateTimeHelperImpl; import com.trihydro.library.model.ActiveTimHoldingDeleteModel; import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Timestamp; +import java.text.ParseException; import java.time.Instant; import java.util.List; @@ -30,13 +34,17 @@ public class ActiveTimHoldingControllerTest extends TestBase data = uut.InsertActiveTimHolding(activeTimHolding); // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); + assertEquals(HttpStatus.OK, data.getStatusCode()); verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET @@ -96,13 +105,13 @@ public void InsertActiveTimHolding_ExistingSDX() throws SQLException { activeTimHolding.setDirection("direction"); activeTimHolding.setStartPoint(startCoord); activeTimHolding.setEndPoint(endCoord); + activeTimHolding.setDateCreated("2021-01-01T00:00:00.000Z"); doReturn(null).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); doReturn(-99l).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); var now = Instant.parse(activeTimHolding.getDateCreated()); java.util.Date date_created = java.util.Date.from(now); - doReturn(date_created).when(mockUtility).convertDate(activeTimHolding.getDateCreated()); - mockUtility.timestampFormat = timestampFormat; + doReturn(date_created).when(dateTimeHelper).convertDate(activeTimHolding.getDateCreated()); Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); String query = "select active_tim_holding_id from active_tim_holding"; @@ -114,8 +123,8 @@ public void InsertActiveTimHolding_ExistingSDX() throws SQLException { ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - Assertions.assertEquals(Long.valueOf(-99), data.getBody()); + assertEquals(HttpStatus.OK, data.getStatusCode()); + assertEquals(Long.valueOf(-99), data.getBody()); verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET @@ -126,6 +135,7 @@ public void InsertActiveTimHolding_ExistingSDX() throws SQLException { verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 12, activeTimHolding.getProjectKey()); verify(mockStatement).executeQuery(query); } @@ -136,18 +146,18 @@ public void InsertActiveTimHolding_ExistingRSU() throws SQLException { setupInsertQueryStatement(); setupPreparedStatement(); ActiveTimHolding activeTimHolding = new ActiveTimHolding(); - activeTimHolding.setRsuTargetId("10.10.10.1"); + activeTimHolding.setRsuTarget("10.10.10.1"); activeTimHolding.setClientId("clientId"); activeTimHolding.setDirection("direction"); activeTimHolding.setStartPoint(startCoord); activeTimHolding.setEndPoint(endCoord); + activeTimHolding.setDateCreated("2021-01-01T00:00:00.000Z"); doReturn(null).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); doReturn(-99l).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); var now = Instant.parse(activeTimHolding.getDateCreated()); java.util.Date date_created = java.util.Date.from(now); - doReturn(date_created).when(mockUtility).convertDate(activeTimHolding.getDateCreated()); - mockUtility.timestampFormat = timestampFormat; + doReturn(date_created).when(dateTimeHelper).convertDate(activeTimHolding.getDateCreated()); Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); String query = "select active_tim_holding_id from active_tim_holding"; @@ -159,8 +169,8 @@ public void InsertActiveTimHolding_ExistingRSU() throws SQLException { ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - Assertions.assertEquals(Long.valueOf(-99), data.getBody()); + assertEquals(HttpStatus.OK, data.getStatusCode()); + assertEquals(Long.valueOf(-99), data.getBody()); verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET @@ -189,12 +199,72 @@ public void InsertActiveTimHolding_FAIL() throws SQLException { ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); // Assert - Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); verify(mockPreparedStatement).close(); verify(mockConnection).close(); } + @Test + public void testInsertActiveTimHolding_TimEndInTableFormat_Success_ReturnsId() throws DateStringNotInISO8601FormatException, ParseException { + // Arrange + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setDateCreated("2021-01-01T00:00:00.000Z"); + activeTimHolding.setDesiredEndDateTime("2021-01-10 00:00:00"); + doReturn(actualDateTimeHelper.convertDate(activeTimHolding.getDateCreated())).when(dateTimeHelper).convertDate(activeTimHolding.getDateCreated()); + doReturn(actualDateTimeHelper.isInTableFormat(activeTimHolding.getDesiredEndDateTime())).when(dateTimeHelper).isInTableFormat(activeTimHolding.getDesiredEndDateTime()); + doReturn(actualDateTimeHelper.convertDateStringFromTableFormatIntoISO8601Format(activeTimHolding.getDesiredEndDateTime())).when(dateTimeHelper).convertDateStringFromTableFormatIntoISO8601Format(activeTimHolding.getDesiredEndDateTime()); + doReturn(actualDateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject("2021-01-10T00:00:00.000Z")).when(dateTimeHelper).convertDateStringFromISO8601FormatIntoTimestampObject("2021-01-10T00:00:00.000Z"); + doReturn(10L).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); + + // Act + ResponseEntity response = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals(10L, response.getBody()); + } + + @Test + public void testInsertActiveTimHolding_TimEndInTableFormat_FailedToConvert_ReturnsNegative2() throws ParseException { + // Arrange + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setDateCreated("2021-01-01T00:00:00.000Z"); + activeTimHolding.setDesiredEndDateTime("2021-01-10 00:00:00"); + doReturn(actualDateTimeHelper.convertDate(activeTimHolding.getDateCreated())).when(dateTimeHelper).convertDate(any()); + doReturn(actualDateTimeHelper.isInTableFormat(activeTimHolding.getDesiredEndDateTime())).when(dateTimeHelper).isInTableFormat(activeTimHolding.getDesiredEndDateTime()); + doThrow(new RuntimeException()).when(dateTimeHelper).convertDateStringFromTableFormatIntoISO8601Format(activeTimHolding.getDesiredEndDateTime()); + + // Act + ResponseEntity response = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals(-2L, response.getBody()); + } + + @Test + public void testInsertActiveTimHolding_TimEndInInvalidFormat_ReturnsNegativeThree() throws ParseException, + DateStringNotInISO8601FormatException { + // Arrange + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setDateCreated("2021-01-01T00:00:00.000Z"); + activeTimHolding.setDesiredEndDateTime("banana"); + doReturn(actualDateTimeHelper.convertDate(activeTimHolding.getDateCreated())).when(dateTimeHelper).convertDate(any()); + doReturn(actualDateTimeHelper.isInTableFormat(activeTimHolding.getDesiredEndDateTime())).when(dateTimeHelper).isInTableFormat(activeTimHolding.getDesiredEndDateTime()); + doThrow(new DateStringNotInISO8601FormatException("invalid format")).when(dateTimeHelper).convertDateStringFromISO8601FormatIntoTimestampObject(activeTimHolding.getDesiredEndDateTime()); + + // Act + ResponseEntity response = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals(-3L, response.getBody()); + } + @Test public void getActiveTimHoldingForRsu_SUCCESS() throws SQLException { // Arrange @@ -203,9 +273,9 @@ public void getActiveTimHoldingForRsu_SUCCESS() throws SQLException { ResponseEntity> data = uut.getActiveTimHoldingForRsu("ipv4Address"); // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); + assertEquals(HttpStatus.OK, data.getStatusCode()); Assertions.assertNotNull(data.getBody()); - Assertions.assertEquals(1, data.getBody().size()); + assertEquals(1, data.getBody().size()); verify(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); verify(mockRs).getString("CLIENT_ID"); verify(mockRs).getString("DIRECTION"); @@ -231,9 +301,9 @@ public void getActiveTimHoldingForRsu_FAIL() throws SQLException { ResponseEntity> data = uut.getActiveTimHoldingForRsu("ipv4Address"); // Assert - Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); Assertions.assertNotNull(data.getBody()); - Assertions.assertEquals(0, data.getBody().size()); + assertEquals(0, data.getBody().size()); verify(mockStatement).close(); verify(mockConnection).close(); verify(mockRs).close(); @@ -245,9 +315,9 @@ void getAllRecords_SuccessfulRetrieval_ShouldReturnRecords() throws SQLException ResponseEntity> response = uut.getAllRecords(); // verify - Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(HttpStatus.OK, response.getStatusCode()); Assertions.assertNotNull(response.getBody()); - Assertions.assertEquals(1, response.getBody().size()); + assertEquals(1, response.getBody().size()); verify(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); verify(mockRs).getString("CLIENT_ID"); verify(mockRs).getString("DIRECTION"); diff --git a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/TimControllerTest.java b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/TimControllerTest.java index 3772b64a7..d7305bc66 100644 --- a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/TimControllerTest.java +++ b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/TimControllerTest.java @@ -7,6 +7,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.trihydro.library.helpers.DateTimeHelper; import java.sql.SQLException; import java.sql.Timestamp; import java.time.Instant; @@ -46,10 +47,12 @@ public class TimControllerTest extends TestBase { private SecurityResultCodeTypeController mockSecurityResultCodeTypeController; @Mock private ResponseEntity> mockResponseEntitySecurityResultCodeTypeList; + @Mock + private DateTimeHelper dateTimeHelper; @BeforeEach public void setupSubTest() { - uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler, mockSecurityResultCodeTypeController); + uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler, mockSecurityResultCodeTypeController, dateTimeHelper); } private void setupInsertQueryStatement() { @@ -112,9 +115,8 @@ public void AddTim_timMetadata_SUCCESS() throws SQLException { var recTime = Instant.parse(tim.getOdeTimMetadata().getOdeReceivedAt()); java.util.Date gen_at = java.util.Date.from(genTime); java.util.Date rec_at = java.util.Date.from(recTime); - doReturn(gen_at).when(mockUtility).convertDate(tim.getOdeTimMetadata().getRecordGeneratedAt()); - doReturn(rec_at).when(mockUtility).convertDate(tim.getOdeTimMetadata().getOdeReceivedAt()); - mockUtility.timestampFormat = timestampFormat; + doReturn(gen_at).when(dateTimeHelper).convertDate(tim.getOdeTimMetadata().getRecordGeneratedAt()); + doReturn(rec_at).when(dateTimeHelper).convertDate(tim.getOdeTimMetadata().getOdeReceivedAt()); // Act Long timId = uut.AddTim(tim); @@ -281,7 +283,7 @@ public void deleteOldTim() throws SQLException { private ReceivedMessageDetails getRxMsg() { ReceivedMessageDetails rxMsg = new ReceivedMessageDetails(); - + OdeLogMsgMetadataLocation locationData = new OdeLogMsgMetadataLocation(); locationData.setElevation("1.0"); locationData.setHeading("2.0"); diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/factory/KafkaFactory.java b/cv-data-service-library/src/main/java/com/trihydro/library/factory/KafkaFactory.java index b8d1c1a79..ce8add417 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/factory/KafkaFactory.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/factory/KafkaFactory.java @@ -4,24 +4,17 @@ import java.util.List; import java.util.Properties; -import com.trihydro.library.helpers.Utility; - +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class KafkaFactory { - private Utility utility; - - @Autowired - public KafkaFactory(Utility _utility) { - utility = _utility; - } /** * Creates a Kafka consumer that has a key.deserializer and value.deserializer @@ -112,8 +105,8 @@ public Consumer createStringConsumer(String host, String consume var consumer = new KafkaConsumer(props); consumer.subscribe(topics); - utility.logWithDate(String.format("Created consumer for consumer group %s, subscribed to topic(s) %s", - consumerGroup, String.join(", ", topics))); + log.info(String.format("Created consumer for consumer group %s, subscribed to topic(s) %s", + consumerGroup, String.join(", ", topics))); return consumer; } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java index 52174c1f5..a920b31a4 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java @@ -8,6 +8,8 @@ import com.grum.geocalc.Coordinate; import com.grum.geocalc.EarthCalc; import com.grum.geocalc.Point; +import lombok.extern.slf4j.Slf4j; + import com.trihydro.library.model.ContentEnum; import com.trihydro.library.model.Milepost; import com.trihydro.library.model.WydotTim; @@ -28,6 +30,7 @@ import us.dot.its.jpo.ode.plugin.j2735.timstorage.MutcdCode.MutcdCodeEnum; @Component +@Slf4j public class CreateBaseTimUtil { private Utility utility; @@ -115,20 +118,20 @@ public WydotTravelerInputData buildTim(WydotTim wydotTim, TimGenerationProps gen */ protected List buildRegions(BigDecimal defaultLaneWidth, List allMileposts, List reducedMileposts, Milepost anchor) { if (reducedMileposts.size() <= 63) { - utility.logWithDate("Less than 63 mileposts, building a single region.", CreateBaseTimUtil.class); + log.info("Less than 63 mileposts, building a single region."); List regions = new ArrayList(); OdeTravelerInformationMessage.DataFrame.Region singleRegion = buildSingleRegion(defaultLaneWidth, allMileposts, reducedMileposts, anchor); regions.add(singleRegion); return regions; } else { - utility.logWithDate("More than 63 mileposts, building multiple regions.", CreateBaseTimUtil.class); + log.info("More than 63 mileposts, building multiple regions."); return buildMultipleRegions(defaultLaneWidth, allMileposts, reducedMileposts, anchor); } } /** * Builds multiple regions based on the given parameters. - * + * * @param defaultLaneWidth the default lane width * @param allMileposts a list of all mileposts * @param reducedMileposts a list of reduced mileposts @@ -154,7 +157,7 @@ protected List buildMultipleRegi } } - utility.logWithDate("Built " + regions.size() + " regions.", CreateBaseTimUtil.class); + log.info("Built {} regions.", regions.size()); return regions; } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateStringNotInISO8601FormatException.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateStringNotInISO8601FormatException.java new file mode 100644 index 000000000..21da57bc5 --- /dev/null +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateStringNotInISO8601FormatException.java @@ -0,0 +1,7 @@ +package com.trihydro.library.helpers; + +public class DateStringNotInISO8601FormatException extends Exception { + public DateStringNotInISO8601FormatException(String message) { + super(message); + } +} diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateTimeHelper.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateTimeHelper.java new file mode 100644 index 000000000..07feaacfa --- /dev/null +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateTimeHelper.java @@ -0,0 +1,52 @@ +package com.trihydro.library.helpers; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.time.ZonedDateTime; +import java.util.Date; + +public interface DateTimeHelper { + Date convertDate(String incomingDate); + + String getStartTime(); + + String getIsoDateTimeString(Date date); + + String getIsoDateTimeString(ZonedDateTime date); + + /** + * Checks if a given date string is in the table format defined by the class. + * The expected format is checked against a predefined format in TABLE_FORMAT. + * + * @param dateString The date string to be checked for table format compliance. + * @return true if the date string matches the table format, false otherwise. + */ + boolean isInTableFormat(String dateString); + + /** + * Checks whether the provided date string is in the ISO8601 format. + * + * @param dateString The date string to be validated against the ISO8601 format. + * @return true if the date string complies with the ISO8601 format, false otherwise. + */ + boolean isInISO8601Format(String dateString); + + String convertDateStringFromTableFormatIntoISO8601Format(String endDateTimeStr) throws ParseException; + + String convertZonedDateTimeToISO8601Format(ZonedDateTime zdt); + + /** + * Converts a date string from the ISO8601 format into a {@link Timestamp} object. + *

+ * The method validates if the provided date string is in the ISO8601 format. If the validation fails, + * a {@link DateStringNotInISO8601FormatException} is thrown. Upon successful validation, the method + * converts the date string into a {@link Date} object using the {@code convertDate} method, + * and then converts it to a {@link Timestamp} object. + * + * @param dateString The date string in the ISO8601 format to be converted into a {@link Timestamp} object. + * @return A {@link Timestamp} object representing the input date string. + * @throws DateStringNotInISO8601FormatException If the provided date string is not in the ISO8601 format. + */ + Timestamp convertDateStringFromISO8601FormatIntoTimestampObject(String dateString) throws + DateStringNotInISO8601FormatException; +} diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateTimeHelperImpl.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateTimeHelperImpl.java new file mode 100644 index 000000000..27c2c821c --- /dev/null +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DateTimeHelperImpl.java @@ -0,0 +1,165 @@ +package com.trihydro.library.helpers; + +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.TimeZone; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class DateTimeHelperImpl implements DateTimeHelper { + /** + * A constant {@link SimpleDateFormat} using the pattern "yyyy-MM-dd HH:mm:ss". + * This format matches the default output of Timestamp.toString(), which is commonly + * how timestamps appear in PostgreSQL tables. It represents a timestamp with year, + * month, day, hour, minute, and second. + * + * Format pattern: "yyyy-MM-dd HH:mm:ss" + * Example value: "2023-10-15 14:30:45" + */ + private static final SimpleDateFormat TABLE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + /** + * A constant {@link SimpleDateFormat} used for formatting and parsing ISO 8601 date-time strings + * with millisecond precision and a trailing 'Z' to denote UTC time. + * + * Example value: "2023-10-15T12:34:56.789Z" + * + * This seems to be the most stable format for our application. + */ + private static final SimpleDateFormat ISO8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + // other date formats + // TODO: identify if formats can be removed in favor of the newer TABLE_FORMAT and ISO8601_FORMAT formats + private final DateFormat utcFormatMilliSec = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private final DateFormat utcFormatSec = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + private final DateFormat utcFormatMin = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); + private final DateFormat utcTextFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z[UTC]'"); + + public DateTimeHelperImpl() { + } + + @Override + public Date convertDate(String incomingDate) { + Date convertedDate = null; + try { + if (incomingDate != null) { + if (incomingDate.contains("UTC")) { + utcTextFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcTextFormat.parse(incomingDate); + } else if (incomingDate.contains(".")) { + utcFormatMilliSec.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcFormatMilliSec.parse(incomingDate); + } else if (incomingDate.length() == 17) { + utcFormatMin.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcFormatMin.parse(incomingDate); + } else { + utcFormatSec.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcFormatSec.parse(incomingDate); + } + } + } catch (ParseException e1) { + log.error("Exception", e1); + } + return convertedDate; + } + + @Override + public String getStartTime() { + Date date = new Date(); + return getIsoDateTimeString(date); + } + + @Override + public String getIsoDateTimeString(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + return sdf.format(date); + } + + @Override + public String getIsoDateTimeString(ZonedDateTime date) { + if (date == null) { + return null; + } + + var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + var utcDate = date.withZoneSameInstant(ZoneOffset.UTC); + return utcDate.format(formatter); + } + + /** + * Checks if a given date string is in the table format defined by the class. + * The expected format is checked against a predefined format in TABLE_FORMAT. + * + * @param dateString The date string to be checked for table format compliance. + * @return true if the date string matches the table format, false otherwise. + */ + @Override + public boolean isInTableFormat(String dateString) { + try { + TABLE_FORMAT.parse(dateString); // check if date is in table format ("2025-04-30 06:00:00") + return true; + } catch (Exception ignored) { + return false; + } + } + + /** + * Checks whether the provided date string is in the ISO8601 format. + * + * @param dateString The date string to be validated against the ISO8601 format. + * @return true if the date string complies with the ISO8601 format, false otherwise. + */ + @Override + public boolean isInISO8601Format(String dateString) { + try { + ISO8601_FORMAT.parse(dateString); // check if date is in ISO8601 format ("2025-04-30T06:00:00.000Z") + return true; + } catch (Exception ignored) { + return false; + } + } + + @Override + public String convertDateStringFromTableFormatIntoISO8601Format(String endDateTimeStr) throws ParseException { + return ISO8601_FORMAT.format(TABLE_FORMAT.parse(endDateTimeStr)); + } + + @Override + public String convertZonedDateTimeToISO8601Format(ZonedDateTime zdt) { + ISO8601_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + return ISO8601_FORMAT.format(convertDate(zdt.toString())); + } + + /** + * Converts a date string from the ISO8601 format into a {@link Timestamp} object. + *

+ * The method validates if the provided date string is in the ISO8601 format. If the validation fails, + * a {@link DateStringNotInISO8601FormatException} is thrown. Upon successful validation, the method + * converts the date string into a {@link Date} object using the {@code convertDate} method, + * and then converts it to a {@link Timestamp} object. + * + * @param dateString The date string in the ISO8601 format to be converted into a {@link Timestamp} object. + * @return A {@link Timestamp} object representing the input date string. + * @throws DateStringNotInISO8601FormatException If the provided date string is not in the ISO8601 format. + */ + @Override + public Timestamp convertDateStringFromISO8601FormatIntoTimestampObject(String dateString) throws + DateStringNotInISO8601FormatException { + if (!isInISO8601Format(dateString)) { + throw new DateStringNotInISO8601FormatException( + "Date string expected to be in ISO8601 format but was not: " + dateString); + } + Date desiredEndDateTime = convertDate(dateString); + return new Timestamp(desiredEndDateTime.getTime()); + } + +} \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/EmailHelper.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/EmailHelper.java index adcaefddd..1cbe3cf4f 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/EmailHelper.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/EmailHelper.java @@ -5,6 +5,7 @@ import com.trihydro.library.model.EmailProps; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.MailException; @@ -13,6 +14,7 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class EmailHelper { private JavaMailSenderImplProvider mailProvider; @@ -49,6 +51,7 @@ public void SendEmail(String[] to, String subject, String body) throws MailExcep */ public void SendEmail(String[] to, String[] bcc, String subject, String body) throws MailException, MessagingException { + log.debug("Sending email to: {}", String.join(", ", to)); JavaMailSenderImpl mailSender = mailProvider.getJSenderImpl(mailProps.getMailHost(), mailProps.getMailPort()); MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8"); @@ -65,6 +68,7 @@ public void SendEmail(String[] to, String[] bcc, String subject, String body) helper.setText(body, true); mailSender.send(mimeMessage); + log.debug("Email sent successfully to: {}", String.join(", ", to)); } /** diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java index 1709b3751..8ffcad458 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java @@ -21,11 +21,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.trihydro.library.model.ContentEnum; -import us.dot.its.jpo.ode.model.OdeLogMetadata; +import us.dot.its.jpo.ode.model.*; import us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy; -import us.dot.its.jpo.ode.model.OdeRequestMsgMetadata; -import us.dot.its.jpo.ode.model.OdeTimPayload; -import us.dot.its.jpo.ode.model.SerialId; import us.dot.its.jpo.ode.plugin.RoadSideUnit.RSU; import us.dot.its.jpo.ode.plugin.SNMP; import us.dot.its.jpo.ode.plugin.ServiceRequest; diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/RegionNameTrimmer.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/RegionNameTrimmer.java index 330f06b2e..de145e677 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/RegionNameTrimmer.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/RegionNameTrimmer.java @@ -1,20 +1,18 @@ package com.trihydro.library.helpers; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.trihydro.library.model.RegionNameElementCollection; + @Component +@Slf4j public class RegionNameTrimmer { private static final int MAX_REGION_NAME_LENGTH = 63; - - private Utility utility; - @Autowired - public RegionNameTrimmer(Utility _utility) { - utility = _utility; - } + public RegionNameTrimmer() {} /** * Trims the region name if it is too long. Region names longer than 63 characters will fail to be processed by the ODE. @@ -51,8 +49,8 @@ private String trimRegionNameWithTimIdDelimiter(RegionNameElementCollection elem if (cannotBeTrimmedAndStillHaveRoomForEllipsis(elements.route, charactersToTrim)) { throw new IllegalArgumentException("Region name is too long and cannot be trimmed without unacceptable data loss"); } - - utility.logWithDate("Trimming 'route' part of region name of TIM to fit within 63 characters."); + + log.info("Trimming 'route' part of region name of TIM to fit within 63 characters."); elements.route = elements.route.substring(0, elements.route.length() - (charactersToTrim + 3)); return elements.direction + "_" + elements.route + "..." + "_" + elements.rsuOrSat + "_" + elements.timType + "_" + elements.timId; } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SQLNullHandler.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SQLNullHandler.java index b10014c44..fa30367d5 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SQLNullHandler.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SQLNullHandler.java @@ -56,7 +56,7 @@ public void setBigDecimalOrNull(PreparedStatement ps, int column, String value) public void setTimestampOrNull(PreparedStatement ps, int column, Timestamp value) throws SQLException { if (value != null) - ps.setTimestamp(column, value); + ps.setTimestamp(column, value, java.util.Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))); else ps.setNull(column, java.sql.Types.TIMESTAMP); } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SnmpHelper.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SnmpHelper.java index 4bdd904f4..5da9a9473 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SnmpHelper.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/SnmpHelper.java @@ -9,13 +9,17 @@ import com.trihydro.library.model.WydotTravelerInputData; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.springframework.stereotype.Component; import us.dot.its.jpo.ode.plugin.SNMP; @Component -public class SnmpHelper { +@Slf4j +public class SnmpHelper { + public SNMP getSnmp(String startDateTime, String endDateTime, WydotTravelerInputData timToSend) { SNMP snmp = new SNMP(); snmp.setChannel(183); @@ -39,7 +43,7 @@ public SNMP getSnmp(String startDateTime, String endDateTime, WydotTravelerInput endDateTime = df.format(endDate); } catch (IllegalArgumentException illArg) { // if we failed here, set the endDateTime for 2 weeks from current time - System.out.println("Illegal Argument exception for endDate: " + illArg.getMessage()); + log.info("Illegal Argument exception for endDate: {}", illArg.getMessage()); endDateTime = java.time.Clock.systemUTC().instant().plus(2, ChronoUnit.WEEKS).toString(); } } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java index f88afdebd..36404bb40 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java @@ -39,6 +39,7 @@ import com.trihydro.library.service.RsuService; import com.trihydro.library.service.TimGenerationProps; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -566,117 +567,213 @@ public List resetTimStartTimeAndResubmitToOde(List a /** * Expires existing TIMs and resubmits TIMs to the ODE + *

+ * This method is called when an 'all clear' is submitted to the system. + *

+ * A side effect of this method is setting the 'marked_for_deletion' flag for the active_tim record to 1 * * @param activeTimIds TIMs to resubmit + * @return List Errors that occurred while processing the request */ - public void expireTimAndResubmitToOde(List activeTimIds) { + public List expireTimAndResubmitToOde(List activeTimIds) { List exceptions = new ArrayList<>(); + + // Initial input validation with improved logging if (activeTimIds == null) { - log.info("No active TIMs found to resubmit to ODE. Returning..."); - return; + log.info("No active TIMs provided to resubmit to ODE (null list). Returning empty exception list."); + return exceptions; } + + if (activeTimIds.isEmpty()) { + log.info("No active TIMs provided to resubmit to ODE (empty list). Returning empty exception list."); + return exceptions; + } + + log.info("Starting expiration process for {} TIMs: {}", activeTimIds.size(), activeTimIds); + + // Track success and failure counts for summary logging + int successCount = 0; + int failureCount = 0; + // iterate over tims, fetch, and push out for (Long activeTimId : activeTimIds) { + log.trace("Processing TIM ID: {} ({} of {})", activeTimId, activeTimIds.indexOf(activeTimId) + 1, activeTimIds.size()); + try { + log.trace("Retrieving update model for TIM ID: {}", activeTimId); var tum = activeTimService.getUpdateModelFromActiveTimId(activeTimId); if (tum == null) { - exceptions.add(new ResubmitTimException(activeTimId, - "Failed to get Update Model from active tim")); + String exceptionMessage = "Failed to get Update Model from active TIM"; + log.warn("{} for TIM ID: {}", exceptionMessage, activeTimId); + exceptions.add(new ResubmitTimException(activeTimId, exceptionMessage)); + failureCount++; continue; } + + log.trace("Validating TIM ID: {}", activeTimId); if (!isValidTim(tum)) { - exceptions.add(new ResubmitTimException(activeTimId, - "Failed to generate valid Update Model from active tim")); + String exceptionMessage = "Failed to generate valid Update Model from active TIM"; + log.warn("{} for TIM ID: {}", exceptionMessage, activeTimId); + exceptions.add(new ResubmitTimException(activeTimId, exceptionMessage)); + failureCount++; continue; } + log.trace("Handling lane width for TIM ID: {}", activeTimId); if (tum.getLaneWidth() == null) { + log.trace("Using default lane width for TIM ID: {}", activeTimId); tum.setLaneWidth(config.getDefaultLaneWidth()); } else { // Database has lane width as cm, but ODE takes m + log.trace("Converting lane width from cm to m for TIM ID: {}", activeTimId); tum.setLaneWidth(tum.getLaneWidth().divide(BigDecimal.valueOf(100))); } + log.trace("Creating WydotTim from update model for TIM ID: {}", activeTimId); WydotTim wydotTim = getWydotTimFromTum(tum); - List reduced_mps = new ArrayList<>(); + List reduced_mps; + + log.trace("Retrieving mileposts for TIM ID: {}", activeTimId); List allMps = getAllMps(wydotTim); + if (allMps.size() < 2) { - String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", - tum.getActiveTimId()); + String exMsg = + String.format("Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d (found %d)", tum.getActiveTimId(), + allMps.size()); log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); + failureCount++; continue; } Milepost firstPoint = allMps.get(0); Milepost secondPoint = allMps.get(1); + log.trace("First point at ({}, {}) and second point at ({}, {}) for TIM ID: {}", firstPoint.getLatitude(), firstPoint.getLongitude(), + secondPoint.getLatitude(), secondPoint.getLongitude(), activeTimId); Milepost anchorMp; try { + log.trace("Calculating anchor coordinate for TIM ID: {}", activeTimId); Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); anchorMp = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), anchorCoordinate.getLongitude()); + log.trace("Anchor milepost created at ({}, {}) for TIM ID: {}", anchorMp.getLatitude(), anchorMp.getLongitude(), activeTimId); } catch (Utility.IdenticalPointsException e) { + log.warn("Identical points found while calculating anchor point for TIM ID: {}. Attempting recovery.", activeTimId); anchorMp = identicalPointsExceptionHandler.recover(allMps); if (anchorMp == null) { String exMsg = String.format( - "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", + "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d and recovery failed", tum.getActiveTimId()); log.error(exMsg, e); exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); + failureCount++; continue; } + log.debug("Successfully recovered from identical points for TIM ID: {}, anchor at ({}, {})", activeTimId, anchorMp.getLatitude(), + anchorMp.getLongitude()); } // reduce the mileposts by removing straight away posts - reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, - config.getPathDistanceLimit()); - - OdeTravelerInformationMessage tim = - getTim(tum, reduced_mps, allMps, anchorMp, false, true); + log.trace("Applying milepost reduction algorithm for TIM ID: {}", activeTimId); + reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, config.getPathDistanceLimit()); + log.debug("Reduced {} mileposts to {} for TIM ID: {}", allMps.size(), reduced_mps.size(), activeTimId); + + log.trace("Generating TIM message for TIM ID: {}", activeTimId); + boolean shouldResetStartTimes = true; + boolean shouldSetDurationTimeToFiveMinutes = true; + OdeTravelerInformationMessage tim = getTim(tum, reduced_mps, allMps, anchorMp, shouldResetStartTimes, shouldSetDurationTimeToFiveMinutes); if (tim == null) { - String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", - tum.getActiveTimId()); + String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", tum.getActiveTimId()); log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); + failureCount++; continue; } + + log.trace("Preparing TIM for sending - TIM ID: {}", activeTimId); WydotTravelerInputData timToSend = new WydotTravelerInputData(); timToSend.setTim(tim); + + log.debug("Sending resubmitted TIM for ID: {}", activeTimId); var extraEx = sendTim(timToSend, tum, activeTimId, reduced_mps); + + log.trace("Marking TIM for deletion - TIM ID: {}", activeTimId); activeTimService.markForDeletion(activeTimId); + if (!extraEx.isEmpty()) { + log.warn("Encountered {} additional exceptions while sending TIM ID: {}", extraEx.size(), activeTimId); exceptions.addAll(extraEx); + failureCount++; + } else { + log.debug("Successfully resubmitted TIM ID: {}", activeTimId); + successCount++; } } catch (Exception ex) { - exceptions.add(new ResubmitTimException(activeTimId, ex.getMessage())); + String errorMsg = String.format("Unexpected exception processing TIM ID %d: %s", activeTimId, ex.getMessage()); + log.error(errorMsg, ex); + exceptions.add(new ResubmitTimException(activeTimId, errorMsg)); + failureCount++; } } - if (!exceptions.isEmpty()) { - log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); + + // Log summary of results + if (exceptions.isEmpty()) { + log.info("Successfully expired and resubmitted all {} TIMs with no errors", activeTimIds.size()); + } else { + log.warn("Completed TIM expiration process. Results: {} successful, {} failed", successCount, failureCount); + log.error("Errors occurred while resubmitting TIMs: {}", + exceptions.stream().map(e -> String.format("TIM %d: %s", e.getActiveTimId(), e.getExceptionMessage())) + .collect(Collectors.joining(", "))); } + + return exceptions; } + /** + * Validates if a TimUpdateModel has the essential fields needed to proceed. + * Checks for existence of start point with latitude and longitude, direction, and route. + * + * @param tum The TimUpdateModel to validate + * @return true if the TimUpdateModel is valid, false otherwise + */ public boolean isValidTim(TimUpdateModel tum) { + if (tum == null) { + log.warn("TimUpdateModel is null"); + return false; + } + + log.trace("Validating TIM with client ID: {}", tum.getClientId()); // start point var stPt = tum.getStartPoint(); - if (stPt == null || stPt.getLatitude() == null || stPt.getLongitude() == null) { + if (stPt == null) { + log.warn("TIM validation failed: Start point is null for TIM with client ID: {}", tum.getClientId()); + return false; + } + + if (stPt.getLatitude() == null || stPt.getLongitude() == null) { + log.warn("TIM validation failed: Start point has null latitude or longitude for TIM with client ID: {}", + tum.getClientId()); return false; } // direction if (tum.getDirection() == null || tum.getDirection().isEmpty()) { + log.warn("TIM validation failed: Direction is null or empty for TIM with client ID: {}", + tum.getClientId()); return false; } // route if (tum.getRoute() == null || tum.getRoute().isEmpty()) { + log.warn("TIM validation failed: Route is null or empty for TIM with client ID: {}", + tum.getClientId()); return false; } + log.trace("TIM validation successful for client ID: {}", tum.getClientId()); return true; } @@ -686,6 +783,17 @@ private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List return getTim(aTim, mps, allMps, anchor, resetStartTimes, false); } + /** + * Creates a TIM message with the specified parameters. + * + * @param aTim The TIM update model containing base information + * @param mps Reduced set of mileposts for the TIM path + * @param allMps Complete set of mileposts + * @param anchor Anchor milepost for the TIM + * @param resetStartTimes Whether to reset the start time to now + * @param resetExpirationTime Whether this is an expiry TIM (sets duration to 5 min) + * @return The constructed TIM message + */ private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List mps, List allMps, Milepost anchor, boolean resetStartTimes, @@ -788,31 +896,36 @@ private List buildMultipleRegion return regions; } - private List sendTim(WydotTravelerInputData timToSend, TimUpdateModel tum, - Long activeTimId, List reduced_mps) { + private List sendTim(WydotTravelerInputData timToSend, TimUpdateModel tum, Long activeTimId, List reduced_mps) { + log.trace("Sending TIM to ODE with client id {}", tum.getClientId()); List exceptions = new ArrayList<>(); + // try to send to RSU if not a sat TIM and along route with RSUs - if (StringUtils.isBlank(tum.getSatRecordId()) && - Arrays.asList(config.getRsuRoutes()).contains(tum.getRoute())) { + log.trace("Checking if active TIM is not a SAT TIM and along route with RSUs"); + if (StringUtils.isBlank(tum.getSatRecordId()) && Arrays.asList(config.getRsuRoutes()).contains(tum.getRoute())) { + log.debug("Sending active TIM with client id {} to RSU", tum.getClientId()); var exMsg = updateAndSendRSU(timToSend, tum); if (StringUtils.isNotBlank(exMsg)) { exceptions.add(new ResubmitTimException(activeTimId, exMsg)); } + } else { + log.debug("SAT record id is not blank or route is not supported. Active TIM with client id {} will not be sent to RSU", tum.getClientId()); } // only send to SDX if the sat record id exists + log.trace("Checking if active TIM is a SAT TIM"); if (!StringUtils.isBlank(tum.getSatRecordId())) { + log.debug("Sending active TIM with client id {} to SDX", tum.getClientId()); var exMsg = updateAndSendSDX(timToSend, tum, reduced_mps); if (StringUtils.isNotBlank(exMsg)) { exceptions.add(new ResubmitTimException(activeTimId, exMsg)); } } else { - String exMsg = "active_tim_id " + tum.getActiveTimId() + - " not sent to SDX (no SAT_RECORD_ID found in database)"; - log.error(exMsg); + log.debug("Active TIM with client id {} is not a SAT TIM and will not be sent to SDX", tum.getClientId()); } + if (!exceptions.isEmpty()) { - log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); + log.error("Errors occurred while resubmitting TIM with client id {}: {}", tum.getClientId(), gson.toJson(exceptions)); } return exceptions; } @@ -857,10 +970,12 @@ private DataFrame getDataFrame(TimUpdateModel aTim, Milepost anchor, boolean res // set startTime // ODE wants a different format: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" // but we have it stored as "2020-05-15 16:09:00" - if (resetStartTimes && aTim.getDurationTime() == 32000) { + if (resetStartTimes) { // note: we were previously requiring durationTime to be 32000 here, this was modified to ensure expiry TIMs get a new start date time + log.debug("Resetting start time for TIM with client id {}", aTim.getClientId()); var newStart = ZonedDateTime.now(); df.setStartDateTime(getIsoDateTimeString(newStart)); } else { + log.debug("Using existing start time for TIM with client id {}", aTim.getClientId()); df.setStartDateTime(getIsoDateTimeString(aTim.getStartDate_Timestamp())); } @@ -1129,7 +1244,7 @@ private String updateAndSendRSU(WydotTravelerInputData timToSend, TimUpdateModel // create new active_tim_holding record, to account for any index changes createNewActiveTimHoldingRecord(timToSend.getTim().getPacketID(), aTim, - rsu.getRsuTarget(), nextRsuIndex, null); + rsu.getRsuTarget(), nextRsuIndex, null, aTim.getEndDateTime()); // set msgCnt to 1 and create new packetId timToSend.getTim().setMsgCnt(1); @@ -1172,7 +1287,7 @@ private String updateAndSendSDX(WydotTravelerInputData timToSend, TimUpdateModel // create new active_tim_holding record createNewActiveTimHoldingRecord(timToSend.getTim().getPacketID(), aTim, null, null, - aTim.getSatRecordId()); + aTim.getSatRecordId(), aTim.getEndDateTime()); log.info("Sending TIM to SDW for refresh: {}", gson.toJson(timToSend)); return odeService.updateTimOnSdw(timToSend); @@ -1205,7 +1320,7 @@ private OdeGeoRegion getServiceRegion(List mileposts) { serviceRegion.setNwCorner(nwCorner); serviceRegion.setSeCorner(seCorner); } else { - System.out.println("getServiceRegion fails due to no mileposts"); + log.info("getServiceRegion fails due to no mileposts"); } return serviceRegion; } @@ -1221,13 +1336,14 @@ private OdeGeoRegion getServiceRegion(List mileposts) { */ private void createNewActiveTimHoldingRecord(String packetId, TimUpdateModel aTim, String rsuTarget, Integer nextRsuIndex, - String satRecordId) { + String satRecordId, String desiredEndDateTime) { // Create a new WydotTim object and set its properties from the TimUpdateModel WydotTim wydotTim = new WydotTim(aTim); // Create a new ActiveTimHolding object with the WydotTim, RSU target, satellite record ID, and end point + log.trace("Preparing to create new active_tim_holding record with desired end date time: '{}' and satRecordId: '{}'", desiredEndDateTime, satRecordId); ActiveTimHolding activeTimHolding = - new ActiveTimHolding(wydotTim, rsuTarget, satRecordId, aTim.getEndPoint()); + new ActiveTimHolding(wydotTim, rsuTarget, satRecordId, aTim.getEndPoint(), desiredEndDateTime); // Set the RSU index and packet ID for the ActiveTimHolding activeTimHolding.setRsuIndex(nextRsuIndex); @@ -1236,4 +1352,4 @@ private void createNewActiveTimHoldingRecord(String packetId, TimUpdateModel aTi // Insert the ActiveTimHolding record into the database activeTimHoldingService.insertActiveTimHolding(activeTimHolding); } -} \ No newline at end of file +} diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java index 4c20350df..99097212e 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java @@ -4,62 +4,24 @@ import java.math.BigDecimal; import java.text.DateFormat; -import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; -import java.util.Date; -import java.util.TimeZone; - +import java.util.Date; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.stereotype.Component; -import com.google.gson.Gson; import com.trihydro.library.model.Coordinate; -import com.trihydro.library.model.Milepost; - -@Component -public class Utility { - private final DateFormat utcFormatMilliSec = - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - private final DateFormat utcFormatSec = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - private final DateFormat utcFormatMin = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); - public DateFormat timestampFormat = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); - public DateFormat utcTextFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z[UTC]'"); - - public Gson gson = new Gson(); - - public Date convertDate(String incomingDate) { - Date convertedDate = null; - try { - if (incomingDate != null) { - if (incomingDate.contains("UTC")) { - utcTextFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcTextFormat.parse(incomingDate); - } else if (incomingDate.contains(".")) { - utcFormatMilliSec.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcFormatMilliSec.parse(incomingDate); - } else if (incomingDate.length() == 17) { - utcFormatMin.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcFormatMin.parse(incomingDate); - } else { - utcFormatSec.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcFormatSec.parse(incomingDate); - } - } - } catch (ParseException e1) { - e1.printStackTrace(); - } - return convertedDate; - } - - public void logWithDate(String msg, Class clazz) { - logWithDate(clazz.getSimpleName() + ": " + msg); - } - - public void logWithDate(String msg) { - Date date = new Date(); - System.out.println(date + " " + msg); - } +import com.trihydro.library.model.Milepost; + +@Component +@Slf4j +public class Utility { + + @Deprecated(forRemoval = true) + private DateFormat timestampFormat = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); public int getMinutesDurationBetweenTwoDates(String startDateTime, String endDateTime) { int duration = getMinutesDurationWithSimpleDateFormat(startDateTime, endDateTime); @@ -74,28 +36,24 @@ public int getMinutesDurationBetweenTwoDates(String startDateTime, String endDat String startDateTimeInZonedDateTime; try { startDateTimeInZonedDateTime = translateToZonedDateTime(startDateTime); - } catch (UnrecognizedDateFormatException e) { - logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + - " and " + endDateTime + ". Unrecognized date format: " + startDateTime); + } catch (UnrecognizedDateFormatException e) { + log.info("Failed to parse dates when getting minutes between: {} and {}. Unrecognized date format: {}", startDateTime, endDateTime, startDateTime); return -1; } String endDateTimeInZonedDateTime; try { endDateTimeInZonedDateTime = translateToZonedDateTime(endDateTime); - } catch (UnrecognizedDateFormatException e) { - logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + - " and " + endDateTime + ". Unrecognized date format: " + startDateTime); + } catch (UnrecognizedDateFormatException e) { + log.info("Failed to parse dates when getting minutes between: {} and {}. Unrecognized date format: {}", startDateTime, endDateTime, startDateTime); return -1; } duration = getMinutesDurationWithZonedDateTime(startDateTimeInZonedDateTime, endDateTimeInZonedDateTime); } - if (duration == -1) { - logWithDate( - "Failed to parse dates when getting minutes between: " + startDateTime + " and " + - endDateTime); + if (duration == -1) { + log.info("Failed to parse dates when getting minutes between: {} and {}", startDateTime, endDateTime); } return duration; } @@ -157,7 +115,7 @@ private int getMinutesDurationWithSimpleDateFormat(String startDateTime, String */ private int getMinutesDurationWithYyMmDdFormat(String startDateTime, String endDateTime) { try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // TODO: use TABLE_FORMAT here Date startDate = simpleDateFormat.parse(startDateTime); Date endDate = simpleDateFormat.parse(endDateTime); @@ -199,7 +157,7 @@ private String translateToZonedDateTime(String dateTimeString) // if not ZonedDateTime or SimpleDateFormat, check for YyMmDdFormat try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // TODO: use TABLE_FORMAT here Date startDate = simpleDateFormat.parse(dateTimeString); // translate to ZonedDateTime @@ -328,6 +286,23 @@ public Coordinate calculateAnchorCoordinate(Milepost firstPoint, Milepost second return new Coordinate(anchorLatitude, anchorLongitude); } + /** + * A {@link DateFormat} instance used to represent timestamps in the application + * using a custom, possibly legacy format. + * + * Format pattern: "dd-MMM-yy hh.mm.ss.SSS a" + * Example value: "24-Apr-25 03.45.12.123 PM" + * + * âš  NOTE: The origin and intended use case of this format are unclear, and it may be a candidate for replacement. + */ + public DateFormat getTimestampFormat() { + return timestampFormat; + } + + public void setTimestampFormat(DateFormat timestampFormat) { + this.timestampFormat = timestampFormat; + } + private static class UnrecognizedDateFormatException extends Exception { public UnrecognizedDateFormatException(String message) { super(message); diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTim.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTim.java index f0baedf94..3a2df7893 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTim.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTim.java @@ -1,10 +1,15 @@ package com.trihydro.library.model; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + import java.sql.Timestamp; +import java.text.SimpleDateFormat; import java.util.List; -import com.fasterxml.jackson.annotation.JsonFormat; - +@Data +@Slf4j public class ActiveTim { private Long activeTimId; @@ -16,6 +21,7 @@ public class ActiveTim { private Timestamp startTimestamp; private String startDateTime; private String endDateTime; + // the expirationDateTime is used by the tim refresh module to resign a TIM and resubmit private String expirationDateTime; private String route; private String clientId; @@ -28,182 +34,50 @@ public class ActiveTim { private Coordinate endPoint; private Integer projectKey; - public Coordinate getEndPoint() { - return endPoint; - } - - public String getExpirationDateTime() { - return expirationDateTime; - } - - public void setExpirationDateTime(String expirationDateTime) { - this.expirationDateTime = expirationDateTime; - } - - public void setEndPoint(Coordinate endPoint) { - this.endPoint = endPoint; - } - - public Coordinate getStartPoint() { - return startPoint; - } - - public void setStartPoint(Coordinate startPoint) { - this.startPoint = startPoint; - } - - public Long getActiveTimId() { - return this.activeTimId; - } - - public void setActiveTimId(Long activeTimId) { - this.activeTimId = activeTimId; - } - - public Long getTimId() { - return this.timId; - } - - public void setTimId(Long timId) { - this.timId = timId; - } - - public String getTimType() { - return this.timType; - } - - public void setTimType(String timType) { - this.timType = timType; - } - - public String getDirection() { - return this.direction; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - public String getStartDateTime() { - return this.startDateTime; - } - - public void setStartDateTime(String startDateTime) { - this.startDateTime = startDateTime; - } - - public Timestamp getStartTimestamp() { - return startTimestamp; - } - - public void setStartTimestamp(Timestamp startTimestamp) { - this.startTimestamp = startTimestamp; - } - - public String getEndDateTime() { - return this.endDateTime; - } - - public void setEndDateTime(String endDateTime) { - this.endDateTime = endDateTime; - } - - public Long getTimTypeId() { - return this.timTypeId; - } - - public void setTimTypeId(Long timTypeId) { - this.timTypeId = timTypeId; - } - - public String getRoute() { - return this.route; - } - - public void setRoute(String route) { - this.route = route; - } - - public String getClientId() { - return this.clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public String getSatRecordId() { - return this.satRecordId; - } - - public void setSatRecordId(String satRecordId) { - this.satRecordId = satRecordId; - } - - public Integer getPk() { - return this.pk; - } - - public void setPk(Integer pk) { - this.pk = pk; - } - - public String getRsuTarget() { - return this.rsuTarget; - } - - public void setRsuTarget(String rsuTarget) { - this.rsuTarget = rsuTarget; - } - - public Integer getRsuIndex() { - return this.rsuIndex; - } - - public void setRsuIndex(Integer rsuIndex) { - this.rsuIndex = rsuIndex; - } - - public List getItisCodes() { - return itisCodes; - } - - public void setItisCodes(List itisCodes) { - this.itisCodes = itisCodes; - } - - public Integer getProjectKey() { - return projectKey; - } - - public void setProjectKey(Integer projectKey) { - this.projectKey = projectKey; - } - - public boolean isIdenticalConditions(List itisCodes, String endDateTime) { + public boolean isIdenticalConditions(List itisCodesToCompare, String endDateTimeToCompare, int minutesUntilEndDateTimeToCompare) { // check if existing condition is identical to requested condition boolean identicalITISCodes = false; boolean identicalEndDate = false; List existingITISCodes = getItisCodes(); if (existingITISCodes != null) { - if (existingITISCodes.equals(itisCodes)) { + if (existingITISCodes.equals(itisCodesToCompare)) { identicalITISCodes = true; } } + // format (turn 2025-04-16T06:00:00.000Z into 2025-04-16 06:00:00) + SimpleDateFormat sourceFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + SimpleDateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + // Check if endDateTimeToCompare matches source format + if (endDateTimeToCompare != null) { + sourceFormat.parse(endDateTimeToCompare); // Validate format + endDateTimeToCompare = targetFormat.format(sourceFormat.parse(endDateTimeToCompare)); + } + } catch (Exception e) { + log.error("Unable to parse or format endDateTimeToCompare: {}", endDateTimeToCompare, e); + return false; + } + // check if end_date is identical - if (getEndDateTime() != null) { + String existingEndDateTime = getEndDateTime(); + if (existingEndDateTime != null) { + log.trace("End date of existing condition: {}", existingEndDateTime); + log.trace("End date of requested condition: {}", endDateTimeToCompare); // existing condition has an end date, check if it is identical - if (getEndDateTime().equals(endDateTime)) { + if (existingEndDateTime.equals(endDateTimeToCompare)) { identicalEndDate = true; } } else { // existing condition has no end date, check if requested condition has no end - // date - if (endDateTime == null) { + // date or if the end date is more than 32000 minutes in the future (if end date is + // more than 32000 minutes in the future, it is considered identical to no end date) + if (endDateTimeToCompare == null || minutesUntilEndDateTimeToCompare >= 32000) { identicalEndDate = true; } } + log.trace("identicalITISCodes: {}", identicalITISCodes); + log.trace("identicalEndDate: {}", identicalEndDate); return identicalITISCodes && identicalEndDate; } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHolding.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHolding.java index 62ea591f1..aae43b90b 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHolding.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHolding.java @@ -1,7 +1,12 @@ package com.trihydro.library.model; +import lombok.Data; +import lombok.NoArgsConstructor; + import java.time.Instant; +@Data +@NoArgsConstructor public class ActiveTimHolding { private Long activeTimHoldingId; private String direction; @@ -15,9 +20,9 @@ public class ActiveTimHolding { private Integer projectKey; private String expirationDateTime; private String packetId; + private String desiredEndDateTime; - - public ActiveTimHolding(WydotTim tim, String rsuTarget, String satRecordId, Coordinate endPt) { + public ActiveTimHolding(WydotTim tim, String rsuTarget, String satRecordId, Coordinate endPt, String desiredEndDateTime) { this.clientId = tim.getClientId(); this.direction = tim.getDirection(); this.rsuTarget = rsuTarget; @@ -25,105 +30,6 @@ public ActiveTimHolding(WydotTim tim, String rsuTarget, String satRecordId, Coor this.startPoint = tim.getStartPoint(); this.endPoint = endPt; this.dateCreated = Instant.now().toString(); - } - - public String getPacketId() { - return packetId; - } - - public void setPacketId(String packetId) { - this.packetId = packetId; - } - - public String getExpirationDateTime() { - return expirationDateTime; - } - - public void setExpirationDateTime(String expirationDateTime) { - this.expirationDateTime = expirationDateTime; - } - - public String getDirection() { - return this.direction; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - public String getDateCreated() { - return dateCreated; - } - - public void setDateCreated(String dateCreated) { - this.dateCreated = dateCreated; - } - - public Integer getRsuIndex() { - return rsuIndex; - } - - public void setRsuIndex(Integer rsuIndex) { - this.rsuIndex = rsuIndex; - } - - public Long getActiveTimHoldingId() { - return activeTimHoldingId; - } - - public void setActiveTimHoldingId(Long activeTimHoldingId) { - this.activeTimHoldingId = activeTimHoldingId; - } - - public Coordinate getEndPoint() { - return endPoint; - } - - public void setEndPoint(Coordinate endPoint) { - this.endPoint = endPoint; - } - - public Coordinate getStartPoint() { - return startPoint; - } - - public void setStartPoint(Coordinate startPoint) { - this.startPoint = startPoint; - } - - public String getClientId() { - return this.clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public String getSatRecordId() { - return this.satRecordId; - } - - public void setSatRecordId(String satRecordId) { - this.satRecordId = satRecordId; - } - - public String getRsuTarget() { - return this.rsuTarget; - } - - public void setRsuTargetId(String rsuTarget) { - this.rsuTarget = rsuTarget; - } - - public ActiveTimHolding() { - this.dateCreated = Instant.now().toString(); - } - - public Integer getProjectKey() { - return projectKey; - } - - public void setProjectKey(Integer projectKey) { - this.projectKey = projectKey; + this.desiredEndDateTime = desiredEndDateTime; } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/Coordinate.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/Coordinate.java index f506484c4..b1682b2ab 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/Coordinate.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/Coordinate.java @@ -42,4 +42,13 @@ public void setLatitude(BigDecimal latitude) { public Boolean isValid() { return latitude != null && longitude != null; } + + @Override + public String toString() { + if (isValid()) { + return String.format("%.6f,%.6f", latitude.doubleValue(), longitude.doubleValue()); + } else { + return "Invalid Coordinate"; + } + } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/RegionNameElementCollection.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/RegionNameElementCollection.java index 45540b41e..d9dd942e2 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/RegionNameElementCollection.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/RegionNameElementCollection.java @@ -54,7 +54,7 @@ public RegionNameElementCollection(String regionName) { return; } } - + public RegionNameElementCollection (String direction, String route, String rsuOrSat, String timType, String timId) { this.direction = direction; this.route = route; diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java index 7333b268e..36103fe2f 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java @@ -3,8 +3,10 @@ import java.math.BigDecimal; import java.sql.Timestamp; +import lombok.ToString; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +@ToString(callSuper = true) public class TimUpdateModel extends ActiveTim { // Tim properties private int msgCnt; diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java index 3bb89b706..7818f5eff 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java @@ -1,5 +1,6 @@ package com.trihydro.library.service; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -8,6 +9,7 @@ import com.trihydro.library.model.TimUpdateModel; import com.trihydro.library.model.WydotTim; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -16,15 +18,19 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class ActiveTimService extends CvDataServiceLibrary { - public Boolean updateActiveTim_SatRecordId(Long activeTimId, String satRecordId) { - String url = String.format("%s/active-tim/update-sat-record-id/%d/%s", config.getCvRestService(), activeTimId, satRecordId); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, Boolean.class); - return response.getBody(); - } + + public Boolean updateActiveTim_SatRecordId(Long activeTimId, String satRecordId) { + String url = String.format("%s/active-tim/update-sat-record-id/%d/%s", config.getCvRestService(), activeTimId, + satRecordId); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(null, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, + Boolean.class); + return response.getBody(); + } public void addItisCodesToActiveTim(ActiveTim activeTim) { String url = String.format("%s/active-tim/itis-codes/%d", config.getCvRestService(), activeTim.getActiveTimId()); @@ -33,6 +39,7 @@ public void addItisCodesToActiveTim(ActiveTim activeTim) { } public boolean deleteActiveTim(Long activeTimId) { + log.trace("Deleting Active TIM with ID: {}", activeTimId); String url = String.format("%s/active-tim/delete-id/%d", config.getCvRestService(), activeTimId); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -42,6 +49,7 @@ public boolean deleteActiveTim(Long activeTimId) { } public boolean deleteActiveTimsById(List activeTimIds) { + log.trace("Deleting Active TIMs with IDs: {}", activeTimIds); String url = String.format("%s/active-tim/delete-ids", config.getCvRestService()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -78,19 +86,40 @@ public List getActiveTimsByWydotTim(List wydotTim // get Active TIMs by client ID direction public List getActiveTimsByClientIdDirection(String clientId, Long timTypeId, String direction) { String url = String.format("%s/active-tim/client-id-direction/%s/%d", config.getCvRestService(), clientId, timTypeId); - if (direction != null) { - url += "/" + direction; - } - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTim[].class); - return Arrays.asList(response.getBody()); - } + log.debug("Getting active TIMs for client ID: {} and direction: {} from URL: {}", clientId, direction, url); + if (direction != null) { + url += "/" + direction; + } + List activeTims = new ArrayList<>(); + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, + ActiveTim[].class); + if (response != null && response.getBody() != null) { + activeTims = Arrays.asList(response.getBody()); + } + } catch (Exception e) { + log.error("Error getting active TIMs for client ID: {} and direction: {} from URL: {}", clientId, + direction, url, e); + } + log.debug("Number of active TIMs found: {}", activeTims.size()); + return activeTims; + } // get buffers for RW TIMs public List getBufferTimsByClientId(String clientId) { String url = String.format("%s/active-tim/buffer-tims/%s", config.getCvRestService(), clientId); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTim[].class); - return Arrays.asList(response.getBody()); - } + log.debug("Getting buffer TIMs for client ID: {} from URL: {}", clientId, url); + List activeTims = new ArrayList<>(); + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTim[].class); + if (response != null && response.getBody() != null) { + activeTims = Arrays.asList(response.getBody()); + } + } catch (Exception e) { + log.error("Error getting buffer TIMs for client ID: {} from URL: {}", clientId, url, e); + } + return activeTims; + } public List getExpiredActiveTims(int limit) { ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/expired?limit=" + limit, ActiveTim[].class); @@ -182,9 +211,17 @@ public String getMinExpiration(String packetID, String expDate) { public TimUpdateModel getUpdateModelFromActiveTimId(Long activeTimId) { String url = String.format("%s/active-tim/update-model/%d", config.getCvRestService(), activeTimId); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, TimUpdateModel.class); - return response.getBody(); - } + log.debug("Getting update model for active TIM ID: {} from URL: {}", activeTimId, url); + TimUpdateModel timUpdateModel = null; + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, TimUpdateModel.class); + timUpdateModel = response.getBody(); + } catch (Exception e) { + log.error("Error getting update model for active TIM ID: {} from URL: {}", activeTimId, url, e); + } + log.debug("Update model found: {}", timUpdateModel); + return timUpdateModel; + } public boolean resetActiveTimsExpirationDate(List activeTimIds) { String url = String.format("%s/active-tim/reset-expiration-date", config.getCvRestService()); @@ -195,16 +232,34 @@ public boolean resetActiveTimsExpirationDate(List activeTimIds) { return response.getBody(); } - public void markForDeletion(Long activeTimId) { - String url = String.format("%s/active-tim/mark-for-deletion/%d", config.getCvRestService(), activeTimId); + public void markForDeletion(Long activeTimId) { + String url = String.format("%s/active-tim/mark-for-deletion/%d", config.getCvRestService(), activeTimId); + log.debug("Marking active TIM for deletion: {} from URL: {}", activeTimId, url); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(null, headers); + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, + Boolean.class); + if (response.getBody() == null || !response.getBody()) { + log.error("Failed to mark active TIM for deletion: {}", activeTimId); + } + } catch (Exception e) { + log.error("Error marking active TIM for deletion: {} from URL: {}", activeTimId, url, e); + } + } + + public List getAllRecords() { + String url = String.format("%s/active-tim/all", config.getCvRestService()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, Boolean.class); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.GET, entity, ActiveTim[].class); + return Arrays.asList(response.getBody()); } - public List getAllRecords() { - String url = String.format("%s/active-tim/all", config.getCvRestService()); + public List getActivePlannedConditionTims() { + String url = String.format("%s/active-tim/get-active-planned-condition-tims", config.getCvRestService()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity<>(headers); diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/DataFrameService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/DataFrameService.java index 2c15d3979..31fe1ed48 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/DataFrameService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/DataFrameService.java @@ -1,5 +1,7 @@ package com.trihydro.library.service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -9,22 +11,32 @@ @Component public class DataFrameService extends CvDataServiceLibrary { + private final Logger logger = LoggerFactory.getLogger(DataFrameService.class); /** * Calls out to cv-data-controller REST service to fetch ITIS codes associated * with a given DataFrame id * - * @param dataFrameId + * @param dataFrameId the DataFrame id to fetch ITIS codes for * @return String array of all ITIS codes associated with dataFrameId */ public String[] getItisCodesForDataFrameId(Integer dataFrameId) { String url = String.format("%s/data-frame/itis-for-data-frame/%d", config.getCvRestService(), dataFrameId); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.GET, entity, - String[].class); - return response.getBody(); + HttpEntity entity = new HttpEntity<>(null, headers); + logger.debug("Getting ITIS codes for dataFrameId: {} from URL: {}", dataFrameId, url); + String[] itisCodes = new String[0]; + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.GET, entity, String[].class); + if (response.getBody() != null) { + itisCodes = response.getBody(); + } + } catch (Exception e) { + logger.error("Error getting ITIS codes for dataFrameId: {} from URL: {}", dataFrameId, url, e); + } + logger.debug("ITIS codes for dataFrameId: {} are: {}", dataFrameId, itisCodes); + return itisCodes; } } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/OdeService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/OdeService.java index 4f65be56c..bbadc647c 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/OdeService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/OdeService.java @@ -6,12 +6,13 @@ import com.google.gson.Gson; import com.trihydro.library.helpers.Utility; -import com.trihydro.library.model.Milepost; import com.trihydro.library.model.OdeProps; import com.trihydro.library.model.TimQuery; import com.trihydro.library.model.WydotRsu; import com.trihydro.library.model.WydotTravelerInputData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import us.dot.its.jpo.ode.plugin.RoadSideUnit.RSU; import us.dot.its.jpo.ode.plugin.SnmpProtocol; @@ -27,8 +28,8 @@ @Component public class OdeService { - - private Gson gson = new Gson(); + private final Logger logger = LoggerFactory.getLogger(OdeService.class); + private final Gson gson = new Gson(); private RestTemplateProvider restTemplateProvider; private Utility utility; private OdeProps odeProps; @@ -40,8 +41,8 @@ public void InjectDependencies(Utility _utility, RestTemplateProvider _restTempl odeProps = _odeProps; } - public String sendNewTimToRsu(WydotTravelerInputData timToSend) { - utility.logWithDate("Sending the following new TIM to ODE for processing: " + gson.toJson(timToSend)); + public String sendNewTimToRsu(WydotTravelerInputData timToSend) { + logger.info("Sending the following new TIM to ODE for processing: {}", gson.toJson(timToSend)); String exMsg = ""; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -49,40 +50,34 @@ public String sendNewTimToRsu(WydotTravelerInputData timToSend) { ResponseEntity response = restTemplateProvider.GetRestTemplate_NoErrors() .exchange(odeProps.getOdeUrl() + "/tim", HttpMethod.POST, entity, String.class); if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) { - exMsg = "Failed to send new TIM to RSU: " + response.getBody(); - utility.logWithDate(exMsg); - } - return exMsg; - } - - public String sendNewTimToSdw(WydotTravelerInputData timToSend, String recordId, List mps) { - String exMsg = ""; - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(timToSend, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate_NoErrors() - .exchange(odeProps.getOdeUrl() + "/tim", HttpMethod.POST, entity, String.class); - if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) { - exMsg = "Failed to send new TIM to SDX: " + response.getBody(); - utility.logWithDate(exMsg); + exMsg = "Failed to send new TIM to RSU: " + response.getBody(); + logger.info(exMsg); } return exMsg; } public String updateTimOnSdw(WydotTravelerInputData timToSend) { - - String exMsg = ""; + String exceptionMessage = ""; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity(timToSend, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate_NoErrors() - .exchange(odeProps.getOdeUrl() + "/tim", HttpMethod.PUT, entity, String.class); - if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) { - exMsg = "Failed to update TIM on SDX: " + response.getBody(); - utility.logWithDate(exMsg); + logger.debug("Sending updated TIM {} to ODE for processing", gson.toJson(timToSend)); + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate_NoErrors() + .exchange(odeProps.getOdeUrl() + "/tim", HttpMethod.PUT, entity, String.class); + if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) { + if (response.getBody() != null) { + exceptionMessage = "Failed to update TIM on SDX: " + response.getBody(); + } else { + exceptionMessage = "Failed to update TIM on SDX: " + response.getStatusCode(); + } + logger.error(exceptionMessage); + } + } catch (RestClientException e) { + exceptionMessage = "An exception occurred while updating TIM on SDX"; + logger.error(exceptionMessage, e); } - return exMsg; + return exceptionMessage; } public Integer findFirstAvailableIndexWithRsuIndex(List indicies) { @@ -102,8 +97,10 @@ public Integer findFirstAvailableIndexWithRsuIndex(List indicies) { public TimQuery submitTimQuery(WydotRsu rsu, int counter) { // stop if this fails twice - if (counter == 2) + if (counter == 2) { + logger.error("Failed to contact ODE to query TIMs on RSU {} after 2 attempts", rsu.getRsuTarget()); return null; + } // tim query to ODE var odeRsu = new RSU(); @@ -118,9 +115,9 @@ public TimQuery submitTimQuery(WydotRsu rsu, int counter) { headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity(rsuJson, headers); - String responseStr = null; - + String responseStr; try { + logger.debug("Sending TIM query to ODE for RSU with rsuJson: {} and headers: {}", rsuJson, headers); responseStr = restTemplateProvider.GetRestTemplate().postForObject(odeProps.getOdeUrl() + "/tim/query", entity, String.class); } catch (RestClientException e) { @@ -131,13 +128,13 @@ public TimQuery submitTimQuery(WydotRsu rsu, int counter) { .replaceAll("\\{", "").replaceAll("\\}", "").replaceAll("\\[", "").replaceAll(" ", "") .replaceAll("\\]", "").replaceAll("\\s", "").split(","); - List results = new ArrayList(); + List results = new ArrayList<>(); for (int i = 0; i < items.length; i++) { try { results.add(Integer.parseInt(items[i])); } catch (NumberFormatException nfe) { - // NOTE: write something here if you need to recover from formatting errors + logger.warn("Failed to parse TIM index from ODE response: {}", items[i]); } } @@ -145,6 +142,7 @@ public TimQuery submitTimQuery(WydotRsu rsu, int counter) { TimQuery timQuery = new TimQuery(); timQuery.setIndicies_set(results); + logger.debug("Received TIM query response from ODE: {}", timQuery); return timQuery; } @@ -155,9 +153,9 @@ public String deleteTimFromRsu(RSU rsu, Integer index) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(rsuJson, headers); - - utility.logWithDate("Deleting TIM on index " + index.toString() + " from rsu " + rsu.getRsuTarget()); + HttpEntity entity = new HttpEntity(rsuJson, headers); + + logger.info("Deleting TIM on index {} from rsu {}", index.toString(), rsu.getRsuTarget()); try { // ODE response is misleading due to poor interpretation of SNMP results. If we @@ -166,8 +164,8 @@ public String deleteTimFromRsu(RSU rsu, Integer index) { restTemplateProvider.GetRestTemplate_NoErrors().exchange( odeProps.getOdeUrl() + "/tim?index=" + index.toString(), HttpMethod.DELETE, entity, String.class); } catch (RestClientException ex) { - exMsg = "Failed to contact ODE to delete message from index " + index + " on RSU " + rsu.getRsuTarget(); - utility.logWithDate(exMsg); + exMsg = "Failed to contact ODE to delete message from index " + index + " on RSU " + rsu.getRsuTarget(); + logger.info(exMsg); } return exMsg; diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuDataService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuDataService.java index 90427a7af..0f6a767b9 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuDataService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuDataService.java @@ -8,6 +8,8 @@ import com.trihydro.library.model.RsuDataServiceProps; import com.trihydro.library.model.RsuIndexInfo; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -15,6 +17,7 @@ import org.springframework.web.client.HttpClientErrorException; @Component +@Slf4j public class RsuDataService { private RsuDataServiceProps config; private Utility utility; @@ -42,7 +45,7 @@ public List getRsuDeliveryStartTimes(String ipv4Address) { response = restTemplateProvider.GetRestTemplate().getForEntity(url, RsuIndexInfo[].class); } catch (HttpClientErrorException ex) { if (ex.getStatusCode() == HttpStatus.UNPROCESSABLE_ENTITY) { - utility.logWithDate("RSU " + ipv4Address + " is unresponsive"); + log.info("RSU {} is unresponsive", ipv4Address); return null; } diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuService.java index 99bc6977a..c03a9f140 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/RsuService.java @@ -15,12 +15,15 @@ import org.gavaghan.geodesy.GeodeticCalculator; import org.gavaghan.geodesy.GeodeticCurve; import org.gavaghan.geodesy.GlobalCoordinates; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Component public class RsuService extends CvDataServiceLibrary { + private final Logger logger = LoggerFactory.getLogger(RsuService.class); private Utility utility; @@ -38,16 +41,34 @@ public List selectAll() { public List selectRsusByRoute(String route) { String url = String.format("%s/rsus-by-route/%s", config.getCvRestService(), route); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - WydotRsu[].class); - return Arrays.asList(response.getBody()); + logger.debug("Selecting RSUs by route: {} from URL: {}", route, url); + WydotRsu[] rsus = new WydotRsu[0]; + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, WydotRsu[].class); + if (response.getBody() != null) { + rsus = response.getBody(); + } + } catch (Exception e) { + logger.error("Error selecting RSUs by route: {} from URL: {}", route, url, e); + } + logger.debug("Number of RSUs selected in selectRsusByRoute(): {}", rsus.length); + return Arrays.asList(rsus); } public List getFullRsusTimIsOn(Long timId) { String url = String.format("%s/rsus-for-tim/%d", config.getCvRestService(), timId); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - WydotRsuTim[].class); - return Arrays.asList(response.getBody()); + logger.debug("Getting RSUs for TIM: {} from URL: {}", timId, url); + WydotRsuTim[] rsus = new WydotRsuTim[0]; + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, WydotRsuTim[].class); + if (response.getBody() != null) { + rsus = response.getBody(); + } + } catch (Exception e) { + logger.error("Error getting RSUs for TIM: {} from URL: {}", timId, url, e); + } + logger.debug("Number of RSUs selected in getFullRsusTimIsOn(): {}", rsus.length); + return Arrays.asList(rsus); } public List getActiveRsuTimIndexes(Integer rsuId) { @@ -66,12 +87,12 @@ public List getRsusByLatLong(String direction, Coordinate startPoint, // if there are no rsus on this route List mainRsus = getRsusByRouteWithRetryAndTimeout(route); - if (mainRsus.size() == 0) { - utility.logWithDate("No RSUs found for route " + route); - return rsus; - } else { - utility.logWithDate("Found " + mainRsus.size() + " RSUs for route " + route); - } + if (mainRsus.size() == 0) { + logger.info("No RSUs found for route {}", route); + return rsus; + } else { + logger.info("Found {} RSUs for route {}", mainRsus.size(), route); + } Ellipsoid reference = Ellipsoid.WGS84; if (direction.equalsIgnoreCase("i")) { @@ -145,9 +166,9 @@ public List getRsusByLatLong(String direction, Coordinate startPoint, } } - if (rsusHigher.size() == 0) { - utility.logWithDate("No RSUs found higher than 'low' point"); - } + if (rsusHigher.size() == 0) { + logger.info("No RSUs found higher than 'low' point"); + } } else { if (numericRoute % 2 == 0) { diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/SdwService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/SdwService.java index 2115485a8..9d5ae0e0c 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/SdwService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/SdwService.java @@ -1,16 +1,11 @@ package com.trihydro.library.service; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Random; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import com.google.gson.Gson; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.AdvisorySituationDataDeposit; import com.trihydro.library.model.SDXDecodeRequest; import com.trihydro.library.model.SDXDecodeResponse; @@ -18,7 +13,8 @@ import com.trihydro.library.model.SdwProps; import com.trihydro.library.model.SemiDialogID; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -31,18 +27,12 @@ import org.springframework.web.client.RestClientException; @Component +@Slf4j +@RequiredArgsConstructor public class SdwService { - public Gson gson = new Gson(); - private SdwProps configProperties; - private Utility utility; - private RestTemplateProvider restTemplateProvider; - - @Autowired - public void InjectDependencies(SdwProps _config, Utility _utility, RestTemplateProvider _restTemplateProvider) { - configProperties = _config; - utility = _utility; - restTemplateProvider = _restTemplateProvider; - } + public final Gson gson = new Gson(); + private final SdwProps configProperties; + private final RestTemplateProvider restTemplateProvider; /** * Fetches messages deposited into the SDX, by the ODE User (identified by @@ -50,40 +40,48 @@ public void InjectDependencies(SdwProps _config, Utility _utility, RestTemplateP * * @param type Type of message to retrieve */ - public List getMsgsForOdeUser(SemiDialogID type) throws RestClientException { + public List getMsgsForOdeUser(SemiDialogID type) throws RestClientException, SdwServiceException { + log.info("Fetching SDX messages for dialog type: {}", type); + + if (type == null) { + throw new SdwServiceException("Null dialog type passed to getMsgsForOdeUser"); + } + List results = null; String url = String.format("%s/api/deposited-by-me/%d", configProperties.getSdwRestUrl(), type.getValue()); HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); headers.add("apikey", configProperties.getSdwApiKey()); - HttpEntity entity = new HttpEntity(null, headers); + HttpEntity entity = new HttpEntity<>(null, headers); try { ResponseEntity response = restTemplateProvider.GetRestTemplate() .exchange(url, HttpMethod.GET, entity, AdvisorySituationDataDeposit[].class); results = Arrays.asList(response.getBody()); + log.info("Successfully retrieved {} SDX messages", results.size()); } catch (RestClientException ex) { - utility.logWithDate("An exception occurred while attempting to get messages from SDX: " + ex.getMessage()); - utility.logWithDate("Is the SDX API key valid?"); - ex.printStackTrace(); + log.error("GET messages failed - {}. DialogType={}, URL={}", ex.getMessage(), type, url); + throw new SdwServiceException("SDX GET messages request failed: " + ex.getMessage(), ex); } return results; } - public List getItisCodesFromAdvisoryMessage(String advisoryMessage) throws IllegalArgumentException { + public List getItisCodesFromAdvisoryMessage(String advisoryMessage) throws SdwServiceException { if (advisoryMessage == null) { - throw new IllegalArgumentException("advisoryMessage cannot be null"); + log.error("Null advisory message provided"); + throw new SdwServiceException("Null advisory message provided"); } - + log.trace("Processing advisory message for ITIS codes, message length: {}", advisoryMessage.length()); int idx = advisoryMessage.indexOf("001F"); if (idx < 0) { - throw new IllegalArgumentException("Cannot determine start of MessageFrame"); + log.error("Invalid message format - missing MessageFrame marker"); + throw new SdwServiceException("Invalid message format - missing MessageFrame marker"); } - List results = new ArrayList(); + List results = new ArrayList<>(); SDXDecodeResponse decodeResponse = null; // Build request @@ -91,7 +89,7 @@ public List getItisCodesFromAdvisoryMessage(String advisoryMessage) thr HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); headers.add("apikey", configProperties.getSdwApiKey()); SDXDecodeRequest request = new SDXDecodeRequest(); @@ -101,16 +99,20 @@ public List getItisCodesFromAdvisoryMessage(String advisoryMessage) thr // Execute request try { - HttpEntity entity = new HttpEntity(request, headers); + log.trace("Sending decode request to SDX API"); + HttpEntity entity = new HttpEntity<>(request, headers); ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, entity, SDXDecodeResponse.class); decodeResponse = response.getBody(); + if (decodeResponse == null || decodeResponse.getDecodedMessage() == null) { + log.warn("Received null decode response from SDX"); + throw new SdwServiceException("SDX decode response is null"); + } + } catch (RestClientException ex) { - utility.logWithDate("An exception occurred while attempting to decode message: " + ex.getMessage()); - utility.logWithDate("Is the SDX API key valid?"); - ex.printStackTrace(); - return null; + log.error("POST decode failed - {}. Message length={}, URL={}", ex.getMessage(), advisoryMessage.length(), url); + throw new SdwServiceException("SDX decode request failed: " + ex.getMessage(), ex); } // Process request (convert decodedMessage into an array of ITIS codes) @@ -123,10 +125,11 @@ public List getItisCodesFromAdvisoryMessage(String advisoryMessage) thr try { results.add(Integer.parseInt(itisCode)); } catch (NumberFormatException ex) { - ex.printStackTrace(); + log.error("Failed to parse ITIS code: {}", m.group(2), ex); } } + log.trace("Successfully extracted {} ITIS codes from message", results.size()); return results; } @@ -134,8 +137,7 @@ public List getItisCodesFromAdvisoryMessage(String advisoryMessage) thr * Returns a pseudo-random 4 byte hex value representing recordId. This 4 byte * limitation comes from asn1_codec SEMI_v2.3.0_070616.asn found at * https://github.com/usdot-jpo-ode/asn1_codec/blob/master/asn1c_combined/SEMI_v2.3.0_070616.asn - * - * @return + * */ public String getNewRecordId() { String hexChars = "ABCDEF1234567890"; @@ -145,18 +147,23 @@ public String getNewRecordId() { int index = (int) (rnd.nextFloat() * hexChars.length()); hexStrB.append(hexChars.charAt(index)); } - String hexStr = hexStrB.toString(); - return hexStr; + return hexStrB.toString(); } public HashMap deleteSdxDataBySatRecordId(List satRecordIds) { + log.trace("deleteSdxDataBySatRecordId called with satRecordIds: {}", satRecordIds); + + log.info("Attempting to delete {} SDX record(s)", + satRecordIds != null ? satRecordIds.size() : 0); + HashMap results = null; - if (satRecordIds == null || satRecordIds.size() == 0 || configProperties.getSdwApiKey() == null) { + if (satRecordIds == null || satRecordIds.isEmpty() || configProperties.getSdwApiKey() == null) { if (configProperties.getSdwApiKey() == null) { - utility.logWithDate("Attempting to delete satellite records failed due to null apiKey"); + log.error("Attempting to delete satellite records failed due to null apiKey"); } else { - utility.logWithDate("Attempting to delete satellite records failed due to no satRecordIds passed in"); + log.error("Attempting to delete satellite records failed due to no satRecordIds passed in"); } + log.trace("Returning null results"); return results; } @@ -172,27 +179,26 @@ public HashMap deleteSdxDataBySatRecordId(List satReco }; ResponseEntity> response; try { + log.debug("Sending request to delete satellite records with IDs: {}", satRecordIds); response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, entity, responseType); } catch (HttpClientErrorException ex) { - utility.logWithDate("An exception occurred while attempting to delete satellite records: " + ex.getMessage()); - utility.logWithDate("Is the SDX API key valid?"); - ex.printStackTrace(); - response = new ResponseEntity>(ex.getStatusCode()); + log.error("DELETE records failed - {}. recordIds={}", ex.getMessage(), satRecordIds); + response = new ResponseEntity<>(ex.getStatusCode()); } if (response.getStatusCode() != HttpStatus.OK) { - utility.logWithDate("Failed to call delete-multiple-by-id on SDX api"); + log.error("Delete operation failed with status: {}", response.getStatusCode()); } return response.getBody(); } public HashMap deleteSdxDataByRecordIdIntegers(List satRecordInts) { HashMap results = null; - if (satRecordInts == null || satRecordInts.size() == 0 || configProperties.getSdwApiKey() == null) { + if (satRecordInts == null || satRecordInts.isEmpty() || configProperties.getSdwApiKey() == null) { if (configProperties.getSdwApiKey() == null) { - utility.logWithDate("Attempting to delete satellite records failed due to null apiKey"); + log.info("Attempting to delete satellite records failed due to null apiKey"); } else { - utility.logWithDate("Attempting to delete satellite records failed due to no satRecordIds passed in"); + log.info("Attempting to delete satellite records failed due to no satRecordIds passed in"); } return results; } @@ -202,20 +208,19 @@ public HashMap deleteSdxDataByRecordIdIntegers(List s headers.setContentType(MediaType.APPLICATION_JSON); headers.add("apikey", configProperties.getSdwApiKey()); HttpEntity> entity = new HttpEntity>(satRecordInts, headers); - ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() { + ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { }; ResponseEntity> response; try { response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, entity, responseType); } catch (HttpClientErrorException ex) { - utility.logWithDate("An exception occurred while attempting to delete satellite records: " + ex.getMessage()); - utility.logWithDate("Is the SDX API key valid?"); - ex.printStackTrace(); - response = new ResponseEntity>(ex.getStatusCode()); + log.error("DELETE records failed - {}. recordIds={}", ex.getMessage(), satRecordInts); + + response = new ResponseEntity<>(ex.getStatusCode()); } if (response.getStatusCode() != HttpStatus.OK) { - utility.logWithDate("Failed to call delete-multiple-by-id on SDX api"); + log.error("Delete operation failed with status: {}", response.getStatusCode()); } return response.getBody(); } @@ -228,4 +233,14 @@ private String getBaseUrlString(String end) { baseUrl += end; return baseUrl; } + + public static class SdwServiceException extends Exception { + public SdwServiceException(String message) { + super(message); + } + + public SdwServiceException(String message, Throwable cause) { + super(message, cause); + } + } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/TimRsuService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/TimRsuService.java index 92c65da7d..d6ab1e800 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/TimRsuService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/TimRsuService.java @@ -5,6 +5,7 @@ import com.trihydro.library.model.TimRsu; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -13,9 +14,11 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class TimRsuService extends CvDataServiceLibrary { public List getTimRsusByTimId(Long timId) { + log.trace("Getting TimRsus for timId: {}", timId); // tim-id String url = String.format("%s/tim-rsu/tim-id/%d", config.getCvRestService(), timId); HttpHeaders headers = new HttpHeaders(); diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/WydotTimService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/WydotTimService.java index f1ab3b7ca..0a41d28d7 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/WydotTimService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/WydotTimService.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; @@ -55,6 +56,7 @@ import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; @Component +@Slf4j public class WydotTimService { protected EmailProps emailProps; @@ -178,7 +180,7 @@ public List getAllMilepostsForTim(WydotTim wydotTim) { } public void sendTimToSDW(WydotTim wydotTim, WydotTravelerInputData timToSend, String regionNamePrev, - TimType timType, Integer pk, Coordinate endPoint, List reducedMileposts) { + TimType timType, Integer pk, Coordinate endPoint, List reducedMileposts, String desiredEndDateTime) { // find active TIMs by client Id and direction, then filter by SAT TIMs List activeSatTims = null; @@ -187,8 +189,7 @@ public void sendTimToSDW(WydotTim wydotTim, WydotTravelerInputData timToSend, St int numberOfSatTims = activeSatTims != null ? activeSatTims.size() : 0; if (numberOfSatTims > 1) { // inform user that there are multiple active SAT TIMs for this client, when we expected zero or one - utility.logWithDate("Multiple active SAT TIMs found for client " + wydotTim.getClientId() + " and direction " - + wydotTim.getDirection() + ". Expected zero or one. Using the first one found."); + log.info("Multiple active SAT TIMs found for client {} and direction {}. Expected zero or one. Using the first one found.", wydotTim.getClientId(), wydotTim.getDirection()); } // retrieve first record if it exists @@ -198,7 +199,8 @@ public void sendTimToSDW(WydotTim wydotTim, WydotTravelerInputData timToSend, St String recordId = activeSatTim != null ? activeSatTim.getSatRecordId() : sdwService.getNewRecordId(); // save new active_tim_holding record - ActiveTimHolding activeTimHolding = new ActiveTimHolding(wydotTim, null, recordId, endPoint); + log.trace("Preparing to create new active_tim_holding record with desired end date time: '{}' and recordId: '{}'", desiredEndDateTime, recordId); + ActiveTimHolding activeTimHolding = new ActiveTimHolding(wydotTim, null, recordId, endPoint, desiredEndDateTime); activeTimHolding.setPacketId(timToSend.getTim().getPacketID()); if (wydotTim instanceof WydotTimRw) { // Set projectKey, if this is a RW TIM @@ -224,7 +226,7 @@ public void sendTimToSDW(WydotTim wydotTim, WydotTravelerInputData timToSend, St try { regionNameTemp = regionNameTrimmer.trimRegionNameIfTooLong(regionNameTemp); } catch (IllegalArgumentException e) { - utility.logWithDate("Failed to trim region name: " + e.getMessage()); + log.info("Failed to trim region name: {}", e.getMessage()); return; } timToSend.getTim().getDataframes()[0].getRegions()[0].setName(regionNameTemp); @@ -239,7 +241,7 @@ public void sendTimToSDW(WydotTim wydotTim, WydotTravelerInputData timToSend, St } public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, String regionNamePrev, - TimType timType, Integer pk, String endDateTime, Coordinate endPoint) { + TimType timType, Integer pk, String endDateTime, Coordinate endPoint, String desiredEndDateTime) { // FIND ALL RSUS TO SEND TO // TODO: should this query a graph db instead to follow with milepost? List rsus = rsuService.getRsusByLatLong(wydotTim.getDirection(), wydotTim.getStartPoint(), endPoint, @@ -247,7 +249,7 @@ public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, S // if no RSUs found if (rsus.size() == 0) { - utility.logWithDate("No RSUs found to place TIM on, returning"); + log.info("No RSUs found to place TIM on, returning"); return; } @@ -278,7 +280,7 @@ public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, S try { regionNameTemp = regionNameTrimmer.trimRegionNameIfTooLong(regionNameTemp); } catch (IllegalArgumentException e) { - utility.logWithDate("Failed to trim region name: " + e.getMessage()); + log.info("Failed to trim region name: {}", e.getMessage()); return; } timToSend.getTim().getDataframes()[0].getRegions()[0].setName(regionNameTemp); @@ -289,7 +291,8 @@ public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, S ActiveTim activeTim = activeTimService.getActiveRsuTim(artqm); // create new active_tim_holding record - ActiveTimHolding activeTimHolding = new ActiveTimHolding(wydotTim, odeRsu.getRsuTarget(), null, endPoint); + log.debug("Preparing to create new active_tim_holding record with desired end date time: '{}' and rsu_target: {}", desiredEndDateTime, odeRsu.getRsuTarget()); + ActiveTimHolding activeTimHolding = new ActiveTimHolding(wydotTim, odeRsu.getRsuTarget(), null, endPoint, desiredEndDateTime); activeTimHolding.setPacketId(timToSend.getTim().getPacketID()); // Set projectKey, if this is a RW TIM @@ -318,8 +321,7 @@ public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, S // if query failed, don't send TIM, // log the error and continue if (timQuery == null) { - utility.logWithDate( - "Returning without sending TIM to RSU. submitTimQuery failed for RSU " + gson.toJson(rsu)); + log.info("Returning without sending TIM to RSU. submitTimQuery failed for RSU {}", gson.toJson(rsu)); continue; } @@ -340,7 +342,7 @@ public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, S // if unable to find next available index, // log error and continue if (nextRsuIndex == null) { - utility.logWithDate("Unable to find an available index for RSU " + gson.toJson(rsu)); + log.info("Unable to find an available index for RSU {}", gson.toJson(rsu)); continue; } @@ -363,9 +365,9 @@ public void sendTimToRsus(WydotTim wydotTim, WydotTravelerInputData timToSend, S public TimDeleteSummary deleteTimsFromRsusAndSdx(List activeTims) { - + log.debug("deleteTimsFromRsusAndSdx called with {} active TIMs", activeTims.size()); var returnValue = new TimDeleteSummary(); - if (activeTims == null || activeTims.isEmpty()) { + if (activeTims.isEmpty()) { return returnValue; } WydotRsu rsu = null; @@ -375,17 +377,18 @@ public TimDeleteSummary deleteTimsFromRsusAndSdx(List activeTims) { .collect(Collectors.toList()); List rsuTims = activeTims.stream().filter(x -> StringUtils.isBlank(x.getSatRecordId())) .collect(Collectors.toList()); + log.trace("Found {} RSU TIMs and {} SAT TIMs to delete", rsuTims.size(), satTims.size()); for (ActiveTim activeTim : rsuTims) { // get RSU TIM is on List timRsus = timRsuService.getTimRsusByTimId(activeTim.getTimId()); // get full RSU - if (timRsus.size() > 0) { + if (!timRsus.isEmpty()) { for (TimRsu timRsu : timRsus) { rsu = getRsu(timRsu.getRsuId()); // delete tim off rsu - utility.logWithDate("Deleting TIM from RSU. Corresponding tim_id: " + activeTim.getTimId()); + log.info("Deleting TIM from RSU. Corresponding tim_id: {}, rsu_index: {}, client_id: {}", activeTim.getTimId(), timRsu.getRsuIndex(), activeTim.getClientId()); if (!deleteTimFromRsu(rsu, timRsu.getRsuIndex())) { returnValue.addfailedRsuTimJson(gson.toJson(timRsu)); } @@ -393,29 +396,33 @@ public TimDeleteSummary deleteTimsFromRsusAndSdx(List activeTims) { } // delete active tim if (activeTimService.deleteActiveTim(activeTim.getActiveTimId())) { + log.debug("Successfully deleted active TIM with ID: {}", activeTim.getActiveTimId()); returnValue.addSuccessfulRsuDeletions(activeTim.getActiveTimId()); } else { + log.debug("Failed to delete active TIM with ID: {}", activeTim.getActiveTimId()); returnValue.addFailedActiveTimDeletions(activeTim.getActiveTimId()); } } - if (satTims != null && satTims.size() > 0) { + if (!satTims.isEmpty()) { // Get the sat_record_id values and active_tim_id values List satRecordIds = satTims.stream().map(ActiveTim::getSatRecordId).collect(Collectors.toList()); List activeSatTimIds = satTims.stream().map(ActiveTim::getActiveTimId).collect(Collectors.toList()); // Issue one delete call to the REST service, encompassing all sat_record_ids + log.debug("Deleting {} SAT TIMs from SDX with record IDs: {}", satRecordIds.size(), satRecordIds); HashMap sdxDelResults = sdwService.deleteSdxDataBySatRecordId(satRecordIds); // Determine if anything failed - Boolean errorsOccurred = sdxDelResults.entrySet().stream() - .anyMatch(x -> x.getValue() != null && x.getValue() == false); + boolean errorsOccurred = sdxDelResults.entrySet().stream() + .anyMatch(x -> x.getValue() != null && !x.getValue()); if (errorsOccurred) { + log.debug("SDX delete failed for record IDs: {}", satRecordIds); // pull out failed deletions for corresponding active_tim records so we don't // orphan them Stream> failedStream = sdxDelResults.entrySet().stream() .filter(x -> x.getValue() == false); - List failedSatRecords = failedStream.map(x -> x.getKey()).collect(Collectors.toList()); + List failedSatRecords = failedStream.map(Entry::getKey).collect(Collectors.toList()); activeSatTimIds = satTims.stream() .filter(x -> !failedSatRecords.contains(Integer.parseUnsignedInt(x.getSatRecordId(), 16))) @@ -426,18 +433,20 @@ public TimDeleteSummary deleteTimsFromRsusAndSdx(List activeTims) { String body = "The following recordIds failed to delete from the SDX: " + failedResultsText; returnValue.setSatelliteErrorSummary(body); try { + log.info("Sending email to support with error summary: {}", body); emailHelper.SendEmail(emailProps.getAlertAddresses(), "SDX Delete Fail", body); } catch (Exception ex) { - utility.logWithDate(body + ", and the email failed to send to support"); - ex.printStackTrace(); + log.error("{}, and the email failed to send to support", body, ex); } } } if (activeTimService.deleteActiveTimsById(activeSatTimIds)) { + log.debug("Successfully deleted {} active TIMs from SDX", activeSatTimIds.size()); returnValue.setSuccessfulSatelliteDeletions(activeSatTimIds); } } + log.trace("Done deleting TIMs from RSUs and SDX. Summary: {}", returnValue); return returnValue; } @@ -456,7 +465,7 @@ public boolean clearTimsById(String timTypeStr, String clientId, String directio activeTims.addAll(activeTimService.getBufferTimsByClientId(clientId)); } - utility.logWithDate(activeTims.size() + " active_tim found for deletion"); + log.info("{} active_tim found for deletion", activeTims.size()); List activeTimIds = activeTims.stream() .map(ActiveTim::getActiveTimId) @@ -523,7 +532,7 @@ private WydotRsu getRsu(Long rsuId) { try { wydotRsu = getRsus().stream().filter(x -> x.getRsuId() == rsuId.intValue()).findFirst().orElse(null); } catch (Exception e) { - e.printStackTrace(); + log.error("Exception", e); } return wydotRsu; @@ -552,12 +561,12 @@ private void sendNewTimToSdw(WydotTravelerInputData timToSend, String recordId, String timToSendJson = gson.toJson(timToSend); try { - utility.logWithDate("Sending new TIM to SDW. sat_record_id: " + recordId); + log.info("Sending new TIM to SDW. sat_record_id: {}", recordId); restTemplateProvider.GetRestTemplate().postForObject(odeProps.getOdeUrl() + "/tim", timToSendJson, String.class); } catch (RuntimeException targetException) { - System.out.println("Failed to send new TIM to SDW"); - targetException.printStackTrace(); + log.info("Failed to send new TIM to SDW"); + log.error("Exception", targetException); } } @@ -574,15 +583,14 @@ public void updateTimOnRsu(WydotTravelerInputData timToSend, Long timId, WydotOd try { var rsu = getRsu(timRsu.getRsuId()); - utility.logWithDate("Preparing to submit updated TIM. Clearing index " + timRsu.getRsuIndex() + " on RSU " - + timRsu.getRsuId()); + log.info("Preparing to submit updated TIM. Clearing index {} on RSU {}", timRsu.getRsuIndex(), timRsu.getRsuId()); // The ODE response code is misleading. If there is a failure in this step or // the next, the issue should get addressed when the RSU Validation task is // ran. deleteTimFromRsu(rsu, timRsu.getRsuIndex()); odeService.sendNewTimToRsu(updatedTim); } catch (Exception ex) { - utility.logWithDate("Failed to send update to RSU."); + log.info("Failed to send update to RSU."); } } @@ -609,12 +617,12 @@ public void updateTimOnSdw(WydotTravelerInputData timToSend, Long timId, String // send TIM try { - utility.logWithDate("Updating TIM on SDW. tim_id: " + timId + ", sat_record_id: " + recordId); + log.info("Updating TIM on SDW. tim_id: {}, sat_record_id: {}", timId, recordId); restTemplateProvider.GetRestTemplate().postForObject(odeProps.getOdeUrl() + "/tim", timToSendJson, String.class); } catch (RuntimeException targetException) { - utility.logWithDate("exception updating tim on SDW"); - targetException.printStackTrace(); + log.info("exception updating tim on SDW"); + log.error("Exception", targetException); } } @@ -635,7 +643,7 @@ public WydotTravelerInputData updateTim(WydotTravelerInputData timToSend, Long t } public Boolean deleteTimFromRsu(WydotRsu rsu, Integer index) { - + log.debug("deleteTimFromRsu called with index {}", index); var odeRsu = new RSU(); odeRsu.setRsuIndex(rsu.getRsuIndex()); odeRsu.setRsuTarget(rsu.getRsuTarget()); @@ -648,7 +656,7 @@ public Boolean deleteTimFromRsu(WydotRsu rsu, Integer index) { headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity(rsuJson, headers); - utility.logWithDate("Deleting TIM on index " + index.toString() + " from rsu " + odeRsu.getRsuTarget()); + log.info("Deleting TIM on index {} from RSU {}", index, odeRsu.getRsuTarget()); var response = restTemplateProvider.GetRestTemplate_NoErrors().exchange( odeProps.getOdeUrl() + "/tim?index=" + index.toString(), HttpMethod.DELETE, entity, String.class); diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/tables/DbTables.java b/cv-data-service-library/src/main/java/com/trihydro/library/tables/DbTables.java index 55a2bcbd1..28c9ec584 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/tables/DbTables.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/tables/DbTables.java @@ -6,8 +6,10 @@ import java.util.Iterator; import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Pair; +@Slf4j public class DbTables { public String buildInsertQueryStatement(String tableName, List table) { @@ -60,8 +62,8 @@ public PreparedStatement buildUpdateStatement(Long id, String tableName, String preparedStatement.setObject(index, id); return preparedStatement; } catch (SQLException ex) { - System.out.println("Error creating update statement"); - ex.printStackTrace(); + log.info("Error creating update statement"); + log.error("Exception", ex); } return null; diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/tables/TimDbTables.java b/cv-data-service-library/src/main/java/com/trihydro/library/tables/TimDbTables.java index bd87d7609..321957b64 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/tables/TimDbTables.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/tables/TimDbTables.java @@ -1,265 +1,114 @@ package com.trihydro.library.tables; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; +import lombok.Getter; import org.springframework.stereotype.Component; +import lombok.extern.slf4j.Slf4j; @Component +@Slf4j +@Getter public class TimDbTables extends DbTables { - private List timTable; - private List dataFrameTable; - private List pathTable; - private List regionTable; - private List nodeXYTable; - private List nodeLLTable; - private List pathNodeXYTable; - private List pathNodeLLTable; - private List timTypeTable; - private List activeTimTable; - private List activeTimHoldingTable; - private List timRsuTable; - private List dataFrameItisCodeTable; - - public List getTimTable() { - if (timTable != null) - return timTable; - else { - timTable = new ArrayList(); - - timTable.add("MSG_CNT"); - timTable.add("PACKET_ID"); - timTable.add("URL_B"); - timTable.add("TIME_STAMP"); - timTable.add("RECORD_GENERATED_BY"); - timTable.add("RMD_LD_ELEVATION"); - timTable.add("RMD_LD_HEADING"); - timTable.add("RMD_LD_LATITUDE"); - timTable.add("RMD_LD_LONGITUDE"); - timTable.add("RMD_LD_SPEED"); - timTable.add("RMD_RX_SOURCE"); - timTable.add("SCHEMA_VERSION"); - timTable.add("SECURITY_RESULT_CODE"); - timTable.add("LOG_FILE_NAME"); - timTable.add("RECORD_GENERATED_AT"); - timTable.add("SANITIZED"); - timTable.add("SERIAL_ID_STREAM_ID"); - timTable.add("SERIAL_ID_BUNDLE_SIZE"); - timTable.add("SERIAL_ID_BUNDLE_ID"); - timTable.add("SERIAL_ID_RECORD_ID"); - timTable.add("SERIAL_ID_SERIAL_NUMBER"); - timTable.add("PAYLOAD_TYPE"); - timTable.add("RECORD_TYPE"); - timTable.add("ODE_RECEIVED_AT"); - timTable.add("SAT_RECORD_ID"); - - return timTable; - } + // All lists are final and initialized in the constructor + private final List timTable; + private final List dataFrameTable; + private final List pathTable; + private final List regionTable; + private final List nodeXYTable; + private final List nodeLLTable; + private final List pathNodeXYTable; + private final List pathNodeLLTable; + private final List timTypeTable; + private final List activeTimTable; + private final List activeTimHoldingTable; + private final List timRsuTable; + private final List dataFrameItisCodeTable; + + public TimDbTables() { + log.debug("Initializing TimDbTables"); + + // Initialize all lists in the constructor + timTable = createColumnList( + "MSG_CNT", "PACKET_ID", "URL_B", "TIME_STAMP", "RECORD_GENERATED_BY", + "RMD_LD_ELEVATION", "RMD_LD_HEADING", "RMD_LD_LATITUDE", "RMD_LD_LONGITUDE", + "RMD_LD_SPEED", "RMD_RX_SOURCE", "SCHEMA_VERSION", "SECURITY_RESULT_CODE", + "LOG_FILE_NAME", "RECORD_GENERATED_AT", "SANITIZED", "SERIAL_ID_STREAM_ID", + "SERIAL_ID_BUNDLE_SIZE", "SERIAL_ID_BUNDLE_ID", "SERIAL_ID_RECORD_ID", + "SERIAL_ID_SERIAL_NUMBER", "PAYLOAD_TYPE", "RECORD_TYPE", "ODE_RECEIVED_AT", + "SAT_RECORD_ID" + ); + + dataFrameTable = createColumnList( + "TIM_ID", "SSP_TIM_RIGHTS", "FRAME_TYPE", "DURATION_TIME", + "PRIORITY", "SSP_LOCATION_RIGHTS", "SSP_MSG_TYPES", + "SSP_MSG_CONTENT", "CONTENT", "URL", "START_DATE_TIME" + ); + + pathTable = createColumnList("SCALE"); + + regionTable = createColumnList( + "DATA_FRAME_ID", "NAME", "LANE_WIDTH", "DIRECTIONALITY", "DIRECTION", + "CLOSED_PATH", "ANCHOR_LAT", "ANCHOR_LONG", "PATH_ID", "GEOMETRY_DIRECTION", + "GEOMETRY_EXTENT", "GEOMETRY_LANE_WIDTH", "GEOMETRY_CIRCLE_POSITION_LAT", + "GEOMETRY_CIRCLE_POSITION_LONG", "GEOMETRY_CIRCLE_POSITION_ELEV", + "GEOMETRY_CIRCLE_RADIUS", "GEOMETRY_CIRCLE_UNITS" + ); + + pathNodeXYTable = createColumnList("NODE_XY_ID", "PATH_ID"); + + pathNodeLLTable = createColumnList("NODE_LL_ID", "PATH_ID"); + + nodeXYTable = createColumnList( + "DELTA", "NODE_LAT", "NODE_LONG", "X", "Y", + "ATTRIBUTES_DWIDTH", "ATTRIBUTES_DELEVATION" + ); + + nodeLLTable = createColumnList( + "DELTA", "NODE_LAT", "NODE_LONG", "X", "Y", + "ATTRIBUTES_DWIDTH", "ATTRIBUTES_DELEVATION" + ); + + timTypeTable = createColumnList("TYPE", "DESCRIPTION"); + + activeTimTable = createColumnList( + "TIM_ID", "DIRECTION", "TIM_START", "TIM_END", "TIM_TYPE_ID", + "ROUTE", "CLIENT_ID", "SAT_RECORD_ID", "PK", "START_LATITUDE", + "START_LONGITUDE", "END_LATITUDE", "END_LONGITUDE", + "EXPIRATION_DATE", "PROJECT_KEY" + ); + + activeTimHoldingTable = createColumnList( + "ACTIVE_TIM_HOLDING_ID", "CLIENT_ID", "DIRECTION", "RSU_TARGET", + "SAT_RECORD_ID", "START_LATITUDE", "START_LONGITUDE", "END_LATITUDE", + "END_LONGITUDE", "RSU_INDEX", "DATE_CREATED", "PROJECT_KEY", + "EXPIRATION_DATE", "PACKET_ID", "TIM_END" + ); + + timRsuTable = createColumnList("TIM_ID", "RSU_ID", "RSU_INDEX"); + + dataFrameItisCodeTable = createColumnList( + "ITIS_CODE_ID", "DATA_FRAME_ID", "TEXT", "POSITION" + ); + + log.debug("TimDbTables initialization complete"); } - public List getDataFrameTable() { - if (dataFrameTable != null) - return dataFrameTable; - else { - dataFrameTable = new ArrayList(); - dataFrameTable.add("TIM_ID"); - dataFrameTable.add("SSP_TIM_RIGHTS"); - dataFrameTable.add("FRAME_TYPE"); - // dataFrameTable.add("MSG_ID"); //ignore msg_id for now since its a full object - // dataFrameTable.add("FURTHER_INFO_ID"); //ignore further_info_id for now - // dataFrameTable.add("VIEW_ANGLE"); //not part of ode DataFrame object - // dataFrameTable.add("MUTCD"); //not part of ode DataFrame object - // dataFrameTable.add("CRC"); //not part of ode DataFrame object - dataFrameTable.add("DURATION_TIME"); - dataFrameTable.add("PRIORITY"); - dataFrameTable.add("SSP_LOCATION_RIGHTS"); - dataFrameTable.add("SSP_MSG_TYPES"); - dataFrameTable.add("SSP_MSG_CONTENT"); - dataFrameTable.add("CONTENT"); - dataFrameTable.add("URL"); - // dataFrameTable.add("POSITION_LAT"); //not part of ode DataFrame object - // dataFrameTable.add("POSITION_LONG"); //not part of ode DataFrame object - // dataFrameTable.add("POSITION_ELEV"); //not part of ode DataFrame object - dataFrameTable.add("START_DATE_TIME"); - return dataFrameTable; + /** + * Create a thread-safe, immutable list from column names + */ + private List createColumnList(String... columns) { + Set uniqueColumns = new LinkedHashSet<>(); + for (String column : columns) { + if (!uniqueColumns.add(column)) { + log.warn("Duplicate column detected and removed: {}", column); + } } + return Collections.unmodifiableList(new ArrayList<>(uniqueColumns)); } - - public List getPathTable() { - if (pathTable != null) - return pathTable; - else { - pathTable = new ArrayList(); - pathTable.add("SCALE"); - return pathTable; - } - } - - public List getRegionTable() { - if (regionTable != null) - return regionTable; - else { - regionTable = new ArrayList(); - regionTable.add("DATA_FRAME_ID"); - regionTable.add("NAME"); - regionTable.add("LANE_WIDTH"); - regionTable.add("DIRECTIONALITY"); - regionTable.add("DIRECTION"); - regionTable.add("CLOSED_PATH"); - regionTable.add("ANCHOR_LAT"); - regionTable.add("ANCHOR_LONG"); - - regionTable.add("PATH_ID"); - - regionTable.add("GEOMETRY_DIRECTION"); - regionTable.add("GEOMETRY_EXTENT"); - regionTable.add("GEOMETRY_LANE_WIDTH"); - - regionTable.add("GEOMETRY_CIRCLE_POSITION_LAT"); - regionTable.add("GEOMETRY_CIRCLE_POSITION_LONG"); - regionTable.add("GEOMETRY_CIRCLE_POSITION_ELEV"); - regionTable.add("GEOMETRY_CIRCLE_RADIUS"); - regionTable.add("GEOMETRY_CIRCLE_UNITS"); - return regionTable; - } - } - - public List getPathNodeXYTable() { - if (pathNodeXYTable != null) - return pathNodeXYTable; - else { - pathNodeXYTable = new ArrayList(); - pathNodeXYTable.add("NODE_XY_ID"); - pathNodeXYTable.add("PATH_ID"); - return pathNodeXYTable; - } - } - - public List getPathNodeLLTable() { - if (pathNodeLLTable != null) - return pathNodeLLTable; - else { - pathNodeLLTable = new ArrayList(); - pathNodeLLTable.add("NODE_LL_ID"); - pathNodeLLTable.add("PATH_ID"); - return pathNodeLLTable; - } - } - - public List getNodeXYTable() { - if (nodeXYTable != null) - return nodeXYTable; - else { - nodeXYTable = new ArrayList(); - nodeXYTable.add("DELTA"); - nodeXYTable.add("NODE_LAT"); - nodeXYTable.add("NODE_LONG"); - nodeXYTable.add("X"); - nodeXYTable.add("Y"); - nodeXYTable.add("ATTRIBUTES_DWIDTH"); - nodeXYTable.add("ATTRIBUTES_DELEVATION"); - return nodeXYTable; - } - } - - public List getNodeLLTable() { - if (nodeLLTable != null) - return nodeLLTable; - else { - nodeLLTable = new ArrayList(); - nodeLLTable.add("DELTA"); - nodeLLTable.add("NODE_LAT"); - nodeLLTable.add("NODE_LONG"); - nodeLLTable.add("X"); - nodeLLTable.add("Y"); - nodeLLTable.add("ATTRIBUTES_DWIDTH"); - nodeLLTable.add("ATTRIBUTES_DELEVATION"); - return nodeLLTable; - } - } - - public List getTimTypeTable() { - if (timTypeTable != null) - return timTypeTable; - else { - timTypeTable = new ArrayList(); - timTypeTable.add("TYPE"); - timTypeTable.add("DESCRIPTION"); - return timTypeTable; - } - } - - public List getActiveTimTable() { - if (activeTimTable != null) - return activeTimTable; - else { - activeTimTable = new ArrayList(); - activeTimTable.add("TIM_ID"); - activeTimTable.add("DIRECTION"); - activeTimTable.add("TIM_START"); - activeTimTable.add("TIM_END"); - activeTimTable.add("TIM_TYPE_ID"); - activeTimTable.add("ROUTE"); - activeTimTable.add("CLIENT_ID"); - activeTimTable.add("SAT_RECORD_ID"); - activeTimTable.add("PK"); - activeTimTable.add("START_LATITUDE"); - activeTimTable.add("START_LONGITUDE"); - activeTimTable.add("END_LATITUDE"); - activeTimTable.add("END_LONGITUDE"); - activeTimTable.add("EXPIRATION_DATE"); - activeTimTable.add("PROJECT_KEY"); - return activeTimTable; - } - } - - public List getActiveTimHoldingTable() { - if (activeTimHoldingTable != null) - return activeTimHoldingTable; - else { - activeTimHoldingTable = new ArrayList(); - activeTimHoldingTable.add("ACTIVE_TIM_HOLDING_ID"); - activeTimHoldingTable.add("CLIENT_ID"); - activeTimHoldingTable.add("DIRECTION"); - activeTimHoldingTable.add("RSU_TARGET"); - activeTimHoldingTable.add("SAT_RECORD_ID"); - activeTimHoldingTable.add("START_LATITUDE"); - activeTimHoldingTable.add("START_LONGITUDE"); - activeTimHoldingTable.add("END_LATITUDE"); - activeTimHoldingTable.add("END_LONGITUDE"); - activeTimHoldingTable.add("RSU_INDEX"); - activeTimHoldingTable.add("DATE_CREATED"); - activeTimHoldingTable.add("PROJECT_KEY"); - activeTimHoldingTable.add("EXPIRATION_DATE"); - activeTimHoldingTable.add("PACKET_ID"); - return activeTimHoldingTable; - } - } - - public List getTimRsuTable() { - if (timRsuTable != null) - return timRsuTable; - else { - timRsuTable = new ArrayList(); - timRsuTable.add("TIM_ID"); - timRsuTable.add("RSU_ID"); - timRsuTable.add("RSU_INDEX"); - return timRsuTable; - } - } - - public List getDataFrameItisCodeTable() { - if (dataFrameItisCodeTable != null) - return dataFrameItisCodeTable; - else { - dataFrameItisCodeTable = new ArrayList(); - dataFrameItisCodeTable.add("ITIS_CODE_ID"); - dataFrameItisCodeTable.add("DATA_FRAME_ID"); - dataFrameItisCodeTable.add("TEXT"); - dataFrameItisCodeTable.add("POSITION"); - return dataFrameItisCodeTable; - } - } - -} +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/CreateBaseTimUtilTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/CreateBaseTimUtilTest.java index 1a445ef04..a505bbe01 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/CreateBaseTimUtilTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/CreateBaseTimUtilTest.java @@ -319,4 +319,4 @@ public void buildTim_threeRegions_SUCCESS() { Assertions.assertEquals(lastMilepostOfSecondRegion.getLatitude().doubleValue(), anchorOfThirdRegion.getLatitude().doubleValue()); Assertions.assertEquals(lastMilepostOfSecondRegion.getLongitude().doubleValue(), anchorOfThirdRegion.getLongitude().doubleValue()); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DateTimeHelperImplTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DateTimeHelperImplTest.java new file mode 100644 index 000000000..0008dfddd --- /dev/null +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DateTimeHelperImplTest.java @@ -0,0 +1,191 @@ +package com.trihydro.library.helpers; + +import static org.junit.jupiter.api.Assertions.*; + +import java.sql.Timestamp; +import java.time.ZonedDateTime; +import java.util.TimeZone; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class DateTimeHelperImplTest { + private final DateTimeHelperImpl dateTimeHelper = new DateTimeHelperImpl(); + + @Test + public void convertDate_min_SUCCESS() { + // Arrange + var date = "2020-10-28T14:53Z"; + + // Act + var convertedDate = new DateTimeHelperImpl().convertDate(date); + + // Assert + Assertions.assertNotNull(convertedDate); + Assertions.assertEquals(1603896780000L, convertedDate.getTime()); + } + + @Test + public void convertDate_sec_SUCCESS() { + // Arrange + // SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + var date = "2020-10-28T14:53:00Z"; + + // Act + var convertedDate = dateTimeHelper.convertDate(date); + + // Assert + Assertions.assertNotNull(convertedDate); + Assertions.assertEquals(1603896780000L, convertedDate.getTime()); + } + + @Test + public void convertDate_milli_SUCCESS() { + // Arrange + // SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + var date = "2020-10-28T14:53:00.123Z"; + + // Act + var convertedDate = dateTimeHelper.convertDate(date); + + // Assert + Assertions.assertNotNull(convertedDate); + Assertions.assertEquals(1603896780123L, convertedDate.getTime()); + } + + @Test + public void convertDate_utcText_SUCCESS() { + // Arrange + // SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + var date = "2020-02-10T17:00:00.000Z[UTC]"; + + // Act + var convertedDate = dateTimeHelper.convertDate(date); + + // Assert + Assertions.assertNotNull(convertedDate); + Assertions.assertEquals(1581354000000L, convertedDate.getTime()); + } + + @Test + public void testConvertDateStringFromISO8601FormatIntoTimestampObject_ISO8601Format_ShouldReturnTimestamp() throws + DateStringNotInISO8601FormatException { + // Arrange + String dateString = "2025-04-30T06:00:00.000Z"; + String expectedTimestampString = "2025-04-30 06:00:00.0"; + + // Act + Timestamp timestamp = dateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject(dateString); + + // Assert + Assertions.assertNotNull(timestamp); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Assertions.assertEquals(expectedTimestampString, timestamp.toString()); + } + + @Test + public void testConvertDateStringFromISO8601FormatIntoTimestampObject_NotISO8601Format_ShouldThrowException() { + // Arrange + String dateString = "2025-04-30 06:00:00"; + + // Act & Assert + Assertions.assertThrows(DateStringNotInISO8601FormatException.class, () -> { + dateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject(dateString); + }); + } + + @Test + public void testIsInTableFormat_TableFormat_ShouldReturnTrue() { + // Arrange + String dateString = "2025-04-30 06:00:00"; + + // Act + boolean result = dateTimeHelper.isInTableFormat(dateString); + + // Assert + assertTrue(result); + } + + @Test + public void testIsInTableFormat_NotTableFormat_ShouldReturnFalse() { + // Arrange + String dateString = "2025-04-30T06:00:00.000Z"; + + // Act + boolean result = dateTimeHelper.isInTableFormat(dateString); + + // Assert + assertFalse(result); + } + + @Test + public void testIsInTableFormat_Null_ShouldReturnFalse() { + // Arrange + String dateString = null; + + // Act + boolean result = dateTimeHelper.isInTableFormat(dateString); + + // Assert + assertFalse(result); + } + + @Test + public void testIsInISO8601Format_ISO8601Format_ShouldReturnTrue() { + // Arrange + String dateString = "2025-04-30T06:00:00.000Z"; + + // Act + boolean result = dateTimeHelper.isInISO8601Format(dateString); + + // Assert + assertTrue(result); + } + + @Test + public void testIsInISO8601Format_NotISO8601Format_ShouldReturnFalse() { + // Arrange + String dateString = "2025-04-30 06:00:00"; + + // Act + boolean result = dateTimeHelper.isInISO8601Format(dateString); + + // Assert + assertFalse(result); + } + + @Test + public void testIsInISO8601Format_Null_ShouldReturnFalse() { + // Arrange + String dateString = null; + + // Act + boolean result = dateTimeHelper.isInISO8601Format(dateString); + + // Assert + assertFalse(result); + } + + @Test + public void testConvertZonedDateTimeToISO8601Format_ValidZonedDateTime_ShouldReturnISO8601Format() { + // Arrange + ZonedDateTime zdt = ZonedDateTime.parse("2025-04-30T06:00:00Z"); + + // Act + String result = dateTimeHelper.convertZonedDateTimeToISO8601Format(zdt); + + // Assert + Assertions.assertNotNull(result); + Assertions.assertEquals("2025-04-30T06:00:00.000Z", result); + } + + @Test + public void testConvertZonedDateTimeToISO8601Format_NullInput_ShouldThrowException() { + // Arrange + ZonedDateTime zdt = null; + + // Act & Assert + Assertions.assertThrows(NullPointerException.class, () -> { + dateTimeHelper.convertZonedDateTimeToISO8601Format(zdt); + }); + } +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java index e89ca7501..6c1c86627 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java @@ -7,9 +7,11 @@ import java.nio.file.Path; import java.nio.file.Paths; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; import us.dot.its.jpo.ode.model.OdeLogMetadata; import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; @@ -28,6 +30,7 @@ /** * Unit tests for JSON to Java Object Converters. */ +@Slf4j public class JsonToJavaConverterTest { private JsonToJavaConverter jsonToJava; @@ -139,7 +142,7 @@ public void TestConvertTimPayloadJsonToJava_Path() throws IOException, URISyntax String value = new String( Files.readAllBytes(Paths.get(getClass().getResource("/rxMsg_TIM_OdeOutput.json").toURI()))); OdeTimPayload odeTimPayloadTest = jsonToJava.convertTimPayloadJsonToJava(value); - System.out.println("PACKETID: " + getTim(odeTimPayload).getPacketID()); + log.info("PACKETID: {}", getTim(odeTimPayload).getPacketID()); for (int i = 0; i < 2; i++) { Assertions.assertEquals( getTim(odeTimPayload).getDataframes()[0].getRegions()[0].getPath().getNodes()[i].getNodeLat(), @@ -215,7 +218,7 @@ public void TestConvertTimPayloadJsonToJava_Path_MultipleRegions() throws IOExce String value = new String( Files.readAllBytes(Paths.get(getClass().getResource("/rxMsg_TIM_OdeOutput_MultipleRegions.json").toURI()))); OdeTimPayload odeTimPayloadTest = jsonToJava.convertTimPayloadJsonToJava(value); - System.out.println("PACKETID: " + getTim(odeTimPayload).getPacketID()); + log.info("PACKETID: {}", getTim(odeTimPayload).getPacketID()); for (int i = 0; i < 2; i++) { Assertions.assertEquals( getTim(odeTimPayload).getDataframes()[0].getRegions()[0].getPath().getNodes()[i].getNodeLat(), @@ -306,7 +309,7 @@ public void TestConvertTimPayloadJsonToJava_Geometry() throws IOException, URISy String value = new String( Files.readAllBytes(Paths.get(getClass().getResource("/rxMsg_TIM_OdeOutput_Geometry.json").toURI()))); OdeTimPayload odeTimPayloadTest = jsonToJava.convertTimPayloadJsonToJava(value); - System.out.println("PACKETID: " + getTim(odeTimPayload).getPacketID()); + log.info("PACKETID: {}", getTim(odeTimPayload).getPacketID()); // test geometry properties // direction @@ -358,7 +361,7 @@ public void convertBroadcastTimPayloadJsonToJava() throws IOException { Path currentRelativePath = Paths.get(""); String s = currentRelativePath.toAbsolutePath().toString(); - System.out.println("Current relative path is: " + s); + log.info("Current relative path is: {}", s); String value = new String(Files.readAllBytes(Paths.get("src/test/resources/broadcastTim_OdeOutput.json"))); // String value = new diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/RegionNameTrimmerTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/RegionNameTrimmerTest.java index 1896d1092..8910c29ba 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/RegionNameTrimmerTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/RegionNameTrimmerTest.java @@ -1,14 +1,13 @@ package com.trihydro.library.helpers; -import static org.junit.Assert.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import static org.junit.Assert.assertEquals; + @ExtendWith(MockitoExtension.class) public class RegionNameTrimmerTest { @@ -45,10 +44,4 @@ public void testTrimRegionName_EqualsMaxLength_SUCCESS() { String trimmedRegionName = uut.trimRegionNameIfTooLong(regionName); assertEquals(regionName, trimmedRegionName); } - - @Test - public void testTrimRegionName_GreaterThanMaxLength_FAILURE() { - String regionName = "I_Prairie Center Circle Drive_RSU-10.145.1.100_RC_averyveryveryongclientidfortestingpurposes"; - assertThrows(IllegalArgumentException.class, () -> uut.trimRegionNameIfTooLong(regionName)); - } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java index 3f65b8a7d..cfe66882f 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java @@ -668,7 +668,7 @@ public void resubmitToOde_usesOldStartTime() throws Utility.IdenticalPointsExcep } @Test - public void c_updatesDurationTimeToFiveMinutes() throws Utility.IdenticalPointsException { + public void testExpireTimAndResubmitToOde_updatesDurationTimeToFiveMinutes() throws Utility.IdenticalPointsException { // Arrange List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); @@ -706,6 +706,46 @@ public void c_updatesDurationTimeToFiveMinutes() throws Utility.IdenticalPointsE Assertions.assertEquals(5, dataFrame.getDurationTime()); } + @Test + public void testExpireTimAndResubmitToOde_setStartDateToCurrentTime() throws Utility.IdenticalPointsException { + // Arrange + List activeTimIds = new ArrayList<>(); + activeTimIds.add(-1L); + setupActiveTimModel(); + setupMilepostReturn(); + tum.setRoute("I 80"); + tum.setSatRecordId("satRecordId"); + tum.setStartDateTime(""); + + // Given a TIM with a durationTime of an hour + var originalStartTime = Instant.parse("2021-01-01T00:00:00.000Z"); + tum.setDurationTime(60); + tum.setEndDateTime("2021-01-01T01:00:00.000Z"); + tum.setStartDate_Timestamp(new Timestamp(originalStartTime.toEpochMilli())); + + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); + doReturn("").when(mockOdeService).updateTimOnSdw(any()); + + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); + + // Act + uut.expireTimAndResubmitToOde(activeTimIds); + + // Assert + verify(mockOdeService).updateTimOnSdw(timCaptor.capture()); + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); + var timSent = timCaptor.getValue(); + var dataFrame = timSent.getTim().getDataframes()[0]; + + Assertions.assertNotNull(dataFrame.getStartDateTime()); + var newStartTime = Instant.parse(dataFrame.getStartDateTime()); + + // A different startTime should have been used + Assertions.assertTrue(newStartTime.getEpochSecond() != originalStartTime.getEpochSecond()); + } + @Test public void resubmitToOde_SdxExistingFail() throws Utility.IdenticalPointsException { // Arrange @@ -1265,6 +1305,18 @@ public void isValidTim_TRUE() { Assertions.assertTrue(success); } + @Test + public void isValidTim_FALSE_NULL() { + // Arrange + TimUpdateModel tum = null; + + // Act + var success = uut.isValidTim(tum); + + // Assert + Assertions.assertFalse(success); + } + @Test public void isValidTim_FALSE_StartPoint() { // Arrange @@ -1279,6 +1331,21 @@ public void isValidTim_FALSE_StartPoint() { Assertions.assertFalse(success); } + @Test + public void isValidTim_FALSE_InvalidCoordinates() { + // Arrange + var tum = new TimUpdateModel(); + tum.setStartPoint(new Coordinate(null, null)); + tum.setDirection("I"); + tum.setRoute("I 80"); + + // Act + var success = uut.isValidTim(tum); + + // Assert + Assertions.assertFalse(success); + } + @Test public void isValidTim_FALSE_Direction() { // Arrange @@ -1407,4 +1474,4 @@ private ActiveTim getActiveTim() { tim.setActiveTimId(-1L); return tim; } -} \ No newline at end of file +} diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TmddDeserializationTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TmddDeserializationTest.java index 3a67bd837..9fd76b76a 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TmddDeserializationTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TmddDeserializationTest.java @@ -17,10 +17,14 @@ import com.trihydro.library.model.tmdd.LinkLocation; import com.trihydro.library.model.tmdd.MessageHeader; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +@Slf4j public class TmddDeserializationTest { + @Test public void deserializes_realData() { // Arrange @@ -109,7 +113,7 @@ private String readFile(String fileName) { try { contents = CharStreams.toString(isr); } catch (IOException e) { - e.printStackTrace(); + log.error("Exception", e); } return contents; diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java index 23ee4ca47..75c81204a 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java @@ -1,8 +1,10 @@ package com.trihydro.library.helpers; import java.math.BigDecimal; +import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.TimeZone; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -13,66 +15,15 @@ import com.trihydro.library.model.Coordinate; import com.trihydro.library.model.Milepost; +import static org.junit.jupiter.api.Assertions.*; + @ExtendWith(MockitoExtension.class) public class UtilityTest { @InjectMocks private Utility uut; - @Test - public void convertDate_min_SUCCESS() { - // Arrange - var date = "2020-10-28T14:53Z"; - - // Act - var convertedDate = uut.convertDate(date); - - // Assert - Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1603896780000L, convertedDate.getTime()); - } - - @Test - public void convertDate_sec_SUCCESS() { - // Arrange - // SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - var date = "2020-10-28T14:53:00Z"; - - // Act - var convertedDate = uut.convertDate(date); - - // Assert - Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1603896780000L, convertedDate.getTime()); - } - @Test - public void convertDate_milli_SUCCESS() { - // Arrange - // SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - var date = "2020-10-28T14:53:00.123Z"; - - // Act - var convertedDate = uut.convertDate(date); - - // Assert - Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1603896780123L, convertedDate.getTime()); - } - - @Test - public void convertDate_utcText_SUCCESS() { - // Arrange - // SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - var date = "2020-02-10T17:00:00.000Z[UTC]"; - - // Act - var convertedDate = uut.convertDate(date); - - // Assert - Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1581354000000L, convertedDate.getTime()); - } @Test public void getMinutesDurationBetweenTwoDates_SUCCESS_simpleDate() { @@ -307,4 +258,4 @@ public void calculateAnchorCoordinate_identicalPoints_negativeLat_negativeLon() }); } -} \ No newline at end of file +} diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/model/TimUpdateModelTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/model/TimUpdateModelTest.java new file mode 100644 index 000000000..688780055 --- /dev/null +++ b/cv-data-service-library/src/test/java/com/trihydro/library/model/TimUpdateModelTest.java @@ -0,0 +1,54 @@ +package com.trihydro.library.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +public class TimUpdateModelTest { + + /** + * Tests the `toString` method of the `TimUpdateModel` class. + * Verifies that the method produces a correct string representation of the object + * by focusing on different combinations of field values. + */ + + @Test + public void testToString_SomeFieldsPopulated() { + TimUpdateModel model = new TimUpdateModel(); + model.setActiveTimId(1L); + model.setPacketId("12345"); + model.setStartPoint(new Coordinate(new BigDecimal("37.7749"), new BigDecimal("-122.4194"))); + model.setDirection("D"); + model.setRoute("Route1"); + + String expected = "TimUpdateModel(super=ActiveTim(activeTimId=1, timId=null, timType=null, timTypeId=null, direction=D, startTimestamp=null, startDateTime=null, endDateTime=null, expirationDateTime=null, route=Route1, clientId=null, satRecordId=null, pk=null, rsuTarget=null, rsuIndex=null, itisCodes=null, startPoint=37.774900,-122.419400, endPoint=null, projectKey=null), msgCnt=0, urlB=null, startDate_Timestamp=null, endDate_Timestamp=null, packetId=12345, timTypeName=null, timTypeDescription=null, regionId=null, regionDescription=null, laneWidth=null, anchorLat=null, anchorLong=null, regionDirection=null, directionality=null, closedPath=null, pathId=null, dataFrameId=0, frameType=null, durationTime=0, doNotUse2=0, doNotUse1=0, doNotUse4=0, doNotUse3=0, dfContent=null, url=null)"; + assertEquals(expected, model.toString()); + } + + @Test + public void testToString_NullStartPoint() { + TimUpdateModel model = new TimUpdateModel(); + model.setActiveTimId(2L); + model.setPacketId("54321"); + model.setStartPoint(null); + model.setDirection("I"); + model.setRoute("Route2"); + + String expected = "TimUpdateModel(super=ActiveTim(activeTimId=2, timId=null, timType=null, timTypeId=null, direction=I, startTimestamp=null, startDateTime=null, endDateTime=null, expirationDateTime=null, route=Route2, clientId=null, satRecordId=null, pk=null, rsuTarget=null, rsuIndex=null, itisCodes=null, startPoint=null, endPoint=null, projectKey=null), msgCnt=0, urlB=null, startDate_Timestamp=null, endDate_Timestamp=null, packetId=54321, timTypeName=null, timTypeDescription=null, regionId=null, regionDescription=null, laneWidth=null, anchorLat=null, anchorLong=null, regionDirection=null, directionality=null, closedPath=null, pathId=null, dataFrameId=0, frameType=null, durationTime=0, doNotUse2=0, doNotUse1=0, doNotUse4=0, doNotUse3=0, dfContent=null, url=null)"; + assertEquals(expected, model.toString()); + } + + @Test + public void testToString_EmptyPacketId() { + TimUpdateModel model = new TimUpdateModel(); + model.setActiveTimId(3L); + model.setPacketId(""); + model.setStartPoint(new Coordinate(new BigDecimal("34.0522"), new BigDecimal("-118.2437"))); + model.setDirection("B"); + model.setRoute(""); + + String expected = "TimUpdateModel(super=ActiveTim(activeTimId=3, timId=null, timType=null, timTypeId=null, direction=B, startTimestamp=null, startDateTime=null, endDateTime=null, expirationDateTime=null, route=, clientId=null, satRecordId=null, pk=null, rsuTarget=null, rsuIndex=null, itisCodes=null, startPoint=34.052200,-118.243700, endPoint=null, projectKey=null), msgCnt=0, urlB=null, startDate_Timestamp=null, endDate_Timestamp=null, packetId=, timTypeName=null, timTypeDescription=null, regionId=null, regionDescription=null, laneWidth=null, anchorLat=null, anchorLong=null, regionDirection=null, directionality=null, closedPath=null, pathId=null, dataFrameId=0, frameType=null, durationTime=0, doNotUse2=0, doNotUse1=0, doNotUse4=0, doNotUse3=0, dfContent=null, url=null)"; + assertEquals(expected, model.toString()); + } +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/models/ActiveTimTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/models/ActiveTimTest.java new file mode 100644 index 000000000..04897ba02 --- /dev/null +++ b/cv-data-service-library/src/test/java/com/trihydro/library/models/ActiveTimTest.java @@ -0,0 +1,271 @@ +package com.trihydro.library.models; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import com.trihydro.library.model.ActiveTim; + +public class ActiveTimTest { + + final String EXISTING_END_DATE_TIME = "2023-04-16 06:00:00"; + final String END_DATE_TIME_TO_COMPARE = "2023-04-16T06:00:00.000Z"; + + /** + * This test verifies that the isIdenticalConditions method returns true + * when the conditions and end date time are identical. + */ + @Test + public void testIdenticalConditionsWithIdenticalITISCodesAndEndDate() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + String existingEndDateTime = EXISTING_END_DATE_TIME; + activeTim.setEndDateTime(existingEndDateTime); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE; + int minutesUntilEndDateTimeToCompare = 1000; + + boolean expectedResult = true; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns true + * when the existing end date is null and the end date to compare is null. + * + * Note: When the end date time to compare is null, the minutes until end date + * time to compare is not used and should be set to -1. + */ + @Test + public void testIdenticalConditionsWithNoEndDate() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = null; + int minutesUntilEndDateTimeToCompare = -1; + + boolean expectedResult = true; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns true + * when the existing end date is null and the end date to compare is not null + * (and the minutes until end date time to compare is more than 32000). + * + * This will be the scenario when a planned county road condition with a total + * duration of more than 32000 minutes is being compared to an existing active + * county road condition with no end date. Since the planned condition will end + * more than 32000 minutes in the future, it is considered identical to the + * existing active condition with no end date. + */ + @Test + public void testIdenticalConditionsWithNoEndDateAndEndDateToCompare() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE; + int minutesUntilEndDateTimeToCompare = 32001; + + boolean expectedResult = true; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns true + * when the existing end date is null and the end date to compare is not null + * (and the minutes until end date time to compare is equal to 32000). + * + * This will be the scenario when a planned county road condition with a total + * duration of exactly 32000 minutes is being compared to an existing active + * county road condition with no end date. Since the planned condition will end + * exactly 32000 minutes in the future, it is considered identical to the + * existing active condition with no end date. + */ + @Test + public void testIdenticalConditionsWithNoEndDateAndEndDateToCompare32000() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE; + int minutesUntilEndDateTimeToCompare = 32000; + + boolean expectedResult = true; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns false + * when the conditions are identical but the end date time is different. + */ + @Test + public void testIdenticalConditionsWithIdenticalITISCodesAndDifferentEndDate() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + String existingEndDateTime = EXISTING_END_DATE_TIME; + activeTim.setEndDateTime(existingEndDateTime); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE.replace("23", "24"); + int minutesUntilEndDateTimeToCompare = 1000; + + boolean expectedResult = false; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns false + * when the conditions are different but the end date time is identical. + */ + @Test + public void testIdenticalConditionsWithDifferentITISCodesAndIdenticalEndDate() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + String existingEndDateTime = EXISTING_END_DATE_TIME; + activeTim.setEndDateTime(existingEndDateTime); + + List itisCodesToCompare = Arrays.asList(1, 2, 4); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE; + int minutesUntilEndDateTimeToCompare = 1000; + + boolean expectedResult = false; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns false + * when the existing end date is not null and the end date to compare is null. + * + * Note: When the end date time to compare is null, the minutes until end date + * time to compare is not used and should be set to -1. + */ + @Test + public void testIdenticalConditionsWithExistingEndDateAndNoEndDateToCompare() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + String existingEndDateTime = EXISTING_END_DATE_TIME; + activeTim.setEndDateTime(existingEndDateTime); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = null; + int minutesUntilEndDateTimeToCompare = -1; + + boolean expectedResult = false; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns false + * when the conditions and end date time are different. + */ + @Test + public void testIdenticalConditionsWithDifferentITISCodesAndEndDate() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + String existingEndDateTime = EXISTING_END_DATE_TIME; + activeTim.setEndDateTime(existingEndDateTime); + + List itisCodesToCompare = Arrays.asList(1, 2, 4); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE.replace("23", "24"); + int minutesUntilEndDateTimeToCompare = 1000; + + boolean expectedResult = false; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + + /** + * This test verifies that the isIdenticalConditions method returns false + * when the existing end date is null and the end date to compare is not null + * (and the minutes until end date time to compare is less than 32000). + * + * This will be the scenario when a planned county road condition with a total + * duration of more than 32000 minutes is being compared to an existing active + * county road condition with no end date. Since the planned condition will end + * less than 32000 minutes in the future, it is not considered identical to the + * existing active condition with no end date. The planned condition will be + * expired and re-submitted as a new active condition with an end date. + */ + @Test + public void testIdenticalConditionsWithNoEndDateAndEndDateToCompareLessThan32000() { + // prepare + ActiveTim activeTim = new ActiveTim(); + List existingItisCodes = Arrays.asList(1, 2, 3); + activeTim.setItisCodes(existingItisCodes); + + List itisCodesToCompare = Arrays.asList(1, 2, 3); + String endDateTimeToCompare = END_DATE_TIME_TO_COMPARE; + int minutesUntilEndDateTimeToCompare = 31999; + + boolean expectedResult = false; + + // execute + boolean actualResult = activeTim.isIdenticalConditions(itisCodesToCompare, endDateTimeToCompare, minutesUntilEndDateTimeToCompare); + + // verify + assertEquals(expectedResult, actualResult); + } + +} diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java index af222d08b..56b36b2e8 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java @@ -1,5 +1,6 @@ package com.trihydro.library.service; +import static org.mockito.Mockito.lenient; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; @@ -7,10 +8,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import com.trihydro.library.model.ActiveRsuTimQueryModel; @@ -24,80 +26,41 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; +import org.mockito.Mockito; +import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; -public class ActiveTimServiceTest extends BaseServiceTest { +class ActiveTimServiceTest { @Mock - private ResponseEntity mockResponseEntity; - @Mock - private ResponseEntity mockResponseEntityBoolean; - @Mock - private ResponseEntity mockResponseEntityActiveTims; - @Mock - private ResponseEntity mockResponseEntityIntegerArray; - @Mock - private ResponseEntity mockResponseEntityString; - @Mock - private ResponseEntity mockResponseEntityTimUpdateModelArray; - @Mock - private ResponseEntity mockResponseEntityActiveTim; + RestTemplateProvider mockRestTemplateProvider; + @Mock - private CVRestServiceProps mockConfig; + CVRestServiceProps mockConfig; - private Long timTypeId = -1l; - private List wydotTims; - private ActiveTim[] aTims; - private ActiveTim aTim; - private TimUpdateModel[] tumArr; - private String baseUrl = "baseUrl"; + Long timTypeId = -1L; + List wydotTims; @InjectMocks - private ActiveTimService uut; - - @BeforeEach - public void setupSubTest() throws SQLException { - doReturn(baseUrl).when(mockConfig).getCvRestService(); - } + ActiveTimService uut; - private void setupBooleanReturn() { - doReturn(true).when(mockResponseEntityBoolean).getBody(); - } + String baseUrl = "http://localhost:8080"; + RestTemplate restTemplate = new RestTemplate(); - private void setupIntegerArrayReturn() { - Integer[] intArray = new Integer[3]; - intArray[0] = 0; - intArray[1] = 1; - intArray[2] = 2; - when(mockResponseEntityIntegerArray.getBody()).thenReturn(intArray); - } - - private void setupActiveTimArrayReturn() { - aTims = new ActiveTim[1]; - aTim = new ActiveTim(); - aTim.setActiveTimId(-1l); - aTims[0] = aTim; - doReturn(aTims).when(mockResponseEntityActiveTims).getBody(); - } - - private void setupActiveTimReturn() { - aTim = new ActiveTim(); - aTim.setActiveTimId(-1l); - doReturn(aTim).when(mockResponseEntityActiveTim).getBody(); - } + @Mock + MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); - private void setupTUMReturn() { - tumArr = new TimUpdateModel[1]; - TimUpdateModel tum = new TimUpdateModel(); - tum.setActiveTimId(-1l); - tum.setClientId("testClient"); - tumArr[0] = tum; - when(mockResponseEntityTimUpdateModelArray.getBody()).thenReturn(tumArr); + @BeforeEach + void setupSubTest() { + mockRestTemplateProvider = Mockito.mock(RestTemplateProvider.class); + mockConfig = Mockito.mock(CVRestServiceProps.class); + uut = new ActiveTimService(); + uut.InjectDependencies(mockConfig, mockRestTemplateProvider); + lenient().when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(restTemplate); + when(mockConfig.getCvRestService()).thenReturn(baseUrl); } - private void setupWydotTims() { + void setupWydotTims() { wydotTims = new ArrayList<>(); WydotTim wydotTim = new WydotTim(); wydotTim.setDirection("d"); @@ -110,37 +73,37 @@ private void setupWydotTims() { } @Test - public void updateActiveTim_SatRecordId() { - // Arrange - setupBooleanReturn(); - Long activeTimId = -1l; + void updateActiveTim_SatRecordId() { + // prepare + Long activeTimId = -1L; String satRecordId = "asdf"; - HttpEntity entity = getEntity(null, String.class); String url = String.format("%s/active-tim/update-sat-record-id/%d/%s", baseUrl, activeTimId, satRecordId); - when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); + String jsonString = "true"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute Boolean data = uut.updateActiveTim_SatRecordId(activeTimId, satRecordId); - // Assert - verify(mockRestTemplate).exchange(url, HttpMethod.PUT, entity, Boolean.class); + // verify + mockServer.verify(); Assertions.assertTrue(data, "Update failed when should have succeeded"); } @Test - public void addItisCodesToActiveTim() { - // Arrange - setupIntegerArrayReturn(); + void addItisCodesToActiveTim() { + // prepare ActiveTim activeTim = new ActiveTim(); - activeTim.setActiveTimId(-1l); + activeTim.setActiveTimId(-1L); String url = String.format("%s/active-tim/itis-codes/%d", baseUrl, activeTim.getActiveTimId()); - when(mockRestTemplate.getForEntity(url, Integer[].class)).thenReturn(mockResponseEntityIntegerArray); + String jsonString = "[0, 1, 2]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute uut.addItisCodesToActiveTim(activeTim); - // Assert - verify(mockRestTemplate).getForEntity(url, Integer[].class); + // verify + mockServer.verify(); Assertions.assertEquals(3, activeTim.getItisCodes().size()); Assertions.assertEquals(Integer.valueOf(0), activeTim.getItisCodes().get(0)); Assertions.assertEquals(Integer.valueOf(1), activeTim.getItisCodes().get(1)); @@ -148,52 +111,53 @@ public void addItisCodesToActiveTim() { } @Test - public void deleteActiveTim() { - // Arrange - setupBooleanReturn(); - Long activeTimId = -1l; + void deleteActiveTim() { + // prepare + Long activeTimId = -1L; String url = String.format("%s/active-tim/delete-id/%d", baseUrl, activeTimId); - HttpEntity entity = getEntity(null, String.class); - when(mockRestTemplate.exchange(url, HttpMethod.DELETE, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); + String jsonString = "true"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute boolean data = uut.deleteActiveTim(activeTimId); - // Assert - verify(mockRestTemplate).exchange(url, HttpMethod.DELETE, entity, Boolean.class); + // verify + mockServer.verify(); Assertions.assertTrue(data, "Reported failure when success"); } @Test - public void deleteActiveTimsById() throws SQLException { - // Arrange - setupBooleanReturn(); + void deleteActiveTimsById() throws SQLException { + // prepare List activeTimIds = new ArrayList(); - activeTimIds.add(-1l); - activeTimIds.add(-2l); - HttpEntity> entity = new HttpEntity>(activeTimIds, getDefaultHeaders()); - when(mockRestTemplate.exchange(baseUrl + "/active-tim/delete-ids", HttpMethod.DELETE, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); + activeTimIds.add(-1L); + activeTimIds.add(-2L); + String url = String.format("%s/active-tim/delete-ids", baseUrl); + String jsonString = "true"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute boolean success = uut.deleteActiveTimsById(activeTimIds); - // Assert + // verify Assertions.assertTrue(success); } @Test - public void getActiveTimIndicesByRsu() { - // Arrange - setupIntegerArrayReturn(); + void getActiveTimIndicesByRsu() { + // prepare String rsuTarget = "10.10.10.10"; String url = String.format("%s/active-tim/indices-rsu/%s", baseUrl, rsuTarget); - when(mockRestTemplate.getForEntity(url, Integer[].class)).thenReturn(mockResponseEntityIntegerArray); + String jsonString = "[0, 1, 2]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getActiveTimIndicesByRsu(rsuTarget); - // Assert - verify(mockRestTemplate).getForEntity(url, Integer[].class); + // verify + mockServer.verify(); Assertions.assertEquals(3, data.size()); Assertions.assertEquals(Integer.valueOf(0), data.get(0)); Assertions.assertEquals(Integer.valueOf(1), data.get(1)); @@ -201,302 +165,492 @@ public void getActiveTimIndicesByRsu() { } @Test - public void getActiveTimsByWydotTim() throws SQLException { - // Arrange + void getActiveTimsByWydotTim() throws SQLException { + // prepare setupWydotTims(); - setupActiveTimArrayReturn(); - HttpEntity> entity = new HttpEntity>(wydotTims, getDefaultHeaders()); - when(mockRestTemplate.exchange(baseUrl + "/active-tim/get-by-wydot-tim/" + timTypeId, HttpMethod.POST, entity, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + String url = String.format("%s/active-tim/get-by-wydot-tim/%d", baseUrl, timTypeId); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getActiveTimsByWydotTim(wydotTims, timTypeId); - // Assert + // verify Assertions.assertNotNull(data); Assertions.assertEquals(1, data.size()); - Assertions.assertEquals(aTim, data.get(0)); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); + } + + @Test + void getActiveTimsByClientIdDirection_SingleTim() { + // prepare + String clientId = "clientId"; + String direction = "westward"; + String url = String.format("%s/active-tim/client-id-direction/%s/%d/%s", baseUrl, clientId, timTypeId, + direction); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getActiveTimsByClientIdDirection(clientId, timTypeId, direction); + + // verify + mockServer.verify(); + Assertions.assertEquals(1, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); } @Test - public void getActiveTimsByClientIdDirection() { - // Arrange - setupActiveTimArrayReturn(); + void getActiveTimByClientIdDirection_MultipleTims() { + // prepare String clientId = "clientId"; String direction = "westward"; String url = String.format("%s/active-tim/client-id-direction/%s/%d/%s", baseUrl, clientId, timTypeId, direction); - when(mockRestTemplate.getForEntity(url, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}," + + "{\"activeTimId\":-2,\"direction\":\"d\",\"clientId\":\"unit_test_id2\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getActiveTimsByClientIdDirection(clientId, timTypeId, direction); - // Assert - verify(mockRestTemplate).getForEntity(url, ActiveTim[].class); - Assertions.assertEquals(Arrays.asList(aTims), data); + // verify + mockServer.verify(); + Assertions.assertEquals(2, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); + Assertions.assertEquals(-2L, data.get(1).getActiveTimId()); + } + + @Test + void getActiveTimByClientIdDirection_NoTims() { + // prepare + String clientId = "clientId"; + String direction = "westward"; + String url = String.format("%s/active-tim/client-id-direction/%s/%d/%s", baseUrl, clientId, timTypeId, + direction); + String jsonString = "[]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getActiveTimsByClientIdDirection(clientId, timTypeId, direction); + + // verify + mockServer.verify(); + Assertions.assertEquals(0, data.size()); + } + + @Test + void getActiveTimByClientIdDirection_ObjectInsteadOfArray() { + // prepare + String clientId = "clientId"; + String direction = "westward"; + String url = String.format("%s/active-tim/client-id-direction/%s/%d/%s", baseUrl, clientId, timTypeId, + direction); + String jsonString = "{\"key\": \"value\"}"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getActiveTimsByClientIdDirection(clientId, timTypeId, direction); + + // verify + mockServer.verify(); + Assertions.assertEquals(0, data.size()); + } + + @Test + void getBufferTimsByClientId_SingleTim() { + // prepare + String clientId = "clientId"; + String url = String.format("%s/active-tim/buffer-tims/%s", baseUrl, clientId); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getBufferTimsByClientId(clientId); + + // verify + mockServer.verify(); + Assertions.assertEquals(1, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); + } + + @Test + void getBufferTimsByClientId_MultipleTims() { + // prepare + String clientId = "clientId"; + String url = String.format("%s/active-tim/buffer-tims/%s", baseUrl, clientId); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}," + + "{\"activeTimId\":-2,\"direction\":\"d\",\"clientId\":\"unit_test_id2\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getBufferTimsByClientId(clientId); + + // verify + mockServer.verify(); + Assertions.assertEquals(2, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); + Assertions.assertEquals(-2L, data.get(1).getActiveTimId()); } @Test - public void getBufferTimsByClientId() { - // Arrange - setupActiveTimArrayReturn(); + void getBufferTimsByClientId_NoTims() { + // prepare String clientId = "clientId"; String url = String.format("%s/active-tim/buffer-tims/%s", baseUrl, clientId); - when(mockRestTemplate.getForEntity(url, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + String jsonString = "[]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getBufferTimsByClientId(clientId); - // Assert - verify(mockRestTemplate).getForEntity(url, ActiveTim[].class); - Assertions.assertEquals(Arrays.asList(aTims), data); + // verify + mockServer.verify(); + Assertions.assertEquals(0, data.size()); } @Test - public void getExpiredActiveTims() { - // Arrange - setupActiveTimArrayReturn(); + void getBufferTimsByClientId_ObjectInsteadOfArray() { + // prepare + String clientId = "clientId"; + String url = String.format("%s/active-tim/buffer-tims/%s", baseUrl, clientId); + String jsonString = "{\"key\": \"value\"}"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getBufferTimsByClientId(clientId); + + // verify + mockServer.verify(); + Assertions.assertEquals(0, data.size()); + } + + @Test + void getExpiredActiveTims() { + // prepare String url = String.format("%s/active-tim/expired?limit=500", baseUrl); - when(mockRestTemplate.getForEntity(url, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getExpiredActiveTims(500); - // Assert - verify(mockRestTemplate).getForEntity(url, ActiveTim[].class); + // verify + mockServer.verify(); Assertions.assertEquals(1, data.size()); - Assertions.assertEquals(Arrays.asList(aTims), data); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); } @Test - public void getActivesTimByType() { - // Arrange - setupActiveTimArrayReturn(); + void getActivesTimByType() { + // prepare String url = String.format("%s/active-tim/tim-type-id/%d", baseUrl, timTypeId); - when(mockRestTemplate.getForEntity(url, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getActivesTimByType(timTypeId); - // Assert - verify(mockRestTemplate).getForEntity(url, ActiveTim[].class); - Assertions.assertEquals(Arrays.asList(aTims), data); + // verify + mockServer.verify(); + Assertions.assertEquals(1, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); } @Test - public void getActiveRsuTim() { - // Arrange - setupActiveTimReturn(); + void getActiveRsuTim() { + // prepare String clientId = "clientId"; String direction = "westward"; String ipv4Address = "10.10.10.10"; String url = String.format("%s/active-tim/active-rsu-tim", baseUrl); ActiveRsuTimQueryModel artqm = new ActiveRsuTimQueryModel(direction, clientId, ipv4Address); - HttpEntity entity = getEntity(artqm, ActiveRsuTimQueryModel.class); - when(mockRestTemplate.exchange(url, HttpMethod.POST, entity, ActiveTim.class)).thenReturn(mockResponseEntityActiveTim); + String jsonString = "{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute ActiveTim data = uut.getActiveRsuTim(artqm); - // Assert - verify(mockRestTemplate).exchange(url, HttpMethod.POST, entity, ActiveTim.class); - Assertions.assertEquals(aTim, data); + // verify + mockServer.verify(); + Assertions.assertNotNull(data); + Assertions.assertEquals(-1L, data.getActiveTimId()); } @Test - public void getExpiringActiveTims() { - // Arrange - setupTUMReturn(); + void getExpiringActiveTims() { + // prepare String url = String.format("%s/active-tim/expiring", baseUrl); - when(mockRestTemplate.getForEntity(url, TimUpdateModel[].class)).thenReturn(mockResponseEntityTimUpdateModelArray); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List data = uut.getExpiringActiveTims(); - // Assert - verify(mockRestTemplate).getForEntity(url, TimUpdateModel[].class); + // verify + mockServer.verify(); Assertions.assertEquals(1, data.size()); - Assertions.assertEquals(Arrays.asList(tumArr), data); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); } @Test - public void getActiveTimsMissingItisCodes() throws SQLException { - // Arrange - TimUpdateModel[] tums = new TimUpdateModel[1]; - TimUpdateModel tum = new TimUpdateModel(); - tum.setTimId(1l); - tum.setDirection("both"); - tum.setRoute("I 80"); - tum.setClientId("123"); - tum.setSatRecordId("HEX"); - tum.setActiveTimId(1l); - tums[0] = tum; + void getActiveTimsMissingItisCodes() throws SQLException { + // prepare + String url = String.format("%s/active-tim/missing-itis", baseUrl); + String jsonString = "[{\"timId\":1,\"direction\":\"both\",\"route\":\"I 80\",\"clientId\":\"123\",\"satRecordId\":\"HEX\",\"activeTimId\":1}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/missing-itis", TimUpdateModel[].class)).thenReturn(mockResponseEntity); - when(mockResponseEntity.getBody()).thenReturn(tums); - // Act + // execute List ats = uut.getActiveTimsMissingItisCodes(); - // Assert + // verify + mockServer.verify(); Assertions.assertEquals(1, ats.size()); ActiveTim tim = ats.get(0); - Assertions.assertEquals(tum, tim); + Assertions.assertEquals(1, tim.getTimId()); + Assertions.assertEquals("both", tim.getDirection()); + Assertions.assertEquals("I 80", tim.getRoute()); + Assertions.assertEquals("123", tim.getClientId()); + Assertions.assertEquals("HEX", tim.getSatRecordId()); + Assertions.assertEquals(1, tim.getActiveTimId()); } @Test - public void getActiveTimsNotSent() { - // Arrange - TimUpdateModel[] tums = new TimUpdateModel[1]; - TimUpdateModel tum = new TimUpdateModel(); - tum.setTimId(1l); - tum.setDirection("both"); - tum.setRoute("I 80"); - tum.setClientId("123"); - tum.setSatRecordId("HEX"); - tum.setActiveTimId(1l); - tums[0] = tum; - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/not-sent", TimUpdateModel[].class)).thenReturn(mockResponseEntity); - when(mockResponseEntity.getBody()).thenReturn(tums); + void getActiveTimsNotSent() { + // prepare + String url = String.format("%s/active-tim/not-sent", baseUrl); + String jsonString = "[{\"timId\":1,\"direction\":\"both\",\"route\":\"I 80\",\"clientId\":\"123\",\"satRecordId\":\"HEX\",\"activeTimId\":1}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List ats = uut.getActiveTimsNotSent(); - // Assert + // verify + mockServer.verify(); Assertions.assertEquals(1, ats.size()); ActiveTim tim = ats.get(0); - Assertions.assertEquals(tum, tim); + Assertions.assertEquals(1, tim.getTimId()); + Assertions.assertEquals("both", tim.getDirection()); + Assertions.assertEquals("I 80", tim.getRoute()); + Assertions.assertEquals("123", tim.getClientId()); + Assertions.assertEquals("HEX", tim.getSatRecordId()); + Assertions.assertEquals(1, tim.getActiveTimId()); } @Test - public void getActiveTimsForSDX_success() { - // Arrange - setupActiveTimArrayReturn(); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-sdx", ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + void getActiveTimsForSDX_success() { + // prepare + String url = String.format("%s/active-tim/all-sdx", baseUrl); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List result = uut.getActiveTimsForSDX(); - // Assert - Assertions.assertEquals(aTims.length, result.size()); - Assertions.assertEquals(aTim, result.get(0)); + // verify + mockServer.verify(); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(-1L, result.get(0).getActiveTimId()); } @Test - public void getActiveTimsForSDX_throwsError() { - // Arrange - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-sdx", ActiveTim[].class)).thenThrow(new RestClientException("timeout")); + void getActiveTimsForSDX_throwsError() { + // prepare + String url = String.format("%s/active-tim/all-sdx", baseUrl); + String jsonString = "{}"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute Assertions.assertThrows(RestClientException.class, () -> { uut.getActiveTimsForSDX(); }); } @Test - public void getActiveTimsWithItisCodesWithExclusions_success() { - // Arrange - setupActiveTimArrayReturn(); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-with-itis?excludeVslAndParking=true", ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + void getActiveTimsWithItisCodesWithExclusions_success() { + // prepare + String url = String.format("%s/active-tim/all-with-itis?excludeVslAndParking=true", baseUrl); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List result = uut.getActiveTimsWithItisCodes(true); - // Assert - Assertions.assertEquals(aTims.length, result.size()); - Assertions.assertEquals(aTim, result.get(0)); + // verify + mockServer.verify(); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(-1L, result.get(0).getActiveTimId()); } @Test - public void getActiveTimsWithItisCodes_success() { - // Arrange - setupActiveTimArrayReturn(); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-with-itis?excludeVslAndParking=false", ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + void getActiveTimsWithItisCodes_success() { + // prepare + String url = String.format("%s/active-tim/all-with-itis?excludeVslAndParking=false", baseUrl); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute List result = uut.getActiveTimsWithItisCodes(false); - // Assert - Assertions.assertEquals(aTims.length, result.size()); - Assertions.assertEquals(aTim, result.get(0)); + // verify + mockServer.verify(); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(-1L, result.get(0).getActiveTimId()); } @Test - public void getActiveTimsWithItisCodes_throwsError() { - // Arrange - when(mockRestTemplate.getForEntity(anyString(), eq(ActiveTim[].class))).thenThrow(new RestClientException("timeout")); + void getActiveTimsWithItisCodes_throwsError() { + // prepare + String url = String.format("%s/active-tim/all-with-itis?excludeVslAndParking=true", baseUrl); + String jsonString = "{}"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute Assertions.assertThrows(RestClientException.class, () -> { uut.getActiveTimsWithItisCodes(true); }); } @Test - public void updateActiveTimExpiration_SUCCESS() { - // Arrange - setupBooleanReturn(); + void updateActiveTimExpiration_SUCCESS() { + // prepare String packetID = "3C8E8DF2470B1A772E"; String expDate = "2020-10-20T16:26:07.000Z"; - HttpEntity entity = getEntity(null, String.class); String url = String.format("%s/active-tim/update-expiration/%s/%s", baseUrl, packetID, expDate); - when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); + String jsonString = "true"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute Boolean data = uut.updateActiveTimExpiration(packetID, expDate); - // Assert - verify(mockRestTemplate).exchange(url, HttpMethod.PUT, entity, Boolean.class); + // verify + mockServer.verify(); Assertions.assertTrue(data, "Update failed when should have succeeded"); } @Test - public void updateActiveTimExpiration_FAIL() { - // Arrange - doReturn(false).when(mockResponseEntityBoolean).getBody(); + void updateActiveTimExpiration_FAIL() { + // prepare String packetID = "3C8E8DF2470B1A772E"; String expDate = "2020-10-20T16:26:07.000Z"; - HttpEntity entity = getEntity(null, String.class); String url = String.format("%s/active-tim/update-expiration/%s/%s", baseUrl, packetID, expDate); - when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); + String jsonString = "false"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute Boolean data = uut.updateActiveTimExpiration(packetID, expDate); - // Assert - verify(mockRestTemplate).exchange(url, HttpMethod.PUT, entity, Boolean.class); + // verify + mockServer.verify(); Assertions.assertFalse(data, "Update succeeded when should have failed"); } @Test - public void getMinExpiration_SUCCESS() { - // Arrange + void getMinExpiration_SUCCESS() { + // prepare String packetID = "3C8E8DF2470B1A772E"; String expDate = "2020-10-20T16:26:07.000Z"; String minDate = "27-OCT-20 06.21.00.000 PM"; String url = String.format("%s/active-tim/get-min-expiration/%s/%s", baseUrl, packetID, expDate); - doReturn(minDate).when(mockResponseEntityString).getBody(); - when(mockRestTemplate.getForEntity(url, String.class)).thenReturn(mockResponseEntityString); + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(minDate, org.springframework.http.MediaType.APPLICATION_JSON)); - // Act + // execute String data = uut.getMinExpiration(packetID, expDate); - // Assert - verify(mockRestTemplate).getForEntity(url, String.class); + // verify + mockServer.verify(); Assertions.assertEquals(minDate, data); } + @Test + void markForDeletion_True() { + // prepare + long activeTimId = 1L; + String url = String.format("%s/active-tim/mark-for-deletion/%d", baseUrl, activeTimId); + String jsonString = "true"; + mockServer.expect(requestTo(url)) + .andRespond( + withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + uut.markForDeletion(activeTimId); + + // verify + mockServer.verify(); + } + + @Test + void markForDeletion_False() { + // prepare + long activeTimId = 1L; + String url = String.format("%s/active-tim/mark-for-deletion/%d", baseUrl, activeTimId); + String jsonString = "false"; + mockServer.expect(requestTo(url)) + .andRespond( + withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + uut.markForDeletion(activeTimId); + + // verify + mockServer.verify(); + } + @Test void getAllRecords_SuccessfulRetrieval_ShouldReturnRecords() { // prepare - ActiveTim[] mockActiveTims = new ActiveTim[1]; - ActiveTim mockActiveTim = new ActiveTim(); - mockActiveTims[0] = mockActiveTim; - ResponseEntity mockResponseEntity = mock(ResponseEntity.class); - when(mockResponseEntity.getBody()).thenReturn(mockActiveTims); - when(mockRestTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(ActiveTim[].class))).thenReturn(mockResponseEntity); + String url = String.format("%s/active-tim/all", baseUrl); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"unit_test_id1\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + List data = uut.getAllRecords(); + + // verify + mockServer.verify(); + Assertions.assertNotNull(data); + Assertions.assertEquals(1, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); + Assertions.assertEquals("d", data.get(0).getDirection()); + Assertions.assertEquals("unit_test_id1", data.get(0).getClientId()); + } + + @Test + void getActivePlannedConditionTims_Success() { + // prepare + String url = String.format("%s/active-tim/get-active-planned-condition-tims", baseUrl); + String jsonString = "[{\"activeTimId\":-1,\"direction\":\"d\",\"clientId\":\"planned-10_trgd_10\"}]"; + mockServer.expect(requestTo(url)) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); // execute - List records = uut.getAllRecords(); + List data = uut.getActivePlannedConditionTims(); // verify - Assertions.assertEquals(mockActiveTims[0], records.get(0)); + mockServer.verify(); + Assertions.assertNotNull(data); + Assertions.assertEquals(1, data.size()); + Assertions.assertEquals(-1L, data.get(0).getActiveTimId()); + Assertions.assertEquals("d", data.get(0).getDirection()); + Assertions.assertEquals("planned-10_trgd_10", data.get(0).getClientId()); } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/DataFrameServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/DataFrameServiceTest.java index 8e01d8cd1..f81d3e8aa 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/DataFrameServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/DataFrameServiceTest.java @@ -1,8 +1,10 @@ package com.trihydro.library.service; -import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; import com.trihydro.library.model.CVRestServiceProps; @@ -11,45 +13,157 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; - -public class DataFrameServiceTest extends BaseServiceTest { +import org.mockito.Mockito; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; +class DataFrameServiceTest { @Mock - private ResponseEntity mockResponseEntityStringArray; - private String[] responseArray = new String[1]; - private String baseUrl = "baseUrl"; + RestTemplateProvider mockRestTemplateProvider; @Mock - private CVRestServiceProps mockConfig; + CVRestServiceProps mockConfig; @InjectMocks - private DataFrameService uut; + DataFrameService uut; + + String baseUrl = "http://localhost:8080"; + RestTemplate restTemplate = new RestTemplate(); + + @Mock + MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); @BeforeEach - public void setupSubTest() { - responseArray[0] = "test"; - doReturn(responseArray).when(mockResponseEntityStringArray).getBody(); - doReturn(baseUrl).when(mockConfig).getCvRestService(); + void setupSubTest() { + mockRestTemplateProvider = Mockito.mock(RestTemplateProvider.class); + mockConfig = Mockito.mock(CVRestServiceProps.class); + uut = new DataFrameService(); + uut.InjectDependencies(mockConfig, mockRestTemplateProvider); + lenient().when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(restTemplate); + when(mockConfig.getCvRestService()).thenReturn(baseUrl); + } + + /** + * Test when the controller returns one or more ITIS codes. + * Verifies that the response body contains the expected ITIS codes. + */ + @Test + void getItisCodesForDataFrameId_NonEmptyArray() { + // prepare + Integer dataFrameId = -1; + String jsonString = "[\"ITIS1\", \"ITIS2\"]"; + mockServer.expect(requestTo(baseUrl + "/data-frame/itis-for-data-frame/-1")) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + String[] data = uut.getItisCodesForDataFrameId(dataFrameId); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertEquals(2, data.length); + } + + /** + * Test when the controller returns an empty array. + * Verifies that the response body is an empty array. + */ + @Test + void getItisCodesForDataFrameId_EmptyArray() { + // prepare + Integer dataFrameId = -1; + String jsonString = "[]"; + mockServer.expect(requestTo(baseUrl + "/data-frame/itis-for-data-frame/-1")) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + String[] data = uut.getItisCodesForDataFrameId(dataFrameId); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertEquals(0, data.length); + } + + /** + * Test when the controller returns an array with a null value. + * Verifies that the response body contains the expected ITIS codes. + */ + @Test + void getItisCodesForDataFrameId_NullPresentInArray() { + // prepare + Integer dataFrameId = -1; + String jsonString = "[null, \"ITIS2\"]"; + mockServer.expect(requestTo(baseUrl + "/data-frame/itis-for-data-frame/-1")) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + String[] data = uut.getItisCodesForDataFrameId(dataFrameId); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertEquals(2, data.length); + Assertions.assertEquals("ITIS2", data[1]); + } + + /** + * Test when the controller returns a JSON string representing a non-array object. + * Verifies that the response body is an empty array. + */ + @Test + void getItisCodesForDataFrameId_ObjectInsteadOfArray() { + // prepare + Integer dataFrameId = -1; + String jsonString = "{\"key\": \"value\"}"; + mockServer.expect(requestTo(baseUrl + "/data-frame/itis-for-data-frame/-1")) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + String[] data = uut.getItisCodesForDataFrameId(dataFrameId); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertEquals(0, data.length); + } + + /** + * Test when the controller returns an empty JSON string. + * Verifies that the response body is an empty array. + */ + @Test + void getItisCodesForDataFrameId_EmptyJsonString() { + // prepare + Integer dataFrameId = -1; + String jsonString = ""; + mockServer.expect(requestTo(baseUrl + "/data-frame/itis-for-data-frame/-1")) + .andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + String[] data = uut.getItisCodesForDataFrameId(dataFrameId); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertEquals(0, data.length); } + /** + * Test when there are issues with obtaining a server connection. + * Verifies that the response body is an empty array. + */ @Test - public void getItisCodesForDataFrameId() { - // Arrange + void getItisCodesForDataFrameId_ServerConnectionIssues() { + // prepare Integer dataFrameId = -1; - String url = String.format("%s/data-frame/itis-for-data-frame/%d", baseUrl, dataFrameId); - HttpEntity entity = getEntity(null, String.class); - when(mockRestTemplate.exchange(url, HttpMethod.GET, entity, String[].class)) - .thenReturn(mockResponseEntityStringArray); + when(mockRestTemplateProvider.GetRestTemplate()).thenThrow(new RuntimeException("Server Connection Issues")); - // Act + // execute String[] data = uut.getItisCodesForDataFrameId(dataFrameId); - // Assert - verify(mockRestTemplate).exchange(url, HttpMethod.GET, entity, String[].class); - Assertions.assertEquals(1, data.length); - Assertions.assertEquals(responseArray[0], data[0]); + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + Assertions.assertEquals(0, data.length); } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/OdeServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/OdeServiceTest.java index 5bad24078..02506ee1b 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/OdeServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/OdeServiceTest.java @@ -1,33 +1,96 @@ package com.trihydro.library.service; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +import com.trihydro.library.model.WydotTravelerInputData; import java.math.BigDecimal; +import com.trihydro.library.helpers.Utility; +import com.trihydro.library.model.OdeProps; +import com.trihydro.library.model.TimQuery; +import com.trihydro.library.model.WydotRsu; + import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import org.mockito.InjectMocks; import org.mockito.Mock; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; +import org.mockito.Mockito; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; -import com.trihydro.library.helpers.Utility; -import com.trihydro.library.model.OdeProps; -import com.trihydro.library.model.TimQuery; -import com.trihydro.library.model.WydotRsu; -public class OdeServiceTest extends BaseServiceTest { +class OdeServiceTest { @Mock Utility mockUtility; @Mock OdeProps mockOdeProps; + @Mock + RestTemplateProvider mockRestTemplateProvider; + @InjectMocks - private OdeService uut; + OdeService uut; + + String baseUrl = "http://localhost:8080"; + RestTemplate restTemplate = new RestTemplate(); + + @Mock + MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); + + @BeforeEach + void setupSubTest() { + mockRestTemplateProvider = Mockito.mock(RestTemplateProvider.class); + mockOdeProps = Mockito.mock(OdeProps.class); + mockUtility = Mockito.mock(Utility.class); + uut = new OdeService(); + uut.InjectDependencies(mockUtility, mockRestTemplateProvider, mockOdeProps); + lenient().when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(restTemplate); + when(mockOdeProps.getOdeUrl()).thenReturn(baseUrl); + lenient().when(mockRestTemplateProvider.GetRestTemplate_NoErrors()).thenReturn(restTemplate); + } + + @Test + void updateTimOnSdw_Success() { + // prepare + WydotTravelerInputData timToSend = new WydotTravelerInputData(); + String url = baseUrl + "/tim"; + String jsonString = "{\"status\":\"success\"}"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + String exceptionMessage = uut.updateTimOnSdw(timToSend); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate_NoErrors(); + mockServer.verify(); + Assertions.assertEquals("", exceptionMessage); + } + + @Test + void updateTimOnSdw_Failure_ServerError() { + // prepare + WydotTravelerInputData timToSend = new WydotTravelerInputData(); + String url = baseUrl + "/tim"; + mockServer.expect(requestTo(url)).andRespond(withServerError()); + + // execute + String exceptionMessage = uut.updateTimOnSdw(timToSend); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate_NoErrors(); + mockServer.verify(); + Assertions.assertEquals("An exception occurred while updating TIM on SDX", exceptionMessage); + } @Test - public void submitTimQuery_wydotRsu_success() { - // Arrange + void submitTimQuery_wydotRsu_NoIndicies() { + // prepare WydotRsu rsu = new WydotRsu(); rsu.setRsuId(-1); rsu.setRsuTarget("10.10.10.10"); @@ -35,16 +98,92 @@ public void submitTimQuery_wydotRsu_success() { rsu.setLongitude(new BigDecimal(-104.000000)); rsu.setRoute("I 80"); rsu.setMilepost(10d); - doReturn("url").when(mockOdeProps).getOdeUrl(); + when(mockOdeProps.getOdeUrl()).thenReturn(baseUrl); + String url = baseUrl + "/tim/query"; + String jsonString = "{\"indicies_set\":\"[]\"}"; + mockServer.expect(requestTo(url)).andRespond(withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); - doReturn("{\"indicies_set\":\"[]\"}").when(mockRestTemplate).postForObject(anyString(), any(), any()); - - // Act + // execute TimQuery timQuery = uut.submitTimQuery(rsu, 1); - // Assert - verify(mockRestTemplate).postForObject(anyString(), any(), any()); + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertNotNull(timQuery); Assertions.assertEquals(0, timQuery.getIndicies_set().size()); } -} \ No newline at end of file + @Test + void submitTimQuery_wydotRsu_SomeIndicies() { + // prepare + WydotRsu rsu = new WydotRsu(); + rsu.setRsuId(-1); + rsu.setRsuTarget("10.10.10.10"); + rsu.setLatitude(new BigDecimal(41.0000)); + rsu.setLongitude(new BigDecimal(-104.000000)); + rsu.setRoute("I 80"); + rsu.setMilepost(10d); + when(mockOdeProps.getOdeUrl()).thenReturn(baseUrl); + String url = baseUrl + "/tim/query"; + String jsonString = "{\"indicies_set\":\"[1, 2, 3]\"}"; + mockServer.expect(requestTo(url)).andRespond( + withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + TimQuery timQuery = uut.submitTimQuery(rsu, 1); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertNotNull(timQuery); + Assertions.assertEquals(3, timQuery.getIndicies_set().size()); + } + + @Test + void submitTimQuery_wydotRsu_ArrayInsteadOfObject() { + // prepare + WydotRsu rsu = new WydotRsu(); + rsu.setRsuId(-1); + rsu.setRsuTarget("10.10.10.10"); + rsu.setLatitude(new BigDecimal(41.0000)); + rsu.setLongitude(new BigDecimal(-104.000000)); + rsu.setRoute("I 80"); + rsu.setMilepost(10d); + when(mockOdeProps.getOdeUrl()).thenReturn(baseUrl); + String url = baseUrl + "/tim/query"; + String jsonString = "[1, 2, 3]"; + mockServer.expect(requestTo(url)).andRespond( + withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + TimQuery timQuery = uut.submitTimQuery(rsu, 1); + + // verify + verify(mockRestTemplateProvider).GetRestTemplate(); + mockServer.verify(); + Assertions.assertNotNull(timQuery); + Assertions.assertEquals(3, timQuery.getIndicies_set().size()); + } + + @Test + void submitTimQuery_wydotRsu_EmptyString() { + // prepare + WydotRsu rsu = new WydotRsu(); + rsu.setRsuId(-1); + rsu.setRsuTarget("10.10.10.10"); + rsu.setLatitude(new BigDecimal(41.0000)); + rsu.setLongitude(new BigDecimal(-104.000000)); + rsu.setRoute("I 80"); + rsu.setMilepost(10d); + when(mockOdeProps.getOdeUrl()).thenReturn(baseUrl); + String url = baseUrl + "/tim/query"; + String jsonString = ""; + mockServer.expect(requestTo(url)).andRespond( + withSuccess(jsonString, org.springframework.http.MediaType.APPLICATION_JSON)); + + // execute + Assertions.assertThrows(NullPointerException.class, () -> { + uut.submitTimQuery(rsu, 1); + }); + } +} diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/SdwServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/SdwServiceTest.java index 7bcf1d9ce..cdc4437ee 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/SdwServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/SdwServiceTest.java @@ -1,23 +1,10 @@ package com.trihydro.library.service; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; - -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.AdvisorySituationDataDeposit; import com.trihydro.library.model.SDXDecodeRequest; import com.trihydro.library.model.SDXDecodeResponse; import com.trihydro.library.model.SdwProps; import com.trihydro.library.model.SemiDialogID; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -32,13 +19,21 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClientException; -public class SdwServiceTest extends BaseServiceTest { +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; - @Mock - SdwProps mockConfig; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class SdwServiceTest extends BaseServiceTest { @Mock - Utility mockUtility; + SdwProps mockConfig; @Mock ResponseEntity mockAsddResponse; @@ -47,56 +42,51 @@ public class SdwServiceTest extends BaseServiceTest { ResponseEntity mockDecodeResponse; @Mock - private ResponseEntity mockRespAdvisorySituationDataDeposit; + ResponseEntity mockRespAdvisorySituationDataDeposit; @Mock - private ResponseEntity> mockRespHashMap; + ResponseEntity> mockRespHashMap; @InjectMocks SdwService sdwService; - private String baseUrl = "http://localhost:12230"; - private String apiKey = "apiKey"; + String baseUrl = "http://localhost:12230"; + String apiKey = "apiKey"; - private void setupConfig() { + void setupConfig() { when(mockConfig.getSdwRestUrl()).thenReturn(baseUrl); setupApiKey(); } - private void setupApiKey() { + void setupApiKey() { when(mockConfig.getSdwApiKey()).thenReturn(apiKey); } @Test - public void deleteSdxDataBySatRecordId_nullRecordIds() { + void deleteSdxDataBySatRecordId_nullRecordIds() { setupApiKey(); HashMap results = sdwService.deleteSdxDataBySatRecordId(null); - verify(mockUtility) - .logWithDate("Attempting to delete satellite records failed due to no satRecordIds passed in"); Assertions.assertNull(results); } @Test - public void deleteSdxDataBySatRecordId_emptyRecordIds() { + void deleteSdxDataBySatRecordId_emptyRecordIds() { setupApiKey(); HashMap results = sdwService.deleteSdxDataBySatRecordId(new ArrayList()); - verify(mockUtility) - .logWithDate("Attempting to delete satellite records failed due to no satRecordIds passed in"); Assertions.assertNull(results); } @Test - public void deleteSdxDataBySatRecordId_nullApiKey() { + void deleteSdxDataBySatRecordId_nullApiKey() { List satNames = new ArrayList(); satNames.add("A9184436"); when(mockConfig.getSdwApiKey()).thenReturn(null); HashMap results = sdwService.deleteSdxDataBySatRecordId(satNames); - verify(mockUtility).logWithDate("Attempting to delete satellite records failed due to null apiKey"); Assertions.assertNull(results); } @Test - public void deleteSdxDataBySatRecordId_success() throws IOException, Exception { + void deleteSdxDataBySatRecordId_success() throws IOException, Exception { // Arrange setupConfig(); List satNames = new ArrayList(); @@ -125,7 +115,7 @@ public void deleteSdxDataBySatRecordId_success() throws IOException, Exception { } @Test - public void deleteSdxDataBySatRecordId_handlesHttpClientErrorException() { + void deleteSdxDataBySatRecordId_handlesHttpClientErrorException() { // Arrange setupConfig(); List satRecordIds = new ArrayList(); @@ -149,12 +139,10 @@ public void deleteSdxDataBySatRecordId_handlesHttpClientErrorException() { // Assert Assertions.assertNull(results); - verify(mockUtility).logWithDate("An exception occurred while attempting to delete satellite records: 400 something went wrong..."); - verify(mockUtility).logWithDate("Is the SDX API key valid?"); } @Test - public void deleteSdxDataByRecordIdIntegers_handlesHttpClientErrorException() { + void deleteSdxDataByRecordIdIntegers_handlesHttpClientErrorException() { // Arrange setupConfig(); List recordIds = new ArrayList(); @@ -177,12 +165,10 @@ public void deleteSdxDataByRecordIdIntegers_handlesHttpClientErrorException() { // Assert Assertions.assertNull(results); - verify(mockUtility).logWithDate("An exception occurred while attempting to delete satellite records: 400 something went wrong..."); - verify(mockUtility).logWithDate("Is the SDX API key valid?"); } @Test - public void getItisCodesFromAdvisoryMessage_success() { + void getItisCodesFromAdvisoryMessage_success() throws SdwService.SdwServiceException { // Arrange setupConfig(); @@ -210,7 +196,7 @@ public void getItisCodesFromAdvisoryMessage_success() { } @Test - public void getItisCodesFromAdvisoryMessage_realData() { + void getItisCodesFromAdvisoryMessage_realData() throws SdwService.SdwServiceException { // Arrange setupConfig(); @@ -234,31 +220,31 @@ public void getItisCodesFromAdvisoryMessage_realData() { } @Test - public void getItisCodesFromAdvisoryMessage_nullAdvisoryMessage() { + void getItisCodesFromAdvisoryMessage_nullAdvisoryMessage() { // Arrange // Act - var exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { + var exception = Assertions.assertThrows(SdwService.SdwServiceException.class, () -> { sdwService.getItisCodesFromAdvisoryMessage(null); }); - Assertions.assertEquals("advisoryMessage cannot be null", exception.getMessage()); + Assertions.assertEquals("Null advisory message provided", exception.getMessage()); } @Test - public void getItisCodesFromAdvisoryMessage_NoMessageFrame() { + void getItisCodesFromAdvisoryMessage_NoMessageFrame() { // Arrange // Act - var exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { + var exception = Assertions.assertThrows(SdwService.SdwServiceException.class, () -> { sdwService.getItisCodesFromAdvisoryMessage("00000000"); }); - Assertions.assertEquals("Cannot determine start of MessageFrame", exception.getMessage()); + Assertions.assertEquals("Invalid message format - missing MessageFrame marker", exception.getMessage()); } @Test - public void getItisCodesFromAdvisoryMessage_handlesNumberFormatException() { + void getItisCodesFromAdvisoryMessage_handlesNumberFormatException() throws SdwService.SdwServiceException { // Arrange setupConfig(); @@ -285,7 +271,7 @@ public void getItisCodesFromAdvisoryMessage_handlesNumberFormatException() { } @Test - public void getItisCodesFromAdvisoryMessage_handlesRestClientException() { + void getItisCodesFromAdvisoryMessage_handlesRestClientException() { // Arrange setupConfig(); @@ -301,17 +287,14 @@ public void getItisCodesFromAdvisoryMessage_handlesRestClientException() { when(mockRestTemplate.exchange(eq(url), eq(HttpMethod.POST), isA(HttpEntity.class), eq(SDXDecodeResponse.class))).thenThrow(new RestClientException("something went wrong...")); - // Act - List result = sdwService.getItisCodesFromAdvisoryMessage("00000000000000001F"); - - // Assert - Assertions.assertNull(result); - verify(mockUtility).logWithDate("An exception occurred while attempting to decode message: something went wrong..."); - verify(mockUtility).logWithDate("Is the SDX API key valid?"); + // Act & Assert + Assertions.assertThrows(SdwService.SdwServiceException.class, () -> { + sdwService.getItisCodesFromAdvisoryMessage("00000000000000001F"); + }); } @Test - public void getMsgsForOdeUser_success() { + void getMsgsForOdeUser_success() throws SdwService.SdwServiceException { // Arrange setupConfig(); AdvisorySituationDataDeposit[] response = new AdvisorySituationDataDeposit[] { @@ -332,7 +315,7 @@ public void getMsgsForOdeUser_success() { } @Test - public void getMsgsForOdeUser_handlesRestClientException() { + void getMsgsForOdeUser_handlesRestClientException() { // Arrange setupConfig(); String url = "http://localhost:12230/api/deposited-by-me/156"; @@ -340,12 +323,9 @@ public void getMsgsForOdeUser_handlesRestClientException() { eq(AdvisorySituationDataDeposit[].class))) .thenThrow(new RestClientException("something went wrong...")); - // Act - List results = sdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep); - - // Assert - Assertions.assertNull(results); - verify(mockUtility).logWithDate("An exception occurred while attempting to get messages from SDX: something went wrong..."); - verify(mockUtility).logWithDate("Is the SDX API key valid?"); + // Act & Assert + Assertions.assertThrows(SdwService.SdwServiceException.class, () -> { + sdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep); + }); } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java index e14436a2f..216da578b 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; @@ -28,13 +27,16 @@ import javax.mail.MessagingException; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -70,6 +72,7 @@ import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; @ExtendWith(MockitoExtension.class) +@Slf4j public class WydotTimServiceTest { @Mock @@ -382,30 +385,6 @@ public void getAllMilepostsForTim_EndPointNull() { verify(mockMilepostService).getMilepostsByPointWithBuffer(any()); } - @Test - public void sendTimToSDW_MultipleActiveSatTimsFound() throws IOException { - // Arrange - List activeSatTims = getActiveTims(true); - when(mockActiveTimService.getActiveTimsByClientIdDirection(any(), any(), any())).thenReturn(activeSatTims); - WydotTim wydotTim = getMockWydotTim(); - WydotTravelerInputData timToSend = getMockWydotTravelerInputDataWithServiceRequest(); - String regionNamePrev = "regionNamePrev"; - TimType timType = new TimType(); - - Coordinate endPoint = new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2)); - List reducedMileposts = getMockMileposts(); - WydotOdeTravelerInformationMessage mockExistingSatTim = getMockWydotOdeTravelerInformationMessage(); - when(mockTimService.getTim(any())).thenReturn(mockExistingSatTim); - RestTemplate mockRestTemplate = new RestTemplate(); - when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(mockRestTemplate); - - // Act - uut.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, 0, endPoint, reducedMileposts); - - // Assert - verify(mockUtility).logWithDate("Multiple active SAT TIMs found for client testclientid and direction D. Expected zero or one. Using the first one found."); - } - @Test public void sendTimToSDW_NoActiveSatTimsFound() throws IOException { // Arrange @@ -419,12 +398,11 @@ public void sendTimToSDW_NoActiveSatTimsFound() throws IOException { when(mockSdwService.getNewRecordId()).thenReturn("newRecordId"); // Act - uut.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, 0, endPoint, reducedMileposts); + uut.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, 0, endPoint, reducedMileposts, null); // Assert verify(mockSdwService).getNewRecordId(); verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); - verify(mockUtility, never()).logWithDate("Multiple active SAT TIMs found for client testclientid and direction D. Expected zero or one. Using the first one found."); } @Test @@ -442,33 +420,12 @@ public void sendTimToSDW_SingleActiveSatTimFound() throws IOException { when(mockTimService.getTim(any())).thenReturn(mockExistingSatTim); // Act - uut.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, 0, endPoint, reducedMileposts); + uut.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, 0, endPoint, reducedMileposts, null); // Assert verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); verify(mockActiveTimService).resetActiveTimsExpirationDate(any()); verify(mockTimService).getTim(any()); - verify(mockUtility, never()).logWithDate("Multiple active SAT TIMs found for client testclientid and direction D. Expected zero or one. Using the first one found."); - } - - @Test - public void sendTimToRsus_NoRsusFound() throws IOException { - // Arrange - when(mockRsuService.getRsusByLatLong(any(), any(), any(), any())).thenReturn(new ArrayList<>()); - - WydotTim wydotTim = getMockWydotTim(); - WydotTravelerInputData timToSend = getMockWydotTravelerInputDataWithServiceRequest(); - String regionNamePrev = "regionNamePrev"; - TimType timType = new TimType(); - String endDateTime = "2024-08-29 14:45:30"; - Coordinate endPoint = new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2)); - - - // Act - uut.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, 0, endDateTime, endPoint); - - // Assert - verify(mockUtility).logWithDate("No RSUs found to place TIM on, returning"); } @Test @@ -496,7 +453,7 @@ public void sendTimToRsus_RsusFound_NoActiveTIMs() throws IOException { Coordinate endPoint = new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2)); // Act - uut.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, 0, endDateTime, endPoint); + uut.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, 0, endDateTime, endPoint, null); // Assert verify(mockOdeService).sendNewTimToRsu(any()); @@ -541,10 +498,9 @@ public void sendTimToRsus_RsusFound_ActiveTIMsExist() throws IOException { )).thenReturn(mockResponse); // Act - uut.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, 0, endDateTime, endPoint); + uut.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, 0, endDateTime, endPoint, null); // Assert - verify(mockUtility).logWithDate("Deleting TIM on index " + rsu.getRsuIndex() + " from rsu " + rsu.getRsuTarget()); verify(mockOdeService).sendNewTimToRsu(any()); } diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/tables/TimDbTablesTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/tables/TimDbTablesTest.java new file mode 100644 index 000000000..fd209b04d --- /dev/null +++ b/cv-data-service-library/src/test/java/com/trihydro/library/tables/TimDbTablesTest.java @@ -0,0 +1,358 @@ +package com.trihydro.library.tables; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class TimDbTablesTest { + + private TimDbTables timDbTables; + + @BeforeEach + void setUp() { + timDbTables = new TimDbTables(); + } + + @Test + void testGetTimTable() { + // Act + List timTable = timDbTables.getTimTable(); + + // Assert + assertNotNull(timTable); + assertEquals(25, timTable.size()); + assertTrue(timTable.contains("MSG_CNT")); + assertTrue(timTable.contains("PACKET_ID")); + assertTrue(timTable.contains("URL_B")); + assertTrue(timTable.contains("TIME_STAMP")); + assertTrue(timTable.contains("RECORD_GENERATED_BY")); + assertTrue(timTable.contains("RMD_LD_ELEVATION")); + assertTrue(timTable.contains("RMD_LD_HEADING")); + assertTrue(timTable.contains("RMD_LD_LATITUDE")); + assertTrue(timTable.contains("RMD_LD_LONGITUDE")); + assertTrue(timTable.contains("RMD_LD_SPEED")); + assertTrue(timTable.contains("RMD_RX_SOURCE")); + assertTrue(timTable.contains("SCHEMA_VERSION")); + assertTrue(timTable.contains("SECURITY_RESULT_CODE")); + assertTrue(timTable.contains("LOG_FILE_NAME")); + assertTrue(timTable.contains("RECORD_GENERATED_AT")); + assertTrue(timTable.contains("SANITIZED")); + assertTrue(timTable.contains("SERIAL_ID_STREAM_ID")); + assertTrue(timTable.contains("SERIAL_ID_BUNDLE_SIZE")); + assertTrue(timTable.contains("SERIAL_ID_BUNDLE_ID")); + assertTrue(timTable.contains("SERIAL_ID_RECORD_ID")); + assertTrue(timTable.contains("SERIAL_ID_SERIAL_NUMBER")); + assertTrue(timTable.contains("PAYLOAD_TYPE")); + assertTrue(timTable.contains("RECORD_TYPE")); + assertTrue(timTable.contains("ODE_RECEIVED_AT")); + assertTrue(timTable.contains("SAT_RECORD_ID")); + } + + @Test + void testGetDataFrameTable() { + // Act + List dataFrameTable = timDbTables.getDataFrameTable(); + + // Assert + assertNotNull(dataFrameTable); + assertEquals(11, dataFrameTable.size()); + assertTrue(dataFrameTable.contains("TIM_ID")); + assertTrue(dataFrameTable.contains("SSP_TIM_RIGHTS")); + assertTrue(dataFrameTable.contains("FRAME_TYPE")); + assertTrue(dataFrameTable.contains("DURATION_TIME")); + assertTrue(dataFrameTable.contains("PRIORITY")); + assertTrue(dataFrameTable.contains("SSP_LOCATION_RIGHTS")); + assertTrue(dataFrameTable.contains("SSP_MSG_TYPES")); + assertTrue(dataFrameTable.contains("SSP_MSG_CONTENT")); + assertTrue(dataFrameTable.contains("CONTENT")); + assertTrue(dataFrameTable.contains("URL")); + assertTrue(dataFrameTable.contains("START_DATE_TIME")); + } + + @Test + void testGetPathTable() { + // Act + List pathTable = timDbTables.getPathTable(); + + // Assert + assertNotNull(pathTable); + assertEquals(1, pathTable.size()); + assertTrue(pathTable.contains("SCALE")); + } + + @Test + void testGetRegionTable() { + // Act + List regionTable = timDbTables.getRegionTable(); + + // Assert + assertNotNull(regionTable); + assertEquals(17, regionTable.size()); + assertTrue(regionTable.contains("DATA_FRAME_ID")); + assertTrue(regionTable.contains("NAME")); + assertTrue(regionTable.contains("LANE_WIDTH")); + assertTrue(regionTable.contains("DIRECTIONALITY")); + assertTrue(regionTable.contains("DIRECTION")); + assertTrue(regionTable.contains("CLOSED_PATH")); + assertTrue(regionTable.contains("ANCHOR_LAT")); + assertTrue(regionTable.contains("ANCHOR_LONG")); + assertTrue(regionTable.contains("PATH_ID")); + assertTrue(regionTable.contains("GEOMETRY_DIRECTION")); + assertTrue(regionTable.contains("GEOMETRY_EXTENT")); + assertTrue(regionTable.contains("GEOMETRY_LANE_WIDTH")); + assertTrue(regionTable.contains("GEOMETRY_CIRCLE_POSITION_LAT")); + assertTrue(regionTable.contains("GEOMETRY_CIRCLE_POSITION_LONG")); + assertTrue(regionTable.contains("GEOMETRY_CIRCLE_POSITION_ELEV")); + assertTrue(regionTable.contains("GEOMETRY_CIRCLE_RADIUS")); + assertTrue(regionTable.contains("GEOMETRY_CIRCLE_UNITS")); + } + + @Test + void testGetPathNodeXYTable() { + // Act + List pathNodeXYTable = timDbTables.getPathNodeXYTable(); + + // Assert + assertNotNull(pathNodeXYTable); + assertEquals(2, pathNodeXYTable.size()); + assertTrue(pathNodeXYTable.contains("NODE_XY_ID")); + assertTrue(pathNodeXYTable.contains("PATH_ID")); + } + + @Test + void testGetPathNodeLLTable() { + // Act + List pathNodeLLTable = timDbTables.getPathNodeLLTable(); + + // Assert + assertNotNull(pathNodeLLTable); + assertEquals(2, pathNodeLLTable.size()); + assertTrue(pathNodeLLTable.contains("NODE_LL_ID")); + assertTrue(pathNodeLLTable.contains("PATH_ID")); + } + + @Test + void testGetNodeXYTable() { + // Act + List nodeXYTable = timDbTables.getNodeXYTable(); + + // Assert + assertNotNull(nodeXYTable); + assertEquals(7, nodeXYTable.size()); + assertTrue(nodeXYTable.contains("DELTA")); + assertTrue(nodeXYTable.contains("NODE_LAT")); + assertTrue(nodeXYTable.contains("NODE_LONG")); + assertTrue(nodeXYTable.contains("X")); + assertTrue(nodeXYTable.contains("Y")); + assertTrue(nodeXYTable.contains("ATTRIBUTES_DWIDTH")); + assertTrue(nodeXYTable.contains("ATTRIBUTES_DELEVATION")); + } + + @Test + void testGetNodeLLTable() { + // Act + List nodeLLTable = timDbTables.getNodeLLTable(); + + // Assert + assertNotNull(nodeLLTable); + assertEquals(7, nodeLLTable.size()); + assertTrue(nodeLLTable.contains("DELTA")); + assertTrue(nodeLLTable.contains("NODE_LAT")); + assertTrue(nodeLLTable.contains("NODE_LONG")); + assertTrue(nodeLLTable.contains("X")); + assertTrue(nodeLLTable.contains("Y")); + assertTrue(nodeLLTable.contains("ATTRIBUTES_DWIDTH")); + assertTrue(nodeLLTable.contains("ATTRIBUTES_DELEVATION")); + } + + @Test + void testGetTimTypeTable() { + // Act + List timTypeTable = timDbTables.getTimTypeTable(); + + // Assert + assertNotNull(timTypeTable); + assertEquals(2, timTypeTable.size()); + assertTrue(timTypeTable.contains("TYPE")); + assertTrue(timTypeTable.contains("DESCRIPTION")); + } + + @Test + void testGetActiveTimTable() { + // Act + List activeTimTable = timDbTables.getActiveTimTable(); + + // Assert + assertNotNull(activeTimTable); + assertEquals(15, activeTimTable.size()); + assertTrue(activeTimTable.contains("TIM_ID")); + assertTrue(activeTimTable.contains("DIRECTION")); + assertTrue(activeTimTable.contains("TIM_START")); + assertTrue(activeTimTable.contains("TIM_END")); + assertTrue(activeTimTable.contains("TIM_TYPE_ID")); + assertTrue(activeTimTable.contains("ROUTE")); + assertTrue(activeTimTable.contains("CLIENT_ID")); + assertTrue(activeTimTable.contains("SAT_RECORD_ID")); + assertTrue(activeTimTable.contains("PK")); + assertTrue(activeTimTable.contains("START_LATITUDE")); + assertTrue(activeTimTable.contains("START_LONGITUDE")); + assertTrue(activeTimTable.contains("END_LATITUDE")); + assertTrue(activeTimTable.contains("END_LONGITUDE")); + assertTrue(activeTimTable.contains("EXPIRATION_DATE")); + assertTrue(activeTimTable.contains("PROJECT_KEY")); + } + + @Test + void testGetActiveTimHoldingTable() { + // Act + List activeTimHoldingTable = timDbTables.getActiveTimHoldingTable(); + + // Assert + assertNotNull(activeTimHoldingTable); + assertEquals(15, activeTimHoldingTable.size()); + assertTrue(activeTimHoldingTable.contains("ACTIVE_TIM_HOLDING_ID")); + assertTrue(activeTimHoldingTable.contains("CLIENT_ID")); + assertTrue(activeTimHoldingTable.contains("DIRECTION")); + assertTrue(activeTimHoldingTable.contains("RSU_TARGET")); + assertTrue(activeTimHoldingTable.contains("SAT_RECORD_ID")); + assertTrue(activeTimHoldingTable.contains("START_LATITUDE")); + assertTrue(activeTimHoldingTable.contains("START_LONGITUDE")); + assertTrue(activeTimHoldingTable.contains("END_LATITUDE")); + assertTrue(activeTimHoldingTable.contains("END_LONGITUDE")); + assertTrue(activeTimHoldingTable.contains("RSU_INDEX")); + assertTrue(activeTimHoldingTable.contains("DATE_CREATED")); + assertTrue(activeTimHoldingTable.contains("PROJECT_KEY")); + assertTrue(activeTimHoldingTable.contains("EXPIRATION_DATE")); + assertTrue(activeTimHoldingTable.contains("PACKET_ID")); + assertTrue(activeTimHoldingTable.contains("TIM_END")); + } + + @Test + void testGetTimRsuTable() { + // Act + List timRsuTable = timDbTables.getTimRsuTable(); + + // Assert + assertNotNull(timRsuTable); + assertEquals(3, timRsuTable.size()); + assertTrue(timRsuTable.contains("TIM_ID")); + assertTrue(timRsuTable.contains("RSU_ID")); + assertTrue(timRsuTable.contains("RSU_INDEX")); + } + + @Test + void testGetDataFrameItisCodeTable() { + // Act + List dataFrameItisCodeTable = timDbTables.getDataFrameItisCodeTable(); + + // Assert + assertNotNull(dataFrameItisCodeTable); + assertEquals(4, dataFrameItisCodeTable.size()); + assertTrue(dataFrameItisCodeTable.contains("ITIS_CODE_ID")); + assertTrue(dataFrameItisCodeTable.contains("DATA_FRAME_ID")); + assertTrue(dataFrameItisCodeTable.contains("TEXT")); + assertTrue(dataFrameItisCodeTable.contains("POSITION")); + } + + @Test + public void testGetActiveTimHoldingTableThreadSafety() throws InterruptedException { + // Number of threads to use in the test + int numThreads = 10; + + // Create a countdown latch to synchronize thread starts + CountDownLatch startLatch = new CountDownLatch(1); + + // Create a countdown latch to wait for all threads to finish + CountDownLatch finishLatch = new CountDownLatch(numThreads); + + // Create an executor service with a fixed thread pool + ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + + // Create an atomic reference to store any exception that might occur + AtomicReference exceptionRef = new AtomicReference<>(); + + // Lists obtained by each thread + List[] obtainedLists = new List[numThreads]; + + // Submit tasks to the executor service + for (int i = 0; i < numThreads; i++) { + final int threadIndex = i; + executorService.submit(() -> { + try { + // Wait for the start signal + startLatch.await(); + + // Get the list + obtainedLists[threadIndex] = timDbTables.getActiveTimHoldingTable(); + + // Check for duplicate columns within the list + Set seenColumns = new HashSet<>(); + for (String column : obtainedLists[threadIndex]) { + if (!seenColumns.add(column.toLowerCase())) { + throw new RuntimeException("Thread " + threadIndex + + " found duplicate column: " + column); + } + } + } catch (Exception e) { + exceptionRef.set(e); + } finally { + // Count down the finish latch + finishLatch.countDown(); + } + }); + } + + // Start all threads simultaneously + startLatch.countDown(); + + // Wait for all threads to finish + boolean allThreadsFinished = finishLatch.await(10, TimeUnit.SECONDS); + + // Shutdown the executor service + executorService.shutdown(); + + // Assert that all threads finished + assertTrue(allThreadsFinished, "Not all threads finished in time"); + + // Check if any thread encountered an exception + if (exceptionRef.get() != null) { + fail("Thread encountered an exception: " + exceptionRef.get().getMessage()); + } + + // Check that all threads got the same list reference (singleton behavior) + for (int i = 1; i < numThreads; i++) { + assertSame(obtainedLists[0], obtainedLists[i], + "Thread " + i + " got a different list instance than thread 0"); + } + + // Check that the list obtained by the first thread has no duplicates + Set uniqueColumns = new HashSet<>(); + for (String column : obtainedLists[0]) { + boolean added = uniqueColumns.add(column.toLowerCase()); + assertTrue(added, "Found duplicate column in the final list: " + column); + } + + // Verify the list size matches the expected number of unique columns + assertEquals(uniqueColumns.size(), obtainedLists[0].size(), + "List size does not match the expected number of unique columns"); + + // Additional test: try to modify the list and see if it affects other threads + try { + obtainedLists[0].add("TEST_COLUMN"); + fail("List should be unmodifiable or thread-safe"); + } catch (UnsupportedOperationException e) { + // This is actually good - it means the list is immutable + } catch (Exception e) { + // Any other exception is unexpected + fail("Unexpected exception when trying to modify the list: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/cv-data-tasks/.gitignore b/cv-data-tasks/.gitignore index 380c34c3f..4f322b844 100644 --- a/cv-data-tasks/.gitignore +++ b/cv-data-tasks/.gitignore @@ -1,2 +1,3 @@ *.jar -*.cer \ No newline at end of file +*.cer +*.crt \ No newline at end of file diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java index a4a8a8e6c..d2753f466 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java @@ -1,15 +1,7 @@ package com.trihydro.tasks; -import com.trihydro.tasks.actions.CleanupStaleActiveTimHoldingRecords; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.io.IOException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.annotation.PostConstruct; - import com.trihydro.library.helpers.CreateBaseTimUtil; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.GsonFactory; @@ -38,6 +30,7 @@ import com.trihydro.library.service.TmddService; import com.trihydro.library.service.WydotTimService; import com.trihydro.tasks.actions.CleanupActiveTims; +import com.trihydro.tasks.actions.CleanupStaleActiveTimHoldingRecords; import com.trihydro.tasks.actions.RemoveExpiredActiveTims; import com.trihydro.tasks.actions.RetentionPolicyEnforcement; import com.trihydro.tasks.actions.ValidateRsus; @@ -45,13 +38,18 @@ import com.trihydro.tasks.actions.ValidateTmdd; import com.trihydro.tasks.actions.VerifyHSMFunctional; import com.trihydro.tasks.config.DataTasksConfiguration; - import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Import; +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + @SpringBootApplication @Import({ SdwService.class, Utility.class, EmailHelper.class, JavaMailSenderImplProvider.class, ActiveTimService.class, ItisCodeService.class, RsuDataService.class, RestTemplateProvider.class, diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupActiveTims.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupActiveTims.java index c64d53ee5..eff83e606 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupActiveTims.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupActiveTims.java @@ -4,58 +4,58 @@ import java.util.List; import com.google.gson.Gson; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.service.ActiveTimService; import com.trihydro.library.service.RestTemplateProvider; import com.trihydro.tasks.config.DataTasksConfiguration; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Component +@Slf4j public class CleanupActiveTims implements Runnable { private DataTasksConfiguration configuration; - private Utility utility; private ActiveTimService activeTimService; private RestTemplateProvider restTemplateProvider; @Autowired - public void InjectDependencies(DataTasksConfiguration _configuration, Utility _utility, - ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider) { - configuration = _configuration; - utility = _utility; - activeTimService = _activeTimService; - restTemplateProvider = _restTemplateProvider; + public void InjectDependencies(DataTasksConfiguration configuration, ActiveTimService activeTimService, + RestTemplateProvider restTemplateProvider) { + this.configuration = configuration; + this.activeTimService = activeTimService; + this.restTemplateProvider = restTemplateProvider; } public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("Running..."); try { - List activeTims = new ArrayList(); + List activeTims = new ArrayList<>(); List tmp = null; // select active tims missing ITIS codes tmp = activeTimService.getActiveTimsMissingItisCodes(); - if (tmp.size() > 0) { - utility.logWithDate("Found " + tmp.size() + " Active TIMs missing ITIS Codes", this.getClass()); + if (!tmp.isEmpty()) { + log.info("Found {} Active TIMs missing ITIS Codes", tmp.size()); activeTims.addAll(tmp); } // add active tims that weren't sent to the SDX or any RSUs tmp = activeTimService.getActiveTimsNotSent(); - if (tmp.size() > 0) { - utility.logWithDate("Found " + tmp.size() + " Active TIMs that weren't distributed", this.getClass()); + if (!tmp.isEmpty()) { + log.info("Found {} Active TIMs that weren't sent to the SDX or any RSUs", tmp.size()); activeTims.addAll(tmp); } - if (activeTims.size() == 0) { - utility.logWithDate("Found 0 Active TIMs", this.getClass()); + if (activeTims.isEmpty()) { + log.info("No Active TIMs to cleanup"); } // delete from rsus and the SDX @@ -67,18 +67,30 @@ public void run() { // send to tim type endpoint to delete from RSUs and SDWs for (ActiveTim activeTim : activeTims) { + try { + activeTimJson = gson.toJson(activeTim); + entity = new HttpEntity(activeTimJson, headers); - activeTimJson = gson.toJson(activeTim); - entity = new HttpEntity(activeTimJson, headers); + log.info("CleanupActiveTims - Deleting ActiveTim: { activeTimId: {}, clientId: {} }", activeTim.getActiveTimId(), + activeTim.getClientId()); + ResponseEntity response = restTemplateProvider.GetRestTemplate() + .exchange(configuration.getWrapperUrl() + "/delete-tim/", HttpMethod.DELETE, entity, String.class); + if (response.getStatusCode().is2xxSuccessful()) { + log.info("Successfully deleted ActiveTim: { activeTimId: {}, clientId: {} }", activeTim.getActiveTimId(), + activeTim.getClientId()); + } else { + log.warn("Failed to delete ActiveTim: { activeTimId: {}, clientId: {} }, response status: {}, response body: {}", + activeTim.getActiveTimId(), activeTim.getClientId(), response.getStatusCode(), response.getBody()); + } + } + catch (Exception e) { + log.error("Exception deleting ActiveTim: { activeTimId: {}, clientId: {} }, error: {}", activeTim.getActiveTimId(), + activeTim.getClientId(), e.getMessage()); + } - utility.logWithDate( - "CleanupActiveTims - Deleting ActiveTim: { activeTimId: " + activeTim.getActiveTimId() + " }", - this.getClass()); - restTemplateProvider.GetRestTemplate().exchange(configuration.getWrapperUrl() + "/delete-tim/", - HttpMethod.DELETE, entity, String.class); } } catch (Exception e) { - e.printStackTrace(); + log.error("Unexpected error occurred while processing expired Active TIMs", e); // don't rethrow error, or the task won't be reran until the service is // restarted. } diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java index 7dbcec91e..0f965284f 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java @@ -82,7 +82,9 @@ public void run() { // TODO: Delete only if the failed update was to expire the active TIM. // TODO: Consider re-submitting the active TIM if the failed update was not meant to expire it. } - removeActiveTimRecords(activeTimIdsToDelete); // active_tim records are no longer up-to-date + if (!activeTimIdsToDelete.isEmpty()) { + removeActiveTimRecords(activeTimIdsToDelete); // active_tim records are no longer up-to-date + } // Delete likely stale active_tim_holding records diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java index 930818b26..e693a630b 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java @@ -3,36 +3,32 @@ import java.util.List; import com.google.gson.Gson; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.service.ActiveTimService; import com.trihydro.library.service.RestTemplateProvider; import com.trihydro.tasks.config.DataTasksConfiguration; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.ResourceAccessException; @Slf4j @Component public class RemoveExpiredActiveTims implements Runnable { - private DataTasksConfiguration configuration; - private Utility utility; - private ActiveTimService activeTimService; - private RestTemplateProvider restTemplateProvider; + private final DataTasksConfiguration configuration; + private final ActiveTimService activeTimService; + private final RestTemplateProvider restTemplateProvider; - @Autowired - public void InjectDependencies(DataTasksConfiguration configuration, Utility _utility, - ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider) { + public RemoveExpiredActiveTims(DataTasksConfiguration configuration, ActiveTimService activeTimService, + RestTemplateProvider restTemplateProvider) { this.configuration = configuration; - utility = _utility; - activeTimService = _activeTimService; - restTemplateProvider = _restTemplateProvider; + this.activeTimService = activeTimService; + this.restTemplateProvider = restTemplateProvider; } /** @@ -55,6 +51,7 @@ public void run() { List activeTims = activeTimService.getExpiredActiveTims(batchSize); log.info("Retrieved a batch of {} expired Active TIMs", activeTims.size()); if (activeTims.isEmpty()) { + log.debug("No more expired Active TIMs to process. Exiting batch processing."); break; } @@ -66,16 +63,27 @@ public void run() { Gson gson = new Gson(); // send to tim type endpoint to delete from RSUs and SDX + log.debug("Sending request to delete expired Active TIMs from RSUs and SDX"); for (ActiveTim activeTim : activeTims) { + try { + activeTimJson = gson.toJson(activeTim); + entity = new HttpEntity<>(activeTimJson, headers); - activeTimJson = gson.toJson(activeTim); - entity = new HttpEntity(activeTimJson, headers); - - log.info("Attempting to delete ActiveTim: { activeTimId: {} }", activeTim.getActiveTimId()); - restTemplateProvider.GetRestTemplate() - .exchange(configuration.getWrapperUrl() + "/delete-tim/", - HttpMethod.DELETE, entity, String.class); + ResponseEntity response = restTemplateProvider.GetRestTemplate() + .exchange(configuration.getWrapperUrl() + "/delete-tim/", + HttpMethod.DELETE, entity, String.class); + if (!response.getStatusCode().is2xxSuccessful()) { + log.warn("Failed to delete Active TIM with id {}. Response: {}", + activeTim.getActiveTimId(), response.getBody()); + } + } catch (Exception e) { + log.error("Exception deleting Active TIM with id {}: {}", activeTim.getActiveTimId(), e.getMessage()); + } } + log.info("Attempted to delete expired active TIMs with ids: {}", + activeTims.stream().map(ActiveTim::getActiveTimId).collect(java.util.stream.Collectors.toList())); + log.debug("Client ids of expired Active TIMs: {}", + activeTims.stream().map(ActiveTim::getClientId).collect(java.util.stream.Collectors.toList())); } catch (ResourceAccessException e) { log.error("Error accessing resource. This indicates that the ODE Wrapper or CV Data Controller is not reachable. No more batches will be processed until the next run.", e); // the error should not be rethrown, or else the task will not run until the service is restarted diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RetentionPolicyEnforcement.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RetentionPolicyEnforcement.java index e89797668..db8af6a4f 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RetentionPolicyEnforcement.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RetentionPolicyEnforcement.java @@ -1,31 +1,30 @@ package com.trihydro.tasks.actions; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.service.StatusLogService; import com.trihydro.library.service.TimService; import com.trihydro.tasks.config.DataTasksConfiguration; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class RetentionPolicyEnforcement implements Runnable { - private Utility utility; private StatusLogService statusLogService; private TimService timService; private DataTasksConfiguration config; @Autowired - public void InjectDependencies(Utility _utility, StatusLogService _statusLogService, - TimService _timService, DataTasksConfiguration _config) { - this.utility = _utility; + public void InjectDependencies(StatusLogService _statusLogService, + TimService _timService, DataTasksConfiguration _config) { this.statusLogService = _statusLogService; this.timService = _timService; this.config = _config; } public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("{}: " + "Running...", this.getClass().getSimpleName()); try { // delete all older than a month: @@ -40,7 +39,7 @@ public void run() { } } catch (Exception e) { - e.printStackTrace(); + log.error("Exception", e); // don't rethrow error, or the task won't be reran until the service is // restarted. } diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateRsus.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateRsus.java index 5b68d8f0f..4f9cfabbd 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateRsus.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateRsus.java @@ -24,7 +24,9 @@ import com.trihydro.tasks.models.RsuValidationRecord; import com.trihydro.tasks.models.RsuValidationResult; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -32,6 +34,7 @@ import us.dot.its.jpo.ode.plugin.SnmpProtocol; @Component +@Slf4j public class ValidateRsus implements Runnable { private DataTasksConfiguration config; private ActiveTimService activeTimService; @@ -68,19 +71,19 @@ public void setDelaySecondValidation(boolean shouldDelay) { } public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("{}: " + "Running...", this.getClass().getSimpleName()); try { validateRsus(); } catch (Exception ex) { var msg = "An unexpected error occurred that prevented RSU validation from completing.\n"; msg += ex.getMessage(); - utility.logWithDate(msg, this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), msg); try { mailHelper.SendEmail(config.getAlertAddresses(), "RSU Validation Error", msg); } catch (Exception mailException) { - mailException.printStackTrace(); + log.error("Exception", mailException); } // don't rethrow error, or the task won't be reran until the service is // restarted. @@ -109,7 +112,7 @@ private void validateRsus() throws Exception { } } } catch (Exception ex) { - utility.logWithDate("Unable to fetch all RSUs - will proceed with partial validation", this.getClass()); + log.info("{}: " + "Unable to fetch all RSUs - will proceed with partial validation", this.getClass().getSimpleName()); unexpectedErrors.add("Error occurred while fetching all RSUs - " + "unable to validate any RSUs that don't have an existing, active TIM. Error:\n" + ex.toString()); @@ -118,12 +121,12 @@ private void validateRsus() throws Exception { // If there isn't anything to verify, exit early. if (rsusToValidate.size() == 0) { var msg = "Unable to find any RSUs to validate."; - utility.logWithDate(msg, this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), msg); try { mailHelper.SendEmail(config.getAlertAddresses(), "RSU Validation Error", msg); } catch (Exception ex) { - ex.printStackTrace(); + log.error("Exception", ex); } return; @@ -202,7 +205,7 @@ private void validateRsus() throws Exception { for (var error : resubmitErrors) { String message = String.format("Error resubmitting Active TIM %d. Error: %s", error.getActiveTimId(), error.getExceptionMessage()); - utility.logWithDate(message, this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), message); unexpectedErrors.add(message); } } @@ -228,7 +231,7 @@ private void validateRsus() throws Exception { try { mailHelper.SendEmail(config.getAlertAddresses(), "RSU Validation Results", email); } catch (Exception ex) { - ex.printStackTrace(); + log.error("Exception", ex); } } } @@ -252,7 +255,7 @@ private void validateRsus(List rsusToValidate) throws Excep tasks.add(new ValidateRsu(rsu.getRsuInformation(), rsuDataService)); } - utility.logWithDate("Validating " + tasks.size() + " RSUs...", this.getClass()); + log.info("{}: " + "Validating {} RSUs...", this.getClass().getSimpleName(), tasks.size()); List> futureResults = null; try { @@ -260,8 +263,8 @@ private void validateRsus(List rsusToValidate) throws Excep futureResults = workerThreadPool.invokeAll(tasks, config.getRsuValTimeoutSeconds(), TimeUnit.SECONDS); shutDownThreadPool(workerThreadPool); } catch (InterruptedException e) { - utility.logWithDate("Error while executing validation tasks:", this.getClass()); - e.printStackTrace(); + log.info("{}: " + "Error while executing validation tasks:", this.getClass().getSimpleName()); + log.error("Exception", e); } if (futureResults == null || futureResults.size() != rsusToValidate.size()) { @@ -277,7 +280,7 @@ private void validateRsus(List rsusToValidate) throws Excep // Something went wrong, and the validation task for this RSU wasn't completed. String rsuIpv4Address = tasks.get(i).getIpv4Address(); String message = "Error while validating RSU " + rsuIpv4Address + ":\n" + e.toString(); - utility.logWithDate(message, this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), message); rsusToValidate.get(i).setError(message); } } @@ -304,9 +307,8 @@ private List getActiveRsuTims() { // Fetch records for prod activeTims = activeTimService.getActiveRsuTims(config.getCvRestService()); } catch (Exception ex) { - utility.logWithDate("Unable to validate RSUs - error occurred while fetching Database records from PROD:", - this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Unable to validate RSUs - error occurred while fetching Database records from PROD:", this.getClass().getSimpleName()); + log.error("Exception", ex); return null; } diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateSdx.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateSdx.java index 462ed42ba..32bf10379 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateSdx.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateSdx.java @@ -1,15 +1,11 @@ package com.trihydro.tasks.actions; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import com.google.gson.Gson; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.TimGenerationHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.AdvisorySituationDataDeposit; import com.trihydro.library.model.ResubmitTimException; @@ -21,229 +17,250 @@ import com.trihydro.tasks.models.CActiveTim; import com.trihydro.tasks.models.CAdvisorySituationDataDeposit; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import javax.mail.MessagingException; + @Component +@Slf4j +@RequiredArgsConstructor public class ValidateSdx implements Runnable { - private DataTasksConfiguration config; - private SdwService sdwService; - private ActiveTimService activeTimService; - private EmailHelper mailHelper; - private EmailFormatter emailFormatter; - private Utility utility; - private TimGenerationHelper timGenerationHelper; - - @Autowired - public void InjectDependencies(DataTasksConfiguration _config, SdwService _sdwService, - ActiveTimService _activeTimService, EmailFormatter _emailFormatter, EmailHelper _mailHelper, - Utility _utility, TimGenerationHelper _timGenerationHelper) { - config = _config; - sdwService = _sdwService; - activeTimService = _activeTimService; - emailFormatter = _emailFormatter; - mailHelper = _mailHelper; - utility = _utility; - timGenerationHelper = _timGenerationHelper; - } + private final DataTasksConfiguration config; + private final SdwService sdwService; + private final ActiveTimService activeTimService; + private final EmailHelper mailHelper; + private final EmailFormatter emailFormatter; + private final TimGenerationHelper timGenerationHelper; public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("Running SDX validation task..."); try { - validateSdx(); + identifyAndReconcileDiscrepancies(); } catch (Exception ex) { - utility.logWithDate("Error while validating SDX:", this.getClass()); - ex.printStackTrace(); + log.error("Exception during SDX validation", ex); // don't rethrow error, or the task won't be reran until the service is // restarted. } } - private void validateSdx() { - List dbRecords = new ArrayList<>(); - List sdxRecords = new ArrayList<>(); - - // Fetch records from the Database - for (ActiveTim activeTim : activeTimService.getActiveTimsForSDX()) { - dbRecords.add(new CActiveTim(activeTim)); + private void identifyAndReconcileDiscrepancies() throws SdwService.SdwServiceException, MessagingException { + // list of invalid records found in the database (missing or invalid sat_record_id) + List invalidDbRecords = new ArrayList<>(); + + // fetch all active tim records with sat_record_id + log.info("Fetching active TIMs from database for SDX validation"); + List activeTims = activeTimService.getActiveTimsForSDX(); + HashMap activeTimMap = new HashMap<>(); + for (ActiveTim activeTim : activeTims) { + var cActiveTim = new CActiveTim(activeTim); + var recordId = cActiveTim.getRecordId(); + // Note: since getRecordId fails to parse integers silently, we must handle the null case instead + // as it represents either a missing (sat)recordId OR an invalid value + if (recordId == null) { + log.warn("Database record {} has no recordId", activeTim.getSatRecordId()); + invalidDbRecords.add(cActiveTim); + continue; + } + activeTimMap.put(recordId, cActiveTim); } - - // Fetch records from SDX - for (AdvisorySituationDataDeposit asdd : sdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)) { - List itisCodes = sdwService.getItisCodesFromAdvisoryMessage(asdd.getAdvisoryMessage()); - CAdvisorySituationDataDeposit record = new CAdvisorySituationDataDeposit(asdd, itisCodes); - - sdxRecords.add(record); + // note: we only care about satellite records (sat_record_id is NOT null) + + // get every TIM in the ASD Data Deposit format from the SDX + // for each tim + // ask SDX to decode each message so we can access the ITIS codes + // create a CAdvisorySituationDataDeposit record + log.info("Fetching AdvisorySituationDataDeposit records from SDX"); + List sdxRecords = sdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep); + HashMap sdxRecordMap = new HashMap<>(); + for (AdvisorySituationDataDeposit sdxRecord : sdxRecords) { + List itisCodes = new ArrayList<>(); + try { + itisCodes = sdwService.getItisCodesFromAdvisoryMessage(sdxRecord.getAdvisoryMessage()); + } catch (SdwService.SdwServiceException e) { + log.warn("Exception retrieving ITIS Codes from AdvisoryMessage for SDX record {}, treating as no ITIS Codes", sdxRecord.getRecordId(), e); + // TODO: consider adding to an "invalid SDX records" list to include in the email? continuing? + } + sdxRecordMap.put(sdxRecord.getRecordId(), new CAdvisorySituationDataDeposit(sdxRecord, itisCodes)); } - Collections.sort(dbRecords); - Collections.sort(sdxRecords); - - // Actions to perform - List toResend = new ArrayList(); - List deleteFromSdx = new ArrayList(); - - // Metrics to collect - List invDbRecords = new ArrayList(); - int numSdxOrphanedRecords = 0; - int numOutdatedSdxRecords = 0; - int numRecordsNotOnSdx = 0; + // use the SDX TIMs to identify the active TIMs to delete from the SDX + List orphanedRecords = getTimsToDeleteFromSDX(activeTimMap, sdxRecordMap); + log.info("Identified {} SDX TIMs to delete from the SDX", orphanedRecords.size()); + + // use the SDX TIMs to identify the active TIMs to resend to the SDX + GetTimsToResendResult getTimsToResendResults = getTimsToResend(activeTimMap, sdxRecordMap); + List toResend = new ArrayList<>(getTimsToResendResults.getRecordsNotOnSdx()); + toResend.addAll(getTimsToResendResults.getOutdatedRecords()); + log.info("Identified {} TIMs to resend to the SDX ({} not on SDX, {} outdated)", toResend.size(), + getTimsToResendResults.getRecordsNotOnSdx().size(), getTimsToResendResults.getOutdatedRecords().size()); + + // delete the SDX TIMs from the SDX + SdxDeletionResults deletionResults = new SdxDeletionResults(new HashMap<>()); + if (!orphanedRecords.isEmpty()) { + log.info("Deleting {} records from the SDX", orphanedRecords.size()); + deletionResults = deleteTimsFromSdx(orphanedRecords); + } - int i = 0; - int j = 0; + // resend the active TIMs to the SDX + List resendResults = new ArrayList<>(); + if (!toResend.isEmpty()) { + log.info("Resending {} TIMs to the SDX", toResend.size()); + resendResults = resendTimsToSdx(toResend); + } - while (i < dbRecords.size() || j < sdxRecords.size()) { + // email a summary of what was done, including invalid records + log.info("Sending SDX validation summary email..."); + sendSummaryEmail(toResend, + orphanedRecords, + resendResults, + deletionResults, + invalidDbRecords, + getTimsToResendResults.getOutdatedRecords().size(), + orphanedRecords.size(), + getTimsToResendResults.getRecordsNotOnSdx().size()); + } - // If either list is at the end, push the remainder of the other list onto their - // corresponding action - if (i == dbRecords.size()) { - // Any remaining sdx records don't have a corresponding database record - deleteFromSdx.addAll(sdxRecords.subList(j, sdxRecords.size())); - numSdxOrphanedRecords += sdxRecords.size() - j; - j = sdxRecords.size(); - continue; - } - if (j == sdxRecords.size()) { - // Any remaining database records don't have a corresponding sdx record - toResend.addAll(dbRecords.subList(i, dbRecords.size())); - numRecordsNotOnSdx += dbRecords.size() - i; - i = dbRecords.size(); + private GetTimsToResendResult getTimsToResend(HashMap activeTimMap, + HashMap sdxRecordMap) { + List outdatedRecords = new ArrayList<>(); + List recordsNotOnSdx = new ArrayList<>(); + // to identify what needs to be resubmitted: + // the goal is to get the condition back on the SDX + // if an active TIM exists && it has no SDX TIM then + // check if active TIM endDateTime is in the future + // then we resubmit it to the SDX + for (CActiveTim activeTim : activeTimMap.values()) { + var sdxRecord = sdxRecordMap.get(activeTim.getRecordId()); + if (sdxRecord != null) { + log.trace("Found sdxRecord with recordId {}", activeTim.getRecordId()); + + // if the ITIS codes are different, we should resubmit it + if (!activeTim.getItisCodes().equals(sdxRecord.getItisCodes())) { + log.trace("Active tim ITIS codes differ from SDX record, so we should resubmit it."); + outdatedRecords.add(activeTim); + } else { + log.trace("Active tim ITIS codes match SDX record, not resubmitting."); + } continue; } + // at this point, we know there is no SDX record for this active tim + recordsNotOnSdx.add(activeTim); + } + return new GetTimsToResendResult(recordsNotOnSdx, outdatedRecords); + } - CActiveTim dbRecord = dbRecords.get(i); - CAdvisorySituationDataDeposit sdxRecord = sdxRecords.get(j); - Integer sdxRecordId = sdxRecord.getRecordId(); - Integer dbRecordId = dbRecord.getRecordId(); - - // If the SAT_RECORD_ID string isn't valid hex, the SDX will reject the record. - // Push onto invDbRecords - if (dbRecordId == null) { - invDbRecords.add(dbRecord); - i++; + private List getTimsToDeleteFromSDX(HashMap activeTimMap, + HashMap sdxRecordMap) { + List orphanedRecords = new ArrayList<>(); + // to identify what needs to be deleted: + // to identify an SDX TIM exists && no active TIM exists + // SDX TIM recordId == Active TIM recordId + // compare the ITIS codes + for (CAdvisorySituationDataDeposit sdxRecord : sdxRecordMap.values()) { + var activeTim = activeTimMap.get(sdxRecord.getRecordId()); + if (activeTim != null) { + log.trace("Found active_tim record with recordId {}", sdxRecord.getRecordId()); continue; } - - if (dbRecordId.equals(sdxRecordId)) { - // Make sure the numeric ITIS Codes are the same. - // TODO: we should also check the TIM's ITISText values, if any are present - if (!sameItisCodes(dbRecord.getItisCodes(), sdxRecord.getItisCodes())) { - numOutdatedSdxRecords++; - toResend.add(dbRecord); - } - i++; - j++; - } else if (dbRecordId > sdxRecordId) { - // The current SDX record doesn't have a corresponding Database record... - numSdxOrphanedRecords++; - deleteFromSdx.add(sdxRecord); - j++; - } else { - // The current Database record doesn't have a corresponding SDX record... - numRecordsNotOnSdx++; - toResend.add(dbRecord); - i++; - } + log.trace("No activeTim found with recordId {}, so we should delete the SDX record", sdxRecord.getRecordId()); + orphanedRecords.add(sdxRecord); } + return orphanedRecords; + } + + private List resendTimsToSdx(List toResend) { + var activeTimIds = toResend.stream().map(x -> x.getActiveTim().getActiveTimId()).collect(Collectors.toList()); + log.trace("Resubmitting the following ActiveTim IDs to ODE: {}", activeTimIds.stream().map(Object::toString).collect(Collectors.joining(","))); + return timGenerationHelper.resubmitToOde(activeTimIds); // TODO: check if we should be using a different method here (this method uses original start time) + } - if (toResend.size() > 0 || deleteFromSdx.size() > 0 || invDbRecords.size() > 0) { - // For now, we'll just report on the invDbRecords - String exceptionText = cleanupData(toResend, deleteFromSdx); - String email = emailFormatter.generateSdxSummaryEmail(numSdxOrphanedRecords, numOutdatedSdxRecords, - numRecordsNotOnSdx, toResend, deleteFromSdx, invDbRecords, exceptionText); + private SdxDeletionResults deleteTimsFromSdx(List toDelete) { + var satRecordIds = toDelete.stream().map(x -> x.getAsdd().getRecordId()).collect(Collectors.toList()); + log.trace("Deleting the following SAT_RECORD_ID records from the SDX: {}", satRecordIds.stream().map(Object::toString).collect(Collectors.joining(","))); + return new SdxDeletionResults(sdwService.deleteSdxDataByRecordIdIntegers(satRecordIds)); + } - try { - mailHelper.SendEmail(config.getAlertAddresses(), "SDX Validation Results", email); - } catch (Exception ex) { - ex.printStackTrace(); + private void sendSummaryEmail(List toResend, + List toDelete, + List resendResults, + SdxDeletionResults deletionResults, + List invalidDbRecords, + int numOutdatedSdxRecords, + int numSdxOrphanedRecords, + int numRecordsNotOnSdx) throws MessagingException { + + log.trace("Generating SDX summary email..."); + + StringBuilder exceptionText = new StringBuilder(); + if (deletionResults.hasErrors()) { + exceptionText.append("The following recordIds failed to delete from the SDX: ") + .append(deletionResults.getFailedRecordIds()) + .append("
"); + } + if (!resendResults.isEmpty()) { + Gson gson = new Gson(); + exceptionText.append("The following exceptions were found while attempting to resubmit TIMs: "); + exceptionText.append("
"); + for (ResubmitTimException rte : resendResults) { + exceptionText.append(gson.toJson(rte)); + exceptionText.append("
"); } } - } - /** - * Call out to appropriate endpoints to remove, or refresh data as appropriate - * - * @param toResend List of records to resubmit to the ODE for processing - * @param deleteFromSdx List of records to delete from the SDX - * @return A String representing exceptions to include in the summary email - */ - private String cleanupData(List toResend, List deleteFromSdx) { - String deleteError = ""; - List resendExceptions = new ArrayList<>(); - if (toResend.size() > 0) { - resendExceptions = resendTims(toResend); + // clarify that SDX deletion failures may indicate errant data in the SDX, suggest next steps + if (deletionResults.hasErrors()) { + exceptionText.append("Note: SDX deletion failures may indicate errant data in the SDX. " + + "Please investigate the failed deletions and consider reaching out to the SDX support team for assistance.
"); } - // delete from SDX the given records - if (deleteFromSdx.size() > 0) { - deleteError = removeFromSdx(deleteFromSdx); + // clarify that TIM resubmission failures may indicate issues with the TIM data or the ODE service, suggest next steps + if (!resendResults.isEmpty()) { + exceptionText.append("Note: TIM resubmission failures may indicate issues with the TIM data or the ODE service. " + + "Please investigate the failed resubmissions and consider reaching out to the ODE support team for assistance.
"); } - String exceptionText = ""; - if (StringUtils.isNotBlank(deleteError) || resendExceptions.size() > 0) { - if (StringUtils.isNotBlank(deleteError)) { - exceptionText += "The following recordIds failed to delete from the SDX: " + deleteError; - exceptionText += "
"; - } + String email = emailFormatter.generateSdxSummaryEmail(numSdxOrphanedRecords, numOutdatedSdxRecords, + numRecordsNotOnSdx, toResend, toDelete, invalidDbRecords, exceptionText.toString()); - if (resendExceptions.size() > 0) { - Gson gson = new Gson(); - exceptionText += "The following exceptions were found while attempting to resubmit TIMs: "; - exceptionText += "
"; - for (ResubmitTimException rte : resendExceptions) { - exceptionText += gson.toJson(rte); - exceptionText += "
"; - } - } + if (!toResend.isEmpty() || !toDelete.isEmpty() || !invalidDbRecords.isEmpty()) { + log.warn("Discrepancies found between the database and the SDX:" + + " {} records resent to SDX, {} records deleted from SDX, {} invalid database records", + toResend.size(), toDelete.size(), invalidDbRecords.size()); + } else { + log.info("No discrepancies found between the database and the SDX."); } - return exceptionText; - } - private List resendTims(List toResend) { - var activeTimIds = toResend.stream().map(x -> x.getActiveTim().getActiveTimId()).collect(Collectors.toList()); - return timGenerationHelper.resubmitToOde(activeTimIds); - } - private String removeFromSdx(List deleteFromSdx) { - // Issue one delete call to the REST service, encompassing all sat_record_ids - var satRecordIds = deleteFromSdx.stream().map(x -> x.getAsdd().getRecordId()).collect(Collectors.toList()); - HashMap sdxDelResults = sdwService.deleteSdxDataByRecordIdIntegers(satRecordIds); - String failedResultsText = ""; - - // Determine if anything failed - Boolean errorsOccurred = sdxDelResults.entrySet().stream() - .anyMatch(x -> x.getValue() != null && x.getValue() == false); - if (errorsOccurred) { - failedResultsText = sdxDelResults.entrySet().stream().filter(x -> x.getValue() == false) - .map(x -> x.getKey().toString()).collect(Collectors.joining(",")); - } - return failedResultsText; + log.trace("Invoking email helper to send SDX summary email to specified addresses..."); + mailHelper.SendEmail(config.getAlertAddresses(), "SDX Validation Results", email); } - private boolean sameItisCodes(List o1, List o2) { - boolean result = true; + // wrapper around HashMap + private static class SdxDeletionResults { + private final HashMap results; - if (o1 == null || o2 == null || o1.size() != o2.size()) { - result = false; - } else { - for (int i = 0; i < o1.size(); i++) { - boolean inBoth = false; - - for (int j = 0; j < o2.size(); j++) { - if (o1.get(i) != null && o1.get(i).equals(o2.get(j))) { - inBoth = true; - break; - } - } + public SdxDeletionResults(HashMap results) { + this.results = results; + } - if (!inBoth) { - result = false; - break; - } - } + public boolean hasErrors() { + return results.entrySet().stream().anyMatch(x -> x.getValue() != null && !x.getValue()); + } + + public String getFailedRecordIds() { + return results.entrySet().stream().filter(x -> x.getValue() == false) + .map(x -> x.getKey().toString()).collect(Collectors.joining(",")); } + } - return result; + @Data + @RequiredArgsConstructor + private static class GetTimsToResendResult { + private final List recordsNotOnSdx; + private final List outdatedRecords; } + } \ No newline at end of file diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateTmdd.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateTmdd.java index 98b74e0fb..145b75614 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateTmdd.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/ValidateTmdd.java @@ -1,6 +1,5 @@ package com.trihydro.tasks.actions; -//import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -9,7 +8,6 @@ import com.google.gson.Gson; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.TimGenerationHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.ActiveTimError; import com.trihydro.library.model.ActiveTimErrorType; @@ -30,15 +28,13 @@ import com.trihydro.tasks.helpers.EmailFormatter; import com.trihydro.tasks.helpers.IdNormalizer; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -//import org.gavaghan.geodesy.Ellipsoid; -//import org.gavaghan.geodesy.GeodeticCalculator; -//import org.gavaghan.geodesy.GeodeticCurve; -//import org.gavaghan.geodesy.GlobalCoordinates; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class ValidateTmdd implements Runnable { private DataTasksConfiguration config; private TmddService tmddService; @@ -47,7 +43,6 @@ public class ValidateTmdd implements Runnable { private IdNormalizer idNormalizer; private EmailFormatter emailFormatter; private EmailHelper mailHelper; - private Utility utility; private WydotTimService wydotTimService; private TimGenerationHelper timGenerationHelper; private Map tmddItisCodes; @@ -56,9 +51,9 @@ public class ValidateTmdd implements Runnable { @Autowired public void InjectDependencies(DataTasksConfiguration config, TmddService tmddService, - ActiveTimService activeTimService, ItisCodeService itisCodeService, IdNormalizer idNormalizer, - EmailFormatter emailFormatter, EmailHelper mailHelper, Utility utility, WydotTimService _wydotTimService, - TimGenerationHelper _timGenerationHelper) { + ActiveTimService activeTimService, ItisCodeService itisCodeService, IdNormalizer idNormalizer, + EmailFormatter emailFormatter, EmailHelper mailHelper, WydotTimService _wydotTimService, + TimGenerationHelper _timGenerationHelper) { this.config = config; this.tmddService = tmddService; this.activeTimService = activeTimService; @@ -66,20 +61,19 @@ public void InjectDependencies(DataTasksConfiguration config, TmddService tmddSe this.idNormalizer = idNormalizer; this.emailFormatter = emailFormatter; this.mailHelper = mailHelper; - this.utility = utility; wydotTimService = _wydotTimService; timGenerationHelper = _timGenerationHelper; } public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("{}: " + "Running...", this.getClass().getSimpleName()); errors = new ArrayList<>(); try { validateTmdd(); } catch (Exception ex) { - utility.logWithDate("Error while validating Database with TMDD:", this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Error while validating Database with TMDD:", this.getClass().getSimpleName()); + log.error("Exception", ex); errors.add(ex.getMessage()); // don't rethrow error, or the task won't be reran until the service is @@ -93,8 +87,8 @@ public void run() { mailHelper.SendEmail(config.getAlertAddresses(), "TMDD Validation Error(s)", email); } catch (Exception ex) { - utility.logWithDate("Failed to send error summary email:", this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Failed to send error summary email:", this.getClass().getSimpleName()); + log.error("Exception", ex); } } } @@ -105,8 +99,8 @@ private void validateTmdd() { try { feus = tmddService.getTmddEvents(); } catch (Exception ex) { - utility.logWithDate("Error fetching FEUs from TMDD:", this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Error fetching FEUs from TMDD:", this.getClass().getSimpleName()); + log.error("Exception", ex); errors.add("Error fetching FEUs from TMDD: " + ex.getMessage()); return; @@ -117,8 +111,8 @@ private void validateTmdd() { try { activeTims = activeTimService.getActiveTimsWithItisCodes(true); } catch (Exception ex) { - utility.logWithDate("Error fetching Active Tims:", this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Error fetching Active Tims:", this.getClass().getSimpleName()); + log.error("Exception", ex); errors.add("Error fetching Active Tims: " + ex.getMessage()); return; @@ -129,8 +123,8 @@ private void validateTmdd() { try { initializeTmddItisCodes(); } catch (Exception ex) { - utility.logWithDate("Unable to initialize TMDD ITIS Code cache:", this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Unable to initialize TMDD ITIS Code cache:", this.getClass().getSimpleName()); + log.error("Exception", ex); errors.add("Unable to initialize TMDD ITIS Code cache: " + ex.getMessage()); return; @@ -233,8 +227,8 @@ private void validateTmdd() { try { mailHelper.SendEmail(config.getAlertAddresses(), "TMDD Validation Results", email); } catch (Exception ex) { - utility.logWithDate("Error sending summary email:", this.getClass()); - ex.printStackTrace(); + log.info("{}: " + "Error sending summary email:", this.getClass().getSimpleName()); + log.error("Exception", ex); errors.add("Error sending summary email: " + ex.getMessage()); } } diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/VerifyHSMFunctional.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/VerifyHSMFunctional.java index 232a9bf07..866dcb62e 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/VerifyHSMFunctional.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/VerifyHSMFunctional.java @@ -10,6 +10,8 @@ import com.trihydro.tasks.models.SignTimModel; import com.trihydro.tasks.models.hsmresponse.HsmResponse; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -19,6 +21,7 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class VerifyHSMFunctional implements Runnable { private DataTasksConfiguration config; private Utility utility; @@ -50,14 +53,14 @@ public void InjectDependencies(DataTasksConfiguration _configuration, Utility _u } public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("{}: " + "Running...", this.getClass().getSimpleName()); try { // ping HSM var response = restTemplateProvider.GetRestTemplate_NoErrors().exchange(config.getHsmUrl() + "/signtim/", HttpMethod.POST, entity, HsmResponse.class); if (response.getStatusCode() != HttpStatus.OK) { - utility.logWithDate("HSM is not responsive! If an email should be sent, it will be shortly."); + log.info("HSM is not responsive! If an email should be sent, it will be shortly."); } if (response.getStatusCode() == HttpStatus.OK) { @@ -65,9 +68,9 @@ public void run() { // send an email telling us its back up String email = "HSM Functional Tester was successful in attempting to sign a TIM"; mailHelper.SendEmail(config.getAlertAddresses(), "HSM Back Up", email); - utility.logWithDate("HSM is back up! Email sent."); + log.info("HSM is back up! Email sent."); } else { - utility.logWithDate("HSM is up!"); + log.info("HSM is up!"); } errorLastSent = null; } else if (shouldSendEmail(errorLastSent)) { @@ -83,7 +86,7 @@ public void run() { mailHelper.SendEmail(config.getAlertAddresses(), "HSM Error", email); } } catch (Exception e) { - e.printStackTrace(); + log.error("Exception", e); // don't rethrow error, or the task won't be reran until the service is // restarted. @@ -94,7 +97,7 @@ public void run() { email += e.getMessage(); mailHelper.SendEmail(config.getAlertAddresses(), "HSM Error", email); } catch (Exception subEx) { - e.printStackTrace(); + log.error("Exception", e); } } } @@ -111,7 +114,7 @@ private boolean shouldSendEmail(Date lastSent) { return true; } - utility.logWithDate("Email should not be sent at this time"); + log.info("Email should not be sent at this time"); return false; } } diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupActiveTimsTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupActiveTimsTest.java index 1928f4775..438aa3242 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupActiveTimsTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupActiveTimsTest.java @@ -1,6 +1,7 @@ package com.trihydro.tasks.actions; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -21,7 +22,12 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @ExtendWith(MockitoExtension.class) @@ -29,37 +35,72 @@ public class CleanupActiveTimsTest { @Mock private DataTasksConfiguration mockConfig; + @Mock private RestTemplate mockRestTemplate; - @Mock - Utility mockUtility; + @Mock ActiveTimService mockActiveTimService; + @Mock RestTemplateProvider mockRestTemplateProvider; @InjectMocks public CleanupActiveTims uut; - @BeforeEach - public void setup() { + @Test + public void run_ShouldCallRestTemplateExchangeTwice_WhenTwoActiveTimsFound() { + // Arrange when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(mockRestTemplate); - List itisTims = new ArrayList(); + List itisTims = new ArrayList<>(); itisTims.add(new ActiveTim()); when(mockActiveTimService.getActiveTimsMissingItisCodes()).thenReturn(itisTims); - List notSentTims = new ArrayList(); + List notSentTims = new ArrayList<>(); notSentTims.add(new ActiveTim()); when(mockActiveTimService.getActiveTimsNotSent()).thenReturn(notSentTims); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ResponseEntity response = new ResponseEntity<>("success", headers, HttpStatus.OK); + when(mockRestTemplate.exchange(contains("/delete-tim/"), any(HttpMethod.class), + Mockito.>any(), Mockito.>any())).thenReturn(response); + + // Act + uut.run(); + + // Assert + verify(mockRestTemplate, Mockito.times(2)).exchange(any(String.class), any(HttpMethod.class), + Mockito.>any(), Mockito.>any()); } @Test - public void cleanupActiveTims_runTest() { + public void run_ShouldCallRestTemplateExchangeTwice_WhenTwoActiveTimsFound_AndFirstRequestReturnsBadRequest() { + // Arrange + when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(mockRestTemplate); + + List itisTims = new ArrayList<>(); + itisTims.add(new ActiveTim()); + when(mockActiveTimService.getActiveTimsMissingItisCodes()).thenReturn(itisTims); + + List notSentTims = new ArrayList<>(); + notSentTims.add(new ActiveTim()); + when(mockActiveTimService.getActiveTimsNotSent()).thenReturn(notSentTims); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ResponseEntity secondResponse = new ResponseEntity<>("success", headers, HttpStatus.OK); + + when(mockRestTemplate.exchange(contains("/delete-tim/"), any(HttpMethod.class), + Mockito.>any(), Mockito.>any())).thenThrow(new HttpClientErrorException(HttpStatus.BAD_REQUEST)) + .thenReturn(secondResponse); + + // Act uut.run(); - // assert exchange called twice - verify(mockRestTemplate, Mockito.times(2)).exchange(any(String.class), any(HttpMethod.class), + // Assert + verify(mockRestTemplate, Mockito.times(2)).exchange(contains("/delete-tim/"), any(HttpMethod.class), Mockito.>any(), Mockito.>any()); } } \ No newline at end of file diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java index d38442461..0e567af2c 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -108,6 +109,28 @@ void run_SubsequentRun_WithMatchingActiveTims_ShouldDeleteStaleRecordsAndActiveT verify(activeTimService).deleteActiveTimsById(List.of(37L)); } + @Test + void run_SubsequentRun_ShouldNotInitiateDeletionsForEmptyList() { + // prepare + ActiveTimHolding ath1 = new ActiveTimHolding(); + ath1.setActiveTimHoldingId(1L); + ActiveTimHolding ath2 = new ActiveTimHolding(); + ath2.setActiveTimHoldingId(2L); + when(activeTimHoldingService.getAllRecords()).thenReturn(Arrays.asList(ath1, ath2)); + when(activeTimService.getAllRecords()).thenReturn(List.of()); + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords = new CleanupStaleActiveTimHoldingRecords(activeTimHoldingService, activeTimService); + cleanupStaleActiveTimHoldingRecords.setStaleRecordsIdentifiedLastRun(Set.of(1L, 2L)); // set stale records from previous run + + // execute + cleanupStaleActiveTimHoldingRecords.run(); + + // verify + assertEquals(0, cleanupStaleActiveTimHoldingRecords.getStaleRecordsIdentifiedLastRun().size()); + verify(activeTimService).getAllRecords(); + verify(activeTimHoldingService).deleteActiveTimHoldingRecords(List.of(1L, 2L)); + verify(activeTimService, never()).deleteActiveTimsById(List.of()); + } + /** * Test to verify that the run method handles database connection failures gracefully. */ diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java index 37351a033..cc4be4b09 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java @@ -1,6 +1,7 @@ package com.trihydro.tasks.actions; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -21,7 +22,12 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @ExtendWith(MockitoExtension.class) @@ -32,8 +38,6 @@ public class RemoveExpiredActiveTimsTest { @Mock private RestTemplate mockRestTemplate; @Mock - Utility mockUtility; - @Mock ActiveTimService mockActiveTimService; @Mock RestTemplateProvider mockRestTemplateProvider; @@ -41,8 +45,9 @@ public class RemoveExpiredActiveTimsTest { @InjectMocks public RemoveExpiredActiveTims uut; - @BeforeEach - public void setup() { + @Test + public void run_ShouldCallRestTemplateExchangeTwice_WhenTwoExpiredActiveTimsExist() { + // Arrange when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(mockRestTemplate); List expiredTims = new ArrayList(); @@ -50,14 +55,44 @@ public void setup() { expiredTims.add(new ActiveTim()); when(mockActiveTimService.getExpiredActiveTims(500)).thenReturn(expiredTims).thenReturn( new ArrayList<>()); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ResponseEntity response = new ResponseEntity<>("success", headers, HttpStatus.OK); + when(mockRestTemplate.exchange(contains("/delete-tim/"), any(HttpMethod.class), + Mockito.>any(), Mockito.>any())).thenReturn(response); + + // Act + uut.run(); + + // Assert + verify(mockRestTemplate, Mockito.times(2)).exchange(any(String.class), any(HttpMethod.class), + Mockito.>any(), Mockito.>any()); } @Test - public void cleanupActiveTims_runTest() { + public void run_ShouldCallRestTemplateExchangeTwice_WhenTwoExpiredActiveTimsExist_AndFirstRequestReturnsBadRequest() { + // Arrange + when(mockRestTemplateProvider.GetRestTemplate()).thenReturn(mockRestTemplate); + + List expiredTims = new ArrayList(); + expiredTims.add(new ActiveTim()); + expiredTims.add(new ActiveTim()); + when(mockActiveTimService.getExpiredActiveTims(500)).thenReturn(expiredTims).thenReturn(new ArrayList<>()); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ResponseEntity secondResponse = new ResponseEntity<>("success", headers, HttpStatus.OK); + + when(mockRestTemplate.exchange(contains("/delete-tim/"), any(HttpMethod.class), + Mockito.>any(), Mockito.>any())).thenThrow(new HttpClientErrorException(HttpStatus.BAD_REQUEST)) + .thenReturn(secondResponse); + + // Act uut.run(); - // assert exchange called twice - verify(mockRestTemplate, Mockito.times(2)).exchange(any(String.class), any(HttpMethod.class), + // Assert + verify(mockRestTemplate, Mockito.times(2)).exchange(contains("/delete-tim/"), any(HttpMethod.class), Mockito.>any(), Mockito.>any()); } } \ No newline at end of file diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateRsusTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateRsusTest.java index d92ac363c..186f96980 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateRsusTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateRsusTest.java @@ -1,43 +1,19 @@ package com.trihydro.tasks.actions; -import static com.trihydro.tasks.TestHelper.importJsonArray; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import javax.mail.MessagingException; - import com.trihydro.library.helpers.EmailHelper; -import com.trihydro.library.helpers.TimGenerationHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.WydotRsu; import com.trihydro.library.service.ActiveTimService; import com.trihydro.library.service.OdeService; -import com.trihydro.library.service.RsuDataService; import com.trihydro.library.service.RsuService; import com.trihydro.tasks.config.DataTasksConfiguration; import com.trihydro.tasks.helpers.EmailFormatter; import com.trihydro.tasks.helpers.ExecutorFactory; import com.trihydro.tasks.models.RsuValidationRecord; import com.trihydro.tasks.models.RsuValidationResult; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -48,6 +24,26 @@ import org.springframework.mail.MailException; import org.springframework.web.client.RestClientException; +import javax.mail.MessagingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static com.trihydro.tasks.TestHelper.importJsonArray; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + @ExtendWith(MockitoExtension.class) public class ValidateRsusTest { @Mock @@ -57,19 +53,13 @@ public class ValidateRsusTest { @Mock private RsuService mockRsuService; @Mock - private RsuDataService mockRsuDataService; - @Mock private OdeService mockOdeService; @Mock - private TimGenerationHelper mockTimGenerationHelper; - @Mock private ExecutorFactory mockExecutorFactory; @Mock private EmailFormatter mockEmailFormatter; @Mock private EmailHelper mockMailHelper; - @Mock - private Utility mockUtility; @Mock private ExecutorService mockExecutorService; @@ -135,19 +125,6 @@ public void validateRsus_noRecords() throws MailException, MessagingException { verify(mockMailHelper).SendEmail(any(), any(), any()); } - @Test - public void validateRsus_unresponsiveActiveTimService() { - // Arrange - when(mockActiveTimService.getActiveRsuTims(any())).thenThrow(new RestClientException("timeout")); - - // Act (error should be handled in runnable) - uut.run(); - - // Assert - verify(mockUtility).logWithDate( - "Unable to validate RSUs - error occurred while fetching Database records from PROD:", ValidateRsus.class); - } - @Test public void validateRsus_noErrors() throws InterruptedException, ExecutionException, MailException, MessagingException { diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateSdxTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateSdxTest.java index eadb2b22d..e1b4366ba 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateSdxTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateSdxTest.java @@ -1,6 +1,5 @@ package com.trihydro.tasks.actions; -import static com.trihydro.tasks.TestHelper.importJsonArray; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -8,6 +7,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.IOException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -16,6 +17,10 @@ import javax.mail.MessagingException; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -41,60 +46,86 @@ import org.springframework.mail.MailException; @ExtendWith(MockitoExtension.class) -public class ValidateSdxTest { +class ValidateSdxTest { // Mocked dependencies @Mock - private EmailHelper mockEmailHelper; + EmailHelper mockEmailHelper; @Mock - private SdwService mockSdwService; + SdwService mockSdwService; @Mock - private ActiveTimService mockActiveTimService; + ActiveTimService mockActiveTimService; @Mock - private EmailFormatter mockEmailFormatter; + EmailFormatter mockEmailFormatter; @Mock - private DataTasksConfiguration mockConfig; + DataTasksConfiguration mockConfig; @Mock - private Utility mockUtility; + Utility mockUtility; @Mock - private TimGenerationHelper mockTimGenerationHelper; + TimGenerationHelper mockTimGenerationHelper; // Argument Captors @Captor - private ArgumentCaptor> toResendCaptor; + ArgumentCaptor> toResendCaptor; @Captor - private ArgumentCaptor> deleteFromSdxCaptor; + ArgumentCaptor> deleteFromSdxCaptor; @Captor - private ArgumentCaptor> invDbRecordsCaptor; + ArgumentCaptor> invDbRecordsCaptor; @Captor - private ArgumentCaptor exceptionMessageCaptor; + ArgumentCaptor exceptionMessageCaptor; + + final Gson gson = createGson(); // Unit under test @InjectMocks ValidateSdx uut; @Test - public void validateSDX_run_noRecords() throws MailException, MessagingException { + void validateSDX_run_noRecords() throws MailException, MessagingException, SdwService.SdwServiceException { uut.run(); // Services were called verify(mockSdwService).getMsgsForOdeUser(SemiDialogID.AdvSitDataDep); verify(mockActiveTimService).getActiveTimsForSDX(); - // No email was sent - verify(mockEmailHelper, times(0)).SendEmail(any(), any(), any()); + // Email was sent + verify(mockEmailHelper, times(1)).SendEmail(any(), any(), any()); } @Test - public void validateSDX_run_allValid() throws MailException, MessagingException { // note: this test uses reflection, which must be manually allowed via the --add-opens JVM arg - ActiveTim[] activeTims = importJsonArray("/activeTims_1.json", ActiveTim[].class); - AdvisorySituationDataDeposit[] asdds = importJsonArray("/asdds_1.json", AdvisorySituationDataDeposit[].class); + void validateSDX_run_allValid() throws MailException, MessagingException, SdwService.SdwServiceException { + // 2 Active TIMs, 2 SDX records. Both Active TIMs correspond to SDX records with matching ITIS codes. + List activeTims = new ArrayList<>(); + List asdds = new ArrayList<>(); + + ActiveTim activeTim1 = new ActiveTim(); + activeTim1.setActiveTimId(100L); + activeTim1.setSatRecordId("00000000"); + activeTim1.setItisCodes(Arrays.asList(8, 7, 6)); + activeTim1.setEndDateTime("2099-12-31 23:59:59"); + activeTims.add(activeTim1); + + AdvisorySituationDataDeposit sdxRecord1 = new AdvisorySituationDataDeposit(); + sdxRecord1.setRecordId(0); + sdxRecord1.setAdvisoryMessage("0"); + asdds.add(sdxRecord1); + doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("0"); + + ActiveTim activeTim2 = new ActiveTim(); + activeTim2.setActiveTimId(101L); + activeTim2.setSatRecordId("00000001"); + activeTim2.setItisCodes(Arrays.asList(8, 7, 6)); + activeTim2.setEndDateTime("2099-12-31 23:59:59"); + activeTims.add(activeTim2); + + AdvisorySituationDataDeposit sdxRecord2 = new AdvisorySituationDataDeposit(); + sdxRecord2.setRecordId(1); + sdxRecord2.setAdvisoryMessage("1"); + asdds.add(sdxRecord2); + doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("1"); // Arrange service responses - when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(Arrays.asList(asdds)); - when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(Arrays.asList(activeTims)); - // Return ITIS codes for ASDDs - doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("0"); - doReturn(Arrays.asList(17, 16, 18)).when(mockSdwService).getItisCodesFromAdvisoryMessage("-1"); + when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(asdds); + when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(activeTims); // Act uut.run(); @@ -105,23 +136,36 @@ public void validateSDX_run_allValid() throws MailException, MessagingException verify(mockActiveTimService).getActiveTimsForSDX(); verify(mockSdwService, times(2)).getItisCodesFromAdvisoryMessage(any()); - // No email was sent - verify(mockEmailHelper, times(0)).SendEmail(any(), any(), any()); + // Email was sent + verify(mockEmailHelper, times(1)).SendEmail(any(), any(), any()); } @Test - public void validateSDX_noSdx() throws MailException, MessagingException { + void validateSDX_noSdx() throws MailException, MessagingException, SdwService.SdwServiceException { // Arrange // 2 Active TIMs, 0 SDX. - ActiveTim[] activeTims = importJsonArray("/activeTims_1.json", ActiveTim[].class); - AdvisorySituationDataDeposit[] asdds = new AdvisorySituationDataDeposit[0]; + List activeTims = new ArrayList<>(); + ActiveTim activeTim1 = new ActiveTim(); + activeTim1.setActiveTimId(100L); + activeTim1.setSatRecordId("00000000"); + activeTim1.setItisCodes(Arrays.asList(8, 7, 6)); + activeTim1.setEndDateTime("2099-12-31 23:59:59"); + activeTims.add(activeTim1); + ActiveTim activeTim2 = new ActiveTim(); + activeTim2.setActiveTimId(101L); + activeTim2.setSatRecordId("00000001"); + activeTim2.setItisCodes(Arrays.asList(8, 7, 6)); + activeTim2.setEndDateTime("2099-12-31 23:59:59"); + activeTims.add(activeTim2); + + List asdds = new ArrayList<>(); // Arrange service responses - when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(Arrays.asList(asdds)); - when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(Arrays.asList(activeTims)); + when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(asdds); + when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(activeTims); List resubExs = new ArrayList<>(); - resubExs.add(new ResubmitTimException(-1l, "Unit test exception")); + resubExs.add(new ResubmitTimException(-1L, "Unit test exception")); when(mockTimGenerationHelper.resubmitToOde(any())).thenReturn(resubExs); // Act @@ -148,25 +192,36 @@ public void validateSDX_noSdx() throws MailException, MessagingException { Assertions.assertEquals(0, invDbRecordsCaptor.getValue().size()); Gson gson = new Gson(); - String exceptionText = "The following exceptions were found while attempting to resubmit TIMs: "; - exceptionText += "
"; + StringBuilder expectedExceptionText = new StringBuilder("The following exceptions were found while attempting to resubmit TIMs: "); + expectedExceptionText.append("
"); for (ResubmitTimException rte : resubExs) { - exceptionText += gson.toJson(rte); - exceptionText += "
"; + expectedExceptionText.append(gson.toJson(rte)); + expectedExceptionText.append("
"); } - Assertions.assertEquals(exceptionText, exceptionMessageCaptor.getValue()); + expectedExceptionText.append("Note: TIM resubmission failures may indicate issues with the TIM data or the ODE service. Please investigate the failed resubmissions and consider reaching out to the ODE support team for assistance.
"); + Assertions.assertEquals(expectedExceptionText.toString(), exceptionMessageCaptor.getValue()); } @Test - public void validateSDX_noDatabase() throws MailException, MessagingException { // note: this test uses reflection, which must be manually allowed via the --add-opens JVM arg + void validateSDX_noDatabase() throws MailException, MessagingException, SdwService.SdwServiceException, IOException { // Arrange // 0 Active TIMS, 2 SDX. - ActiveTim[] activeTims = new ActiveTim[0]; - AdvisorySituationDataDeposit[] asdds = importJsonArray("/asdds_1.json", AdvisorySituationDataDeposit[].class); + List activeTims = new ArrayList<>(); + List asdds = new ArrayList<>(); + AdvisorySituationDataDeposit asdd1 = new AdvisorySituationDataDeposit(); + asdd1.setRecordId(0); + asdd1.setAdvisoryMessage("0"); + asdds.add(asdd1); + AdvisorySituationDataDeposit asdd2 = new AdvisorySituationDataDeposit(); + asdd2.setRecordId(1); + asdd2.setAdvisoryMessage("1"); + asdds.add(asdd2); + doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("0"); + doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("1"); // Arrange service responses - when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(Arrays.asList(asdds)); - when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(Arrays.asList(activeTims)); + when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(asdds); + when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(activeTims); HashMap sdxDelResults = new HashMap<>(); sdxDelResults.put(0, true); @@ -196,27 +251,66 @@ public void validateSDX_noDatabase() throws MailException, MessagingException { Assertions.assertEquals(2, deleteFromSdxCaptor.getValue().size()); Assertions.assertEquals(0, invDbRecordsCaptor.getValue().size()); - String exText = "The following recordIds failed to delete from the SDX: 1
"; - Assertions.assertEquals(exText, exceptionMessageCaptor.getValue()); + String expectedExceptionText = "The following recordIds failed to delete from the SDX: 1
Note: SDX deletion failures may indicate errant data in the SDX. Please investigate the failed deletions and consider reaching out to the SDX support team for assistance.
"; + Assertions.assertEquals(expectedExceptionText, exceptionMessageCaptor.getValue()); } @Test - public void validateSDX_mixSuccess() throws MailException, MessagingException { // note: this test uses reflection, which must be manually allowed via the --add-opens JVM arg + void validateSDX_mixSuccess() throws MailException, MessagingException, SdwService.SdwServiceException { // 3 Active TIMs, 3 SDX records. // 2 Active TIM and SDX records are aligned. Of those, 1 pair is accurate while // another is "stale". // The last Active TIM isn't present on the SDX, and the last SDX record is // orphaned. - ActiveTim[] activeTims = importJsonArray("/activeTims_2.json", ActiveTim[].class); - AdvisorySituationDataDeposit[] asdds = importJsonArray("/asdds_2.json", AdvisorySituationDataDeposit[].class); + List activeTims = new ArrayList<>(); + List asdds = new ArrayList<>(); + + // Create corresponding pair of records with matching ITIS codes, should result in no action + ActiveTim at1 = new ActiveTim(); // corresponds to asdd1 + at1.setActiveTimId(100L); + at1.setSatRecordId("00000000"); + at1.setItisCodes(Arrays.asList(8, 7, 6)); + at1.setEndDateTime("2099-12-31 23:59:59"); + activeTims.add(at1); + + AdvisorySituationDataDeposit a1 = new AdvisorySituationDataDeposit(); // corresponds to at1 + a1.setRecordId(0); + a1.setAdvisoryMessage("0"); + asdds.add(a1); + doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("0"); + + // Create corresponding pair of records with differing ITIS codes, should be marked for resend + ActiveTim at2 = new ActiveTim(); // corresponds to asdd2, but is stale + at2.setActiveTimId(101L); + at2.setSatRecordId("00000001"); + at2.setItisCodes(Arrays.asList(1, 2, 3)); + at2.setEndDateTime("2099-12-31 23:59:59"); + + AdvisorySituationDataDeposit a2 = new AdvisorySituationDataDeposit(); // corresponds to at2, but is stale + a2.setRecordId(1); + a2.setAdvisoryMessage("-1"); + asdds.add(a2); + doReturn(List.of(0)).when(mockSdwService).getItisCodesFromAdvisoryMessage("-1"); // stale record + + // Create ActiveTim with no corresponding ASDD + activeTims.add(at2); + ActiveTim at3 = new ActiveTim(); // no corresponding ASDD + at3.setActiveTimId(102L); + at3.setSatRecordId("00000017"); + at3.setItisCodes(Arrays.asList(17, 16, 18)); + at3.setEndDateTime("2099-12-31 23:59:59"); + activeTims.add(at3); + + // Create ASDD with no corresponding ActiveTim + AdvisorySituationDataDeposit a3 = new AdvisorySituationDataDeposit(); // no corresponding ActiveTim + a3.setRecordId(11); + a3.setAdvisoryMessage("1"); + asdds.add(a3); + doReturn(Arrays.asList(17, 16, 18)).when(mockSdwService).getItisCodesFromAdvisoryMessage("1"); // Arrange service responses - when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(Arrays.asList(asdds)); - when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(Arrays.asList(activeTims)); - // Return ITIS codes for ASDDs - doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("0"); - // Stale record - doReturn(Arrays.asList(0)).when(mockSdwService).getItisCodesFromAdvisoryMessage("-1"); + when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(asdds); + when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(activeTims); HashMap sdxDelResults = new HashMap<>(); sdxDelResults.put(0, true); @@ -224,7 +318,7 @@ public void validateSDX_mixSuccess() throws MailException, MessagingException { when(mockSdwService.deleteSdxDataByRecordIdIntegers(any())).thenReturn(sdxDelResults); List resubExs = new ArrayList<>(); - resubExs.add(new ResubmitTimException(-1l, "Unit test exception")); + resubExs.add(new ResubmitTimException(-1L, "Unit test exception")); when(mockTimGenerationHelper.resubmitToOde(any())).thenReturn(resubExs); // Act @@ -235,7 +329,7 @@ public void validateSDX_mixSuccess() throws MailException, MessagingException { // - Number of stale records on SDX (different ITIS codes than ActiveTim): 1 // - Number of messages on SDX without corresponding Database record: 1 // - Number of Database records without corresponding message in SDX: 1 - // - toResend count: 1 + // - toResend count: 2 // - deleteFromSdx count: 1 // - invDbRecords count: 0 @@ -256,13 +350,65 @@ public void validateSDX_mixSuccess() throws MailException, MessagingException { Assertions.assertEquals(0, invDbRecordsCaptor.getValue().size()); Gson gson = new Gson(); - String exceptionText = "The following recordIds failed to delete from the SDX: 1
"; - exceptionText += "The following exceptions were found while attempting to resubmit TIMs: "; - exceptionText += "
"; + StringBuilder expectedExceptionText = new StringBuilder("The following recordIds failed to delete from the SDX: 1
"); + expectedExceptionText.append("The following exceptions were found while attempting to resubmit TIMs: "); + expectedExceptionText.append("
"); for (ResubmitTimException rte : resubExs) { - exceptionText += gson.toJson(rte); - exceptionText += "
"; + expectedExceptionText.append(gson.toJson(rte)); + expectedExceptionText.append("
"); } - Assertions.assertEquals(exceptionText, exceptionMessageCaptor.getValue()); + expectedExceptionText.append("Note: SDX deletion failures may indicate errant data in the SDX. Please investigate the failed deletions and consider reaching out to the SDX support team for assistance.
Note: TIM resubmission failures may indicate issues with the TIM data or the ODE service. Please investigate the failed resubmissions and consider reaching out to the ODE support team for assistance.
"); + Assertions.assertEquals(expectedExceptionText.toString(), exceptionMessageCaptor.getValue()); + } + + @Test + void validateSDX_run_emptyDeleteList() throws MailException, MessagingException, SdwService.SdwServiceException { + // Arrange + // Setup active TIMs and SDX records where nothing needs to be deleted + ActiveTim[] activeTims = new ActiveTim[1]; + ActiveTim activeTim = new ActiveTim(); + activeTim.setActiveTimId(100L); + activeTim.setSatRecordId("00000000"); + activeTim.setItisCodes(Arrays.asList(8, 7, 6)); + activeTim.setEndDateTime("2099-12-31 23:59:59"); + activeTims[0] = activeTim; + + AdvisorySituationDataDeposit[] asdds = new AdvisorySituationDataDeposit[1]; + AdvisorySituationDataDeposit asdd = new AdvisorySituationDataDeposit(); + asdd.setRecordId(0); + asdd.setAdvisoryMessage("0"); + asdds[0] = asdd; + + when(mockSdwService.getMsgsForOdeUser(SemiDialogID.AdvSitDataDep)).thenReturn(Arrays.asList(asdds)); + when(mockActiveTimService.getActiveTimsForSDX()).thenReturn(Arrays.asList(activeTims)); + doReturn(Arrays.asList(8, 7, 6)).when(mockSdwService).getItisCodesFromAdvisoryMessage("0"); + + // Act + uut.run(); + + // Assert + // Verify SDX deletion was not called since there were no records to delete + verify(mockSdwService, times(0)).deleteSdxDataByRecordIdIntegers(any()); + + // Email should be sent since there were no discrepancies + verify(mockEmailHelper, times(1)).SendEmail(any(), any(), any()); + } + + /** + * Creates a Gson instance with a LocalDateTime adapter to avoid reflection issues. + * @return Gson instance + */ + static Gson createGson() { + return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new TypeAdapter() { + @Override + public void write(JsonWriter out, LocalDateTime value) throws IOException { + out.value(value.toString()); + } + + @Override + public LocalDateTime read(JsonReader in) throws IOException { + return LocalDateTime.parse(in.nextString()); + } + }).create(); } } \ No newline at end of file diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateTmddTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateTmddTest.java index 71079a958..cd21ab3f2 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateTmddTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/ValidateTmddTest.java @@ -1,23 +1,8 @@ package com.trihydro.tasks.actions; -import static com.trihydro.tasks.TestHelper.importJsonArray; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.List; - -import javax.mail.MessagingException; - import com.google.gson.Gson; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.GsonFactory; -import com.trihydro.library.helpers.TimGenerationHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.ActiveTimError; import com.trihydro.library.model.ActiveTimValidationResult; @@ -32,21 +17,32 @@ import com.trihydro.tasks.config.DataTasksConfiguration; import com.trihydro.tasks.helpers.EmailFormatter; import com.trihydro.tasks.helpers.IdNormalizer; - import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mail.MailException; import org.springframework.mail.MailSendException; import org.springframework.web.client.RestClientException; +import javax.mail.MessagingException; +import java.util.Arrays; +import java.util.List; + +import static com.trihydro.tasks.TestHelper.importJsonArray; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + @ExtendWith(MockitoExtension.class) public class ValidateTmddTest { @Mock @@ -58,39 +54,30 @@ public class ValidateTmddTest { @Mock private ItisCodeService mockItisCodeService; - @Mock - private Utility mockUtility; - - @Spy - private IdNormalizer spyIdNormalizer; - @Mock private EmailHelper mockEmailHelper; - @Mock - private DataTasksConfiguration mockConfig; - @Mock private EmailFormatter mockEmailFormatter; @Mock private WydotTimService mockWydotTimService; - @Mock - private TimGenerationHelper mockTimGenerationHelper; - @Captor private ArgumentCaptor> unableToVerifyCaptor; @Captor private ArgumentCaptor> validationResultsCaptor; - @Captor - private ArgumentCaptor logMessageCaptor; - @Captor private ArgumentCaptor exceptionMessageCaptor; + @Mock(answer = Answers.CALLS_REAL_METHODS) + IdNormalizer mockIdNormalizer; + + @Mock + DataTasksConfiguration mockConfig; + @InjectMocks ValidateTmdd uut; @@ -260,75 +247,4 @@ public void validateTmdd_run_unableToVerifyErrors() throws Exception { // Email was sent verify(mockEmailHelper).SendEmail(any(), any(), any()); } - - @Test - public void validateTmdd_run_activeTimServiceError() throws MailException, MessagingException { - // Arrange - when(mockActiveTimService.getActiveTimsWithItisCodes(true)).thenThrow(new RestClientException("timeout")); - - // Act - uut.run(); - - // Assert - verify(mockUtility, times(2)).logWithDate(logMessageCaptor.capture(), eq(ValidateTmdd.class)); - Assertions.assertEquals("Error fetching Active Tims:", logMessageCaptor.getValue()); - verify(mockEmailHelper).SendEmail(any(), any(), any()); - } - - @Test - public void validateTmdd_run_tmddServiceError() throws Exception { - // Arrange - when(mockTmddService.getTmddEvents()).thenThrow(new RestClientException("timeout")); - - // Act - uut.run(); - - // Assert - verify(mockUtility, times(2)).logWithDate(logMessageCaptor.capture(), eq(ValidateTmdd.class)); - Assertions.assertEquals("Error fetching FEUs from TMDD:", logMessageCaptor.getValue()); - verify(mockEmailHelper).SendEmail(any(), any(), any()); - } - - @Test - public void validateTmdd_run_itisCodeServiceError() throws MailException, MessagingException { - // Arrange - when(mockItisCodeService.selectAllTmddItisCodes()).thenThrow(new RestClientException("timeout")); - - // Act - uut.run(); - - // Assert - verify(mockUtility, times(2)).logWithDate(logMessageCaptor.capture(), eq(ValidateTmdd.class)); - Assertions.assertEquals("Unable to initialize TMDD ITIS Code cache:", logMessageCaptor.getValue()); - verify(mockEmailHelper).SendEmail(any(), any(), any()); - } - - @Test - public void validateTmdd_run_emailSendError() throws Exception { - // Arrange - // Load in fake data so we can accumulate validation errors - Gson tmddDeserializer = new GsonFactory().getTmddDeserializer(); - - ActiveTim[] activeTims = importJsonArray("/activeTims_5.json", ActiveTim[].class); - FullEventUpdate[] feus = importJsonArray("/feus_2.json", FullEventUpdate[].class, tmddDeserializer); - TmddItisCode[] itisCodes = importJsonArray("/tmdd_itis_codes.json", TmddItisCode[].class); - - when(mockTmddService.getTmddEvents()).thenReturn(Arrays.asList(feus)); - when(mockActiveTimService.getActiveTimsWithItisCodes(true)).thenReturn(Arrays.asList(activeTims)); - when(mockItisCodeService.selectAllTmddItisCodes()).thenReturn(Arrays.asList(itisCodes)); - - TimDeleteSummary tds = new TimDeleteSummary(); - when(mockWydotTimService.deleteTimsFromRsusAndSdx(any())).thenReturn(tds); - - // Throw exception when sending email - doThrow(new MailSendException("unable to send")).when(mockEmailHelper).SendEmail(any(), any(), any()); - - // Act - uut.run(); - - // Assert - verify(mockUtility, times(3)).logWithDate(logMessageCaptor.capture(), eq(ValidateTmdd.class)); - Assertions.assertEquals("Error sending summary email:", logMessageCaptor.getAllValues().get(1)); - Assertions.assertEquals("Failed to send error summary email:", logMessageCaptor.getAllValues().get(2)); - } } \ No newline at end of file diff --git a/cv-data-tasks/src/test/resources/activeTims_1.json b/cv-data-tasks/src/test/resources/activeTims_1.json deleted file mode 100644 index 2174efcc9..000000000 --- a/cv-data-tasks/src/test/resources/activeTims_1.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "activeTimId": 0, - "satRecordId": "00000000", - "itisCodes": [6, 7, 8] - }, - { - "activeTimId": 10, - "satRecordId": "FFFFFFFF", - "itisCodes": [16, 17, 18] - } -] diff --git a/cv-data-tasks/src/test/resources/activeTims_2.json b/cv-data-tasks/src/test/resources/activeTims_2.json deleted file mode 100644 index dbd7d0da3..000000000 --- a/cv-data-tasks/src/test/resources/activeTims_2.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { - "activeTimId": 0, - "satRecordId": "00000000", - "itisCodes": [6, 7, 8] - }, - { - "activeTimId": 10, - "satRecordId": "FFFFFFFF", - "itisCodes": [16, 17, 18] - }, - { - "activeTimId": 0, - "satRecordId": "00000001", - "itisCodes": [6, 7, 8] - } -] diff --git a/cv-data-tasks/src/test/resources/asdds_1.json b/cv-data-tasks/src/test/resources/asdds_1.json deleted file mode 100644 index 66a6d3ce3..000000000 --- a/cv-data-tasks/src/test/resources/asdds_1.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "advisoryMessage": "0", - "recordId": 0 - }, - { - "advisoryMessage": "-1", - "recordId": -1 - } -] diff --git a/cv-data-tasks/src/test/resources/asdds_2.json b/cv-data-tasks/src/test/resources/asdds_2.json deleted file mode 100644 index c9f69227c..000000000 --- a/cv-data-tasks/src/test/resources/asdds_2.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "advisoryMessage": "0", - "recordId": 0 - }, - { - "advisoryMessage": "-1", - "recordId": -1 - }, - { - "advisoryMessage": "", - "recordId": 2 - } - ] - \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f067bf275..881afec97 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,8 +36,8 @@ services: - /home/timmadmin/timm/ode-wrapper:/keystore logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} cv-data-tasks: build: cv-data-tasks @@ -84,12 +84,15 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} depends_on: - - ode_wrapper - - cv-data-controller - - rsu-data-controller + ode_wrapper: + condition: service_healthy + cv-data-controller: + condition: service_started + rsu-data-controller: + condition: service_started tim-refresh: build: tim-refresh @@ -113,8 +116,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} cv-data-controller: build: cv-data-controller @@ -145,8 +148,8 @@ services: - /home/timmadmin/timm/cv-data-controller:/keystore logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} rsu-data-controller: build: rsu-data-controller @@ -164,8 +167,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} tim_logger_mongo: build: ode-mongo-logger @@ -187,8 +190,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} bsm_logger_mongo: build: ode-mongo-logger @@ -210,8 +213,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} driveralert_logger_mongo: build: ode-mongo-logger @@ -233,8 +236,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} tim_logger: build: ode-data-logger @@ -254,8 +257,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} logger-kafka-consumer: build: logger-kafka-consumer @@ -280,8 +283,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} cert-expiration: build: cert-expiration @@ -301,5 +304,5 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" \ No newline at end of file + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} diff --git a/gather_and_compress_jars.sh b/gather_and_compress_jars.sh old mode 100644 new mode 100755 diff --git a/load-test/requests/create-update-rc-tim.js b/load-test/requests/create-update-rc-tim.js index d1486a05f..b430636f9 100644 --- a/load-test/requests/create-update-rc-tim.js +++ b/load-test/requests/create-update-rc-tim.js @@ -3,7 +3,7 @@ export default [ timRcList: [ { route: "WY 233", - roadCode: "233-Test", + roadCode: "test-KEMWYO233B", advisory: [5895, 5907], direction: "B", startPoint: { latitude: 41.80825184, longitude: -110.5341319 }, @@ -15,7 +15,7 @@ export default [ timRcList: [ { route: "US 20", - roadCode: "20-Test", + roadCode: "test-THERUS20WYO789SB", advisory: [5895, 5907], direction: "B", startPoint: { latitude: 43.40291046, longitude: -108.1676454 }, @@ -27,7 +27,7 @@ export default [ timRcList: [ { route: "WY 387", - roadCode: "387-Test", + roadCode: "test-RJWYO387WB", advisory: [5907], direction: "B", startPoint: { latitude: 43.61739936, longitude: -105.78594247 }, @@ -39,7 +39,7 @@ export default [ timRcList: [ { route: "US 20", - roadCode: "20-Test-2", + roadCode: "test-LUUS20EB", advisory: [5906], direction: "B", startPoint: { latitude: 42.76189009, longitude: -104.44152876 }, @@ -47,7 +47,7 @@ export default [ }, { route: "WY 270", - roadCode: "270-Test", + roadCode: "test-LUW270NB", advisory: [5906], direction: "B", startPoint: { latitude: 42.78126098, longitude: -104.6179708 }, @@ -55,7 +55,7 @@ export default [ }, { route: "US 85", - roadCode: "85-Test", + roadCode: "test-LUUS85NB", advisory: [5906], direction: "B", startPoint: { latitude: 42.77033909, longitude: -104.44953692 }, @@ -63,7 +63,7 @@ export default [ }, { route: "WY 270", - roadCode: "270-Test-2", + roadCode: "test-LUW270GB", advisory: [5906], direction: "B", startPoint: { latitude: 42.60951184, longitude: -104.66387969 }, @@ -71,7 +71,7 @@ export default [ }, { route: "US 20", - roadCode: "20-Test-3", + roadCode: "test-LUUS1820WB", advisory: [5906], direction: "B", startPoint: { latitude: 42.7599073, longitude: -104.89314146 }, @@ -79,7 +79,7 @@ export default [ }, { route: "US 85", - roadCode: "85-Test-2", + roadCode: "test-LUUS85SB", advisory: [5906], direction: "B", startPoint: { latitude: 42.60991089, longitude: -104.43064063 }, @@ -91,7 +91,7 @@ export default [ timRcList: [ { route: "WY 222", - roadCode: "222-Test", + roadCode: "test-CHEYW222FTAB", advisory: [5895], direction: "B", startPoint: { latitude: 41.10533726, longitude: -104.88640271 }, @@ -99,7 +99,7 @@ export default [ }, { route: "WY 212", - roadCode: "212-Test", + roadCode: "test-CHEYW212FMRB", advisory: [5895], direction: "B", startPoint: { latitude: 41.1889919, longitude: -104.76461603 }, @@ -107,7 +107,7 @@ export default [ }, { route: "WY 212", - roadCode: "212-Test-2", + roadCode: "test-CHEYW212CLDB", advisory: [5895], direction: "B", startPoint: { latitude: 41.0992874, longitude: -104.8477219 }, @@ -115,4 +115,4 @@ export default [ }, ], }, -]; \ No newline at end of file +]; diff --git a/load-test/test_create-update-rc-tim.js b/load-test/test_create-update-rc-tim.js index 55708edf3..87c1af19d 100644 --- a/load-test/test_create-update-rc-tim.js +++ b/load-test/test_create-update-rc-tim.js @@ -16,4 +16,4 @@ export default function () { const reqBody = requests[Math.floor(Math.random() * requests.length)]; let res = http.post(url, JSON.stringify(reqBody), { headers: headers }); check(res, { "status was 200": (r) => r.status == 200 }); -} \ No newline at end of file +} diff --git a/local-deployment/docker-compose.yml b/local-deployment/docker-compose.yml index d33ed2200..68b29368f 100644 --- a/local-deployment/docker-compose.yml +++ b/local-deployment/docker-compose.yml @@ -2,7 +2,7 @@ version: "2" services: # external dependencies ---------------------------------------------- kafka: - image: bitnami/kafka:latest + image: bitnamilegacy/kafka:3.8.0 hostname: kafka ports: - "9092:9092" @@ -23,11 +23,12 @@ services: KAFKA_CFG_LOG_RETENTION_HOURS: 2 logging: options: - max-size: "10m" - max-file: "5" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} + restart: always kafka_init: - image: bitnami/kafka:latest + image: bitnamilegacy/kafka:3.8.0 depends_on: kafka: condition: service_started @@ -81,8 +82,9 @@ services: - ${DOCKER_SHARED_VOLUME}/uploads:/home/uploads logging: options: - max-size: "10m" - max-file: "5" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} + restart: always adm: image: usdotjpoode/asn1_codec:2025-q1 @@ -98,9 +100,9 @@ services: - ${DOCKER_SHARED_VOLUME}:/asn1_codec_share logging: options: - max-size: "10m" - max-file: "5" - restart: on-failure + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} + restart: always aem: image: usdotjpoode/asn1_codec:2025-q1 @@ -116,9 +118,9 @@ services: - ${DOCKER_SHARED_VOLUME}:/asn1_codec_share logging: options: - max-size: "10m" - max-file: "5" - restart: on-failure + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} + restart: always # Note: the SDX does not accept unsigned data, so this is commented out for now # sdw_depositor: @@ -139,7 +141,8 @@ services: # options: # max-size: "10m" # max-file: "5" - + # restart: always + postgres: image: postgis/postgis:15-master restart: always @@ -152,7 +155,8 @@ services: - pgdb:/var/lib/postgresql/data logging: options: - max-size: '10m' + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} mongo: image: mongo @@ -166,7 +170,8 @@ services: - mongodb_data_container:/data/db logging: options: - max-size: '10m' + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} # end of external dependencies ---------------------------------------------- # timm apps -------------------------------------------------------------- @@ -193,8 +198,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} ode-wrapper: build: ../ode-wrapper @@ -226,8 +231,8 @@ services: - "7777:7777" logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} rsu-data-controller: build: ../rsu-data-controller @@ -245,8 +250,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} cv-data-tasks: build: ../cv-data-tasks @@ -257,7 +262,7 @@ services: CONFIG_CV_REST_SERVICE_DEV: http://cv-data-controller:8888 CONFIG_CV_REST_SERVICE_PROD: http://cv-data-controller:8888 CONFIG_RSU_DATA_SERVICE_URL: http://rsu-data-controller:8898 - CONFIG_WRAPPER_URL: "http://ode-wrapper:7777" + CONFIG_WRAPPER_URL: ${ODE_WRAPPER_REST_SERVICE} CONFIG_MAIL_HOST: 172.0.0.1 CONFIG_MAIL_PORT: 123 CONFIG_ENVIRONMENT_NAME: local @@ -293,12 +298,15 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} depends_on: - - ode-wrapper - - cv-data-controller - - rsu-data-controller + ode-wrapper: + condition: service_healthy + cv-data-controller: + condition: service_started + rsu-data-controller: + condition: service_started tim_logger: build: ../ode-data-logger @@ -318,8 +326,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} logger-kafka-consumer: build: ../logger-kafka-consumer @@ -344,8 +352,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} tim-refresh: build: ../tim-refresh @@ -369,8 +377,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} cert-expiration: build: ../cert-expiration @@ -389,8 +397,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} tim_logger_mongo: build: ../ode-mongo-logger @@ -412,8 +420,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} bsm_logger_mongo: build: ../ode-mongo-logger @@ -435,8 +443,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} driveralert_logger_mongo: build: ../ode-mongo-logger @@ -458,8 +466,8 @@ services: LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: - max-size: "20m" - max-file: "3" + max-size: ${LOGGING_OPTIONS_MAX_SIZE} + max-file: ${LOGGING_OPTIONS_MAX_FILES} # end of timm apps --------------------------------------------------------- # volumes --------------------------------------------------------------------- diff --git a/logger-kafka-consumer/.mvn/wrapper/MavenWrapperDownloader.java b/logger-kafka-consumer/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/logger-kafka-consumer/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/logger-kafka-consumer/.mvn/wrapper/maven-wrapper.jar b/logger-kafka-consumer/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/logger-kafka-consumer/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/logger-kafka-consumer/.mvn/wrapper/maven-wrapper.properties b/logger-kafka-consumer/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce..000000000 --- a/logger-kafka-consumer/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/logger-kafka-consumer/mvnw b/logger-kafka-consumer/mvnw deleted file mode 100644 index a16b5431b..000000000 --- a/logger-kafka-consumer/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/logger-kafka-consumer/mvnw.cmd b/logger-kafka-consumer/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/logger-kafka-consumer/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java index 627195a68..b8650fe55 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java @@ -1,5 +1,7 @@ package com.trihydro.loggerkafkaconsumer.app; +import com.trihydro.library.helpers.DateTimeHelper; +import com.trihydro.library.helpers.DateTimeHelperImpl; import java.util.Date; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -7,7 +9,6 @@ import com.google.gson.Gson; import com.trihydro.library.factory.KafkaFactory; import com.trihydro.library.helpers.EmailHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.CertExpirationModel; import com.trihydro.library.model.TopicDataWrapper; @@ -21,12 +22,14 @@ import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import org.springframework.stereotype.Component; import us.dot.its.jpo.ode.model.OdeData; @Component @Slf4j +@Import(DateTimeHelperImpl.class) public class LoggerKafkaConsumer { private final LoggerConfiguration loggerConfig; @@ -35,22 +38,24 @@ public class LoggerKafkaConsumer { private final ActiveTimHoldingService activeTimHoldingService; private final TimService timService; private final TimDataConverter timDataConverter; - private final Utility utility; private final EmailHelper emailHelper; + private final DateTimeHelper dateTimeHelper; + + private final Gson gson = new Gson(); @Autowired public LoggerKafkaConsumer(LoggerConfiguration _loggerConfig, KafkaFactory _kafkaFactory, ActiveTimService _activeTimService, TimService _timService, - TimDataConverter _timDataConverter, Utility _utility, EmailHelper _emailHelper, - ActiveTimHoldingService _activeTimHoldingService) throws Exception { + TimDataConverter _timDataConverter, EmailHelper _emailHelper, + ActiveTimHoldingService _activeTimHoldingService, DateTimeHelper dateTimeHelper) throws Exception { loggerConfig = _loggerConfig; kafkaFactory = _kafkaFactory; activeTimService = _activeTimService; timService = _timService; timDataConverter = _timDataConverter; - utility = _utility; emailHelper = _emailHelper; activeTimHoldingService = _activeTimHoldingService; + this.dateTimeHelper = dateTimeHelper; log.info("Logger Kafka Consumer starting.............."); @@ -59,126 +64,194 @@ public LoggerKafkaConsumer(LoggerConfiguration _loggerConfig, KafkaFactory _kafk startKafkaConsumer(); } + /** + * Starts the Kafka consumer to process messages from configured topics. + * Handles TIM messages, driver alerts, and certificate expiration messages. + * + * @throws Exception If there is an issue starting or running the consumer + */ public void startKafkaConsumer() throws Exception { + log.info("Starting Kafka consumer at {}:9092 for group {} and topic {}", loggerConfig.getKafkaHostServer(), loggerConfig.getDepositGroup(), + loggerConfig.getDepositTopic()); String endpoint = loggerConfig.getKafkaHostServer() + ":9092"; - var stringConsumer = kafkaFactory.createStringConsumer(endpoint, loggerConfig.getDepositGroup(), - loggerConfig.getDepositTopic(), loggerConfig.getMaxPollIntervalMs(), - loggerConfig.getMaxPollRecords()); + var stringConsumer = kafkaFactory.createStringConsumer(endpoint, loggerConfig.getDepositGroup(), loggerConfig.getDepositTopic(), + loggerConfig.getMaxPollIntervalMs(), loggerConfig.getMaxPollRecords()); - Gson gson = new Gson(); + log.debug("Kafka consumer configuration: maxPollIntervalMs={}, maxPollRecords={}", loggerConfig.getMaxPollIntervalMs(), + loggerConfig.getMaxPollRecords()); try { - OdeData odeData; var recordCount = 0; + log.info("Kafka consumer loop started, waiting for messages..."); + while (true) { ConsumerRecords records = stringConsumer.poll(100); recordCount = records.count(); + if (recordCount > 0) { - log.info("Found {} records to parse", recordCount); + log.info("Polling found {} new record(s) to process", recordCount); } + for (ConsumerRecord record : records) { - TopicDataWrapper tdw = null; + log.debug("Processing record from partition={}, offset={}", record.partition(), record.offset()); + + TopicDataWrapper tdw; try { tdw = gson.fromJson(record.value(), TopicDataWrapper.class); + log.trace("Deserialized JSON to TopicDataWrapper"); } catch (Exception e) { - // Could be ioException, JsonParseException, JsonMappingException - log.error("Failed to parse record: {}", record.value(), e); + log.error("Failed to parse record from partition={}, offset={}: {}", record.partition(), record.offset(), e.getMessage()); + log.debug("Problematic record content: {}", record.value()); + continue; } + if (tdw != null && tdw.getData() != null) { - log.info("Found data for topic: {}", tdw.getTopic()); - switch (tdw.getTopic()) { - case "topic.OdeTimJson": - log.trace("Before processing JSON: {}", tdw.getData()); - odeData = timDataConverter.processTimJson(tdw.getData()); - log.trace("After processing JSON: {}", gson.toJson(odeData)); - if (odeData != null) { - if (odeData.getMetadata() - .getRecordGeneratedBy() == us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy.TMC) { - timService.addActiveTimToDatabase(odeData); - } else if (odeData.getMetadata().getRecordGeneratedBy() == null) { - // we shouldn't get here...log it - log.error("Failed to get recordGeneratedBy, continuing..."); - } else { - timService.addTimToDatabase(odeData); - } - } else { - log.error("Failed to parse topic.OdeTimJson, insert fails"); - } - break; - - case "topic.OdeTIMCertExpirationTimeJson": - try { - CertExpirationModel certExpirationModel = gson.fromJson(tdw.getData(), - CertExpirationModel.class); - var success = timService.updateActiveTimExpiration(certExpirationModel); - if (success) { - log.info("Successfully updated expiration date"); - } else { - // Check for issues - var activeTim = activeTimService - .getActiveTimByPacketId(certExpirationModel.getPacketID()); - - // Check if activeTim exists - if (activeTim == null) { - // active_tim not created yet, check active_tim_holding - var ath = activeTimHoldingService - .getActiveTimHoldingByPacketId(certExpirationModel.getPacketID()); - - if (ath != null) { - // update ath expiration - success = activeTimHoldingService.updateTimExpiration( - certExpirationModel.getPacketID(), - certExpirationModel.getExpirationDate()); - } - } else if (messageSuperseded(certExpirationModel.getStartDateTime(), activeTim)) { - // Message superseded - log.info("Unable to update expiration date for Active Tim {} (Packet ID: {}). Message superseded.", - activeTim.getActiveTimId(), certExpirationModel.getPacketID()); - } - - if (!success) { - // Message either not superseded, or not found in active_tim nor holding tables. error case - log.error("Failed to update expiration for data: {}", tdw.getData()); - - String body = "logger-kafka-consumer failed attempting to update the expiration for an ActiveTim record"; - body += "
"; - body += "The associated expiration topic record is:
"; - body += tdw.getData(); - emailHelper.SendEmail(loggerConfig.getAlertAddresses(), - "Failed To Update ActiveTim Expiration", body); - } - } - } catch (Exception ex) { - log.error("Failed to parse topic.OdeTIMCertExpirationTimeJson, insert fails", ex); - } - break; + log.info("Processing message for topic: {}", tdw.getTopic()); + + try { + switch (tdw.getTopic()) { + case "topic.OdeTimJson": + processOdeTimJson(tdw); + break; + + case "topic.OdeTIMCertExpirationTimeJson": + processOdeTIMCertExpirationTimeJson(tdw); + break; + + default: + log.warn("Unhandled topic: {}", tdw.getTopic()); + } + } catch (Exception e) { + log.error("Error processing message for topic {}: {}", tdw.getTopic(), e.getMessage(), e); } } else { - log.error("Logger Kafka Consumer failed to deserialize proper TopicDataWrapper"); + log.error("Deserialization failed - received invalid TopicDataWrapper"); if (tdw != null) { - log.error("Data: {}", tdw.getData()); + log.debug("Partial deserialization - wrapper exists but data is null"); } } } } } catch (Exception ex) { - log.error("Error in Kafka Consumer: {}", ex.getMessage()); - emailHelper.ContainerRestarted(loggerConfig.getAlertAddresses(), loggerConfig.getMailPort(), - loggerConfig.getMailHost(), loggerConfig.getFromEmail(), "Logger Kafka Consumer"); + log.error("Critical error in Kafka consumer main loop: {}", ex.getMessage(), ex); + + emailHelper.ContainerRestarted(loggerConfig.getAlertAddresses(), loggerConfig.getMailPort(), loggerConfig.getMailHost(), + loggerConfig.getFromEmail(), "Logger Kafka Consumer"); throw ex; } finally { + log.info("Closing Kafka consumer connection"); try { stringConsumer.close(); + log.info("Kafka consumer closed successfully"); } catch (Exception consumerEx) { - log.error("Failed to close consumer", consumerEx); + log.error("Failed to close Kafka consumer cleanly: {}", consumerEx.getMessage(), consumerEx); + } + } + } + + /** + * Process messages from the OdeTimJson topic + */ + private void processOdeTimJson(TopicDataWrapper tdw) { + log.trace("Starting OdeTimJson processing for message with length: {} bytes", tdw.getData().length()); + log.trace("Message content: {}", tdw.getData()); + + OdeData odeData = timDataConverter.processTimJson(tdw.getData()); + + if (odeData == null) { + log.error("Failed to parse topic.OdeTimJson message, database insertion skipped"); + return; + } + + log.trace("Successfully parsed OdeTimJson into OdeData object: {}", gson.toJson(odeData)); + + try { + if (odeData.getMetadata().getRecordGeneratedBy() == us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy.TMC) { + log.debug("Processing TIM generated by TMC"); + timService.addActiveTimToDatabase(odeData); + log.debug("Successfully added active TIM to database"); + } else if (odeData.getMetadata().getRecordGeneratedBy() == null) { + log.error("Failed to get recordGeneratedBy from metadata, defaulting to standard TIM processing"); + timService.addTimToDatabase(odeData); // TODO: identify if this method call can be removed + } else { + log.debug("Processing standard TIM with recordGeneratedBy: {}", odeData.getMetadata().getRecordGeneratedBy()); + timService.addTimToDatabase(odeData); + log.debug("Successfully added TIM to database"); + } + } catch (Exception e) { + log.error("Exception while processing OdeTimJson: {}", e.getMessage(), e); + } + } + + /** + * Process messages from the OdeTIMCertExpirationTimeJson topic + */ + private void processOdeTIMCertExpirationTimeJson(TopicDataWrapper tdw) { + log.debug("Starting OdeTIMCertExpirationTimeJson processing"); + + try { + CertExpirationModel certExpirationModel = gson.fromJson(tdw.getData(), CertExpirationModel.class); + log.debug("Processing certificate expiration for packet ID: {}", certExpirationModel.getPacketID()); + + var success = timService.updateActiveTimExpiration(certExpirationModel); + + if (success) { + log.info("Successfully updated expiration date for packet ID: {}", certExpirationModel.getPacketID()); + return; + } + + // Handle failure cases when the update was not successful + log.debug("Initial update attempt failed, checking for special cases"); + + // Check if active TIM exists + var activeTim = activeTimService.getActiveTimByPacketId(certExpirationModel.getPacketID()); + + if (activeTim == null) { + // Active TIM not found, try the holding table + log.debug("Active TIM not found for packet ID: {}, checking holding table", certExpirationModel.getPacketID()); + + var ath = activeTimHoldingService.getActiveTimHoldingByPacketId(certExpirationModel.getPacketID()); + + if (ath != null) { + log.debug("Found record in holding table, updating expiration"); + success = activeTimHoldingService.updateTimExpiration(certExpirationModel.getPacketID(), certExpirationModel.getExpirationDate()); + + if (success) { + log.info("Successfully updated expiration date in holding table for packet ID: {}", certExpirationModel.getPacketID()); + } else { + log.warn("Failed to update expiration date in holding table for packet ID: {}", certExpirationModel.getPacketID()); + } + } else { + log.warn("No record found in active TIM or holding tables for packet ID: {}", certExpirationModel.getPacketID()); + } + } else if (messageSuperseded(certExpirationModel.getStartDateTime(), activeTim)) { + // Message is superseded by newer data + log.info("Unable to update expiration date for Active TIM {} (Packet ID: {}). Message superseded by newer data.", + activeTim.getActiveTimId(), certExpirationModel.getPacketID()); + success = true; // Consider this a success case since no action is needed } + + if (!success) { + // Final failure case after all recovery attempts + log.error("Failed to update expiration for packet ID: {}, expiration date: {}", certExpirationModel.getPacketID(), + certExpirationModel.getExpirationDate()); + + String body = "logger-kafka-consumer failed attempting to update the expiration for an ActiveTim record"; + body += "
"; + body += "The associated expiration topic record is:
"; + body += tdw.getData(); + + emailHelper.SendEmail(loggerConfig.getAlertAddresses(), "Failed To Update ActiveTim Expiration", body); + } + } catch (Exception ex) { + log.error("Failed to parse topic.OdeTIMCertExpirationTimeJson: {}", ex.getMessage(), ex); } } private boolean messageSuperseded(String startTime, ActiveTim dbRecord) { try { - Date expectedStart = utility.convertDate(startTime); + Date expectedStart = dateTimeHelper.convertDate(startTime); if (expectedStart == null || dbRecord.getStartTimestamp() == null) { return false; diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverter.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverter.java index 7d0a3c554..69a22cab6 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverter.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverter.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; + import com.google.gson.Gson; import com.trihydro.library.helpers.JsonToJavaConverter; @@ -16,6 +19,7 @@ import us.dot.its.jpo.ode.util.JsonUtils; @Component +@Slf4j public class TimDataConverter { public Gson gson = new Gson(); @@ -41,7 +45,7 @@ public OdeData processTimJson(String value) { try { recordGeneratedByStr = mapper.treeToValue(recordGeneratedBy, String.class); } catch (JsonProcessingException e) { - e.printStackTrace(); + log.error("Exception", e); } // if broadcast tim, translate accordingly, else translate as received TIM diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java index 18069c563..d401694a2 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java @@ -97,7 +97,7 @@ private ActiveTimHolding getSingleActiveTimHoldingFromResultSet(ResultSet rs) th activeTimHolding.setActiveTimHoldingId(rs.getLong("ACTIVE_TIM_HOLDING_ID")); activeTimHolding.setClientId(rs.getString("CLIENT_ID")); activeTimHolding.setDirection(rs.getString("DIRECTION")); - activeTimHolding.setRsuTargetId(rs.getString("RSU_TARGET")); + activeTimHolding.setRsuTarget(rs.getString("RSU_TARGET")); activeTimHolding.setSatRecordId(rs.getString("SAT_RECORD_ID")); activeTimHolding.setStartPoint( new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); @@ -110,6 +110,7 @@ private ActiveTimHolding getSingleActiveTimHoldingFromResultSet(ResultSet rs) th } activeTimHolding.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); activeTimHolding.setPacketId(rs.getString("PACKET_ID")); + activeTimHolding.setDesiredEndDateTime(rs.getString("TIM_END")); } return activeTimHolding; } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java index c9f61034b..14a7dfb1f 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java @@ -1,5 +1,7 @@ package com.trihydro.loggerkafkaconsumer.app.services; +import com.trihydro.library.helpers.DateStringNotInISO8601FormatException; +import com.trihydro.library.helpers.DateTimeHelper; import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; @@ -7,6 +9,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; +import java.sql.Types; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -30,14 +33,17 @@ public class ActiveTimService extends BaseService { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; private final Calendar UTCCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + private DateTimeHelper dateTimeHelper; @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { // TODO: use constructor instead of InjectDependencies + public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler, DateTimeHelper dateTimeHelper) { // TODO: use constructor instead of InjectDependencies timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; + this.dateTimeHelper = dateTimeHelper; } public Long insertActiveTim(ActiveTim activeTim) { + log.debug("Inserting active_tim record with client_id = {} and sat_record_id = {}", activeTim.getClientId(), activeTim.getSatRecordId()); String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim", timDbTables.getActiveTimTable()); @@ -55,24 +61,47 @@ public Long insertActiveTim(ActiveTim activeTim) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getDirection()); } else if (col.equals("TIM_START")) { log.info("Converting {} for TIM_START value", activeTim.getStartDateTime()); - java.util.Date tim_start_date = utility.convertDate(activeTim.getStartDateTime()); + Date tim_start_date = dateTimeHelper.convertDate(activeTim.getStartDateTime()); Timestamp ts = new Timestamp(tim_start_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); } else if (col.equals("TIM_END")) { - if (activeTim.getEndDateTime() != null) { - java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); - Timestamp ts = new Timestamp(tim_end_date.getTime()); - sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); - } else { - preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); + String endDateTimeStr = activeTim.getEndDateTime(); + + if (endDateTimeStr == null) { + preparedStatement.setNull(fieldNum, Types.TIMESTAMP); + fieldNum++; + continue; + } + log.trace("End date time string is not null. This is probably a planned condition with a long interval."); + + if (dateTimeHelper.isInTableFormat(endDateTimeStr)) { + log.trace("End date time string is in table format. Attempting to convert to ISO8601 format."); + try { + endDateTimeStr = dateTimeHelper.convertDateStringFromTableFormatIntoISO8601Format(endDateTimeStr); + } catch (Exception e) { + log.error("TIM_END candidate value was identified to be in table format but failed to convert to ISO8601 format: {}. Unable to continue.", endDateTimeStr); + return -2L; + } + } + + Timestamp timEndTimestamp; + try { + log.trace("Converting date string into timestamp object"); + timEndTimestamp = dateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject(endDateTimeStr); } + catch (DateStringNotInISO8601FormatException e) { + log.error("TIM_END candidate value was not in ISO8601 format and failed to convert to timestamp object: {}. Unable to continue.", endDateTimeStr); + return -3L; + } + log.trace("Converted date string from ISO8601 format into timestamp object. Setting TIM_END value in prepared statement."); + sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, timEndTimestamp); } else if (col.equals("EXPIRATION_DATE")) { if (activeTim.getExpirationDateTime() != null) { - java.util.Date tim_exp_date = utility.convertDate(activeTim.getExpirationDateTime()); + Date tim_exp_date = dateTimeHelper.convertDate(activeTim.getExpirationDateTime()); Timestamp ts = new Timestamp(tim_exp_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); } else { - preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); + preparedStatement.setNull(fieldNum, Types.TIMESTAMP); } } else if (col.equals("TIM_TYPE_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimTypeId()); @@ -115,7 +144,9 @@ public Long insertActiveTim(ActiveTim activeTim) { fieldNum++; } - return dbInteractions.executeAndLog(preparedStatement, "active tim"); + Long activeTimId = dbInteractions.executeAndLog(preparedStatement, "active tim"); + log.trace("Done inserting active_tim record with client_id = {} and sat_record_id = {}", activeTim.getClientId(), activeTim.getSatRecordId()); + return activeTimId; } catch (SQLException e) { log.error("Error inserting active_tim", e); } @@ -124,6 +155,7 @@ public Long insertActiveTim(ActiveTim activeTim) { } public boolean updateActiveTim(ActiveTim activeTim) { + log.debug("Updating active_tim record with client_id = {} and sat_record_id = {}", activeTim.getClientId(), activeTim.getSatRecordId()); boolean activeTimIdResult = false; String updateTableSQL = "UPDATE ACTIVE_TIM SET TIM_ID = ?, START_LATITUDE = ?, START_LONGITUDE = ?, END_LATITUDE = ?,"; @@ -151,23 +183,41 @@ public boolean updateActiveTim(ActiveTim activeTim) { sqlNullHandler.setBigDecimalOrNull(preparedStatement, 4, end_lat); sqlNullHandler.setBigDecimalOrNull(preparedStatement, 5, end_lon); - java.util.Date tim_start_date = utility.convertDate(activeTim.getStartDateTime()); + Date tim_start_date = dateTimeHelper.convertDate(activeTim.getStartDateTime()); Timestamp ts = new Timestamp(tim_start_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, 6, ts); - if (activeTim.getEndDateTime() == null) { - preparedStatement.setNull(7, java.sql.Types.TIMESTAMP); - } else { - java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); - Timestamp ts2 = new Timestamp(tim_end_date.getTime()); - sqlNullHandler.setTimestampOrNull(preparedStatement, 7, ts2); + String endDateTimeStr = activeTim.getEndDateTime(); + if (endDateTimeStr == null) + preparedStatement.setNull(7, Types.TIMESTAMP); + else { + if (dateTimeHelper.isInTableFormat(endDateTimeStr)) { + log.trace("End date time string is in table format. Attempting to convert to ISO8601 format."); + try { + endDateTimeStr = dateTimeHelper.convertDateStringFromTableFormatIntoISO8601Format(endDateTimeStr); + } catch (Exception e) { + log.error("TIM_END candidate value was identified to be in table format but failed to convert to ISO8601 format: {}. Unable to continue.", endDateTimeStr); + return false; + } + } + Timestamp timEndTimestamp; + try { + log.trace("Converting date string into timestamp object"); + timEndTimestamp = dateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject(endDateTimeStr); + } + catch (DateStringNotInISO8601FormatException e) { + log.error("TIM_END candidate value was not in ISO8601 format and failed to convert to timestamp object: {}. Unable to continue.", endDateTimeStr); + return false; + } + log.trace("Converted date string from ISO8601 format into timestamp object. Setting TIM_END value in prepared statement."); + sqlNullHandler.setTimestampOrNull(preparedStatement, 7, timEndTimestamp); } sqlNullHandler.setIntegerOrNull(preparedStatement, 8, activeTim.getPk()); sqlNullHandler.setIntegerOrNull(preparedStatement, 9, activeTim.getProjectKey()); sqlNullHandler.setLongOrNull(preparedStatement, 10, activeTim.getActiveTimId()); activeTimIdResult = dbInteractions.updateOrDelete(preparedStatement); - log.info("------ Updated active_tim with id: {} --------------", activeTim.getActiveTimId()); + log.trace("Done updating active_tim record with client_id = {} and active_tim_id = {}", activeTim.getClientId(), activeTim.getActiveTimId()); } catch (SQLException e) { log.error("Error updating active_tim with id: {}", activeTim.getActiveTimId(), e); } @@ -388,7 +438,7 @@ public String getMinExpiration(String packetID, String expDate) throws ParseExce // coalesce function with the expDate passed in value. while (rs.next()) { var tmpTs = rs.getTimestamp("MINSTART", UTCCalendar); - minStart = utility.timestampFormat.format(tmpTs); + minStart = utility.getTimestampFormat().format(tmpTs); } } catch (SQLException e) { log.error("Error getting min expiration date with packetID: {}, expDate: {}", packetID, expDate, e); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/BaseService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/BaseService.java index c0e8cc821..360889378 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/BaseService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/BaseService.java @@ -11,10 +11,13 @@ import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.SecurityResultCodeType; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class BaseService { protected DbInteractions dbInteractions; @@ -47,7 +50,7 @@ public List GetSecurityResultCodeTypes() { securityResultCodeTypes.add(securityResultCodeType); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -60,7 +63,7 @@ public List GetSecurityResultCodeTypes() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameItisCodeService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameItisCodeService.java index 8d54e50ce..dc0d21ccd 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameItisCodeService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameItisCodeService.java @@ -7,11 +7,13 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class DataFrameItisCodeService extends BaseService { private TimDbTables timDbTables; @@ -59,7 +61,7 @@ public Long insertDataFrameItisCode(Long dataFrameId, String itis, Integer posit Long dataFrameItisCodeId = dbInteractions.executeAndLog(preparedStatement, "dataFrameItisCode"); return dataFrameItisCodeId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); return Long.valueOf(0); } finally { try { @@ -70,7 +72,7 @@ public Long insertDataFrameItisCode(Long dataFrameId, String itis, Integer posit if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ItisCodeService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ItisCodeService.java index e35144641..9cdc0db56 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ItisCodeService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ItisCodeService.java @@ -9,12 +9,14 @@ import com.trihydro.library.model.ItisCode; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Component +@Slf4j public class ItisCodeService extends BaseService { - public List selectAllItisCodes() { + public List selectAllItisCodes() { List itisCodes = new ArrayList(); Connection connection = null; Statement statement = null; @@ -38,7 +40,7 @@ public List selectAllItisCodes() { itisCodes.add(itisCode); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { @@ -48,7 +50,7 @@ public List selectAllItisCodes() { if (connection != null) connection.close(); } catch (SQLException ex) { - ex.printStackTrace(); + log.error("Exception", ex); } } return itisCodes; diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java index 37d49acff..741bb67e6 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java @@ -7,12 +7,14 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage; @Component +@Slf4j public class NodeLLService extends BaseService { private TimDbTables timDbTables; @@ -65,7 +67,7 @@ else if (col.equals("ATTRIBUTES_DELEVATION")) Long nodeLLId = dbInteractions.executeAndLog(preparedStatement, "nodell"); return nodeLLId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -75,7 +77,7 @@ else if (col.equals("ATTRIBUTES_DELEVATION")) if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return Long.valueOf(0); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java index c522ac062..22d54b585 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java @@ -7,12 +7,14 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage; @Component +@Slf4j public class NodeXYService extends BaseService { private TimDbTables timDbTables; @@ -65,7 +67,7 @@ else if (col.equals("ATTRIBUTES_DELEVATION")) Long nodeXYId = dbInteractions.executeAndLog(preparedStatement, "nodexy"); return nodeXYId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -75,7 +77,7 @@ else if (col.equals("ATTRIBUTES_DELEVATION")) if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return Long.valueOf(0); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeLLService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeLLService.java index 42e0d6d5a..4f83de3d3 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeLLService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeLLService.java @@ -7,10 +7,12 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class PathNodeLLService extends BaseService { private TimDbTables timDbTables; @@ -46,7 +48,7 @@ else if (col.equals("PATH_ID")) return pathNodeLLId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -56,7 +58,7 @@ else if (col.equals("PATH_ID")) if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return Long.valueOf(0); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeXYService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeXYService.java index 9669a925d..9927ef925 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeXYService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathNodeXYService.java @@ -7,10 +7,13 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class PathNodeXYService extends BaseService { private TimDbTables timDbTables; @@ -46,7 +49,7 @@ else if (col.equals("PATH_ID")) return pathNodeXYId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -56,7 +59,7 @@ else if (col.equals("PATH_ID")) if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return Long.valueOf(0); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathService.java index ecd093509..3487e2445 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/PathService.java @@ -7,10 +7,13 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class PathService extends BaseService { private TimDbTables timDbTables; @@ -43,7 +46,7 @@ public Long InsertPath() { Long pathId = dbInteractions.executeAndLog(preparedStatement, "pathId"); return pathId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -53,7 +56,7 @@ public Long InsertPath() { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } // if we got here, its an error diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RegionService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RegionService.java index d00ed2104..01f35ad58 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RegionService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RegionService.java @@ -8,6 +8,8 @@ import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -15,6 +17,7 @@ import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region; @Component +@Slf4j public class RegionService extends BaseService { private TimDbTables timDbTables; @@ -112,7 +115,7 @@ else if (col.equals("GEOMETRY_DIRECTION")) { Long regionId = dbInteractions.executeAndLog(preparedStatement, "regionID"); return regionId; } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -122,7 +125,7 @@ else if (col.equals("GEOMETRY_DIRECTION")) { if (connection != null) connection.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return Long.valueOf(0); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RsuService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RsuService.java index 42637a0a2..c84700fd7 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RsuService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/RsuService.java @@ -8,12 +8,15 @@ import com.trihydro.library.model.WydotRsu; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.stereotype.Component; @Component +@Slf4j public class RsuService extends BaseService { - public ArrayList getRsus() { + public ArrayList getRsus() { ArrayList rsus = new ArrayList(); Connection connection = null; @@ -40,7 +43,7 @@ public ArrayList getRsus() { } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -53,7 +56,7 @@ public ArrayList getRsus() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java index fc006b924..4664eca4e 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java @@ -1,10 +1,12 @@ package com.trihydro.loggerkafkaconsumer.app.services; +import com.trihydro.library.helpers.DateTimeHelper; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; +import java.sql.Types; import java.text.ParseException; import java.time.LocalDateTime; import java.time.ZonedDateTime; @@ -23,6 +25,7 @@ import com.trihydro.library.model.WydotRsu; import com.trihydro.library.tables.TimDbTables; +import java.util.Date; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -65,6 +68,7 @@ public class TimService extends BaseService { private ActiveTimHoldingService activeTimHoldingService; private PathNodeLLService pathNodeLLService; private NodeLLService nodeLLService; + private DateTimeHelper dateTimeHelper; @Autowired public void InjectDependencies(ActiveTimService _ats, TimDbTables _timDbTables, @@ -74,7 +78,7 @@ public void InjectDependencies(ActiveTimService _ats, TimDbTables _timDbTables, DataFrameItisCodeService _dataFrameItisCodeService, PathNodeXYService _pathNodeXYService, NodeXYService _nodeXYService, Utility _utility, ActiveTimHoldingService _athService, PathNodeLLService _pathNodeLLService, - NodeLLService _nodeLLService) { // TODO: use constructor instead of InjectDependencies + NodeLLService _nodeLLService, DateTimeHelper dateTimeHelper) { // TODO: use constructor instead of InjectDependencies activeTimService = _ats; timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; @@ -92,9 +96,10 @@ public void InjectDependencies(ActiveTimService _ats, TimDbTables _timDbTables, activeTimHoldingService = _athService; pathNodeLLService = _pathNodeLLService; nodeLLService = _nodeLLService; + this.dateTimeHelper = dateTimeHelper; } - public void addTimToDatabase(OdeData odeData) { + public void addTimToDatabase(OdeData odeData) { // TODO: identify if this method can be removed try { log.info("Called addTimToDatabase"); @@ -124,10 +129,10 @@ public void addTimToDatabase(OdeData odeData) { log.info("addTimToDatabase - No dataframes found in TIM (tim_id: {})", timId); return; } - OdeTravelerInformationMessage.DataFrame firstDataFrame = dFrames[0]; + DataFrame firstDataFrame = dFrames[0]; Long dataFrameId = dataFrameService.AddDataFrame(firstDataFrame, timId); - us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region[] regions = firstDataFrame.getRegions(); + Region[] regions = firstDataFrame.getRegions(); addRegions(firstDataFrame, dataFrameId); String firstRegionName = regions[0].getName(); // all regions have the same name @@ -152,189 +157,279 @@ public void addTimToDatabase(OdeData odeData) { * Adds an active TIM to the database. This only handles a single TIM at a time. */ public void addActiveTimToDatabase(OdeData odeData) { - log.info("Called addActiveTimToDatabase"); - - ActiveTim activeTim; + log.info("Processing active TIM to add to database"); + // Initial validation OdeTimPayload payload = (OdeTimPayload) odeData.getPayload(); if (payload == null) { + log.warn("Cannot process active TIM: payload is null"); return; } + OdeTravelerInformationMessage tim = getTim(payload); if (tim == null) { + log.warn("Cannot process active TIM: TravelerInformationMessage is null"); return; } + + log.trace("Processing TIM with packet ID: {}", tim.getPacketID()); + DataFrame[] dframes = tim.getDataframes(); if (dframes == null || dframes.length == 0) { + log.warn("Cannot process active TIM: no dataframes found in TIM with packet ID: {}", tim.getPacketID()); return; } - OdeTravelerInformationMessage.DataFrame.Region[] regions = dframes[0].getRegions(); + + Region[] regions = dframes[0].getRegions(); if (regions == null || regions.length == 0) { + log.warn("Cannot process active TIM: no regions found in first dataframe of TIM with packet ID: {}", tim.getPacketID()); return; } + String firstRegionName = regions[0].getName(); if (StringUtils.isEmpty(firstRegionName) || StringUtils.isBlank(firstRegionName)) { + log.warn("Cannot process active TIM: empty region name in TIM with packet ID: {}", tim.getPacketID()); return; } + OdeRequestMsgMetadata metaData = (OdeRequestMsgMetadata) odeData.getMetadata(); if (metaData == null) { + log.warn("Cannot process active TIM: metadata is null for TIM with packet ID: {}", tim.getPacketID()); return; } - // get information from the region name, first check splitname length - activeTim = setActiveTimByRegionName(firstRegionName); + // Extract information from region name + log.debug("Extracting TIM information from region name: '{}'", firstRegionName); + ActiveTim activeTim = setActiveTimByRegionName(firstRegionName); if (activeTim == null) { + log.warn("Cannot process active TIM: failed to extract information from region name: '{}' for TIM with packet ID: {}", firstRegionName, + tim.getPacketID()); return; } + log.debug("Extracted information - Route: {}, Direction: {}, ClientId: {}, TimType: {}", activeTim.getRoute(), activeTim.getDirection(), + activeTim.getClientId(), activeTim.getTimType()); + String satRecordId = activeTim.getSatRecordId(); + if (satRecordId != null) { + log.debug("TIM has satellite record ID: {}", satRecordId); + } - // see if TIM exists already - java.sql.Timestamp ts = null; + // Check if TIM already exists in database + Timestamp ts = null; if (StringUtils.isNotEmpty(tim.getTimeStamp()) && StringUtils.isNotBlank(tim.getTimeStamp())) { - ts = java.sql.Timestamp.valueOf(LocalDateTime.parse(tim.getTimeStamp(), DateTimeFormatter.ISO_DATE_TIME)); + try { + ts = Timestamp.valueOf(LocalDateTime.parse(tim.getTimeStamp(), DateTimeFormatter.ISO_DATE_TIME)); + log.trace("Parsed timestamp: {} from TIM timestamp: {}", ts, tim.getTimeStamp()); + } catch (Exception e) { + log.warn("Failed to parse timestamp from TIM: {}", e.getMessage()); + } } + + log.debug("Checking if TIM already exists with packet ID: {} and timestamp: {}", tim.getPacketID(), ts); Long timId = getTimId(tim.getPacketID(), ts); if (timId == null) { // TIM doesn't currently exist. Add it. + log.info("TIM not found in database, adding new TIM with packet ID: {}", tim.getPacketID()); timId = AddTim(metaData, null, tim, null, null, null, satRecordId, firstRegionName); if (timId != null) { - // we inserted a new TIM, add additional data + log.debug("Successfully added TIM with ID: {}", timId); + // Add additional data + log.trace("Adding dataframe for TIM ID: {}", timId); Long dataFrameId = dataFrameService.AddDataFrame(dframes[0], timId); + + log.trace("Adding regions for dataframe ID: {}", dataFrameId); addRegions(dframes[0], dataFrameId); + + log.trace("Adding ITIS codes for dataframe ID: {}", dataFrameId); addDataFrameItis(dframes[0], dataFrameId); } else { - // failed to insert new tim and failed to fetch existing, log and return - log.info("Failed to insert tim, and failed to fetch existing tim. No data inserted for OdeData: {}", gson.toJson(odeData)); + log.error("Failed to insert TIM and failed to fetch existing TIM. No data inserted for TIM with packet ID: {}", tim.getPacketID()); + log.debug("OdeData for TIM with packet ID: {}: {}", tim.getPacketID(), gson.toJson(odeData)); return; } } else { - log.info("TIM already exists, tim_id {}", timId); + log.info("TIM already exists in database with ID: {} and packet ID: {}", timId, tim.getPacketID()); } - // ensure we handle a new satRecordId + // Update satellite record ID if available if (satRecordId != null && !satRecordId.isEmpty()) { - updateTimSatRecordId(timId, satRecordId); - log.info("Added sat_record_id of {} to TIM with tim_id {}", satRecordId, timId); + log.debug("Updating TIM ID: {} with satellite record ID: {}", timId, satRecordId); + boolean updated = updateTimSatRecordId(timId, satRecordId); + if (updated) { + log.debug("Successfully updated satellite record ID for TIM ID: {}", timId); + } else { + log.warn("Failed to update satellite record ID for TIM ID: {}", timId); + } } - // TODO : Change to loop through RSU array - doing one rsu for now - RSU firstRsu = null; - if (metaData.getRequest() != null && metaData.getRequest().getRsus() != null - && metaData.getRequest().getRsus().length > 0) { + // Handle RSU information + RSU firstRsu = null; // TODO: update to handle multiple RSUs if needed + if (metaData.getRequest() != null && metaData.getRequest().getRsus() != null && metaData.getRequest().getRsus().length > 0) { firstRsu = metaData.getRequest().getRsus()[0]; activeTim.setRsuTarget(firstRsu.getRsuTarget()); + log.debug("Set RSU target: {} for TIM ID: {}", firstRsu.getRsuTarget(), timId); } + // Set satellite record ID from metadata if available if (metaData.getRequest() != null && metaData.getRequest().getSdw() != null) { - activeTim.setSatRecordId(metaData.getRequest().getSdw().getRecordId()); + String metadataSatRecordId = metaData.getRequest().getSdw().getRecordId(); + activeTim.setSatRecordId(metadataSatRecordId); + log.debug("Set satellite record ID from metadata: {} for TIM ID: {}", metadataSatRecordId, timId); } - // the ODE now parses all dataframes to find the most recent and sets it - // to this new OdeTimStartDateTime. We'll take advantage. - // Occasionally the OdeTimStartDateTime is null...set to dfames[0] startDateTime - // in that case + // Set start date and TIM ID var stDate = metaData.getOdeTimStartDateTime(); if (StringUtils.isEmpty(stDate)) { stDate = dframes[0].getStartDateTime(); - log.info("addActiveTimToDatabase did not find odeTimStartDateTime, setting to dataframe value {}", stDate); + log.debug("Using dataframe start time: {} (metadata start time was empty) for TIM ID: {}", stDate, timId); + } else { + log.debug("Using metadata start time: {} for TIM ID: {}", stDate, timId); } activeTim.setStartDateTime(stDate); activeTim.setTimId(timId); - ActiveTimHolding ath = null; - - // if this is an RSU TIM + // Get active TIM holding record + ActiveTimHolding ath; if (activeTim.getRsuTarget() != null && firstRsu != null) { - // save TIM RSU in DB - WydotRsu rsu = rsuService.getRsus().stream().filter(x -> x.getRsuTarget().equals(activeTim.getRsuTarget())) - .findFirst().orElse(null); + // RSU TIM handling + log.debug("Processing RSU TIM with target: {} for TIM ID: {}", activeTim.getRsuTarget(), timId); + + // Save TIM-RSU association + WydotRsu rsu = rsuService.getRsus().stream().filter(x -> x.getRsuTarget().equals(activeTim.getRsuTarget())).findFirst().orElse(null); if (rsu != null) { + log.trace("Associating TIM ID: {} with RSU ID: {} (index: {})", timId, rsu.getRsuId(), rsu.getRsuIndex()); timRsuService.AddTimRsu(timId, rsu.getRsuId(), rsu.getRsuIndex()); + } else { + log.warn("RSU with target: {} not found in database for TIM ID: {}", activeTim.getRsuTarget(), timId); } - ath = activeTimHoldingService.getRsuActiveTimHolding(activeTim.getClientId(), activeTim.getDirection(), - activeTim.getRsuTarget()); - if (ath == null) { - log.info("Could not find active_tim_holding for client_id '{}', direction '{}', rsu_target '{}'", - activeTim.getClientId(), activeTim.getDirection(), activeTim.getRsuTarget()); - } + // Get active TIM holding for RSU + log.trace("Retrieving active TIM holding for client ID: {}, direction: {}, RSU target: {}", activeTim.getClientId(), + activeTim.getDirection(), activeTim.getRsuTarget()); + ath = activeTimHoldingService.getRsuActiveTimHolding(activeTim.getClientId(), activeTim.getDirection(), activeTim.getRsuTarget()); } else { - // SDX tim, fetch holding - ath = activeTimHoldingService.getSdxActiveTimHolding(activeTim.getClientId(), activeTim.getDirection(), - activeTim.getSatRecordId()); + // SDX TIM handling + log.debug("Processing SDX TIM with satellite record ID: {} for TIM ID: {}", activeTim.getSatRecordId(), timId); + + // Get active TIM holding for SDX + log.trace("Retrieving active TIM holding for client ID: {}, direction: {}, satellite record ID: {}", activeTim.getClientId(), + activeTim.getDirection(), activeTim.getSatRecordId()); + ath = activeTimHoldingService.getSdxActiveTimHolding(activeTim.getClientId(), activeTim.getDirection(), activeTim.getSatRecordId()); + } - if (ath == null) { - log.info("Could not find active_tim_holding for client_id '{}', direction '{}', sat_record_id '{}'", - activeTim.getClientId(), activeTim.getDirection(), activeTim.getSatRecordId()); + if (ath == null) { + if (activeTim.getRsuTarget() != null) { + log.warn("No active TIM holding found for RSU TIM with client ID: '{}', direction: '{}', RSU target: '{}'", activeTim.getClientId(), + activeTim.getDirection(), activeTim.getRsuTarget()); + } else { + log.warn("No active TIM holding found for SAT TIM with client ID: '{}', direction: '{}', satellite record ID: '{}'", activeTim.getClientId(), + activeTim.getDirection(), activeTim.getSatRecordId()); } + } else { + log.debug("Found active TIM holding with ID: {}", ath.getActiveTimHoldingId()); } - // set end time if duration is not indefinite - if (dframes[0].getDurationTime() != 32000) { - ZonedDateTime zdt = ZonedDateTime.parse(dframes[0].getStartDateTime()); - zdt = zdt.plusMinutes(dframes[0].getDurationTime()); - activeTim.setEndDateTime(zdt.toString()); + // Handle duration and end time + int durationTime = dframes[0].getDurationTime(); + final int INDEFINITE_DURATION = 32000; + final int SHORT_DURATION_MINUTES = 5; + + if (durationTime != INDEFINITE_DURATION) { + // Finite duration handling + log.debug("TIM ID: {} has finite duration: {} minutes", timId, durationTime); + + if (durationTime == SHORT_DURATION_MINUTES) { + log.info("Short duration TIM detected (5 minutes) for TIM ID: {} - likely an expiry TIM", timId); + } + + try { + ZonedDateTime zdt = ZonedDateTime.parse(dframes[0].getStartDateTime()).plusMinutes(durationTime); + String endDateTime = dateTimeHelper.convertZonedDateTimeToISO8601Format(zdt); + log.debug("Calculated end time: {} for TIM ID: {}", endDateTime, timId); + activeTim.setEndDateTime(endDateTime); + } catch (Exception e) { + log.error("Failed to calculate end time for TIM ID: {} - {}", timId, e.getMessage()); + } + } else { + // Indefinite duration handling + log.debug("TIM ID: {} has indefinite duration (32000 minutes)", timId); + + if (ath != null && ath.getDesiredEndDateTime() != null) { + log.debug("Using end time from active TIM holding: {} for TIM ID: {}", ath.getDesiredEndDateTime(), timId); + activeTim.setEndDateTime(ath.getDesiredEndDateTime()); + } else { + log.debug("No desired end time found in active TIM holding for TIM ID: {}, setting to null", timId); + activeTim.setEndDateTime(null); + } } + // Set additional fields from active TIM holding if (ath != null) { - // set activeTim start/end points from holding table + log.trace("Copying data from active TIM holding to active TIM for TIM ID: {}", timId); + activeTim.setStartPoint(ath.getStartPoint()); activeTim.setEndPoint(ath.getEndPoint()); - - // set projectKey activeTim.setProjectKey(ath.getProjectKey()); - // set expiration time if found if (StringUtils.isNotBlank(ath.getExpirationDateTime())) { + log.debug("Setting expiration time: {} for TIM ID: {}", ath.getExpirationDateTime(), timId); activeTim.setExpirationDateTime(ath.getExpirationDateTime()); } } - // if true, TIM came from WYDOT + // Insert or update active TIM record if (activeTim.getTimType() != null) { - - ActiveTim activeTimDb = null; - - // if RSU TIM - if (activeTim.getRsuTarget() != null) // look for active RSU tim that matches incoming TIM - { - activeTimDb = activeTimService.getActiveRsuTim(activeTim.getClientId(), activeTim.getDirection(), - activeTim.getRsuTarget()); - } else // else look for active SAT tim that matches incoming TIM - { + // TIM came from WYDOT + log.debug("Processing WYDOT TIM ID: {} of type: {}", timId, activeTim.getTimType()); + + ActiveTim activeTimDb; + if (activeTim.getRsuTarget() != null) { + // Look for active RSU TIM + log.trace("Looking for existing active RSU TIM with client ID: {}, direction: {}, RSU target: {}", activeTim.getClientId(), + activeTim.getDirection(), activeTim.getRsuTarget()); + activeTimDb = activeTimService.getActiveRsuTim(activeTim.getClientId(), activeTim.getDirection(), activeTim.getRsuTarget()); + } else { + // Look for active satellite TIM + log.trace("Looking for existing active satellite TIM with satellite record ID: {}, direction: {}", activeTim.getSatRecordId(), + activeTim.getDirection()); activeTimDb = activeTimService.getActiveSatTim(activeTim.getSatRecordId(), activeTim.getDirection()); } - // if there is no active TIM, insert new one if (activeTimDb == null) { + // Insert new active TIM + log.info("Inserting new active TIM for TIM ID: {}", timId); activeTimService.insertActiveTim(activeTim); - } else { // else update active TIM - // If we couldn't find an Active TIM Holding record, we should persist the - // existing values - // for startPoint, endPoint, and projectKey + } else { + // Update existing active TIM + log.info("Updating existing active TIM with ID: {} for TIM ID: {}", activeTimDb.getActiveTimId(), timId); + + // Preserve existing values if no active TIM holding if (ath == null) { + log.debug("Preserving existing start/end points and project key from active TIM ID: {}", activeTimDb.getActiveTimId()); activeTim.setStartPoint(activeTimDb.getStartPoint()); activeTim.setEndPoint(activeTimDb.getEndPoint()); activeTim.setProjectKey(activeTimDb.getProjectKey()); } + activeTim.setActiveTimId(activeTimDb.getActiveTimId()); activeTimService.updateActiveTim(activeTim); } - } else { - // not from WYDOT application - // just log for now - log.info("Inserting new active_tim, no TimType found - not from WYDOT application"); + // Not from WYDOT application + log.info("Inserting new active TIM for TIM ID: {} (not from WYDOT application - no TimType found)", timId); activeTimService.insertActiveTim(activeTim); } - // remove active_tim_holding now that we've saved its values + // Clean up active TIM holding if (ath != null) { + log.debug("Deleting active TIM holding with ID: {} after processing", ath.getActiveTimHoldingId()); activeTimHoldingService.deleteActiveTimHolding(ath.getActiveTimHoldingId()); } + + log.info("Successfully processed active TIM with ID: {} (packet ID: {})", timId, tim.getPacketID()); } public Long getTimId(String packetId, Timestamp timeStamp) { @@ -389,9 +484,9 @@ public Long AddTim(OdeMsgMetadata odeTimMetadata, ReceivedMessageDetails receive j2735TravelerInformationMessage.getUrlB()); } else if (col.equals("TIME_STAMP")) { String timeStamp = j2735TravelerInformationMessage.getTimeStamp(); - java.sql.Timestamp ts = null; + Timestamp ts = null; if (StringUtils.isNotEmpty(timeStamp) && StringUtils.isNotBlank(timeStamp)) { - ts = java.sql.Timestamp + ts = Timestamp .valueOf(LocalDateTime.parse(timeStamp, DateTimeFormatter.ISO_DATE_TIME)); } sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); @@ -407,7 +502,7 @@ public Long AddTim(OdeMsgMetadata odeTimMetadata, ReceivedMessageDetails receive } } else if (col.equals("RECORD_GENERATED_AT")) { if (odeTimMetadata.getRecordGeneratedAt() != null) { - java.util.Date recordGeneratedAtDate = utility.convertDate(odeTimMetadata.getRecordGeneratedAt()); + Date recordGeneratedAtDate = dateTimeHelper.convertDate(odeTimMetadata.getRecordGeneratedAt()); Timestamp ts = new Timestamp(recordGeneratedAtDate.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); } else { @@ -425,7 +520,7 @@ public Long AddTim(OdeMsgMetadata odeTimMetadata, ReceivedMessageDetails receive sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, odeTimMetadata.getPayloadType()); } else if (col.equals("ODE_RECEIVED_AT")) { if (odeTimMetadata.getOdeReceivedAt() != null) { - java.util.Date receivedAtDate = utility.convertDate(odeTimMetadata.getOdeReceivedAt()); + Date receivedAtDate = dateTimeHelper.convertDate(odeTimMetadata.getOdeReceivedAt()); Timestamp ts = new Timestamp(receivedAtDate.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); } else { @@ -474,7 +569,7 @@ public Long AddTim(OdeMsgMetadata odeTimMetadata, ReceivedMessageDetails receive // location data is null, set all to null (with correct type) if (col.equals("RMD_LD_ELEVATION") || col.equals("RMD_LD_HEADING") || col.equals("RMD_LD_LATITUDE") || col.equals("RMD_LD_LONGITUDE") || col.equals("RMD_LD_SPEED")) { - preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); + preparedStatement.setNull(fieldNum, Types.NUMERIC); } } if (col.equals("RMD_RX_SOURCE") && receivedMessageDetails.getRxSource() != null) { @@ -487,18 +582,18 @@ public Long AddTim(OdeMsgMetadata odeTimMetadata, ReceivedMessageDetails receive if (securityResultCodeType != null) { preparedStatement.setInt(fieldNum, securityResultCodeType.getSecurityResultCodeTypeId()); } else { - preparedStatement.setNull(fieldNum, java.sql.Types.INTEGER); + preparedStatement.setNull(fieldNum, Types.INTEGER); } } } else { // message details are null, set all to null (with correct type) if (col.equals("RMD_LD_ELEVATION") || col.equals("RMD_LD_HEADING") || col.equals("RMD_LD_LATITUDE") || col.equals("RMD_LD_LONGITUDE") || col.equals("RMD_LD_SPEED")) { - preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); + preparedStatement.setNull(fieldNum, Types.NUMERIC); } else if (col.equals("RMD_RX_SOURCE")) { preparedStatement.setString(fieldNum, null); } else if (col.equals("SECURITY_RESULT_CODE")) { - preparedStatement.setNull(fieldNum, java.sql.Types.INTEGER); + preparedStatement.setNull(fieldNum, Types.INTEGER); } } @@ -572,7 +667,7 @@ public void addDataFrameItis(DataFrame dataFrame, Long dataFrameId) { if (itisCodeId != null) { dataFrameItisCodeService.insertDataFrameItisCode(dataFrameId, itisCodeId, i); } else { - log.warn("Could not find corresponding itis code it for {}", timItisCode); + log.warn("Could not find corresponding itis code id for {}", timItisCode); } } else { dataFrameItisCodeService.insertDataFrameItisCode(dataFrameId, timItisCode, i); diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimTypeService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimTypeService.java index 8691e0313..3c5bfab97 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimTypeService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimTypeService.java @@ -9,12 +9,15 @@ import com.trihydro.library.model.TimType; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.stereotype.Component; @Component +@Slf4j public class TimTypeService extends BaseService { - public List getTimTypes() { + public List getTimTypes() { List timTypes = new ArrayList(); Connection connection = null; ResultSet rs = null; @@ -35,7 +38,7 @@ public List getTimTypes() { timTypes.add(timType); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } finally { try { // close prepared statement @@ -48,7 +51,7 @@ public List getTimTypes() { if (rs != null) rs.close(); } catch (SQLException e) { - e.printStackTrace(); + log.error("Exception", e); } } return timTypes; diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimServiceTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimServiceTest.java index 8fb77ec80..018e1126b 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimServiceTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimServiceTest.java @@ -1,15 +1,17 @@ package com.trihydro.loggerkafkaconsumer.app.services; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +import com.trihydro.library.helpers.DateStringNotInISO8601FormatException; +import com.trihydro.library.helpers.DateTimeHelper; +import com.trihydro.library.helpers.DateTimeHelperImpl; import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Timestamp; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Instant; @@ -33,16 +35,21 @@ public class ActiveTimServiceTest extends TestBase { private Coordinate startPoint; private Coordinate endPoint; + + @Mock + private DateTimeHelper dateTimeHelper; + + private final DateTimeHelper actualDateTimeHelper = new DateTimeHelperImpl(); @BeforeEach public void setupSubTest() { - uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler); + uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler, dateTimeHelper); startPoint = new Coordinate(BigDecimal.valueOf(-1), BigDecimal.valueOf(-2)); endPoint = new Coordinate(BigDecimal.valueOf(-3), BigDecimal.valueOf(-4)); } @Test - public void insertActiveTim_SUCCESS() throws SQLException { + public void insertActiveTim_SUCCESS() throws SQLException, DateStringNotInISO8601FormatException { // Arrange ActiveTim activeTim = new ActiveTim(); activeTim.setStartPoint(startPoint); @@ -58,15 +65,14 @@ public void insertActiveTim_SUCCESS() throws SQLException { java.util.Date endDate = java.util.Date.from(endTime); Timestamp startDateTimestamp = Timestamp.from(stTime); Timestamp endDateTimestamp = Timestamp.from(endTime); - doReturn(stDate).when(mockUtility).convertDate(activeTim.getStartDateTime()); - doReturn(endDate).when(mockUtility).convertDate(activeTim.getEndDateTime()); - mockUtility.timestampFormat = timestampFormat; + doReturn(stDate).when(dateTimeHelper).convertDate(activeTim.getStartDateTime()); + doReturn(endDateTimestamp).when(dateTimeHelper).convertDateStringFromISO8601FormatIntoTimestampObject(activeTim.getEndDateTime()); // Act Long data = uut.insertActiveTim(activeTim); // Assert - Assertions.assertEquals(Long.valueOf(-1), data); + assertEquals(Long.valueOf(-1), data); verify(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, activeTim.getTimId());// TIM_ID verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, null);// DIRECTION @@ -102,13 +108,66 @@ public void insertActiveTim_FAIL() throws SQLException { Long data = uut.insertActiveTim(activeTim); // Assert - Assertions.assertEquals(Long.valueOf(0), data); + assertEquals(Long.valueOf(0), data); verify(mockPreparedStatement).close(); verify(mockConnection).close(); } @Test - public void updateActiveTim_SUCCESS() throws SQLException { + public void insertActiveTim_TimEndInTableFormat_Success_ReturnsId() throws DateStringNotInISO8601FormatException, ParseException { + // Arrange + ActiveTim activeTim = new ActiveTim(); + activeTim.setStartDateTime("2020-02-03T16:22:23.000Z"); + activeTim.setEndDateTime("2020-02-05 16:22:23"); + doReturn(actualDateTimeHelper.convertDate(activeTim.getStartDateTime())).when(dateTimeHelper).convertDate(activeTim.getStartDateTime()); + doReturn(actualDateTimeHelper.isInTableFormat(activeTim.getEndDateTime())).when(dateTimeHelper).isInTableFormat(activeTim.getEndDateTime()); + doReturn(actualDateTimeHelper.convertDateStringFromTableFormatIntoISO8601Format(activeTim.getEndDateTime())).when(dateTimeHelper).convertDateStringFromTableFormatIntoISO8601Format(activeTim.getEndDateTime()); + doReturn(actualDateTimeHelper.convertDateStringFromISO8601FormatIntoTimestampObject("2020-02-05T16:22:23.000Z")).when(dateTimeHelper).convertDateStringFromISO8601FormatIntoTimestampObject("2020-02-05T16:22:23.000Z"); + doReturn(10L).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim"); + + // Act + Long id = uut.insertActiveTim(activeTim); + + // Assert + assertEquals(10L, id); + } + + @Test + public void insertActiveTim_TimEndInTableFormat_FailedToConvert_ReturnsNegativeTwo() throws ParseException { + // Arrange + ActiveTim activeTim = new ActiveTim(); + activeTim.setStartDateTime("2020-02-03T16:22:23.000Z"); + activeTim.setEndDateTime("2020-02-05 16:22:23"); + doReturn(actualDateTimeHelper.convertDate(activeTim.getStartDateTime())).when(dateTimeHelper).convertDate(activeTim.getStartDateTime()); + doReturn(actualDateTimeHelper.isInTableFormat(activeTim.getEndDateTime())).when(dateTimeHelper).isInTableFormat(activeTim.getEndDateTime()); + doThrow(new RuntimeException()).when(dateTimeHelper).convertDateStringFromTableFormatIntoISO8601Format(activeTim.getEndDateTime()); + + // Act + Long result = uut.insertActiveTim(activeTim); + + // Assert + assertEquals(-2L, result); + } + + @Test + public void insertActiveTim_TimEndInvalidFormat_FailedToConvert_ReturnsNegativeThree() throws + DateStringNotInISO8601FormatException { + // Arrange + ActiveTim activeTim = new ActiveTim(); + activeTim.setStartDateTime("2020-02-03T16:22:23.000Z"); + activeTim.setEndDateTime("banana"); + doReturn(actualDateTimeHelper.convertDate(activeTim.getStartDateTime())).when(dateTimeHelper).convertDate(activeTim.getStartDateTime()); + doThrow(new DateStringNotInISO8601FormatException("not in ISO8601 format")).when(dateTimeHelper).convertDateStringFromISO8601FormatIntoTimestampObject(activeTim.getEndDateTime()); + + // Act + Long result = uut.insertActiveTim(activeTim); + + // Assert + assertEquals(-3L, result); + } + + @Test + public void updateActiveTim_SUCCESS() throws SQLException, DateStringNotInISO8601FormatException { // Arrange doReturn(true).when(mockDbInteractions).updateOrDelete(mockPreparedStatement); ActiveTim activeTim = new ActiveTim(); @@ -121,12 +180,10 @@ public void updateActiveTim_SUCCESS() throws SQLException { var stTime = Instant.parse(activeTim.getStartDateTime()); var endTime = Instant.parse(activeTim.getEndDateTime()); java.util.Date stDate = java.util.Date.from(stTime); - java.util.Date endDate = java.util.Date.from(endTime); Timestamp startDateTimestamp = Timestamp.from(stTime); Timestamp endDateTimestamp = Timestamp.from(endTime); - doReturn(stDate).when(mockUtility).convertDate(activeTim.getStartDateTime()); - doReturn(endDate).when(mockUtility).convertDate(activeTim.getEndDateTime()); - mockUtility.timestampFormat = timestampFormat; + doReturn(stDate).when(dateTimeHelper).convertDate(activeTim.getStartDateTime()); + doReturn(endDateTimestamp).when(dateTimeHelper).convertDateStringFromISO8601FormatIntoTimestampObject(activeTim.getEndDateTime()); // Act boolean data = uut.updateActiveTim(activeTim); @@ -181,8 +238,8 @@ public void getActiveSatTim_SUCCESS() throws SQLException { // Assert Assertions.assertNotNull(data, "Null ActiveTim returned"); - Assertions.assertEquals(Long.valueOf(99), data.getActiveTimId()); - Assertions.assertEquals(Long.valueOf(-99), data.getTimId()); + assertEquals(Long.valueOf(99), data.getActiveTimId()); + assertEquals(Long.valueOf(-99), data.getTimId()); verify(mockStatement).executeQuery(query); verify(mockStatement).close(); verify(mockRs).close(); @@ -227,8 +284,8 @@ public void getActiveRsuTim_SUCCESS() throws SQLException { // Assert Assertions.assertNotNull(data, "Null ActiveTim returned"); - Assertions.assertEquals(Long.valueOf(99), data.getActiveTimId()); - Assertions.assertEquals(Long.valueOf(-99), data.getTimId()); + assertEquals(Long.valueOf(99), data.getActiveTimId()); + assertEquals(Long.valueOf(-99), data.getTimId()); verify(mockStatement).executeQuery(query); verify(mockStatement).close(); verify(mockRs).close(); @@ -257,7 +314,8 @@ public void getMinExpiration_SUCCESS() throws Exception { Timestamp dbValue = Timestamp.valueOf("2021-01-01 00:00:00"); String expDate = "2021-01-03T00:00:00.000Z"; // Later than dbValue when(mockRs.getTimestamp(eq("MINSTART"), any())).thenReturn(dbValue); - mockUtility.timestampFormat = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); + mockUtility.setTimestampFormat(new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a")); + when(mockUtility.getTimestampFormat()).thenReturn(timestampFormat); String query = "SELECT LEAST((SELECT TO_TIMESTAMP('03-Jan-21 12.00.00.000 AM', 'DD-MON-YYYY HH12.MI.SS a')), " + "(COALESCE((SELECT MIN(EXPIRATION_DATE) FROM ACTIVE_TIM atim INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID " @@ -269,7 +327,7 @@ public void getMinExpiration_SUCCESS() throws Exception { // Assert verify(mockStatement).executeQuery(query); - Assertions.assertEquals("01-Jan-21 12.00.00.000 AM", minExp); + assertEquals("01-Jan-21 12.00.00.000 AM", minExp); verify(mockStatement).close(); verify(mockRs).close(); verify(mockConnection).close(); diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java index cc7cc6d01..8e90f88a5 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java @@ -1,5 +1,11 @@ package com.trihydro.loggerkafkaconsumer.app.services; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.sql.PreparedStatement; import com.trihydro.library.helpers.DbInteractions; import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; @@ -10,7 +16,6 @@ import org.mockito.Mock; import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import static org.mockito.Mockito.*; diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimServiceTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimServiceTest.java index 3c1bc5a24..9c8c26054 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimServiceTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimServiceTest.java @@ -15,6 +15,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; +import com.trihydro.library.helpers.DateTimeHelper; import java.math.BigDecimal; import java.sql.SQLException; import java.text.ParseException; @@ -96,16 +97,18 @@ public class TimServiceTest extends TestBase { private NodeLLService mockNodeLLService; @Mock private PathNodeLLService mockPathNodeLLService; + @Mock + private DateTimeHelper dateTimeHelper; private WydotRsu rsu; - private Long pathId = -99l; + private final Long pathId = -99l; @BeforeEach public void setupSubTest() { uut.InjectDependencies(mockActiveTimService, mockTimDbTables, mockSqlNullHandler, mockPathService, mockRegionService, mockDataFrameService, mockRsuService, mockTts, mockItisCodesService, mockTimRsuService, mockDataFrameItisCodeService, mockPathNodeXYService, mockNodeXYService, mockUtility, - mockActiveTimHoldingService, mockPathNodeLLService, mockNodeLLService); + mockActiveTimHoldingService, mockPathNodeLLService, mockNodeLLService, dateTimeHelper); ArrayList rsus = new ArrayList<>(); rsu = new WydotRsu(); @@ -364,7 +367,7 @@ public void addTimToDatabase_addTimFAIL() { verifyNoInteractions(mockDataFrameItisCodeService); // verify only these were called on the uut verify(uut).InjectDependencies(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any()); + any(), any(), any(), any(), any(), any(), any()); verify(uut).InjectBaseDependencies(any(), any()); verify(uut).addTimToDatabase(odeData); verify(uut).AddTim(any(), any(), any(), any(), any(), any(), any(), any()); diff --git a/ode-data-logger/src/main/java/com/trihydro/cvlogger/app/OdeLoggingConsumer.java b/ode-data-logger/src/main/java/com/trihydro/cvlogger/app/OdeLoggingConsumer.java index 00c8cfbe8..e6421ef6a 100644 --- a/ode-data-logger/src/main/java/com/trihydro/cvlogger/app/OdeLoggingConsumer.java +++ b/ode-data-logger/src/main/java/com/trihydro/cvlogger/app/OdeLoggingConsumer.java @@ -1,8 +1,6 @@ package com.trihydro.cvlogger.app; import java.io.IOException; -import java.sql.PreparedStatement; -import java.sql.Statement; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -13,9 +11,9 @@ import com.google.gson.Gson; import com.trihydro.cvlogger.config.DataLoggerConfiguration; import com.trihydro.library.helpers.EmailHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.TopicDataWrapper; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.admin.Admin; import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.consumer.ConsumerRecord; @@ -27,21 +25,16 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class OdeLoggingConsumer { - - static PreparedStatement preparedStatement = null; - static Statement statement = null; private DataLoggerConfiguration configProperties; - private Utility utility; private EmailHelper emailHelper; @Autowired - public OdeLoggingConsumer(DataLoggerConfiguration configProperties, Utility _utility, - EmailHelper _emailHelper) throws IOException, Exception { + public OdeLoggingConsumer(DataLoggerConfiguration configProperties, EmailHelper _emailHelper) throws IOException, Exception { this.configProperties = configProperties; - utility = _utility; emailHelper = _emailHelper; - System.out.println("starting.............."); + log.info("starting.............."); setupTopic(); startKafkaConsumer(); } @@ -69,10 +62,10 @@ public void setupTopic() { } } catch (InterruptedException e) { - e.printStackTrace(); + log.error("Exception", e); return; } catch (ExecutionException e) { - e.printStackTrace(); + log.error("Exception", e); return; } finally { admin.close(); @@ -93,7 +86,7 @@ public void startKafkaConsumer() throws Exception { KafkaConsumer stringConsumer = new KafkaConsumer(consumerProps); String consumerTopic = configProperties.getDepositTopic(); stringConsumer.subscribe(Arrays.asList(consumerTopic)); - System.out.println("Subscribed to topic " + consumerTopic); + log.info("Subscribed to topic {}", consumerTopic); Properties producerProps = new Properties(); producerProps.put("bootstrap.servers", endpoint); @@ -109,9 +102,7 @@ public void startKafkaConsumer() throws Exception { Duration polTime = Duration.ofMillis(100); ConsumerRecords records = stringConsumer.poll(polTime); for (ConsumerRecord record : records) { - String logTxt = String.format("Found topic %s, submitting to %s for later consumption", - record.topic(), producerTopic); - utility.logWithDate(logTxt); + log.info("Found topic {}, submitting to {} for later consumption", record.topic(), producerTopic); TopicDataWrapper tdw = new TopicDataWrapper(); tdw.setTopic(record.topic()); tdw.setData(record.value()); @@ -121,20 +112,20 @@ public void startKafkaConsumer() throws Exception { } } } catch (Exception ex) { - utility.logWithDate(ex.getMessage()); - emailHelper.ContainerRestarted(configProperties.getAlertAddresses(), configProperties.getMailPort(), + log.info(ex.getMessage()); + emailHelper.ContainerRestarted(configProperties.getAlertAddresses(), configProperties.getMailPort(), configProperties.getMailHost(), configProperties.getFromEmail(), consumerTopic + " Consumer"); throw (ex); } finally { try { stringConsumer.close(); } catch (Exception consumerEx) { - consumerEx.printStackTrace(); + log.error("Exception", consumerEx); } try { stringProducer.close(); } catch (Exception producerEx) { - producerEx.printStackTrace(); + log.error("Exception", producerEx); } } } diff --git a/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/OdeMongoLoggingConsumer.java b/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/OdeMongoLoggingConsumer.java index fc8e6ee75..259d399c9 100644 --- a/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/OdeMongoLoggingConsumer.java +++ b/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/OdeMongoLoggingConsumer.java @@ -12,6 +12,7 @@ import com.trihydro.library.helpers.Utility; import com.trihydro.mongologger.app.loggers.MongoLogger; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; @@ -19,9 +20,10 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class OdeMongoLoggingConsumer { - PreparedStatement preparedStatement = null; + PreparedStatement preparedStatement = null; Statement statement = null; private MongoLoggerConfiguration mongoLoggerConfig; private MongoLogger mongoLogger; @@ -36,8 +38,8 @@ public OdeMongoLoggingConsumer(MongoLoggerConfiguration _mongoLoggerConfig, Mong utility = _utility; emailHelper = _emailHelper; - utility.logWithDate("starting.............."); - startKafkaConsumer(); + log.info("starting.............."); + startKafkaConsumer(); } public void startKafkaConsumer() throws Exception { @@ -56,7 +58,7 @@ public void startKafkaConsumer() throws Exception { String topic = mongoLoggerConfig.getDepositTopic(); stringConsumer.subscribe(Arrays.asList(topic)); - System.out.println("Subscribed to topic " + topic); + log.info("Subscribed to topic {}", topic); try { while (true) { ConsumerRecords records = stringConsumer.poll(100); @@ -66,8 +68,8 @@ public void startKafkaConsumer() throws Exception { } if (recStrings.size() > 0) { - utility.logWithDate(String.format("Found %d %s records to parse", recStrings.size(), topic)); - String[] recStringArr = recStrings.toArray(new String[recStrings.size()]); + log.info("Found {} {} records to parse", recStrings.size(), topic); + String[] recStringArr = recStrings.toArray(new String[recStrings.size()]); if (topic.equals("topic.OdeTimJson")) { mongoLogger.logTim(recStringArr); @@ -79,15 +81,15 @@ public void startKafkaConsumer() throws Exception { } } } catch (Exception ex) { - utility.logWithDate("Exception in mongo logger application " + ex.getMessage()); - emailHelper.ContainerRestarted(mongoLoggerConfig.getAlertAddresses(), mongoLoggerConfig.getMailPort(), + log.info("Exception in mongo logger application {}", ex.getMessage()); + emailHelper.ContainerRestarted(mongoLoggerConfig.getAlertAddresses(), mongoLoggerConfig.getMailPort(), mongoLoggerConfig.getMailHost(), mongoLoggerConfig.getFromEmail(), topic + " Mongo Consumer"); throw (ex); } finally { try { stringConsumer.close(); } catch (Exception consumerEx) { - consumerEx.printStackTrace(); + log.error("Exception", consumerEx); } } } diff --git a/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java b/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java index e54cf41bc..e6e9ca25b 100644 --- a/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java +++ b/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java @@ -9,8 +9,9 @@ import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import lombok.extern.slf4j.Slf4j; + import com.trihydro.library.helpers.EmailHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.mongologger.app.MongoLoggerConfiguration; import java.util.List; @@ -19,17 +20,16 @@ import org.springframework.stereotype.Component; @Component +@Slf4j public class MongoLogger { - private final Utility utility; private final EmailHelper emailHelper; private final String databaseName; private final String[] alertAddresses; private final MongoClient mongoClient; @Autowired - public MongoLogger(MongoLoggerConfiguration config, Utility utility, EmailHelper emailHelper) { + public MongoLogger(MongoLoggerConfiguration config, EmailHelper emailHelper) { this.emailHelper = emailHelper; - this.utility = utility; this.mongoClient = configureMongoClient(config); this.databaseName = config.getMongoDatabase(); // the name of the database to deposit records into this.alertAddresses = config.getAlertAddresses(); // the email addresses to send alerts to @@ -69,7 +69,7 @@ public void logMultipleToCollection(String[] records, String collectionName) { MongoCollection collection = database.getCollection(collectionName); collection.insertMany(docs); } catch (Exception ex) { - utility.logWithDate("Error logging to mongo collection: " + ex.getMessage()); + log.info("Error logging to mongo collection: {}", ex.getMessage()); String body = "The MongoLogger failed attempting to insert a record to "; body += collectionName; @@ -79,8 +79,8 @@ public void logMultipleToCollection(String[] records, String collectionName) { try { emailHelper.SendEmail(alertAddresses, "MongoLogger Failed to Connect to MongoDB", body); } catch (Exception e) { - utility.logWithDate("Error sending email: " + e.getMessage()); - e.printStackTrace(); + log.info("Error sending email: {}", e.getMessage()); + log.error("Exception", e); } } } diff --git a/ode-wrapper/.gitignore b/ode-wrapper/.gitignore index fe145021e..e8fcdc139 100644 --- a/ode-wrapper/.gitignore +++ b/ode-wrapper/.gitignore @@ -1,3 +1,4 @@ ode-wrapper-*.jar +*.crt *.jks *.p12 \ No newline at end of file diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java index 26131ecba..005522e9d 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java @@ -1,6 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; +import com.trihydro.library.helpers.DateTimeHelper; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -15,6 +16,7 @@ import com.trihydro.library.model.TimQuery; import com.trihydro.library.model.WydotRsu; import com.trihydro.library.service.ActiveTimService; + import com.trihydro.library.service.OdeService; import com.trihydro.library.service.RestTemplateProvider; import com.trihydro.library.service.TimTypeService; @@ -22,6 +24,7 @@ import com.trihydro.odewrapper.config.BasicConfiguration; import com.trihydro.odewrapper.helpers.SetItisCodes; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -37,6 +40,7 @@ @CrossOrigin @RestController @Api(description = "Utilities") +@Slf4j public class UtilityController extends WydotTimBaseController { class RsuCheckResults { @@ -58,9 +62,9 @@ class RsuClearSuccess { public UtilityController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, OdeService _odeService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, - Utility _utility, TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + Utility _utility, TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); this.odeService = _odeService; } @@ -68,7 +72,7 @@ public UtilityController(BasicConfiguration _basicConfiguration, WydotTimService public ResponseEntity allRsusTimCheck() { // String url = configuration.getOdeUrl(); - utility.logWithDate("RSU TIM Check", this.getClass()); + log.info("{}: " + "RSU TIM Check", this.getClass().getSimpleName()); List rsuCheckResultsList = new ArrayList(); @@ -103,7 +107,7 @@ public ResponseEntity allRsusTimCheck() { @RequestMapping(value = "/rsu-tim-check/{address:.+}", method = RequestMethod.GET, headers = "Accept=application/json") public ResponseEntity rsuTimCheck(@PathVariable String address) { - utility.logWithDate("RSU TIM Check", this.getClass()); + log.info("{}: " + "RSU TIM Check", this.getClass().getSimpleName()); List rsuCheckResultsList = new ArrayList(); @@ -117,7 +121,8 @@ public ResponseEntity rsuTimCheck(@PathVariable String address) { rsuCheckResults.rsuIndexList = new ArrayList(); rsuCheckResults.activeTimIndicesList = new ArrayList(); - utility.logWithDate(rsu.getRsuTarget(), this.getClass()); + String msg = rsu.getRsuTarget(); + log.info("{}: {}", this.getClass().getSimpleName(), msg); rsuCheckResults.rsuTarget = rsu.getRsuTarget(); com.trihydro.library.model.TimQuery timQuery = odeService.submitTimQuery(rsu, 0); @@ -142,7 +147,13 @@ public ResponseEntity rsuTimCheck(@PathVariable String address) { @RequestMapping(value = "/delete-tim", method = RequestMethod.DELETE, headers = "Accept=application/json") public ResponseEntity deleteTim(@RequestBody ActiveTim activeTim) { - wydotTimService.deleteTimsFromRsusAndSdx(Stream.of(activeTim).collect(Collectors.toList())); + log.trace("deleteTim called with ActiveTim { id: {}, clientId: {} }", activeTim.getActiveTimId(), activeTim.getClientId()); + TimDeleteSummary timDeleteSummary = wydotTimService.deleteTimsFromRsusAndSdx(Stream.of(activeTim).collect(Collectors.toList())); + if (!timDeleteSummary.getFailedActiveTimDeletions().isEmpty()) { + String errorMessage = "Failed to delete Active TIMs with IDs: " + timDeleteSummary.getFailedActiveTimDeletions(); + log.error(errorMessage); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage); + } String responseMessage = "success"; return ResponseEntity.status(HttpStatus.OK).body(responseMessage); } @@ -191,4 +202,4 @@ public ResponseEntity clearRsu(@RequestBody String[] addresses) { return ResponseEntity.status(HttpStatus.OK).body(gson.toJson(deleteResults)); } -} \ No newline at end of file +} diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java index 506eb93ce..28526937f 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java @@ -1,25 +1,8 @@ package com.trihydro.odewrapper.controller; -import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import com.trihydro.library.model.TimUpdateModel; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.TimeZone; - import com.google.gson.Gson; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -28,6 +11,7 @@ import com.trihydro.library.model.ContentEnum; import com.trihydro.library.model.Coordinate; import com.trihydro.library.model.Milepost; +import com.trihydro.library.model.ResubmitTimException; import com.trihydro.library.model.TimType; import com.trihydro.library.model.WydotTim; import com.trihydro.library.model.WydotTimRw; @@ -45,17 +29,31 @@ import com.trihydro.odewrapper.model.WydotTimParking; import com.trihydro.odewrapper.model.WydotTimRc; import com.trihydro.odewrapper.model.WydotTimVsl; - import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; - import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Objects; + @Component @Slf4j public abstract class WydotTimBaseController { - + private final DateTimeHelper dateTimeHelper; protected static BasicConfiguration configuration; protected WydotTimService wydotTimService; protected TimTypeService timTypeService; @@ -68,6 +66,7 @@ public abstract class WydotTimBaseController { protected TimGenerationHelper timGenerationHelper; private final IdenticalPointsExceptionHandler identicalPointsExceptionHandler; + private List routes = new ArrayList<>(); protected static Gson gson = new Gson(); private List timTypes; @@ -76,7 +75,8 @@ public WydotTimBaseController(BasicConfiguration _basicConfiguration, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { configuration = _basicConfiguration; wydotTimService = _wydotTimService; timTypeService = _timTypeService; @@ -87,27 +87,19 @@ public WydotTimBaseController(BasicConfiguration _basicConfiguration, utility = _utility; timGenerationHelper = _timGenerationHelper; this.identicalPointsExceptionHandler = identicalPointsExceptionHandler; + this.dateTimeHelper = dateTimeHelper; } protected String getStartTime() { - Date date = new Date(); - return getIsoDateTimeString(date); + return dateTimeHelper.getStartTime(); } protected String getIsoDateTimeString(Date date) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - return sdf.format(date); + return dateTimeHelper.getIsoDateTimeString(date); } protected String getIsoDateTimeString(ZonedDateTime date) { - if (date == null) { - return null; - } - - var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - var utcDate = date.withZoneSameInstant(ZoneOffset.UTC); - return utcDate.format(formatter); + return dateTimeHelper.getIsoDateTimeString(date); } protected ControllerResult validateInputParking(WydotTimParking tim) { @@ -254,11 +246,11 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { var convertedDate = LocalDate.parse(tim.getSchedStart(), DateTimeFormatter.ISO_LOCAL_DATE); var startOfDay = convertedDate.atStartOfDay(ZoneId.systemDefault()); - tim.setSchedStart(getIsoDateTimeString(startOfDay)); + tim.setSchedStart(dateTimeHelper.getIsoDateTimeString(startOfDay)); } catch (DateTimeParseException e) { resultMessages.add( "Bad value supplied for schedStart. Should follow the format: yyyy-MM-dd"); - e.printStackTrace(); + log.error("Exception", e); } } if (tim.getSchedEnd() != null) { @@ -269,11 +261,11 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { // construction is only scheduled for a day (ex. 5-12 to 5-12) var endOfDay = ZonedDateTime.of(LocalDateTime.of(convertedDate, LocalTime.MAX), ZoneId.systemDefault()); - tim.setSchedEnd(getIsoDateTimeString(endOfDay)); + tim.setSchedEnd(dateTimeHelper.getIsoDateTimeString(endOfDay)); } catch (DateTimeParseException e) { resultMessages.add( "Bad value supplied for schedEnd. Should follow the format: yyyy-MM-dd"); - e.printStackTrace(); + log.error("Exception", e); } } if (tim.getBuffers() != null) { @@ -312,16 +304,20 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { public boolean routeSupported(String route) { // call out to REST service to get all routes once, then use that - // if (routes.size() == 0) { - // String url = String.format("%s/routes", configuration.getCvRestService()); - // ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - // String[].class); - // routes = Arrays.asList(response.getBody()); - // } - // return routes.contains(route); - - // Since routes are not loaded, assume all route are supported. - return !route.isEmpty(); + if (routes.size() == 0) { + String url = String.format("%s/routes", configuration.getCvRestService()); + log.trace("Getting routes from {}", url); + try { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, String[].class); + if (response != null && response.getBody() != null) { + routes = Arrays.asList(response.getBody()); + } + } catch (Exception e) { + log.error("Unexpected error getting routes from {} ", url, e); + } + } + log.trace("Number of routes retrieved: {}", routes.size()); + return routes.contains(route); } protected ControllerResult validateInputRc(WydotTimRc tim) { @@ -696,7 +692,10 @@ protected void expireReduceCreateSendTims(WydotTim wydotTim, TimType timType, for (ActiveTim existingTim : existingTims) { existingTimIds.add(existingTim.getActiveTimId()); } - timGenerationHelper.expireTimAndResubmitToOde(existingTimIds); + List resubmitTimExceptions = timGenerationHelper.expireTimAndResubmitToOde(existingTimIds); + if (resubmitTimExceptions.size() > 0) { + log.warn("One or more TIMs failed to resubmit to ODE. See logs for details."); + } // Get mileposts that will define the TIM's region var milepostsAll = wydotTimService.getAllMilepostsForTim(wydotTim); @@ -704,7 +703,7 @@ protected void expireReduceCreateSendTims(WydotTim wydotTim, TimType timType, // Per J2735, NodeSetLL's must contain at least 2 nodes. ODE will fail to // PER-encode TIM if we supply less than 2. if (milepostsAll.size() < 2) { - utility.logWithDate("Found less than 2 mileposts, unable to generate TIM."); + log.info("Found less than 2 mileposts, unable to generate TIM."); return; } Milepost firstPoint = milepostsAll.get(0); @@ -758,12 +757,12 @@ protected void createSendTims(WydotTim wydotTim, TimType timType, String startDa if (Arrays.asList(configuration.getRsuRoutes()).contains(wydotTim.getRoute())) { // send TIM to RSUs wydotTimService.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, pk, - endDateTime, endPoint); + endDateTime, endPoint, endDateTime); } // send TIM to SDW // remove rsus from TIM timToSend.getRequest().setRsus(null); wydotTimService.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, pk, endPoint, - reducedMileposts); + reducedMileposts, endDateTime); } } \ No newline at end of file diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java index c7ed16e7b..5a9d750b0 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java @@ -1,9 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.util.ArrayList; -import java.util.List; - +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -19,7 +17,8 @@ import com.trihydro.odewrapper.model.ControllerResult; import com.trihydro.odewrapper.model.TimBowrList; import com.trihydro.odewrapper.model.WydotTimBowr; - +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -29,12 +28,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.util.ArrayList; +import java.util.List; + @CrossOrigin @RestController +@Slf4j @Api(description = "Blow Over Weight Restrictions") public class WydotTimBowrController extends WydotTimBaseController { @@ -42,16 +43,17 @@ public class WydotTimBowrController extends WydotTimBaseController { @Autowired public WydotTimBowrController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); } @RequestMapping(value = "/create-or-update-bowr-tim", method = RequestMethod.POST, headers = "Accept=application/json") public ResponseEntity createOrUpdateBowrTim(@RequestBody TimBowrList timBowrList) { - utility.logWithDate("Create Or Update Blow Over Weight Restriction TIM", this.getClass()); + log.info("{}: " + "Create Or Update Blow Over Weight Restriction TIM", this.getClass().getSimpleName()); List results = new ArrayList(); List errors = new ArrayList(); @@ -77,7 +79,8 @@ public ResponseEntity createOrUpdateBowrTim(@RequestBody TimBowrList tim processRequestAsync(timsToSend); String responseMessage = gson.toJson(results); if (errors.size() > 0) { - utility.logWithDate("Failed to send TIMs: " + gson.toJson(errors), this.getClass()); + String msg = "Failed to send TIMs: " + gson.toJson(errors); + log.info("{}: {}", this.getClass().getSimpleName(), msg); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseMessage); } return ResponseEntity.status(HttpStatus.OK).body(responseMessage); @@ -85,7 +88,7 @@ public ResponseEntity createOrUpdateBowrTim(@RequestBody TimBowrList tim @RequestMapping(value = "/submit-bowr-clear/{clientId}", method = RequestMethod.DELETE, headers = "Accept=application/json") public ResponseEntity submitBowrClear(@PathVariable String clientId) { - utility.logWithDate("Submit Blow Over Weight Restriction Clear", this.getClass()); + log.info("{}: " + "Submit Blow Over Weight Restriction Clear", this.getClass().getSimpleName()); List existingTimIds = new ArrayList(); @@ -100,7 +103,7 @@ public ResponseEntity submitBowrClear(@PathVariable String clientId) { Long timTypeId = timType != null ? timType.getTimTypeId() : null; List existingActiveTims = activeTimService.getActiveTimsByClientIdDirection(clientId, timTypeId, null); if (existingActiveTims.size() == 0) { - utility.logWithDate("No active TIMs found for client id: " + clientId, this.getClass()); + log.info("{}: " + "No active TIMs found for client id: {}", this.getClass().getSimpleName(), clientId); String responseMessage = "No active TIMs found for client id: " + clientId; return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseMessage); } diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java index 9fc7ea454..feee1a20e 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java @@ -1,12 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -21,7 +16,8 @@ import com.trihydro.odewrapper.model.ControllerResult; import com.trihydro.odewrapper.model.TimRcList; import com.trihydro.odewrapper.model.WydotTimRc; - +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -30,12 +26,17 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + @CrossOrigin @RestController +@Slf4j @Api(description = "Chain Controls") public class WydotTimCcController extends WydotTimBaseController { @@ -43,11 +44,12 @@ public class WydotTimCcController extends WydotTimBaseController { @Autowired public WydotTimCcController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); } @RequestMapping(value = "/cc-tim", method = RequestMethod.POST, headers = "Accept=application/json") @@ -56,10 +58,11 @@ public ResponseEntity createChainControlTim(@RequestBody TimRcList timRc DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); - utility.logWithDate(dateFormat.format(date) + " - CHAIN CONTROL TIM", this.getClass()); + String msg = dateFormat.format(date) + " - CHAIN CONTROL TIM"; + log.info("{}: {}", this.getClass().getSimpleName(), msg); String post = gson.toJson(timRcList); - utility.logWithDate(post.toString(), this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), post.toString()); List resultList = new ArrayList(); ControllerResult resultTim = null; @@ -105,4 +108,4 @@ public void run() { } }).start(); } -} \ No newline at end of file +} diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java index 6efd15d6b..8ef40de90 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java @@ -1,11 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -20,7 +16,7 @@ import com.trihydro.odewrapper.model.ControllerResult; import com.trihydro.odewrapper.model.TimIncidentList; import com.trihydro.odewrapper.model.WydotTimIncident; - +import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -31,10 +27,12 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + @CrossOrigin @RestController @Api(description = "Incidents") @@ -46,9 +44,10 @@ public class WydotTimIncidentController extends WydotTimBaseController { @Autowired public WydotTimIncidentController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, - MilepostReduction _milepostReduction, Utility _utility, TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + MilepostReduction _milepostReduction, Utility _utility, TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, _restTemplateProvider, _milepostReduction, - _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); } @RequestMapping(value = "/incident-tim", method = RequestMethod.POST, headers = "Accept=application/json") diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java index b7b9ddc26..2a8220e93 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java @@ -1,6 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; +import com.trihydro.library.helpers.DateTimeHelper; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -14,6 +15,7 @@ import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.ContentEnum; import com.trihydro.library.service.ActiveTimService; + import com.trihydro.library.service.RestTemplateProvider; import com.trihydro.library.service.TimTypeService; import com.trihydro.library.service.WydotTimService; @@ -22,7 +24,8 @@ import com.trihydro.odewrapper.model.ControllerResult; import com.trihydro.odewrapper.model.TimParkingList; import com.trihydro.odewrapper.model.WydotTimParking; - +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -38,6 +41,7 @@ @CrossOrigin @RestController +@Slf4j @Api(description = "Parking") public class WydotTimParkingController extends WydotTimBaseController { @@ -45,11 +49,12 @@ public class WydotTimParkingController extends WydotTimBaseController { @Autowired public WydotTimParkingController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); } @RequestMapping(value = "/parking-tim", method = RequestMethod.POST, headers = "Accept=application/json") @@ -58,9 +63,10 @@ public ResponseEntity createParkingTim(@RequestBody TimParkingList timPa DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); - utility.logWithDate(dateFormat.format(date) + " - Create Parking TIM", this.getClass()); + String msg = dateFormat.format(date) + " - Create Parking TIM"; + log.info("{}: {}", this.getClass().getSimpleName(), msg); String post = gson.toJson(timParkingList); - utility.logWithDate(post.toString(), this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), post.toString()); List resultList = new ArrayList(); ControllerResult resultTim = null; @@ -127,7 +133,8 @@ public ResponseEntity deleteParkingTim(@PathVariable String id) { DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); - utility.logWithDate(dateFormat.format(date) + " - Delete Parking TIM", this.getClass()); + String msg = dateFormat.format(date) + " - Delete Parking TIM"; + log.info("{}: {}", this.getClass().getSimpleName(), msg); // expire and clear TIM wydotTimService.clearTimsById("P", id, null); @@ -146,4 +153,4 @@ public void run() { } }).start(); } -} \ No newline at end of file +} diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java index 86ad03ba8..dfd3e109c 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java @@ -1,17 +1,13 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.ActiveTim; import com.trihydro.library.model.ContentEnum; +import com.trihydro.library.model.ResubmitTimException; import com.trihydro.library.model.WydotTim; import com.trihydro.library.service.ActiveTimService; import com.trihydro.library.service.RestTemplateProvider; @@ -22,7 +18,8 @@ import com.trihydro.odewrapper.model.ControllerResult; import com.trihydro.odewrapper.model.TimRcList; import com.trihydro.odewrapper.model.WydotTimRc; - +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -31,26 +28,34 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + @CrossOrigin @RestController @Api(description = "Road Conditions") +@Slf4j public class WydotTimRcController extends WydotTimBaseController { private final String type = "RC"; protected static BasicConfiguration configuration; + private final DateTimeHelper dateTimeHelper; @Autowired public WydotTimRcController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); configuration = _basicConfiguration; + this.dateTimeHelper = dateTimeHelper; } @RequestMapping(value = "/create-update-rc-tim", method = RequestMethod.POST, headers = "Accept=application/json") @@ -59,9 +64,10 @@ public ResponseEntity createUpdateRoadConditionsTim(@RequestBody TimRcLi DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); - utility.logWithDate(dateFormat.format(date) + " - Create Update RC TIM", this.getClass()); + String msg1 = dateFormat.format(date) + " - Create Update RC TIM"; + log.info("{}: {}", this.getClass().getSimpleName(), msg1); String post = gson.toJson(timRcList); - utility.logWithDate(post.toString(), this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), post.toString()); List resultList = new ArrayList(); List errList = new ArrayList(); @@ -90,30 +96,34 @@ public ResponseEntity createUpdateRoadConditionsTim(@RequestBody TimRcLi String responseMessage = gson.toJson(resultList); if (errList.size() > 0) { - utility.logWithDate("Failed to send TIMs: " + gson.toJson(errList), this.getClass()); + String msg = "Failed to send TIMs: " + gson.toJson(errList); + log.info("{}: {}", this.getClass().getSimpleName(), msg); } return ResponseEntity.status(HttpStatus.OK).body(responseMessage); } @RequestMapping(value = "/submit-rc-ac", method = RequestMethod.PUT, headers = "Accept=application/json") public ResponseEntity submitAllClearRoadConditionsTim(@RequestBody TimRcList timRcList) { - List resultList = new ArrayList(); DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); - utility.logWithDate(dateFormat.format(date) + " - All Clear", this.getClass()); + log.info("All Clear - {}", dateFormat.format(date)); String post = gson.toJson(timRcList); - utility.logWithDate(post.toString(), this.getClass()); + log.debug("Request payload: {}", post); List errList = new ArrayList(); ControllerResult resultTim = null; List existingTimIds = new ArrayList(); + log.info("Starting all-clear TIM processing for {} TIMs", timRcList.getTimRcList().size()); + for (WydotTimRc wydotTim : timRcList.getTimRcList()) { resultTim = validateRcAc(wydotTim); if (resultTim.getResultMessages().size() > 0) { + log.warn("Validation failed for TIM with clientId: {}, Direction: {}", wydotTim.getClientId(), wydotTim.getDirection()); + log.warn("Validation messages: {}", String.join(", ", resultTim.getResultMessages())); resultList.add(resultTim); errList.add(resultTim); continue; @@ -122,22 +132,26 @@ public ResponseEntity submitAllClearRoadConditionsTim(@RequestBody TimRc // get existing active tims from wydotTim var timType = getTimType(type); Long timTypeId = timType != null ? timType.getTimTypeId() : null; + log.debug("Looking for active TIMs with clientId: {}, timTypeId: {}, direction: {}", wydotTim.getClientId(), timTypeId, + wydotTim.getDirection()); + List existingActiveTims = new ArrayList<>(); var direction = wydotTim.getDirection().toUpperCase(); - // 'B' TIMs are split it into 'I' and 'D' so they should be handled separately so if we are passed the 'B' direction + // 'B' TIMs are split it into 'I' and 'D' so they should be handled separately if (!direction.equals("B")) { - existingActiveTims = activeTimService.getActiveTimsByClientIdDirection(wydotTim.getClientId(), - timTypeId, - wydotTim.getDirection()); + existingActiveTims = activeTimService.getActiveTimsByClientIdDirection(wydotTim.getClientId(), timTypeId, wydotTim.getDirection()); } else { - existingActiveTims = activeTimService.getActiveTimsByClientIdDirection(wydotTim.getClientId(), - timTypeId, null); + existingActiveTims = activeTimService.getActiveTimsByClientIdDirection(wydotTim.getClientId(), timTypeId, null); } + log.info("Found {} active TIMs for clientId: {}, direction: {}", existingActiveTims.size(), wydotTim.getClientId(), + wydotTim.getDirection()); + // get ids from existingActiveTims for (ActiveTim existingActiveTim : existingActiveTims) { existingTimIds.add(existingActiveTim.getActiveTimId()); + log.debug("Added ActiveTimId to delete list: {}", existingActiveTim.getActiveTimId()); } resultTim.getResultMessages().add("success"); resultList.add(resultTim); @@ -145,7 +159,22 @@ public ResponseEntity submitAllClearRoadConditionsTim(@RequestBody TimRc // Expire existing tims if (existingTimIds.size() > 0) { - timGenerationHelper.expireTimAndResubmitToOde(existingTimIds); + log.info("Attempting to expire {} TIMs: {}", existingTimIds.size(), existingTimIds); + try { + List exceptions = timGenerationHelper.expireTimAndResubmitToOde(existingTimIds); + if (exceptions != null && !exceptions.isEmpty()) { + log.warn("Encountered {} exceptions while expiring TIMs", exceptions.size()); + for (ResubmitTimException ex : exceptions) { + log.warn("Failed to expire TIM {}: {}", ex.getActiveTimId(), ex.getExceptionMessage()); + } + } else { + log.info("Successfully requested expiration for all {} TIMs", existingTimIds.size()); + } + } catch (Exception e) { + log.error("Unexpected error during TIM expiration", e); + } + } else { + log.warn("No active TIMs found to expire - this may indicate an unnecessary all-clear request"); } String responseMessage = gson.toJson(resultList); diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java index 7c014d700..2fd997a0e 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java @@ -1,12 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; - +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -23,7 +18,8 @@ import com.trihydro.odewrapper.config.BasicConfiguration; import com.trihydro.odewrapper.helpers.SetItisCodes; import com.trihydro.odewrapper.model.ControllerResult; - +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.gavaghan.geodesy.Ellipsoid; import org.gavaghan.geodesy.GeodeticCalculator; @@ -37,12 +33,17 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + @CrossOrigin @RestController +@Slf4j @Api(description = "Road Construction") public class WydotTimRwController extends WydotTimBaseController { @@ -51,18 +52,19 @@ public class WydotTimRwController extends WydotTimBaseController { @Autowired public WydotTimRwController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); } @RequestMapping(value = "/rw-tim", method = RequestMethod.POST, headers = "Accept=application/json") public ResponseEntity createRoadContructionTim(@RequestBody TimRwList timRwList) { - utility.logWithDate("Create/Update RW TIM", this.getClass()); + log.info("{}: " + "Create/Update RW TIM", this.getClass().getSimpleName()); String post = gson.toJson(timRwList); - utility.logWithDate(post.toString(), this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), post.toString()); List resultList = new ArrayList(); ControllerResult resultTim = null; @@ -275,7 +277,7 @@ public void run() { @RequestMapping(value = "/rw-tim/{id}", method = RequestMethod.DELETE, headers = "Accept=application/json") public ResponseEntity deleteRoadContructionTim(@PathVariable String id) { - utility.logWithDate("Delete RW TIM", this.getClass()); + log.info("{}: " + "Delete RW TIM", this.getClass().getSimpleName()); // expire and clear TIM wydotTimService.clearTimsById(type, id, null, true); @@ -314,4 +316,4 @@ public Collection getRoadConstructionTim() { return activeTims; } -} \ No newline at end of file +} diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java index 987a8d78a..002791719 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java @@ -1,13 +1,7 @@ package com.trihydro.odewrapper.controller; import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; - +import com.trihydro.library.helpers.DateTimeHelper; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; import com.trihydro.library.helpers.Utility; @@ -23,7 +17,8 @@ import com.trihydro.odewrapper.model.ControllerResult; import com.trihydro.odewrapper.model.TimVslList; import com.trihydro.odewrapper.model.WydotTimVsl; - +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -32,12 +27,18 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + @CrossOrigin @RestController +@Slf4j @Api(description = "Variable Speed Limits") public class WydotTimVslController extends WydotTimBaseController { @@ -45,11 +46,12 @@ public class WydotTimVslController extends WydotTimBaseController { @Autowired public WydotTimVslController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler, + DateTimeHelper dateTimeHelper) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler, dateTimeHelper); } @RequestMapping(value = "/vsl-tim", method = RequestMethod.POST, headers = "Accept=application/json") @@ -58,9 +60,10 @@ public ResponseEntity createUpdateVslTim(@RequestBody TimVslList timVslL DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); - utility.logWithDate(dateFormat.format(date) + " - Create/Update VSL TIM", this.getClass()); + String msg = dateFormat.format(date) + " - Create/Update VSL TIM"; + log.info("{}: {}", this.getClass().getSimpleName(), msg); String post = gson.toJson(timVslList); - utility.logWithDate(post.toString(), this.getClass()); + log.info("{}: {}", this.getClass().getSimpleName(), post.toString()); List resultList = new ArrayList(); ControllerResult resultTim = null; diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/HttpLoggingFilter.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/HttpLoggingFilter.java index cabc4ed20..5b6527a69 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/HttpLoggingFilter.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/HttpLoggingFilter.java @@ -15,33 +15,32 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.HttpLoggingModel; import com.trihydro.library.service.LoggingService; import com.trihydro.odewrapper.config.BasicConfiguration; import com.trihydro.odewrapper.model.BufferedRequestWrapper; import com.trihydro.odewrapper.model.BufferedResponseWrapper; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; // Adapted from https://stackoverflow.com/a/39137815 @Component +@Slf4j public class HttpLoggingFilter implements Filter { private LoggingService loggingService; private BasicConfiguration basicConfiguration; - private Utility utility; private BufferedResponseWrapperFactory buffRespWrapFac; private BufferedRequestWrapperFactory buffReqWrapFac; @Autowired public void InjectDependencies(LoggingService _loggingService, BasicConfiguration _basicConfiguration, - Utility _utility, BufferedResponseWrapperFactory _buffRespWrapFac, + BufferedResponseWrapperFactory _buffRespWrapFac, BufferedRequestWrapperFactory _buffReqWrapFac) { loggingService = _loggingService; basicConfiguration = _basicConfiguration; - utility = _utility; buffRespWrapFac = _buffRespWrapFac; buffReqWrapFac = _buffReqWrapFac; } @@ -64,7 +63,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha String servletPath = httpServletRequest.getServletPath(); chain.doFilter(bufferedRequest, bufferedResponse); - if (servletPath.contains("swagger") || servletPath.contains("api-docs")) { + if (servletPath.contains("swagger") || servletPath.contains("api-docs") || servletPath.contains("/actuator/health")) { return; } @@ -98,14 +97,14 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha logMessage.append(" [RESPONSE:").append(serverResponse).append("]"); } } - utility.logWithDate(logMessage.toString()); + log.info(logMessage.toString()); HttpLoggingModel httpLoggingModel = new HttpLoggingModel(); httpLoggingModel.setRequest(logMessage.toString()); httpLoggingModel.setRequestTime(requestTime); httpLoggingModel.setResponseTime(new Timestamp(System.currentTimeMillis())); loggingService.LogHttpRequest(httpLoggingModel); } catch (Throwable a) { - utility.logWithDate(a.getMessage()); + log.info(a.getMessage()); } } diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java index 062daa1a3..10825aa36 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java @@ -155,6 +155,7 @@ public List setItisCodesParking(WydotTimParking wydotTim) { } public List setItisCodesIncident(WydotTimIncident wydotTim) { + log.debug("Setting ITIS codes for incident TIM with client id: {}", wydotTim.getClientId()); List items = new ArrayList<>(); // action @@ -194,9 +195,11 @@ public List setItisCodesIncident(WydotTimIncident wydotTim) { // If no incident problem is provided, default to "Incident" (ITIS code 531) if (items.isEmpty()) { + log.info("Incident problem is null or not found, defaulting to 'Incident' (ITIS code 531)"); items.add("531"); // 531 is "Incident" } + log.trace("Done setting ITIS codes for incident TIM with client id: {}. ITIS codes: {}", wydotTim.getClientId(), items); return items; } @@ -209,7 +212,7 @@ private List handleOtherIncidentProblem(WydotTimIncident wydotTim) { String problemOtherText = wydotTim.getProblemOtherText(); if (!problemOtherText.contains("GVW")) { - log.error("Unsupported problemOtherText: {}", problemOtherText); + log.warn("Unsupported problemOtherText: {}", problemOtherText); return items; } diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimBowr.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimBowr.java index c0d0c5be5..5936a829a 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimBowr.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimBowr.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.model; +import com.trihydro.library.model.Coordinate; import com.trihydro.library.model.WydotTim; import io.swagger.annotations.ApiModelProperty; diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java index 7a257292c..e5df9469e 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java @@ -2,6 +2,7 @@ import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import com.trihydro.library.helpers.CreateBaseTimUtil; +import com.trihydro.library.helpers.DateTimeHelperImpl; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.JavaMailSenderImplProvider; import com.trihydro.library.helpers.MilepostReduction; @@ -25,7 +26,6 @@ import com.trihydro.library.service.TimService; import com.trihydro.library.service.TimTypeService; import com.trihydro.library.service.WydotTimService; - import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -57,7 +57,8 @@ Utility.class, WydotTimService.class, RegionNameTrimmer.class, - IdenticalPointsExceptionHandler.class + IdenticalPointsExceptionHandler.class, + DateTimeHelperImpl.class } ) public class ApplicationConfig implements WebMvcConfigurer { diff --git a/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRcControllerTest.java b/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRcControllerTest.java deleted file mode 100644 index bd914b6e0..000000000 --- a/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRcControllerTest.java +++ /dev/null @@ -1,290 +0,0 @@ -package com.trihydro.odewrapper; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; - -import com.google.gson.Gson; -import com.trihydro.library.helpers.CreateBaseTimUtil; -import com.trihydro.library.helpers.TimGenerationHelper; -import com.trihydro.library.helpers.Utility; -import com.trihydro.library.model.ActiveTim; -import com.trihydro.library.model.Coordinate; -import com.trihydro.library.model.ItisCode; -import com.trihydro.library.service.ActiveTimService; -import com.trihydro.library.service.TimTypeService; -import com.trihydro.odewrapper.config.BasicConfiguration; -import com.trihydro.odewrapper.controller.WydotTimRcController; -import com.trihydro.odewrapper.helpers.SetItisCodes; -import com.trihydro.odewrapper.model.ControllerResult; -import com.trihydro.odewrapper.model.TimRcList; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -@ExtendWith(MockitoExtension.class) -public class WydotTimRcControllerTest { - - @Mock - BasicConfiguration mockBasicConfiguration; - @Mock - TimTypeService mockTimTypeService; - @Mock - TimGenerationHelper mockTimGenerationHelper; - @Mock - CreateBaseTimUtil mockCreateBaseTimUtil; - @Mock - SetItisCodes setItisCodes; - @Mock - Utility utility; - @Mock - ActiveTimService mockActiveTimService; - - @InjectMocks - @Spy - WydotTimRcController uut; - - private Gson gson = new Gson(); - - @BeforeEach - public void setup() throws Exception { - List itisCodes = new ArrayList<>(); - ItisCode ic = new ItisCode(); - ic.setCategoryId(-1); - ic.setDescription("description"); - ic.setItisCode(-2); - ic.setItisCodeId(-3); - itisCodes.add(ic); - List itisCodesIncident = new ArrayList<>(); - itisCodesIncident.add("531"); - lenient().doReturn(itisCodesIncident).when(setItisCodes).setItisCodesRc(any()); - lenient().doReturn(itisCodes).when(setItisCodes).getItisCodes(); - - lenient().doReturn(true).when(uut).routeSupported(isA(String.class)); - } - - private List getActiveTims(boolean isSat) { - List activeTims = new ArrayList(); - ActiveTim aTim = new ActiveTim(); - ActiveTim aTim2 = new ActiveTim(); - aTim.setActiveTimId(-1l); - aTim2.setActiveTimId(-2l); - if (isSat) { - aTim.setSatRecordId("C27CBB9F"); - aTim2.setSatRecordId("86E03786"); - } else { - aTim.setStartPoint(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))); - aTim.setEndPoint(new Coordinate(BigDecimal.valueOf(3), BigDecimal.valueOf(4))); - aTim.setTimId(-10l); - aTim2.setStartPoint(new Coordinate(BigDecimal.valueOf(5), BigDecimal.valueOf(6))); - aTim2.setEndPoint(new Coordinate(BigDecimal.valueOf(7), BigDecimal.valueOf(8))); - aTim2.setTimId(-20l); - } - activeTims.add(aTim); - activeTims.add(aTim2); - - return activeTims; - } - - @Test - public void testCreateRcTim_bothDirections_success() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578}, \"roadCode\": \"LARI80WQDHLD\", \"direction\": \"b\",\"advisory\": [4871]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - - // Act - ResponseEntity data = uut.createUpdateRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("success", resultArr[0].resultMessages.get(0)); - Assertions.assertEquals("b", resultArr[0].direction); - Assertions.assertEquals("I80", resultArr[0].route); - } - - @Test - public void testCreateRcTim_bothDirections_RouteNotSupported() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I70\", \"roadCode\": \"LARI80WQDHLD\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578}, \"direction\":\"b\",\"advisory\": [4871]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - doReturn(false).when(uut).routeSupported("I70"); - - // Act - ResponseEntity data = uut.createUpdateRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("route not supported", resultArr[0].resultMessages.get(0)); - Assertions.assertEquals("b", resultArr[0].direction); - } - - @Test - public void testCreateRcTim_bothDirections_NoItisCodes() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"b\",\"advisory\": [11]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - lenient().doReturn(new ArrayList<>()).when(setItisCodes).setItisCodesRc(any()); - - // Act - ResponseEntity data = uut.createUpdateRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("No ITIS codes found", resultArr[0].resultMessages.get(0)); - Assertions.assertEquals("b", resultArr[0].direction); - } - - @Test - public void testCreateRcTim_oneDirection_success() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"i\",\"advisory\": [4871]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - - // Act - ResponseEntity data = uut.createUpdateRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("success", resultArr[0].resultMessages.get(0)); - Assertions.assertEquals("i", resultArr[0].direction); - Assertions.assertEquals("I80", resultArr[0].route); - } - - @Test - public void testCreateVslTim_oneDirection_NoMileposts() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"i\",\"advisory\": [4871]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - - // Act - ResponseEntity data = uut.createUpdateRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("success", resultArr[0].resultMessages.get(0)); - Assertions.assertEquals("i", resultArr[0].direction); - Assertions.assertEquals("I80", resultArr[0].route); - } - - @Test - public void testCreateRcTim_oneDirection_NoItisCodes() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"i\",\"advisory\": [11]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - - // Act - ResponseEntity data = uut.createUpdateRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("success", resultArr[0].resultMessages.get(0)); - Assertions.assertEquals("i", resultArr[0].direction); - Assertions.assertEquals("I80", resultArr[0].route); - } - - @Test - public void testAllClear() throws Exception { - - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"d\",\"advisory\": [5378]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - - // Act - ResponseEntity data = uut.submitAllClearRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("success", resultArr[0].resultMessages.get(0)); - - // Parameters required for AC - Assertions.assertEquals("d", resultArr[0].direction); - Assertions.assertEquals("LARI80WQDHLD", resultArr[0].clientId); - - // Route isn't required for an AC, so it isn't set in the response. - Assertions.assertEquals(null, resultArr[0].route); - } - - @Test - public void testAllClear_CallsResubmitToOde_ExpiresExistingTims() throws Exception { - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"d\",\"advisory\": [5378]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - List activeTims = getActiveTims(false); - when(mockActiveTimService.getActiveTimsByClientIdDirection(any(), any(), any())).thenReturn(activeTims); - - // Act - ResponseEntity data = uut.submitAllClearRoadConditionsTim(timRcList); - - // Assert - verify(mockTimGenerationHelper).expireTimAndResubmitToOde(any()); - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - } - - @Test - public void testAllClear_Bidirectional() throws Exception { - // Arrange - String rcJson = "{\"timRcList\": [{ \"route\": \"I80\", \"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"roadCode\": \"LARI80WQDHLD\", \"direction\":\"b\",\"advisory\": [5378]} ]}"; - TimRcList timRcList = gson.fromJson(rcJson, TimRcList.class); - - // Act - ResponseEntity data = uut.submitAllClearRoadConditionsTim(timRcList); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - ControllerResult[] resultArr = gson.fromJson(data.getBody(), ControllerResult[].class); - Assertions.assertNotNull(resultArr); - Assertions.assertEquals(1, resultArr.length); - Assertions.assertEquals("success", resultArr[0].resultMessages.get(0)); - - // Parameters required for AC - Assertions.assertEquals("b", resultArr[0].direction); - Assertions.assertEquals("LARI80WQDHLD", resultArr[0].clientId); - - // Route isn't required for an AC, so it isn't set in the response. - Assertions.assertEquals(null, resultArr[0].route); - verify(mockActiveTimService).getActiveTimsByClientIdDirection("LARI80WQDHLD", null, null); - } -} \ No newline at end of file diff --git a/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRwControllerTest.java b/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRwControllerTest.java index 7c933ca79..2131548eb 100644 --- a/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRwControllerTest.java +++ b/ode-wrapper/src/test/java/com/trihydro/odewrapper/WydotTimRwControllerTest.java @@ -6,6 +6,7 @@ import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verify; +import com.trihydro.library.helpers.DateTimeHelper; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -49,12 +50,14 @@ public class WydotTimRwControllerTest { SetItisCodes setItisCodes; @Mock Utility utility; + @Mock + DateTimeHelper dateTimeHelper; @InjectMocks @Spy WydotTimRwController uut; - private Gson gson = new Gson(); + private final Gson gson = new Gson(); @BeforeEach public void setup() throws Exception { @@ -97,7 +100,7 @@ public void testCreateRwTim_oneDirection_SUCCESS() throws Exception { } @Test - public void testCreateRwTim_bothDirections_NoMileposts() throws Exception { + public void testCreateRwTim_bothDirections_NoMileposts() { // Arrange String rwJson = "{ \"timRwList\": [ {\"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"highway\": \"I-80\",\"pk\": \"15917\",\"id\": \"15917\",\"projectKey\": 19185,\"direction\":\"d\",\"surface\": \"P\",\"schedStart\": \"2018-04-16\"}]}"; @@ -116,7 +119,7 @@ public void testCreateRwTim_bothDirections_NoMileposts() throws Exception { } @Test - public void testCreateRwTim_bothDirections_NoItisCodes() throws Exception { + public void testCreateRwTim_bothDirections_NoItisCodes() { String rwJson = "{ \"timRwList\": [ {\"startPoint\": {\"latitude\": 41.161446, \"longitude\": -104.653162},\"endPoint\": {\"latitude\": 41.170465, \"longitude\": -104.085578},\"highway\": \"I-80\",\"pk\": \"15917\",\"id\": \"15917\",\"projectKey\": 19185,\"direction\":\"d\",\"surface\": \"P\",\"schedStart\": \"2018-04-16\"}]}"; TimRwList timRwList = gson.fromJson(rwJson, TimRwList.class); @@ -136,7 +139,7 @@ public void testCreateRwTim_bothDirections_NoItisCodes() throws Exception { } @Test - public void testDeleteRwTimsByClientId() throws Exception { + public void testDeleteRwTimsByClientId() { // Arrange String id = "15917"; diff --git a/ode-wrapper/src/test/java/com/trihydro/odewrapper/controller/UtilityControllerTest.java b/ode-wrapper/src/test/java/com/trihydro/odewrapper/controller/UtilityControllerTest.java index f17b5363e..36bfeab60 100644 --- a/ode-wrapper/src/test/java/com/trihydro/odewrapper/controller/UtilityControllerTest.java +++ b/ode-wrapper/src/test/java/com/trihydro/odewrapper/controller/UtilityControllerTest.java @@ -15,6 +15,7 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.trihydro.library.helpers.TimGenerationHelper; +import com.trihydro.library.model.ResubmitTimException; import com.trihydro.library.model.TimDeleteSummary; import com.trihydro.library.model.TimQuery; import com.trihydro.library.model.WydotRsu; diff --git a/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/HttpLoggingFilterTest.java b/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/HttpLoggingFilterTest.java index c8813f519..a2b232b8a 100644 --- a/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/HttpLoggingFilterTest.java +++ b/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/HttpLoggingFilterTest.java @@ -1,33 +1,31 @@ package com.trihydro.odewrapper.helpers; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; - -import java.io.IOException; -import java.util.Collections; -import java.util.Enumeration; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.trihydro.library.helpers.Utility; import com.trihydro.library.service.LoggingService; import com.trihydro.odewrapper.config.BasicConfiguration; import com.trihydro.odewrapper.model.BufferedRequestWrapper; import com.trihydro.odewrapper.model.BufferedResponseWrapper; - import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; +import java.util.Enumeration; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + @ExtendWith(MockitoExtension.class) public class HttpLoggingFilterTest { @Mock @@ -43,8 +41,6 @@ public class HttpLoggingFilterTest { LoggingService mockLoggingService; @Mock BasicConfiguration mockBasicConfiguration; - @Mock - Utility mockUtility; @Mock BufferedResponseWrapperFactory mockBufferedResponseWrapperFactory; @@ -94,37 +90,31 @@ public void doFilter_skip_apiDocs() throws IOException, ServletException { } @Test - public void doFilter_SUCCESS() throws IOException, ServletException { + public void doFilter_skip_actuatorHealth() throws IOException, ServletException { // Arrange - doReturn("/").when(mockHttpServletRequest).getServletPath(); - doReturn(2000).when(mockBasicConfiguration).getHttpLoggingMaxSize(); - doReturn("log body").when(mockBufferedRequestWrapper).getRequestBody(); - doReturn(200).when(mockBufferedResponseWrapper).getStatus(); - doReturn("response").when(mockBufferedResponseWrapper).getContent(); + doReturn("/actuator/health").when(mockHttpServletRequest).getServletPath(); // Act uut.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain); // Assert - verify(mockLoggingService).LogHttpRequest(any()); + verifyNoInteractions(mockLoggingService); } @Test - public void doFilter_SUCCESS_truncate() throws IOException, ServletException { + public void doFilter_SUCCESS() throws IOException, ServletException { // Arrange doReturn("/").when(mockHttpServletRequest).getServletPath(); - doReturn(150).when(mockBasicConfiguration).getHttpLoggingMaxSize(); - doReturn("this is a long request body to be truncated").when(mockBufferedRequestWrapper).getRequestBody(); + doReturn(2000).when(mockBasicConfiguration).getHttpLoggingMaxSize(); + doReturn("log body").when(mockBufferedRequestWrapper).getRequestBody(); doReturn(200).when(mockBufferedResponseWrapper).getStatus(); - doReturn("this is a longer content").when(mockBufferedResponseWrapper).getContent(); + doReturn("response").when(mockBufferedResponseWrapper).getContent(); // Act uut.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain); // Assert verify(mockLoggingService).LogHttpRequest(any()); - verify(mockUtility).logWithDate( - "REST Request - [HTTP METHOD:null] [PATH INFO:/] [REQUEST PARAMETERS:{}] [REQUEST BODY:this is a long request...] [RESPONSE CODE:200] [RESPONSE:thi...]"); } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index d8100f086..1d2c5a0ac 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 @@ -33,16 +35,6 @@ cert-expiration - - - - org.springframework.plugin - spring-plugin-core - 1.2.0.RELEASE - - - - com.google.code.gson diff --git a/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/controller/RsuTimController.java b/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/controller/RsuTimController.java index 11bcf002a..832ec8f87 100644 --- a/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/controller/RsuTimController.java +++ b/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/controller/RsuTimController.java @@ -6,6 +6,8 @@ import com.trihydro.rsudatacontroller.model.RsuTim; import com.trihydro.rsudatacontroller.service.RsuService; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; @@ -14,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController +@Slf4j @RequestMapping("rsu") public class RsuTimController { private RsuService rsuService; @@ -44,13 +47,13 @@ public ResponseEntity> GetRsuTimsDeliveryStart(@PathVariable("rsuAd try { results = rsuService.getAllDeliveryStartTimes(ipv4Address); } catch (Exception ex) { - utility.logWithDate("Error invoking or reading from SNMP process: "); - ex.printStackTrace(); + log.info("Error invoking or reading from SNMP process: "); + log.error("Exception", ex); return ResponseEntity.status(500).body(null); } if (results == null) { - utility.logWithDate("Responding with HTTP 422 (RSU: " + ipv4Address + ")"); + log.info("Responding with HTTP 422 (RSU: {})", ipv4Address); return ResponseEntity.unprocessableEntity().body(null); } diff --git a/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/service/RsuService.java b/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/service/RsuService.java index 1866b4fbf..c52be4ee8 100644 --- a/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/service/RsuService.java +++ b/rsu-data-controller/src/main/java/com/trihydro/rsudatacontroller/service/RsuService.java @@ -15,10 +15,13 @@ import com.trihydro.rsudatacontroller.model.RsuTim; import com.trihydro.rsudatacontroller.process.ProcessFactory; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class RsuService { private static final String oid_rsuSRMDeliveryStart = "1.0.15628.4.1.4.1.7"; private static final DateTimeFormatter rsuDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -51,7 +54,7 @@ public List getAllDeliveryStartTimes(String rsuIpv4Address) throws Excep // If timeout occurred, return null if (snmpWalkOutput.matches("snmpwalk: Timeout")) { - utility.logWithDate("SNMP Timeout occurred (RSU: " + rsuIpv4Address + ")"); + log.info("SNMP Timeout occurred (RSU: {})", rsuIpv4Address); return null; } diff --git a/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/controller/RsuTimControllerTest.java b/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/controller/RsuTimControllerTest.java index 2c944ea38..4a933f1b1 100644 --- a/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/controller/RsuTimControllerTest.java +++ b/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/controller/RsuTimControllerTest.java @@ -1,16 +1,7 @@ package com.trihydro.rsudatacontroller.controller; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.List; - -import com.trihydro.library.helpers.Utility; import com.trihydro.rsudatacontroller.model.RsuTim; import com.trihydro.rsudatacontroller.service.RsuService; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -20,14 +11,16 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.Mockito.when; + @ExtendWith(MockitoExtension.class) public class RsuTimControllerTest { @Mock RsuService mockRsuService; - @Mock - Utility mockUtility; - @InjectMocks RsuTimController uut; @@ -50,7 +43,7 @@ public void getAllDeliveryStartTimes_badIpv4() { // Assert Assertions.assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); - Assertions.assertEquals(null, result.getBody()); + Assertions.assertNull(result.getBody()); } @Test @@ -63,8 +56,7 @@ public void getAllDeliveryStartTimes_unresponsiveRsu() throws Exception { // Assert Assertions.assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, result.getStatusCode()); - Assertions.assertEquals(null, result.getBody()); - verify(mockUtility).logWithDate(any()); + Assertions.assertNull(result.getBody()); } @Test @@ -77,7 +69,6 @@ public void getAllDeliveryStartTimes_processError() throws Exception { // Assert Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode()); - Assertions.assertEquals(null, result.getBody()); - verify(mockUtility).logWithDate(any()); + Assertions.assertNull(result.getBody()); } } \ No newline at end of file diff --git a/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/service/RsuServiceTest.java b/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/service/RsuServiceTest.java index 7b6456c19..39f406f13 100644 --- a/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/service/RsuServiceTest.java +++ b/rsu-data-controller/src/test/java/com/trihydro/rsudatacontroller/service/RsuServiceTest.java @@ -1,22 +1,9 @@ package com.trihydro.rsudatacontroller.service; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - import com.trihydro.library.helpers.Utility; import com.trihydro.rsudatacontroller.config.BasicConfiguration; import com.trihydro.rsudatacontroller.model.RsuTim; import com.trihydro.rsudatacontroller.process.ProcessFactory; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,6 +13,17 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + @ExtendWith(MockitoExtension.class) public class RsuServiceTest { @Mock @@ -38,9 +36,6 @@ public class RsuServiceTest { @Mock BasicConfiguration mockConfig; - @Mock - Utility mockUtility; - @Mock InputStream mockInputStream; @@ -134,7 +129,6 @@ public void getAllDeliveryStartTimes_snmpTimeout() throws Exception { // Assert Assertions.assertNull(results); - verify(mockUtility).logWithDate(any()); } @Test diff --git a/tim-refresh/.gitignore b/tim-refresh/.gitignore index f23b9489f..8d59a882c 100644 --- a/tim-refresh/.gitignore +++ b/tim-refresh/.gitignore @@ -1 +1,2 @@ -*.jar \ No newline at end of file +*.jar +*.crt \ No newline at end of file diff --git a/tim-refresh/src/main/java/com/trihydro/timrefresh/Application.java b/tim-refresh/src/main/java/com/trihydro/timrefresh/Application.java index c3a89b046..d36fffbfe 100644 --- a/tim-refresh/src/main/java/com/trihydro/timrefresh/Application.java +++ b/tim-refresh/src/main/java/com/trihydro/timrefresh/Application.java @@ -1,19 +1,22 @@ package com.trihydro.timrefresh; import java.text.SimpleDateFormat; -import java.util.Date; - +import java.util.Date; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableScheduling; - -@SpringBootApplication -@EnableScheduling -public class Application { +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableScheduling +@Slf4j +public class Application { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); - public static void main(String[] args) { - System.out.println("Starting TIM Refresh application at " + dateFormat.format(new Date())); + public static void main(String[] args) { + log.info("Starting TIM Refresh application at {}", dateFormat.format(new Date())); SpringApplication.run(Application.class, args); } } \ No newline at end of file diff --git a/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java b/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java index 51aecd98e..81bb39726 100644 --- a/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java +++ b/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java @@ -17,6 +17,7 @@ import com.trihydro.library.model.SdwProps; import com.trihydro.library.service.ActiveTimHoldingService; import com.trihydro.library.service.ActiveTimService; + import com.trihydro.library.service.DataFrameService; import com.trihydro.library.service.MilepostService; import com.trihydro.library.service.OdeService; @@ -39,7 +40,7 @@ @Import({ ActiveTimHoldingService.class, ActiveTimService.class, DataFrameService.class, MilepostService.class, OdeService.class, PathNodeXYService.class, RegionService.class, RsuService.class, SdwService.class, Utility.class, RestTemplateProvider.class, MilepostReduction.class, JavaMailSenderImplProvider.class, - EmailHelper.class, TimGenerationHelper.class, PathNodeLLService.class, SnmpHelper.class, + EmailHelper.class, TimGenerationHelper.class, PathNodeLLService.class, SnmpHelper.class, RegionNameTrimmer.class, CreateBaseTimUtil.class, IdenticalPointsExceptionHandler.class }) public class TimRefreshConfiguration implements CVRestServiceProps, SdwProps, EmailProps, OdeProps, TimGenerationProps { diff --git a/tim-refresh/src/test/java/com/trihydro/timrefresh/TimRefreshControllerTest.java b/tim-refresh/src/test/java/com/trihydro/timrefresh/TimRefreshControllerTest.java index 2d406f8fd..aceefce71 100644 --- a/tim-refresh/src/test/java/com/trihydro/timrefresh/TimRefreshControllerTest.java +++ b/tim-refresh/src/test/java/com/trihydro/timrefresh/TimRefreshControllerTest.java @@ -24,19 +24,22 @@ import com.trihydro.library.model.TimUpdateModel; import com.trihydro.library.service.ActiveTimService; import com.trihydro.library.service.SdwService; -import com.trihydro.timrefresh.config.TimRefreshConfiguration; - +import com.trihydro.timrefresh.config.TimRefreshConfiguration; + +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.mail.MailException; - -@ExtendWith(MockitoExtension.class) -public class TimRefreshControllerTest { +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.springframework.mail.MailException; + +@ExtendWith(MockitoExtension.class) +@Slf4j +public class TimRefreshControllerTest { private long timID = 1l; @Mock @@ -56,8 +59,8 @@ public class TimRefreshControllerTest { private TimRefreshController controllerUnderTest; @BeforeEach - public void setup(TestInfo testInfo) { - System.out.println("Executing " + testInfo.getTestMethod().get().getName()); + public void setup(TestInfo testInfo) { + log.info("Executing {}", testInfo.getTestMethod().get().getName()); } @Test