Versionen im Vergleich

Schlüssel

  • Diese Zeile wurde hinzugefügt.
  • Diese Zeile wurde entfernt.
  • Formatierung wurde geändert.

...

...

Problemstellung

Im Dateisystem des jadice web toolkit Servers sammeln sich häufig temporäre Dateien an. Diese sollen so früh und umfassend wie möglich aufgeräumt werden.

Diese Die Dateien haben in der Regel folgenden Ursprung:

  • Das Laden von Dokumenten im jadice web toolkit erfolgt in kundenspezifischen DocumentDataProvidern. Oft werden dabei Dateien aus einem Archiv ins Filesystem des JWT Servers heruntergeladen und von dort für die Anzeige im Browser eingelesen. Diese Dateien sollen nach der Bearbeitung eines Dokuments aus dem Filesystem des Servers gelöscht werden. Das Ende der Bearbeitung ist allerdings serverseitig nicht zuverlässig erkennbar. Beispielsweise erhält der Server beim Schließen des Browsertabs keine Benachrichtigung darüber, dass ein Dokument nicht mehr benötigt wird.
  • Gecachte InputStreams der jadice document platform: Vom Browser wird die aktuell angezeigte Seite sowie benachbarte Seiten angefordert. Falls der Benutzer einen Dokumentbereich erst zu einem späteren Zeitpunkt betrachtet, muss die aus dem Archiv heruntergeladene Datei zu diesem späteren Zeitpunkt erneut gelesen werden. Daher wird der zugehörige InputStream serverseitig gecacht und nicht geschlossen. Typischerweise wird man hier FileCacheInputStreams verwenden, da ein Zwischenhalten der Dokumentdaten im Hauptspeicher diesen schnell ausschöpfen würde. Erfolgt über eine konfigurierbare Zeitspanne (im Standard 60 Minuten) kein Zugriff mehr auf ein Dokument, werden alle zugehörigen Elemente aus dem Cache entfernt. 

Intelligente Speicherverwaltung mit dem BufferManager 

Eine Möglichkeit, die einzelnen Dateien frühzeitig freizugeben bzw. überflüssig zu machen, stellt die Verwendung des BufferManagers dar. Damit werden Dokumente initial komplett eingelesen und nach dem initialen Einlesen intelligent im Hauptspeicher und einer einzigen, vom BufferManager verwalteten temporären Datei gepuffert. Sobald im serverseitigen Cache keine Einträge mehr auf das Dokument vorhanden sind (im Standard 60 Minuten nach dem letzten Zugriff), gibt der Buffer Manager den zugehörigen Pufferbereich zum Überschreiben frei. Heruntergeladene Archivdateien können damit direkt nach dem Einlesen gelöscht werden.

Der Buffer Manager stellt die folgenden Konfigurationsmöglichkeiten zur Verfügung:

...

  • Falls es einen freien Bereich im Hauptspeicherpuffer gibt, wird dieser verwendet.
  • Falls der Hauptspeicherbereich keinen freien Platz besitzt, wird ein freier Bereich in der Pufferdatei verwendet.
  • Falls weder in Hauptspeicher noch in der Pufferdatei ein freier Bereich existiert, wird die Pufferdatei vergrößert.
  • Die Pufferdatei wird nicht verkleinert.
  • Durch das Wiederbeschreiben freigegebener Bereiche ist Größe der Pufferdatei beschränkt (abhängig vom Nutzungsverhalten).
  • Beim Neustart des Servers wird eine bestehende Puffderdatei Pufferdatei gelöscht und eine neue Pufferdatei mit der Größe 0 angelegt.

Wichtig ist das Zusammenspiel des BufferManagers mit dem serverseitigen Cache im jadice web toolkit. Sobald im Cache keine Einträge mehr auf ein Dokument vorhanden sind (im Standard 60 Minuten nach dem letzten Zugriff), gibt der Buffer Manager den zugehörigen Pufferbereich zum Überschreiben frei.

Heruntergeladene Archivdateien können also direkt nach dem Einlesen gelöscht werden, sobald ihre Daten in der Obhut des BufferManager liegen.

Näheres zur Arbeitsweise des BufferManager siehe Dokumentation für Entwickler.

Step-by-step guide

Hinzufügen der Maven Dependency

Codeblock
<dependency>
	<groupId>com.levigo.jadice.documentplatform.core</groupId>
	<artifactId>jadice-buffer-manager</artifactId>
</dependency>

Aktivierung des Buffer Managers 

Allein das Hinzufügen der Maven Dependency führt bereits zur Aktivierung des BufferManagers mit vorkonfigurierten Werten. Sollen abweichende Einstellungen verwendet werden, können diese entweder über die Jadice.properties oder programmatisch gesetzt werden.

  • Konfigurativ über die Jadice.properties:

    Codeblock
    bufferManager.enabled=true
    # bufferManager.bufferSize=65536
    # bufferManager.fileStoreStrategy=AUTO
    # bufferManager.fileStoreMode=PLAIN
    # bufferManager.bufferStoreFile=C:\\User\\Temp
    # The default is 80 buffers, i.e. ~5MB of memory buffers.
    # We increase this value since we want to be on the safe side for larger/multiple streams.
    # (2000 buffers = 125 MB, which may still be too low)
    bufferManager.memoryBuffers=2000


  • Alternativ: Programmatisch beim Starten des Servers (z.B. im ContextListener)

    Codeblock
    ...
    import com.levigo.jadice.bm.BufferManagerConfigurer;
    import com.levigo.jadice.bm.internal.DefaultBufferManager;
    ...
     
    private void configureBufferManager() {
      try {
        // the following configuration sample is usually NOT REQUIRED, since an appropriate strategy is automatically detected
        // BufferManagerConfigurer.configure().memoryBuffers(2000).fileStoreStrategy(BufferManagerConfigurer.FileStoreStrategy.ASYNCHRONOUS_FILE_CHANNEL).install(); 
        BufferManagerConfigurer.configure().install();   // <-- mimimal configuration 
        DefaultBufferManager.setEnabled(true);           // <-- important! 
     } catch (JadiceException e) {
        e.printStackTrace();
     }
    }


Nutzung des Buffer Managers im DocumentDataProvider

Im DocumentDataProvider muss darauf geachtet werden, die InputsStreams komplett zu lesen. Hierzu kann man ganz einfach die IOUtils nutzen:

Codeblock
...
 
import com.levigo.jadice.document.io.IOUtils;
import com.levigo.jadice.document.io.SeekableInputStream;
 
...

@Override
public void read(Reader reader, MyArchiveSource source) throws JadiceException, IOException {
 
 // here the file download from the archive takes place; file is placed in the file system
 ...
  
 if (!new File(source.getPath()).exists())
    throw new IOException("Can't find Resource on Filesystem: " + source.getPath());

 try(FileInputStream fis = new FileInputStream(source.getPath())) {
    SeekableInputStream sis = IOUtils.wrap(fis);  // <-- important!
    IOUtils.drainSynchronously(sis);              // <-- important!
    reader.read(sis);
 } finally {
    // delete the file in the filesystem (only required when using a FileInputStream!)
    new File(source.getPath()).delete();
 }
}

@Override
public void recover(Reader reader, MyArchiveHandle handle) throws RecoverFailedException, JadiceException {
 // same as in read()...
 ...
}


Konfiguration der maximalen Verweildauer von Einträgen im serverseitigen Cache

Dies konfigurieren wir in der Datei Jadice.properties:

...