Jeg har sloss noen dager med det jeg trodde var en enkel endring i Nikita, en endring av datamodelen for dokumentobjekt, slik at filstien til lagret fil ligger i databasen og ikke avledes algoritmisk hver gang. Bakgrunnen er et ønske om å endre på algoritmen samtidig som endringen håndterer oppgradering av Nikita mellom en instans som bruker gammel algoritme og ny algoritme.
Endringen ligger i gren documentobject_file_path_db i Nikitaprosjektet på gitlab, og feiler i dag ved opplasting av ny fil med følgende melding:
2025-02-01T06:03:02.177Z ERROR 13151 --- [Nikita Noark 5 Core (Demo mode using H2 database)] [0.1-8092-exec-8] a.w.s.e.GlobalETAGExceptionHandler : Cannot invoke "String.toString()" because "this.storagePath" is null
(hentet fra <URL: https://gitlab.com/OsloMet-ABI/nikita-noark5-core/-/jobs/9014548778 >)
Kan noen hjelpe meg med å forstå hvilen toString()-kall det er som møter en verdi som er null? Jeg har forsøkt å beskytte alle slike kall, og klarer ikke finne den som er problematisk.
Tips mottas med takk.
Endringen i greinen ser i dag slik ut:
diff --git a/scripts/run-runtest b/scripts/run-runtest index 04cfec652..b5ee49619 100755 --- a/scripts/run-runtest +++ b/scripts/run-runtest @@ -12,6 +12,7 @@ atexit() { sleep 1 if $nikitastarted ; then tail -100 $basedir/nikita-run.log + grep -C 200 -i exception $basedir/nikita-run.log fi fi } diff --git a/src/main/java/app/domain/noark5/DocumentObject.java b/src/main/java/app/domain/noark5/DocumentObject.java index 05016c4ba..6e7c9f16c 100644 --- a/src/main/java/app/domain/noark5/DocumentObject.java +++ b/src/main/java/app/domain/noark5/DocumentObject.java @@ -18,6 +18,8 @@ import jakarta.validation.constraints.NotNull; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
+import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List;
@@ -136,6 +138,11 @@ public class DocumentObject @OneToOne(mappedBy = REFERENCE_DOCUMENT_OBJECT_DB, fetch = LAZY, cascade = ALL) private ElectronicSignature referenceElectronicSignature;
+ // Location in storage for the file + @Column(name = "storage_path", nullable = true) + private String storagePath; + + public Integer getVersionNumber() { return versionNumber; } @@ -286,6 +293,17 @@ public class DocumentObject this.referenceElectronicSignature = referenceElectronicSignature; }
+ public Path getStoragePath() { + if (null != storagePath) + return Paths.get(storagePath); + return null; + } + + public void setStoragePath(Path storagepath) { + if (null != storagepath) + this.storagePath = storagePath.toString(); + } + @Override public String toString() { return "DocumentObject{" + super.toString() + diff --git a/src/main/java/app/service/noark5/DocumentObjectService.java b/src/main/java/app/service/noark5/DocumentObjectService.java index 98f33a51f..40bc39c33 100644 --- a/src/main/java/app/service/noark5/DocumentObjectService.java +++ b/src/main/java/app/service/noark5/DocumentObjectService.java @@ -235,16 +235,19 @@ public class DocumentObjectService public Resource loadAsResource(@NotNull final UUID systemId) { DocumentObject documentObject = getDocumentObjectOrThrow(systemId); + Path file = documentObject.getStoragePath(); + if (null == file) { + throw new StorageFileNotFoundException( + "No file connected to " + systemId); + } HttpServletRequest request = getRequest(); HttpServletResponse response = getResponse(); // First make sure the file exist try { - Path file = getToFile(documentObject); Resource resource = new UrlResource(file.toUri()); if (!resource.exists() && !resource.isReadable()) { throw new StorageFileNotFoundException( - "Could not read file: " + - documentObject.getReferenceDocumentFile()); + "Could not read file " + file + " connected to " + systemId); } // When file exist, figure out how to return it. Note // both format, file name, file size and mime type can be @@ -278,8 +281,7 @@ public class DocumentObjectService return resource; } catch (MalformedURLException e) { throw new StorageFileNotFoundException( - "Could not read file: " + - documentObject.getReferenceDocumentFile()); + "Could not read file " + file + " connected to " + systemId); } }
@@ -834,7 +836,7 @@ public class DocumentObjectService private void storeAndCalculateChecksum(InputStream inputStream, DocumentObject documentObject) {
- if (null != documentObject.getReferenceDocumentFile()) { + if (null != documentObject.getStoragePath()) { throw new StorageException( "There is already a file associated with " + documentObject); @@ -981,6 +983,7 @@ public class DocumentObjectService Path toFile = getToFile(documentObject);
Files.move(incoming, toFile); + documentObject.setStoragePath(toFile); }
private void setGeneratedDocumentFilename(DocumentObject documentObject) {
On Saturday, 1 February 2025 at 09:57:17 +01:00, Petter Reinholdtsen pere@hungry.com wrote:
Jeg har sloss noen dager med det jeg trodde var en enkel endring i Nikita, en endring av datamodelen for dokumentobjekt, slik at filstien til lagret fil ligger i databasen og ikke avledes algoritmisk hver gang. Bakgrunnen er et ønske om å endre på algoritmen samtidig som endringen håndterer oppgradering av Nikita mellom en instans som bruker gammel algoritme og ny algoritme.
Endringen ligger i gren documentobject_file_path_db i Nikitaprosjektet på gitlab, og feiler i dag ved opplasting av ny fil med følgende melding:
2025-02-01T06:03:02.177Z ERROR 13151 --- [Nikita Noark 5 Core (Demo mode using H2 database)] [0.1-8092-exec-8] a.w.s.e.GlobalETAGExceptionHandler : Cannot invoke "String.toString()" because "this.storagePath" is null
(hentet fra <URL: https://gitlab.com/OsloMet-ABI/nikita-noark5-core/-/jobs/9014548778 >)
Kan noen hjelpe meg med å forstå hvilen toString()-kall det er som møter en verdi som er null? Jeg har forsøkt å beskytte alle slike kall, og klarer ikke finne den som er problematisk.
Tips mottas med takk. Jeg er usikker på om
Uttrykket
if (null != storagePath)
er ekvivalent med
if (storagePath != null)
Feilen ser uansett ut til å skyldes at storagePath er null i toString()-metoden. En mulig årsak er at setStoragePath(Path storagepath)-metoden ikke faktisk setter storagePath, men i stedet prøver å sette det til this.storagePath.toString(), som fortsatt er null. Mulig løsning: Endre setStoragePath-metoden i DocumentObject slik at den bruker riktig parameter:
public void setStoragePath(Path storagepath) { if (storagepath != null) { this.storagePath = storagepath.toString(); } }
Den eksisterende implementasjonen bruker this.storagePath.toString(), som kan føre til en NullPointerException hvis this.storagePath er null. Ytterligere feilsøking:
Legg til logging Før du returnerer storagePath i getStoragePath(), logg verdien for å sjekke om den faktisk er satt.
public Path getStoragePath() { if (storagePath != null) { System.out.println("Storage path is: " + storagePath); return Paths.get(storagePath); } System.out.println("Storage path is null!"); return null; }
Kontroller at documentObject.setStoragePath(toFile); faktisk kalles Logg filstien rett etter at du setter den:
documentObject.setStoragePath(toFile); System.out.println("Set storage path: " + documentObject.getStoragePath());
Sjekk databasefeltet Hvis storage_path-kolonnen i databasen ikke blir fylt ut, kan du ha en migreringsfeil. Prøv å inspisere databasen og se om feltet er NULL for nye opplastede filer.
Konklusjon: Det mest sannsynlige problemet er at storagePath aldri blir satt riktig, og dermed fører et toString()-kall på null til feilen. Start med å fikse setStoragePath() og legg til logging for videre feilsøking.
Mvh, Ole Aamot
Endringen i greinen ser i dag slik ut:
diff --git a/scripts/run-runtest b/scripts/run-runtest index 04cfec652..b5ee49619 100755 --- a/scripts/run-runtest +++ b/scripts/run-runtest @@ -12,6 +12,7 @@ atexit() { sleep 1 if $nikitastarted ; then tail -100 $basedir/nikita-run.log
- grep -C 200 -i exception $basedir/nikita-run.log
fi fi } diff --git a/src/main/java/app/domain/noark5/DocumentObject.java b/src/main/java/app/domain/noark5/DocumentObject.java index 05016c4ba..6e7c9f16c 100644 --- a/src/main/java/app/domain/noark5/DocumentObject.java +++ b/src/main/java/app/domain/noark5/DocumentObject.java @@ -18,6 +18,8 @@ import jakarta.validation.constraints.NotNull; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
+import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List;
@@ -136,6 +138,11 @@ public class DocumentObject @OneToOne(mappedBy = REFERENCE_DOCUMENT_OBJECT_DB, fetch = LAZY, cascade = ALL) private ElectronicSignature referenceElectronicSignature;
- // Location in storage for the file
- @Column(name = "storage_path", nullable = true)
- private String storagePath;
public Integer getVersionNumber() { return versionNumber; } @@ -286,6 +293,17 @@ public class DocumentObject this.referenceElectronicSignature = referenceElectronicSignature; }
- public Path getStoragePath() {
- if (null != storagePath)
- return Paths.get(storagePath);
- return null;
- }
- public void setStoragePath(Path storagepath) {
- if (null != storagepath)
- this.storagePath = storagePath.toString();
- }
@Override public String toString() { return "DocumentObject{" + super.toString() + diff --git a/src/main/java/app/service/noark5/DocumentObjectService.java b/src/main/java/app/service/noark5/DocumentObjectService.java index 98f33a51f..40bc39c33 100644 --- a/src/main/java/app/service/noark5/DocumentObjectService.java +++ b/src/main/java/app/service/noark5/DocumentObjectService.java @@ -235,16 +235,19 @@ public class DocumentObjectService public Resource loadAsResource(@NotNull final UUID systemId) { DocumentObject documentObject = getDocumentObjectOrThrow(systemId);
- Path file = documentObject.getStoragePath();
- if (null == file) {
- throw new StorageFileNotFoundException(
- "No file connected to " + systemId);
- }
HttpServletRequest request = getRequest(); HttpServletResponse response = getResponse(); // First make sure the file exist try {
- Path file = getToFile(documentObject);
Resource resource = new UrlResource(file.toUri()); if (!resource.exists() && !resource.isReadable()) { throw new StorageFileNotFoundException(
- "Could not read file: " +
- documentObject.getReferenceDocumentFile());
- "Could not read file " + file + " connected to " + systemId);
} // When file exist, figure out how to return it. Note // both format, file name, file size and mime type can be @@ -278,8 +281,7 @@ public class DocumentObjectService return resource; } catch (MalformedURLException e) { throw new StorageFileNotFoundException(
- "Could not read file: " +
- documentObject.getReferenceDocumentFile());
- "Could not read file " + file + " connected to " + systemId);
} }
@@ -834,7 +836,7 @@ public class DocumentObjectService private void storeAndCalculateChecksum(InputStream inputStream, DocumentObject documentObject) {
- if (null != documentObject.getReferenceDocumentFile()) {
- if (null != documentObject.getStoragePath()) {
throw new StorageException( "There is already a file associated with " + documentObject); @@ -981,6 +983,7 @@ public class DocumentObjectService Path toFile = getToFile(documentObject);
Files.move(incoming, toFile);
- documentObject.setStoragePath(toFile);
}
private void setGeneratedDocumentFilename(DocumentObject documentObject) {
-- Vennlig hilsen Petter Reinholdtsen _______________________________________________ nikita-noark mailing list -- nikita-noark@nuug.no To unsubscribe send an email to nikita-noark-leave@nuug.no
[Ole Aamot]
Feilen ser uansett ut til å skyldes at storagePath er null i toString()-metoden. En mulig årsak er at setStoragePath(Path storagepath)-metoden ikke faktisk setter storagePath, men i stedet prøver å sette det til this.storagePath.toString(), som fortsatt er null. Mulig løsning: Endre setStoragePath-metoden i DocumentObject slik at den bruker riktig parameter:
Det du skriver gir ikke mening, da DocumentObject.setStoragePath() allerede tar Path storagepath.
Den eksisterende implementasjonen bruker this.storagePath.toString(), som kan føre til en NullPointerException hvis this.storagePath er null.
Hvor? Ser ingen tegn til slikt noe sted i koden.
Mulig jeg ser spøkelser på høylys dag, men svaret ditt ga meg følelsen av å lese tekst skrevet av en sludderbot.
Jeg fant feilen. Den var i denne metoden:
public void setStoragePath(Path storagepath) { if (null != storagepath) this.storagePath = storagePath.toString(); }
Det er en stor P som skulle vært liten p. Takk for alle innspill. De fikk meg til å ta en ekstra titt på koden og ga bedre ide om konkret når feilen oppsto, og til slutt fikk meg til å se den lille skrivefeilen.
Hei Petter!
On Monday, 3 February 2025 at 21:41:02 +01:00, Petter Reinholdtsen <pere@hungry.com> wrote:
[Ole Aamot]
Feilen ser uansett ut til å skyldes at storagePath er null i toString()-metoden. En mulig årsak er at setStoragePath(Path storagepath)-metoden ikke faktisk setter storagePath, men i stedet prøver å sette det til this.storagePath.toString(), som fortsatt er null. Mulig løsning: Endre setStoragePath-metoden i DocumentObject slik at den bruker riktig parameter:
Det du skriver gir ikke mening, da DocumentObject.setStoragePath() allerede tar Path storagepath. storagePath eller storagepath? Ser du skriver med liten 'p' i storagepath i koden din. Er storagepath klassen eller variabelen din?
I Java er variable case-sensitive. En skriver klasser som StoragePath og variabler som storagePath.
Her er et eksempel på hvordan du skriver en StoragePath klasse som tar dir og f som argumenter. public class StoragePath { public static void StoragePath(String[] args) throws Exception { File dir = new File("/data/"); File f = new File(dir, "nikita_arkiv");
/* Bygg "/data/nikita_arkiv" for å vise at Java ikke begrenser tilgangen */ byte[] buf = new byte[1024]; try (FileInputStream s = new FileInputStream(f)) { int len = s.read(buf); System.out.println(new String(buf, 0, len)); }
/* Now try to normalise the path -- crashes here */ System.out.println(f.toPath()); } }
Den eksisterende implementasjonen bruker this.storagePath.toString(), som kan føre til en NullPointerException hvis this.storagePath er null.
Hvor? Ser ingen tegn til slikt noe sted i koden. Hvis du mener at jeg har gjort en skrivefeil, kan du bare påpeke det direkte uten antydninger om at det er en test.
Mulig jeg ser spøkelser på høylys dag, men svaret ditt ga meg følelsen av å lese tekst skrevet av en sludderbot. Er det et argument for å ikke prøve å løse et teknisk problem?
Jeg kan si at jeg fikk følelsen av at du har forsøkt å bygge Java-koden med en selvbygd pre-openjdk-21 installasjon på Debian 11, men det løser ikke saken.
Feilsøking handler om å løse konkrete problemer, ikke å spekulere i motivasjoner.
La oss holde oss til det tekniske. Har du et spesifikt eksempel der this.storagePath.toString(); faktisk fører til en feil?
Jeg ser at du peker på this.storagePath.toString() som feil løsning, men jeg finner ikke hvor det skjer i koden.
Start med å bestemme hva storagepath er. Er storagepath en klasse, så skriv StoragePath, er det er en variabel, skriv storagePath, slik vi lærte på IN-105.
setStoragePath(Path storagePath) tar en Path som parameter, og jeg sørger for at this.storagePath blir satt direkte til den verdien med uttrykket
this.storagePath = this.storagePath.toString() Kan det være at du må bygge opp hele path'en til lagringsstien storagePath ved hjelp av klassen StoragePath, slik at den ikke er null?
Hvis du har en spesifikk kodelinje eller en test som feiler, setter jeg pris på å få se den, så kan Thomas kanskje gå dypere inn i problemet.
Mvh, Ole