diff --git a/src/main/java/at/procon/eventhub/dto/DriverCardRefDto.java b/src/main/java/at/procon/eventhub/dto/DriverCardRefDto.java index 7ea04a4..62b0c95 100644 --- a/src/main/java/at/procon/eventhub/dto/DriverCardRefDto.java +++ b/src/main/java/at/procon/eventhub/dto/DriverCardRefDto.java @@ -1,5 +1,6 @@ package at.procon.eventhub.dto; +import at.procon.eventhub.reference.DriverCardNumberNormalizer; import at.procon.eventhub.reference.TachographNationRegistry; /** @@ -43,6 +44,6 @@ public record DriverCardRefDto( } private static String normalizeNullable(String value) { - return value == null || value.isBlank() ? null : value.trim(); + return DriverCardNumberNormalizer.canonical(value); } } diff --git a/src/main/java/at/procon/eventhub/persistence/DriverIdentityRepository.java b/src/main/java/at/procon/eventhub/persistence/DriverIdentityRepository.java index 3ba8187..6897820 100644 --- a/src/main/java/at/procon/eventhub/persistence/DriverIdentityRepository.java +++ b/src/main/java/at/procon/eventhub/persistence/DriverIdentityRepository.java @@ -2,6 +2,7 @@ package at.procon.eventhub.persistence; import at.procon.eventhub.dto.DriverCardRefDto; import at.procon.eventhub.dto.DriverRefDto; +import at.procon.eventhub.reference.DriverCardNumberNormalizer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.time.LocalDate; @@ -16,9 +17,6 @@ import org.springframework.transaction.annotation.Transactional; @Repository public class DriverIdentityRepository { - private static final String YELLOWFOX_SYNTHETIC_REFERENCE_NATION = "YELLOWFOX"; - private static final String UNKNOWN_CARD_NATION = "UNKNOWN"; - private final JdbcTemplate jdbcTemplate; private final ObjectMapper objectMapper; @@ -41,7 +39,7 @@ public class DriverIdentityRepository { DriverCardRefDto driverCard = driverRef.driverCard(); String cardNation = driverCard == null ? null : normalizeNullable(driverCard.nation()); Integer cardNationNumericCode = driverCard == null ? null : driverCard.nationNumericCode(); - String cardNumber = driverCard == null ? null : normalizeDriverCardNumber(cardNation, driverCard.number()); + String cardNumber = driverCard == null ? null : normalizeDriverCardNumber(driverCard.number()); UUID driverId = findBySourceDriverEntityId(normalizedTenantKey, eventSourceId, sourceDriverEntityId); UUID driverCardId = resolveOrCreateDriverCardId(cardNation, cardNationNumericCode, cardNumber, driverId); @@ -782,20 +780,8 @@ public class DriverIdentityRepository { return legacyColumns != null && legacyColumns == 3; } - private String normalizeDriverCardNumber(String cardNation, String cardNumber) { - String normalized = normalizeNullable(cardNumber); - if (normalized == null) { - return null; - } - if (isSyntheticYellowFoxCardNation(cardNation)) { - return normalized.length() <= 14 ? normalized : normalized.substring(0, 14); - } - return normalized; - } - - private boolean isSyntheticYellowFoxCardNation(String cardNation) { - return YELLOWFOX_SYNTHETIC_REFERENCE_NATION.equalsIgnoreCase(cardNation) - || UNKNOWN_CARD_NATION.equalsIgnoreCase(cardNation); + private String normalizeDriverCardNumber(String cardNumber) { + return DriverCardNumberNormalizer.canonical(cardNumber); } private UUID createDriverCard( diff --git a/src/main/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequest.java b/src/main/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequest.java index 1fab3d5..12c7e51 100644 --- a/src/main/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequest.java +++ b/src/main/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequest.java @@ -1,5 +1,6 @@ package at.procon.eventhub.processing.model; +import at.procon.eventhub.reference.DriverCardNumberNormalizer; import java.time.OffsetDateTime; import java.util.Objects; import java.util.UUID; @@ -25,7 +26,7 @@ public record UnifiedDriverEventsRequest( tenantKey = normalize(tenantKey); driverSourceEntityId = normalize(driverSourceEntityId); driverCardNation = normalizeUpper(driverCardNation); - driverCardNumber = normalize(driverCardNumber); + driverCardNumber = normalizeDriverCardNumber(driverCardNumber); vehicleSourceEntityId = normalize(vehicleSourceEntityId); vin = normalizeUpper(vin); registrationNation = normalizeUpper(registrationNation); @@ -171,4 +172,8 @@ public record UnifiedDriverEventsRequest( private static String normalizeUpper(String value) { return value == null || value.isBlank() ? null : value.trim().toUpperCase(); } + + private static String normalizeDriverCardNumber(String value) { + return DriverCardNumberNormalizer.canonical(value); + } } diff --git a/src/main/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequest.java b/src/main/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequest.java index f5b1f47..8ae9e0a 100644 --- a/src/main/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequest.java +++ b/src/main/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequest.java @@ -1,5 +1,6 @@ package at.procon.eventhub.processing.model; +import at.procon.eventhub.reference.DriverCardNumberNormalizer; import java.time.OffsetDateTime; import java.util.Set; import java.util.UUID; @@ -23,7 +24,7 @@ public record UnifiedRuntimeProcessingRequest( tenantKey = normalize(tenantKey); driverSourceEntityId = normalize(driverSourceEntityId); driverCardNation = normalizeUpper(driverCardNation); - driverCardNumber = normalize(driverCardNumber); + driverCardNumber = normalizeDriverCardNumber(driverCardNumber); boolean includesFileSession = sourceFamilies != null && sourceFamilies.contains(UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION); boolean includesExternalDb = sourceFamilies != null && sourceFamilies.stream() .anyMatch(family -> family == UnifiedEventSourceFamily.TACHOGRAPH_DB || family == UnifiedEventSourceFamily.YELLOWFOX_DB); @@ -196,4 +197,8 @@ public record UnifiedRuntimeProcessingRequest( private static String normalizeUpper(String value) { return value == null || value.isBlank() ? null : value.trim().toUpperCase(); } + + private static String normalizeDriverCardNumber(String value) { + return DriverCardNumberNormalizer.canonical(value); + } } diff --git a/src/main/java/at/procon/eventhub/processing/service/UnifiedEventTimelineReconstructor.java b/src/main/java/at/procon/eventhub/processing/service/UnifiedEventTimelineReconstructor.java index bc0c752..d59bf5e 100644 --- a/src/main/java/at/procon/eventhub/processing/service/UnifiedEventTimelineReconstructor.java +++ b/src/main/java/at/procon/eventhub/processing/service/UnifiedEventTimelineReconstructor.java @@ -136,6 +136,7 @@ public class UnifiedEventTimelineReconstructor { BigDecimal longitude = event.position() == null ? null : event.position().longitude(); result.add(new ExtractedSupportEvent( eventId, + text(raw, "driverKey"), event.occurredAt(), event.eventDomain().name(), text(raw, "supportEventType") == null ? event.eventType().name() : text(raw, "supportEventType"), diff --git a/src/main/java/at/procon/eventhub/reference/DriverCardNumberNormalizer.java b/src/main/java/at/procon/eventhub/reference/DriverCardNumberNormalizer.java new file mode 100644 index 0000000..cc05870 --- /dev/null +++ b/src/main/java/at/procon/eventhub/reference/DriverCardNumberNormalizer.java @@ -0,0 +1,30 @@ +package at.procon.eventhub.reference; + +public final class DriverCardNumberNormalizer { + + public static final int CANONICAL_LENGTH = 14; + + private DriverCardNumberNormalizer() { + } + + public static String canonical(String rawCardNumber) { + String normalized = normalize(rawCardNumber); + if (normalized == null) { + return null; + } + return normalized.length() <= CANONICAL_LENGTH + ? normalized + : normalized.substring(0, CANONICAL_LENGTH); + } + + public static String full(String rawCardNumber) { + return normalize(rawCardNumber); + } + + private static String normalize(String value) { + if (value == null || value.isBlank()) { + return null; + } + return value.trim().toUpperCase(); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java index 3e950b6..7f28d1f 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java @@ -6,6 +6,7 @@ public record ExtractedDriverCard( String sourceDriverCardId, String cardNation, String cardNumber, + String fullCardNumber, String issuingAuthorityName, OffsetDateTime issueDate, OffsetDateTime validityBegin, diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java index 76fd8fc..620e62b 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java @@ -5,6 +5,7 @@ import java.time.OffsetDateTime; public record ExtractedSupportEvent( String eventId, + String driverKey, OffsetDateTime occurredAt, String eventDomain, String eventType, diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java index 8783a8a..09bcca6 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java @@ -60,6 +60,7 @@ public class DriverCardXmlExtractionService { driverKeyFactory.createSourceDriverCardId(driverKey), driverCard.cardNation(), driverCard.cardNumber(), + driverCard.fullCardNumber(), driverCard.issuingAuthorityName(), driverCard.issueDate(), driverCard.validityBegin(), @@ -73,7 +74,7 @@ public class DriverCardXmlExtractionService { extractVehicleUsageIntervals(document, registrationsByKey, vehiclesByKey, warnings); List activityIntervals = assignVehicleCoverage(extractActivityIntervals(document, warnings), vehicleUsageIntervals); - List supportEvents = extractSupportEvents(document, vehicleUsageIntervals, warnings); + List supportEvents = extractSupportEvents(document, driverKey, vehicleUsageIntervals, warnings); DriverExtractionSession driverSession = new DriverExtractionSession( driverKey, @@ -114,12 +115,14 @@ public class DriverCardXmlExtractionService { } Element cardIdentification = child(identification, "cardIdentification"); String cardNation = childText(cardIdentification, "cardIssuingMemberState"); - String cardNumber = joinCardNumber(identification); + String fullCardNumber = joinCardNumber(identification); + String cardNumber = driverKeyFactory.canonicalCardNumber(fullCardNumber); String authority = childText(child(cardIdentification, "cardIssuingAuthorityName"), "name"); return new ExtractedDriverCard( null, cardNation, cardNumber, + fullCardNumber, authority, offsetDateTime(childText(cardIdentification, "cardIssueDate")), offsetDateTime(childText(cardIdentification, "cardValidityBegin")), @@ -307,17 +310,18 @@ public class DriverCardXmlExtractionService { private List extractSupportEvents( Document document, + String driverKey, List vehicleUsageIntervals, List warnings ) { VehicleUsageLookup vehicleUsageLookup = new VehicleUsageLookup(vehicleUsageIntervals); List supportEvents = new ArrayList<>(); Element root = document.getDocumentElement(); - extractCardPlaceSupportEvents(root, vehicleUsageLookup, supportEvents, warnings); - extractCardGnssSupportEvents(root, vehicleUsageLookup, supportEvents, warnings); - extractCardSpecificConditionSupportEvents(root, vehicleUsageLookup, supportEvents, warnings); - extractCardBorderCrossingSupportEvents(root, vehicleUsageLookup, supportEvents, warnings); - extractCardLoadUnloadSupportEvents(root, vehicleUsageLookup, supportEvents, warnings); + extractCardPlaceSupportEvents(root, driverKey, vehicleUsageLookup, supportEvents, warnings); + extractCardGnssSupportEvents(root, driverKey, vehicleUsageLookup, supportEvents, warnings); + extractCardSpecificConditionSupportEvents(root, driverKey, vehicleUsageLookup, supportEvents, warnings); + extractCardBorderCrossingSupportEvents(root, driverKey, vehicleUsageLookup, supportEvents, warnings); + extractCardLoadUnloadSupportEvents(root, driverKey, vehicleUsageLookup, supportEvents, warnings); supportEvents.sort(Comparator.comparing(ExtractedSupportEvent::occurredAt) .thenComparing(ExtractedSupportEvent::eventDomain, Comparator.nullsLast(String::compareTo)) .thenComparing(ExtractedSupportEvent::eventId, Comparator.nullsLast(String::compareTo))); @@ -326,6 +330,7 @@ public class DriverCardXmlExtractionService { private void extractCardPlaceSupportEvents( Element root, + String driverKey, VehicleUsageLookup vehicleUsageLookup, List supportEvents, List warnings @@ -344,6 +349,10 @@ public class DriverCardXmlExtractionService { } ExtractedCardVehicleUsageInterval usage = vehicleUsageLookup.resolve(occurredAt); + if (!hasKnownRegistration(usage)) { + warnings.add(new ExtractionWarning("CARD_PLACE_UNKNOWN_VRN", "Driver-card place record has unknown vehicle registration and was ignored.", path)); + continue; + } Element gnss = child(record, "entryGnssPlaceRecord"); Element geoCoordinates = child(gnss, "geoCoordinates"); BigDecimal latitude = geoCoordinate(geoCoordinates, "latitude", true); @@ -352,6 +361,7 @@ public class DriverCardXmlExtractionService { String entryType = childText(record, "entryTypeDailyWorkPeriod"); supportEvents.add(new ExtractedSupportEvent( "CARDPLACE-" + (i + 1), + driverKey, occurredAt, "PLACE", mapPlaceEntryType(entryType), @@ -379,6 +389,7 @@ public class DriverCardXmlExtractionService { private void extractCardGnssSupportEvents( Element root, + String driverKey, VehicleUsageLookup vehicleUsageLookup, List supportEvents, List warnings @@ -397,6 +408,11 @@ public class DriverCardXmlExtractionService { } ExtractedCardVehicleUsageInterval usage = vehicleUsageLookup.resolve(occurredAt); + if (!hasKnownRegistration(usage)) { + warnings.add(new ExtractionWarning("CARD_GNSS_MISSING_VRN", "Driver-card GNSS record is missing vehicle registration.", path)); + continue; + } + Element gnss = child(record, "gnssPlaceRecord"); Element geoCoordinates = child(gnss, "geoCoordinates"); BigDecimal latitude = geoCoordinate(geoCoordinates, "latitude", true); @@ -404,6 +420,7 @@ public class DriverCardXmlExtractionService { String authenticationStatus = normalizeToken(childText(gnss, "authenticationStatus")); supportEvents.add(new ExtractedSupportEvent( "CARDGNSS-" + (i + 1), + driverKey, occurredAt, "POSITION", "POSITION_RECORDED", @@ -431,6 +448,7 @@ public class DriverCardXmlExtractionService { private void extractCardSpecificConditionSupportEvents( Element root, + String driverKey, VehicleUsageLookup vehicleUsageLookup, List supportEvents, List warnings @@ -449,10 +467,15 @@ public class DriverCardXmlExtractionService { } ExtractedCardVehicleUsageInterval usage = vehicleUsageLookup.resolve(occurredAt); + if (!hasKnownRegistration(usage)) { + warnings.add(new ExtractionWarning("CARD_SPECIFIC_CONDITION_UNKNOWN_VRN", "Driver-card specific-condition record has unknown vehicle registration and was ignored.", path)); + continue; + } String conditionCode = normalizeToken(childText(record, "specificConditionType")); String[] specificCondition = mapSpecificCondition(conditionCode); supportEvents.add(new ExtractedSupportEvent( "CARDSC-" + (i + 1), + driverKey, occurredAt, "SPECIFIC_CONDITION", specificCondition[0], @@ -480,6 +503,7 @@ public class DriverCardXmlExtractionService { private void extractCardBorderCrossingSupportEvents( Element root, + String driverKey, VehicleUsageLookup vehicleUsageLookup, List supportEvents, List warnings @@ -499,12 +523,17 @@ public class DriverCardXmlExtractionService { } ExtractedCardVehicleUsageInterval usage = vehicleUsageLookup.resolve(occurredAt); + if (!hasKnownRegistration(usage)) { + warnings.add(new ExtractionWarning("CARD_BORDER_CROSSING_UNKNOWN_VRN", "Driver-card border-crossing record has unknown vehicle registration and was ignored.", path)); + continue; + } Element geoCoordinates = child(gnss, "geoCoordinates"); BigDecimal latitude = geoCoordinate(geoCoordinates, "latitude", true); BigDecimal longitude = geoCoordinate(geoCoordinates, "longitude", false); String authenticationStatus = normalizeToken(childText(gnss, "authenticationStatus")); supportEvents.add(new ExtractedSupportEvent( "CARDBORDER-" + (i + 1), + driverKey, occurredAt, "BORDER_CROSSING", "BORDER_OUTBOUND", @@ -532,6 +561,7 @@ public class DriverCardXmlExtractionService { private void extractCardLoadUnloadSupportEvents( Element root, + String driverKey, VehicleUsageLookup vehicleUsageLookup, List supportEvents, List warnings @@ -550,6 +580,10 @@ public class DriverCardXmlExtractionService { } ExtractedCardVehicleUsageInterval usage = vehicleUsageLookup.resolve(occurredAt); + if (!hasKnownRegistration(usage)) { + warnings.add(new ExtractionWarning("CARD_LOAD_UNLOAD_UNKNOWN_VRN", "Driver-card load/unload record has unknown vehicle registration and was ignored.", path)); + continue; + } Element gnss = child(record, "gnssPlaceAuthRecord"); Element geoCoordinates = child(gnss, "geoCoordinates"); BigDecimal latitude = geoCoordinate(geoCoordinates, "latitude", true); @@ -558,6 +592,7 @@ public class DriverCardXmlExtractionService { String operation = mapOperation(childText(record, "operationType")); supportEvents.add(new ExtractedSupportEvent( "CARDLOAD-" + (i + 1), + driverKey, occurredAt, "LOAD_UNLOAD", operation, @@ -640,6 +675,17 @@ public class DriverCardXmlExtractionService { return !usage.from().isAfter(timestamp) && timestamp.isBefore(usageEndExclusive(usage, null)); } + private boolean hasKnownRegistration(ExtractedCardVehicleUsageInterval usage) { + return usage != null && hasKnownRegistrationKey(usage.registrationKey()); + } + + private boolean hasKnownRegistrationKey(String registrationKey) { + return registrationKey != null + && !registrationKey.isBlank() + && !registrationKey.startsWith("UNKNOWN:") + && !registrationKey.endsWith(":UNKNOWN"); + } + private OffsetDateTime usageEndExclusive(ExtractedCardVehicleUsageInterval usage, OffsetDateTime fallbackExclusiveEnd) { if (usage.to() == null) { return fallbackExclusiveEnd == null ? OffsetDateTime.MAX : fallbackExclusiveEnd; diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java index 2b8ca0a..134c5e9 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java @@ -1,5 +1,6 @@ package at.procon.eventhub.tachographfilesession.service; +import at.procon.eventhub.reference.DriverCardNumberNormalizer; import org.springframework.stereotype.Component; @Component @@ -7,10 +8,18 @@ public class DriverKeyFactory { public String createDriverKey(String cardNation, String cardNumber) { String normalizedNation = normalize(cardNation, "UNKNOWN"); - String normalizedCardNumber = normalize(cardNumber, "UNKNOWN"); + String normalizedCardNumber = normalize(canonicalCardNumber(cardNumber), "UNKNOWN"); return normalizedNation + ":" + normalizedCardNumber; } + public String canonicalCardNumber(String cardNumber) { + return DriverCardNumberNormalizer.canonical(cardNumber); + } + + public String fullCardNumber(String cardNumber) { + return DriverCardNumberNormalizer.full(cardNumber); + } + public String createSourceDriverId(String driverKey) { return "DRV:" + driverKey; } @@ -23,6 +32,6 @@ public class DriverKeyFactory { if (value == null || value.isBlank()) { return fallback; } - return value.trim(); + return value.trim().toUpperCase(); } } diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilder.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilder.java index cf1a0c4..992caa5 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilder.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilder.java @@ -111,9 +111,7 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE eventSource, sourcePackageRef ); - List supportEvents = List.of(); - /* - buildSupportEvents( + List supportEvents = buildSupportEvents( session, timeline.supportEvents(), driverRef, @@ -122,7 +120,6 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE eventSource, sourcePackageRef ); - */ return new TachographTimelineEventBundle(activityEvents, vehicleUsageEvents, supportEvents); } @@ -309,6 +306,7 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE Map raw = new LinkedHashMap<>(); raw.put("sourceRowId", supportEvent.eventId()); raw.put("supportEventId", supportEvent.eventId()); + raw.put("driverKey", supportEvent.driverKey()); raw.put("supportEventType", supportEvent.eventType()); raw.put("slot", supportEvent.slot()); raw.put("registrationKey", supportEvent.registrationKey()); diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java index a08ac29..a8f8c91 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java @@ -64,7 +64,8 @@ public class VehicleUnitXmlExtractionService { continue; } String cardNation = text(record, "fullCardNumber/cardIssuingMemberState"); - String cardNumber = joinCardNumber(record, "fullCardNumber/cardNumber"); + String fullCardNumber = joinCardNumber(record, "fullCardNumber/cardNumber"); + String cardNumber = driverKeyFactory.canonicalCardNumber(fullCardNumber); if (cardNumber == null) { sessionWarnings.add(new ExtractionWarning( "MISSING_VU_DRIVER_CARD", @@ -86,6 +87,7 @@ public class VehicleUnitXmlExtractionService { driverKeyFactory.createSourceDriverCardId(driverKey), cardNation, cardNumber, + fullCardNumber, null, null, null, @@ -366,6 +368,14 @@ public class VehicleUnitXmlExtractionService { Map driversByKey, List warnings ) { + if (!hasKnownRegistration(vehicleContext.registration())) { + warnings.add(new ExtractionWarning( + "VU_SUPPORT_EVENTS_UNKNOWN_VRN", + "Vehicle-unit support events were ignored because vehicle registration is unknown.", + "/VehicleUnit/Activities" + )); + return; + } extractVuPlaceSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, warnings); extractVuGnssSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, warnings); extractVuSpecificConditionSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, warnings); @@ -415,6 +425,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VUPLACE-" + (i + 1) + "-" + assignment.driverKey(), + assignment.driverKey(), occurredAt, "PLACE", mapPlaceEntryType(entryType), @@ -487,6 +498,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VUGNSS-" + (i + 1) + "-" + assignment.driverKey() + "-" + assignment.slot(), + assignment.driverKey(), occurredAt, "POSITION", "POSITION_RECORDED", @@ -553,6 +565,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VUSC-" + (i + 1) + "-" + assignment.driverKey() + "-" + assignment.slot(), + assignment.driverKey(), occurredAt, "SPECIFIC_CONDITION", specificCondition[0], @@ -625,6 +638,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VUBORDER-" + (i + 1) + "-" + assignment.driverKey() + "-" + assignment.slot(), + assignment.driverKey(), occurredAt, "BORDER_CROSSING", "BORDER_OUTBOUND", @@ -698,6 +712,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VULOAD-" + (i + 1) + "-" + assignment.driverKey() + "-" + assignment.slot(), + assignment.driverKey(), occurredAt, "LOAD_UNLOAD", operation, @@ -763,6 +778,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VUSPEED-" + (i + 1) + "-" + assignment.driverKey() + "-BEGIN", + assignment.driverKey(), beginAt, "SPEEDING", "SPEEDING", @@ -793,6 +809,7 @@ public class VehicleUnitXmlExtractionService { assignment, new ExtractedSupportEvent( "VUSPEED-" + (i + 1) + "-" + assignment.driverKey() + "-END", + assignment.driverKey(), endAt, "SPEEDING", "SPEEDING", @@ -899,6 +916,17 @@ public class VehicleUnitXmlExtractionService { builder.supportEvents.add(supportEvent); } + private boolean hasKnownRegistration(ExtractedVehicleRegistration registration) { + return registration != null && hasKnownRegistrationKey(registration.registrationKey()); + } + + private boolean hasKnownRegistrationKey(String registrationKey) { + return registrationKey != null + && !registrationKey.isBlank() + && !registrationKey.startsWith("UNKNOWN:") + && !registrationKey.endsWith(":UNKNOWN"); + } + private List resolveDriverAssignments( OffsetDateTime occurredAt, String explicitDriverKey, @@ -925,7 +953,7 @@ public class VehicleUnitXmlExtractionService { private String driverKeyFromCardNode(Element node, String basePath) { String cardNation = text(node, basePath + "/cardIssuingMemberState"); - String cardNumber = joinCardNumber(node, basePath + "/cardNumber"); + String cardNumber = driverKeyFactory.canonicalCardNumber(joinCardNumber(node, basePath + "/cardNumber")); return cardNumber == null ? null : driverKeyFactory.createDriverKey(cardNation, cardNumber); } @@ -1109,6 +1137,7 @@ public class VehicleUnitXmlExtractionService { String sourceDriverCardId, String cardNation, String cardNumber, + String fullCardNumber, String issuingAuthorityName, OffsetDateTime issueDate, OffsetDateTime validityBegin, @@ -1119,6 +1148,7 @@ public class VehicleUnitXmlExtractionService { sourceDriverCardId, cardNation, cardNumber, + fullCardNumber, issuingAuthorityName, issueDate, validityBegin, diff --git a/src/main/java/at/procon/eventhub/yellowfox/service/YellowFoxD8BookingRowMapper.java b/src/main/java/at/procon/eventhub/yellowfox/service/YellowFoxD8BookingRowMapper.java index 6a34622..bd32b88 100644 --- a/src/main/java/at/procon/eventhub/yellowfox/service/YellowFoxD8BookingRowMapper.java +++ b/src/main/java/at/procon/eventhub/yellowfox/service/YellowFoxD8BookingRowMapper.java @@ -4,6 +4,7 @@ import at.procon.eventhub.dto.DriverCardRefDto; import at.procon.eventhub.dto.DriverRefDto; import at.procon.eventhub.dto.VehicleRefDto; import at.procon.eventhub.dto.VehicleRegistrationRefDto; +import at.procon.eventhub.reference.DriverCardNumberNormalizer; import at.procon.eventhub.yellowfox.dto.YellowFoxD8BookingDto; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -149,10 +150,6 @@ public class YellowFoxD8BookingRowMapper { } private String normalizeBookingDriverCardNumber(String value) { - if (value == null || value.isBlank()) { - return null; - } - String trimmed = value.trim(); - return trimmed.length() <= 14 ? trimmed : trimmed.substring(0, 14); + return DriverCardNumberNormalizer.canonical(value); } } diff --git a/src/test/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequestTest.java b/src/test/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequestTest.java index 8850ebf..5cfb043 100644 --- a/src/test/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequestTest.java +++ b/src/test/java/at/procon/eventhub/processing/model/UnifiedDriverEventsRequestTest.java @@ -32,7 +32,7 @@ class UnifiedDriverEventsRequestTest { " default ", " DRIVER:42 ", "at", - " 123 ", + " 1234567890123457 ", OffsetDateTime.parse("2026-05-01T00:00:00Z"), OffsetDateTime.parse("2026-05-02T00:00:00Z") ); @@ -41,7 +41,7 @@ class UnifiedDriverEventsRequestTest { assertThat(request.tenantKey()).isEqualTo("default"); assertThat(request.driverSourceEntityId()).isEqualTo("DRIVER:42"); assertThat(request.driverCardNation()).isEqualTo("AT"); - assertThat(request.driverCardNumber()).isEqualTo("123"); + assertThat(request.driverCardNumber()).isEqualTo("12345678901234"); assertThat(request.hasDriverSelector()).isTrue(); assertThat(request.hasVehicleSelector()).isFalse(); } diff --git a/src/test/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequestTest.java b/src/test/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequestTest.java index 5d9c71f..ec4dd27 100644 --- a/src/test/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequestTest.java +++ b/src/test/java/at/procon/eventhub/processing/model/UnifiedRuntimeProcessingRequestTest.java @@ -17,7 +17,7 @@ class UnifiedRuntimeProcessingRequestTest { Set.of(UnifiedEventSourceFamily.TACHOGRAPH_DB, UnifiedEventSourceFamily.YELLOWFOX_DB), "DRIVER:42", "AT", - "123", + "1234567890123457", OffsetDateTime.parse("2026-05-01T00:00:00Z"), OffsetDateTime.parse("2026-05-02T00:00:00Z") ); @@ -29,7 +29,7 @@ class UnifiedRuntimeProcessingRequestTest { ); assertThat(request.driverSourceEntityId()).isEqualTo("DRIVER:42"); assertThat(request.driverCardNation()).isEqualTo("AT"); - assertThat(request.driverCardNumber()).isEqualTo("123"); + assertThat(request.driverCardNumber()).isEqualTo("12345678901234"); assertThat(request.eventBackend()).isEqualTo(UnifiedRuntimeEventBackend.SOURCE_DB); assertThat(request.expandVehicleEvents()).isTrue(); assertThat(request.vehicleOccurredFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z")); diff --git a/src/test/java/at/procon/eventhub/processing/service/TachographFileSessionRuntimeEventLoaderTest.java b/src/test/java/at/procon/eventhub/processing/service/TachographFileSessionRuntimeEventLoaderTest.java index 88d19cd..99b35f6 100644 --- a/src/test/java/at/procon/eventhub/processing/service/TachographFileSessionRuntimeEventLoaderTest.java +++ b/src/test/java/at/procon/eventhub/processing/service/TachographFileSessionRuntimeEventLoaderTest.java @@ -72,7 +72,7 @@ class TachographFileSessionRuntimeEventLoaderTest { return new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -99,6 +99,7 @@ class TachographFileSessionRuntimeEventLoaderTest { )), List.of(new ExtractedSupportEvent( "SUP-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:45:00Z"), "POSITION", "POSITION_RECORDED", diff --git a/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverEventSourceServiceTest.java b/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverEventSourceServiceTest.java index 375d6a9..81969c6 100644 --- a/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverEventSourceServiceTest.java +++ b/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverEventSourceServiceTest.java @@ -88,7 +88,7 @@ class UnifiedDriverEventSourceServiceTest { return new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -115,6 +115,7 @@ class UnifiedDriverEventSourceServiceTest { )), List.of(new ExtractedSupportEvent( "SUP-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:45:00Z"), "POSITION", "POSITION_RECORDED", diff --git a/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverTimelineServiceTest.java b/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverTimelineServiceTest.java index b9e0e1c..26b34f8 100644 --- a/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverTimelineServiceTest.java +++ b/src/test/java/at/procon/eventhub/processing/service/UnifiedDriverTimelineServiceTest.java @@ -73,7 +73,7 @@ class UnifiedDriverTimelineServiceTest { return new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -100,6 +100,7 @@ class UnifiedDriverTimelineServiceTest { )), List.of(new ExtractedSupportEvent( "SUP-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:45:00Z"), "POSITION", "POSITION_RECORDED", diff --git a/src/test/java/at/procon/eventhub/processing/service/UnifiedVehicleEventSourceServiceTest.java b/src/test/java/at/procon/eventhub/processing/service/UnifiedVehicleEventSourceServiceTest.java index 05f223b..7cce143 100644 --- a/src/test/java/at/procon/eventhub/processing/service/UnifiedVehicleEventSourceServiceTest.java +++ b/src/test/java/at/procon/eventhub/processing/service/UnifiedVehicleEventSourceServiceTest.java @@ -93,7 +93,7 @@ class UnifiedVehicleEventSourceServiceTest { return new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -120,6 +120,7 @@ class UnifiedVehicleEventSourceServiceTest { )), List.of(new ExtractedSupportEvent( "SUP-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:45:00Z"), "POSITION", "POSITION_RECORDED", diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java index 83617aa..b29cbd8 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java @@ -110,5 +110,73 @@ class DriverCardXmlExtractionServiceTest { assertThat(driver.cardVehicleUsageIntervals().get(1).to()).isNull(); assertThat(driver.cardActivityIntervals()).hasSize(3); assertThat(driver.cardActivityIntervals().get(2).registrationKey()).isEqualTo("12:W-54321B"); + assertThat(driver.supportEvents()).hasSize(5); + assertThat(driver.supportEvents().stream() + .filter(event -> "12:W-54321B".equals(event.registrationKey())) + .map(event -> event.eventDomain() + ":" + event.eventType())) + .containsExactly("BORDER_CROSSING:BORDER_OUTBOUND", "LOAD_UNLOAD:UNLOAD"); + } + + @Test + void ignoresSupportEventsWhenVehicleRegistrationIsUnknown() { + String xml = DriverCardXmlSamples.validDriverCardXml() + .replace("W-12345A", "") + .replace("W-54321B", ""); + + TachographFileSession session = service.extract( + parser.parse(xml), + new TachographFileSessionMetadata( + "default", + "legalrequirements-drivercard", + "sample", + "sample.ddd", + "abc", + 10, + "42", + "def", + true, + null + ), + Instant.now(), + Instant.now().plus(4, ChronoUnit.HOURS) + ); + + DriverExtractionSession driver = session.driversByKey().values().iterator().next(); + assertThat(driver.cardVehicleUsageIntervals()).hasSize(2); + assertThat(driver.cardVehicleUsageIntervals()).extracting("registrationKey") + .containsOnly("12:UNKNOWN"); + assertThat(driver.supportEvents()).isEmpty(); + } + + @Test + void usesCanonicalCardNumberForDriverIdentityAndKeepsFullCardNumberAsEvidence() { + String xml = DriverCardXmlSamples.validDriverCardXml() + .replace("123456789012", "12345678901234") + .replace("0", "5") + .replace("0", "7"); + + TachographFileSession session = service.extract( + parser.parse(xml), + new TachographFileSessionMetadata( + "default", + "legalrequirements-drivercard", + "sample", + "sample.ddd", + "abc", + 10, + "42", + "def", + true, + null + ), + Instant.now(), + Instant.now().plus(4, ChronoUnit.HOURS) + ); + + DriverExtractionSession driver = session.driversByKey().values().iterator().next(); + assertThat(driver.driverKey()).isEqualTo("12:12345678901234"); + assertThat(driver.driverCard().cardNumber()).isEqualTo("12345678901234"); + assertThat(driver.driverCard().fullCardNumber()).isEqualTo("1234567890123457"); + assertThat(driver.driverCard().sourceDriverCardId()).isEqualTo("CARD:12:12345678901234"); } } diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverTimelineBuilderTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverTimelineBuilderTest.java index ef2e0d1..4bfc480 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverTimelineBuilderTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverTimelineBuilderTest.java @@ -74,6 +74,7 @@ class DriverTimelineBuilderTest { ), List.of(new ExtractedSupportEvent( "SUP-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:30:00Z"), "PLACE", "BEGIN_DAILY_WORK_PERIOD", diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/EventBackedDriverTimelineBuilderTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/EventBackedDriverTimelineBuilderTest.java index 55e3421..2c83ed0 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/EventBackedDriverTimelineBuilderTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/EventBackedDriverTimelineBuilderTest.java @@ -43,7 +43,7 @@ class EventBackedDriverTimelineBuilderTest { DriverExtractionSession driver = new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -70,6 +70,7 @@ class EventBackedDriverTimelineBuilderTest { )), List.of(new ExtractedSupportEvent( "SUP-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:45:00Z"), "POSITION", "POSITION_RECORDED", diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilderTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilderTest.java index f4953dc..7020e9a 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilderTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/IntervalBackedDriverTimelineEventBuilderTest.java @@ -42,7 +42,7 @@ class IntervalBackedDriverTimelineEventBuilderTest { DriverExtractionSession driver = new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -92,7 +92,7 @@ class IntervalBackedDriverTimelineEventBuilderTest { DriverExtractionSession driver = new DriverExtractionSession( "12:123", new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null), - new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null), + new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null), List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")), List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")), List.of(new ExtractedCardVehicleUsageInterval( @@ -108,6 +108,7 @@ class IntervalBackedDriverTimelineEventBuilderTest { List.of(), List.of(new ExtractedSupportEvent( "VUGNSS-1", + "12:123", OffsetDateTime.parse("2026-05-01T08:45:00Z"), "POSITION", "POSITION_RECORDED", diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java index ab2909d..61983d4 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java @@ -132,6 +132,66 @@ class VehicleUnitXmlExtractionServiceTest { assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to()).isNull(); } + @Test + void ignoresSupportEventsWhenVehicleRegistrationIsUnknown() throws Exception { + String xml = VehicleUnitXmlSamples.vehicleUnitXml() + .replace("W-1000V", ""); + + TachographFileSession session = service.extract( + new TachographXmlParser.ParsedTachographXml(document(xml), "VehicleUnit"), + new TachographFileSessionMetadata( + "default", + "legalrequirements-vehicleunit", + "sample-vu", + "sample-vu.ddd", + "abc", + 10, + "42", + "def", + false, + null + ), + Instant.now(), + Instant.now().plus(4, ChronoUnit.HOURS) + ); + + assertThat(session.driversByKey()).hasSize(2); + assertThat(session.driversByKey().values()) + .allSatisfy(driver -> assertThat(driver.supportEvents()).isEmpty()); + } + + @Test + void usesCanonicalCardNumberForVehicleUnitDriverIdentityAndKeepsFullCardNumberAsEvidence() throws Exception { + String xml = VehicleUnitXmlSamples.vehicleUnitXml() + .replaceFirst("123456789012", "12345678901234") + .replaceFirst("0", "5") + .replaceFirst("0", "7"); + + TachographFileSession session = service.extract( + new TachographXmlParser.ParsedTachographXml(document(xml), "VehicleUnit"), + new TachographFileSessionMetadata( + "default", + "legalrequirements-vehicleunit", + "sample-vu", + "sample-vu.ddd", + "abc", + 10, + "42", + "def", + false, + null + ), + Instant.now(), + Instant.now().plus(4, ChronoUnit.HOURS) + ); + + DriverExtractionSession firstDriver = session.driversByKey().get("12:12345678901234"); + assertThat(firstDriver).isNotNull(); + assertThat(firstDriver.driverCard().cardNumber()).isEqualTo("12345678901234"); + assertThat(firstDriver.driverCard().fullCardNumber()).isEqualTo("1234567890123457"); + assertThat(firstDriver.driverCard().sourceDriverCardId()).isEqualTo("CARD:12:12345678901234"); + } + private Document document(String xml) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(false);