Aus diesen einfachen Beobachtungen heraus wird deutlich, wie ein External Hook funktioniert: MetaLib schickt die Benutzereingaben an das find-Skript, MetaLib zeigt die Trefferanzahl und startet das present-Skript, dem es die URL aus dem find-Skript übergibt. Schließlich parst das present-Skript den HTML-Quelltext der externen Seite und MetaLib gibt die Daten formatiert aus. Das ist natürlich vereinfacht (siehe 3. für eine genauere Erklärung), aber im Großen und Ganzen ist das der ganze Vorgang. Ob es sich aber tatächlich um HTML handelt oder ein anderes Datenformat, in dem die Daten der externen Quelle vorliegen, wird erst durch Analyse derselben ersichtlich.
<book> <title>Freiheit Pur</title> <author>Horst Stowasser</author> <fulltext_url>http://www.mama-anarchija.net/media/downloads/FreiheitPur.pdf</fulltext_url> </book>
Ähnlich verhält es sich, wenn die Daten in sauber gekapseltem XHTML vorliegen. Das Beispiel bleibt im Wesentlichen das Gleiche, nur dass dieses in einem mikroformat-artigen [3], XHTML-konformen Format vorliegt:
<div class="book"> <span class="book_title">Freiheit Pur</span> <span class="book_author">Horst Stowasser</span> <a href="http://www.mama-anarchija.net/media/downloads/FreiheitPur.pdf" class="book_url">zum Volltext</span> </div>
Es ist allerdings zu beachten, dass HTML-Seiten für Menschen und nicht für Maschinen (d.h. Skripte) gemacht werden und deshalb nur selten so einfach und sauber strukturiert sind wie im vorgenannten Beispiel. Deshalb sollte man, wenn man auf die Daten mit einem Skript zugreifen will, nach Möglichkeit ein maschinenlesbares Format wählen. Viele Webapplikationen bieten im Rahmen ihrer API Datenformate an, die weniger Overhead produzieren und einfacher zu parsen sind. Ein solches Format, das bspw. bei del.icio.us eingesetzt wird, ist JSON (JavaScript Object Notation). Dieser Standard reduziert durch eine minimalistische Syntax den beim Datenaustausch verursachten Overhead auf ein Minimum. Die JSON-Repräsentation eines Bookmarks bei del.icio.us sieht so aus:
{ "u":"http://www.ib.hu-berlin.de/studium/praktikum/bibliothekspraktika.htm", "n":"Liste von Einrichtungen, in denen Studenten des IB der HU schon Praktika gemacht haben.." "d":"Einrichtungen Bibliothekspraktikum", "t":[ "ausbildung", "bibliothekswissenschaft", "informationswissenschaft", "praktikum", "linkliste" ] }
Die geschweiften Klammern umschließen ein Objekt, alle Bezeichner, die links der Doppelpunkte stehen, sind Variablennamen, alles, was rechts der Doppelpunkte liegt, sind Variablen, die eckigen Klammern umschließen Elemente eines Arrays. In dieser Form sind die Daten optimal weiterzuverarbeiten. Jeder Datensatz läßt sich in einem einzigen, guten regulären Ausdruck auswerten.
Die drei genannten Beispiele verdeutlichen, wie wichtig semantisches Markup ist. Um eine Webseite effektiv auszuwerten muss entweder der Quelltext formal und in seinen Bezeichnern einheitlich und standardkonform sein oder die Daten der Seite auch noch in einer anderen Inkarnation, etwa als JSON-String oder XML vorliegen. Die reine Ausrichtung an Formalia, z.B. an der Tabellenstruktur einer Seite,macht die Programmierung mühselig und fehleranfällig.
Nachdem nun bestehende Skripte durchgearbeitet und die auszuwertenden Daten in ihrer Struktur analysiert wurden, fehlt nur noch die Umsetzung in Programmcode.
Außerdem bietet sich die Nutzung der Standardausgabe (STDERR) an. Alle Daten die dorthin geschrieben werden, lassen sich in der Logdatei des Webservers (s.u.) wiederfinden und können während der Debugging-Phase sehr nützlich sein.
Da die offizielle Dokumentation zur Erstellung externer Suchskripte nur sehr sporadisch vorhanden ist [4], ist es in jedem Fall ratsam, bei der Integration der Skripte in die MetaLib-Software die Logdateien des Webservers (zu finden unter /exlibris/metalib/m3_3/log) mitzuverfolgen. Am günstigsten ist es, zwei Konsolenfenster mit Verbindung zum Server, auf dem MetaLib läuft, offenzuhalten. Im einen Terminal kann man dann die Skripte bearbeiten (am besten mit vi) und testen, im anderen mittels tail -f zeitnah die Ergebnisse in den entsprechenden Logdateien (insbesondere z39_gate_7333.log) beobachten.
Die Weboberfäche gibt für Entwickler keinerlei Informationen über etwaige Fehler aus, entweder es funktioniert oder nicht, daher sollte mit erhöhter Sorgfalt die Funktionalität der Skripte auch mit ungewöhnlichen Eingaben auf der Kommandozeile getestet werden. Hierbei ist insbesondere der verwendete Zeichensatz der verschiedenen Instanzen, durch die die Daten fließen, zu beachten. MetaLib bietet volle Unicode-Unterstützung, aber nicht alle externen Resourcen. Gerade bei Umlauten, Ligaturen oder Währungssymbolen kann es sonst zu kaum nachvollziehbaren Laufzeitfehlern kommen.
Abschließend sollte noch auf die Möglichkeiten der Automatisierung über Pipes in unixoiden Betriebssystemen hingewiesen werden. Um nicht jedes Mal beim Testen der Skripte die Befehle eingeben zu müssen, können die Tastatureingaben in eine Datei geschrieben und an die Skripte übergeben werden. Für ein Skript l_huib_delicious_find [5] kann man die Testanfrage [6]
FIND-REQUEST=WRD=(bibliothek)
in eine Datei test_find schreiben und diese mit dem Befehl
cat test_find | ./l_huib_delicious_find
an das Skript übergeben. Analog kann man auch eine Testanfrage für das present-Skript übergeben oder mit mehr Aufwand auch komplexere Verschachtelungen erstellen. Das folgende kleine Shellskript automatisiert die Simulation von MetaLib, bzw. Benutzereingabe vollständig. Lediglich die Dateien test_find und test_present müssen für veränderte Suchanfragen angepasst werden.
#!/bin/sh cat test_find | ./l_huib_elis_find | grep '^FIND' > test.tmp cat test.tmp test_present | ./l_huib_elis_present rm test.tmp
Der Ablauf ist folgender: cat gibt die Anfragen aus test_find an l_huib_elis_find weiter, dessen Ausgabe wiederum von grep nach einer Zeile, die mit FIND beginnt, durchsucht wird und das Ergebnis in die (temporäre) Datei test.tmp geschrieben wird. cat gibt nun nacheinander den Inhalt der Dateien test.tmp und test_present an ./l_huib_elis_present weiter, dessen Ausgabe daraufhin auf dem Bildschirm erscheint. Anschließend wird die temporäre Datei test.tmp gelöscht.
Noch einfacher läßt sich das gewünschte Verhalten mit dem konsequenten Einsatz von cat erreichen:
cat test_find | ./l_huib_elis_find | cat - test_present
Der Trick ist hierbei, dass cat - zunächst von der Standardeingabe liest, diese und anschließend die angegebenen Dateien ausgibt.
Schematisch sieht die Architektur von MetaLib folgendermaßen aus:
| Abb. 1: Schematischer Aufbau von MetaLib |
|---|
![]() |
| (Quelle: Lohrum, Stefan: MetaLib-Schulung : WWW Server als Targets, 2002, ZIB Berlin, S.3 [leicht modifiziert]) |
Besonders aufschlussreich an dieser Grafik ist vor Allem, dass sowohl die Z39.50-Konnektoren (unten links), als auch die External Hooks (unten, mitte) über den "Universal Gateway" in MetaLib integriert werden. Das erklärt das zunächst etwas befremdliche Konzept der Trennung zwischen Suche und Darstellung, denn Z39.50 funktioniert genau nach diesem Prinzip.
Hinzuzufügen sei auch noch, dass die External Hooks nicht nur auf das HTTP-Protokoll beschränkt sind. Perl ist eine sehr mächtige Programmiersprache, insbesondere dank der vielen verfügbaren Module, die die Funktionalität erweitern, und es wäre theorethisch möglich, beliebige Aktionen, ausgehend von der Benutzereingabe, auszuführen, bspw. könnte ein FTP-Server oder P2P-Netzwerke nach passenden Dokumenten durchsucht werden. Man könnte sogar so weit gehen, eine Z39.50-Schnittstelle oder die eigene MetaLib-Instanz anzusprechen, wobei das natürlich keinen Sinn ergeben würde.[7] Die Beispiele sollen nur demonstrieren, dass die External Hooks in der Tat sehr mächtig sind und weit mehr können, als nur HTML-Seiten parsen.
Im Folgenden werden die wichtigsten Informationen zum tatsächlichen Einbinden von External Hooks in MetaLib beschrieben.
MetaLib an find-Skript:
FIND-REQUEST=WRD=(bibliothek AND wissenschaft)
find-Skript an MetaLib:
SET-RESULT=65 FIND-REQUEST=33e8f3a4d6
MetaLib an present-Skript:
FIND-REQUEST=33e8f3a4d6 SET-ENTRY=0 MAX-RECORD=1
present-Skript an MetaLib:
RECORD-FORMAT="PLAIN" RECORD 100-1 $$aEwert, G. 100-1 $$aUmstätter, W. 245-1 $$aLehrbuch der Bibliotheksverwaltung YR $$a1997 END-RECORD END-OF-DATA
Hier geschieht folgendes:
bibliothek und wissenschaft in die freie Suche ein und MetaLib übergibt die Informationen an das find-Skript.65 Treffer und das Ergebnis wird auf dem externen Server unter der Zeichenkette 33e8f3a4d6 im Cache gehalten.33e8f3a4d6 gepufferte Suchergebnis zu und gibt alle Suchergebnisse vom 0. bis zum 1. im Datenformat "PLAIN" aus, wobei "PLAIN" hier im wesentlichen USMARC bedeutet. Der eine angeforderte Datensatz wird eingeschlossen von RECORD und END-RECORD und nach Ausgabe aller Datensätze signalisiert END-OF-DATA den Abschluss der Kommunikation zwischen eetaLib und Skripten.
Sehr nützlich ist auch der Befehl ERROR, mit dem eine Fehlermeldung an MetaLib zurückgegeben werden kann. Falls bspw. ein Verbindungsfehler auftritt kann dies mit dem folgenden Skript an MetaLib und damit auch an den Benutzer weitergegeben werden:
if ($http_status=501) { print "ERROR=Der Dienst ist im Moment nicht verfügbar." }
Besondere Sorgfalt sollte man auf das Parsen der Eingabe im find-Skript verwenden, denn sowohl einzelne Suchwörter, als auch die Datenfelder werden mit booleschen Operatoren verknüpft. Dabei ist zu beachten, dass die Suchworte in Klammern stehen. Als Feldbezeichner können folgende Zeichenketten von MetaLib übergeben werden: [8]
| WRD | alle Felder |
|---|---|
| WAU | Autor |
| WTI | Titel |
| WSU | Schlagwort |
| ISSN | ISSN |
| ISBN | ISBN |
| WYR | Jahr |
Abschließend sei darauf hingewiesen, dass die Variable FIND_REQUEST bei jeder Suchanfrage tatsächlich dreimal verwendet wird, aber in zwei unterschiedlichen Bedeutungen. FIND_REQUEST kann
bedeuten.
Nur die 1. Variante ist obligatorisch. In vielen Fällen wird eine externe Datenquelle über keine nutzbare Cache-Funktionalität verfügen und in diesem Fall macht es wenig Sinn, FIND-REQUEST an MetaLib zurückzugeben. [9]
Für eine elaborierte Erläuterung des Protokolls sind S.34-39 des Resource Management Guides [10] zu konsultieren.
l_[Abkürzung der eigenen Instititution]_[Abkürzung der externen Quelle]_[find|present|present_single]
Möchte man als Angehörige des Instituts für Bibliotheks- und Informationswissenschaft an der Humboldt-Universität zu Berlin (huib) einen External Hook zum fachbezogenen Repository E-LIS (elis) erstellen, sollten die Namen der Skripte entsprechend
l_huib_elis_find
bzw.
l_huib_elis_present
lauten. Falls weitere Differenzierung notwendig ist, etwa weil man auf eine Quelle zugreift, für die es bereits ein Skript gibt, das aber eine andere Schnittstelle nutzt, so fügt man die zusätzliche Angabe am besten zwischen dem Namen der Quelle und dem Skripttyp ein. Angenommen, E-LIS böte eine XML-Schnittstelle, so wäre
l_huib_elis_xml_find
ein guter Name für ein entsprechendes find-Skript. Durch konsequente Einhaltung dieser Konvention wird der Austausch von Skripten mit anderen MetaLib-Anwendern und die Einarbeitung in die Knowledge Base vereinfacht.
Die Skripte sollten physisch im Verzeichnis
~/m3_3/vir_ext/
abgelegt werden. Abschließend müssen noch symbolische Links zu den Skripten im Verzeichnis
/exlibris/metalib/m3_3/dat01/vir_ext
angelegt werden. Am besten wechselt man einfach in das Verzeichnis und erzeugt die Links mit ln, z.B. für die vorgenannten Skripte:
cd /exlibris/metalib/m3_3/dat01/vir_ext ln -s ~/m3_3/vir_ext/l_huib_elis_find ln -s ~/m3_3/vir_ext/l_huib_elis_present
Zu diesem Zeitpunkt ist der External Hook also funktionsfähig und unter richtigem Namen im richtigen Verzeichnis gespeichert und verlinkt. Nun muss nur noch in die MetaLib-Software integriert werden.
A in der Spalte "Config Action" den Konfigurationsdialog. Dort trägt man im Bereich "Add a new configuration" in Großbuchstaben den Dateinamen der Skripte ohne Skripttypbezeichnung (also z.B. L_HUIB_ELIS) ein und wählt als "Access Method" EXTERNAL.| Find Module | l_huib_elis_find |
|---|---|
| Present Module | l_huib_elis_present |
Als "Record Type" sollte man PLAIN eintragen, denn das ist grundsätzlich das zu empfehlende Ausgabeformat für das present-Skript [12]. Im Bereich "Term Transformation" muss im Normalfall nichts eingetragen werden, ob und wann doch sollte im MetaLib-Handbuch [13] nachgeschlagen werden. Auch der Bereich "Conversion" sollte recht minimalistisch ausfallen, ein einfacher Eintrag von vir_fix_doc_standard, dem Standard-Konversionsprogramm im Feld "Conversion program" sollte genügen.
Nach dem Speichern der Einstellungen ("SAVE & EXIT") sollte die Resource nebst Konfigurationscode in der Liste der Resourcen erscheinen. Hier muss man noch einmal sichergehen, ob der Status auf "aktiv" (Radiobutton neben dem "A" in der Spalte "Status") gesetzt ist, bevor im Benutzerinterface getestet werden kann, ob die Suche in der neuen Resource funktioniert.
#!/usr/bin/perl use LWP::Simple; use LWP::UserAgent; $|++;
Vor dem eigentlichen Programmablauf werden zwei Submodule des Standardmoduls LWP geladen, das die einfache Kommunikation über HTTP ermöglicht. Außerdem wird durch Setzen der speziellen Variable $| die Ausgabe ungepuffert ausgegeben.
while ($line = <STDIN>) { if ($line =~ /^FIND-REQUEST/) { ($find_request) = $line =~ /^FIND-REQUEST=\s*(.*)/ } }
Die while-Schleife liest solange von der Standardeingabe, bis das Dateiende-Signal eingegeben wird. Zeilen, die nicht mit FIND-REQUEST beginnen, werden ignoriert. Der eigentliche Suchstring nach FIND-REQUEST= wird der Variablen $find_request zugewiesen.
%map = ('WRD' => '_fulltext_', 'WTI' => 'title', 'WAU' => 'authors', 'WYR' => 'year', 'WSU' => 'keywords' ); foreach (split(/\)\s*[ANDORNT]+?\s+?/, $find_request)) { ($ccl) = $_ =~ /(W[A-Z]+)\s*=\s*\(.+?\)/; ($query{$ccl}) = $_ =~ /W[A-Z]+\s*=\s*\((.+?)\)/; if ($query{$ccl} =~ / OR /) { $boolean{$ccl} = "$map{$ccl}_merge=ANY"; } else { $boolean{$ccl} = "$map{$ccl}_merge=ALL"; } $query{$ccl} =~ s/ AND /\+/; $query{$ccl} =~ s/ OR /\+/; $query{$ccl} =~ s/ NOT (.+?)[\s]//; }
In diesem Abschnitt erfolgt mithilfe der assoziativen Arrays %map, %query und %boolean die Umsetzung der von MetaLib übergebenen Daten in solche, die in der URL an E-LIS geschickt werden. Im Einzelnen:
%map: Weist den Feldbezeichnern, die MetaLib übergibt, entsprechende Variablen für die Anfrage an E-LIS zu (z.B. $map{"WRD"} = "_fulltext_")%query: Verknüpft die Feldbezeichner mit den entsprechenden Suchanfragen (z.B. $query{"WRD"} = "biblothek AND wissenschaft")%boolean: Verknüpft die Feldbezeichner mit einer Zeichenkette, die entweder dem booleschen AND- oder dem OR-Operator entspricht (z.B. $boolean("WRD") = _fulltext_merge=ALL für den vorgenannten Beispielwert von %query) [14]
Die foreach-Schleife durchläuft alle Feldname-Feldwert-Tupel und zerlegt diese wiederum in den Feldnamen ($ccl), die Anfrage ($query{$ccl}) und den zugehörigen booleschen Operationsmodus. Anschließend werden die booleschen Operatoren in Pluszeichen umgewandelt (AND,OR), bzw. übergangen (NOT). [15]
$url = "http://eprints.rclis.org/perl/search/advanced?linguabib=en&linguabib=de&linguabib_merge=ANY&"; while (($command, $query) = each(%query)) { $url .= "$map{$command}=$query&"; $url .= "$boolean{$command}&"; } $url .= "_action_search=Search";
Dieser Programmteil erzeugt die URL, aus den assoziativen Arrays. Deren Elemente werden mit dem Grundgerüst, das "fest-verdrahtete" Angaben über die bevorzugte Sprache der Suchergebnisse (deutsch oder englisch) enthält, konkateniert.
$ua = new LWP::UserAgent; $ua->agent("HUIB/METALIB"); my $http_query = new HTTP::Request('GET', $url); my $response = $ua->request($http_query); $raw_html = $response->content; ($cachenummer) = $raw_html =~ /\/perl\/search\/advanced\?_cache=(\d+)&_/; ($ergebnisse) = $raw_html =~ /Displaying results.+>(.+)<\/span>\./;
Im nächsten Schritt wird die Anfrage abgeschickt. [16] Der Inhalt des HTML-Antwort wird nach der $cachenummer durchsucht, die es dem present-Skript ermöglichen wird, direkt auf das Suchergebnis zuzugreifen, sodass E-LIS nicht noch einmal die eigene Datenbank durchsuchen muss. Anschließend wird noch die Anzahl der Ergebnisse ausgelesen und der Variablen $ergebnisse zuegewiesen.
print "SET-RESULT=$ergebnisse\n"; print "FIND-REQUEST=$cachenummer\n";
Ganz zum Schluss werden Ergebnisanzahl und Cachenummer dem Protokoll gemäß an die Standardausgabe und damit an MetaLib geschrieben. Aus Sicht des Benutzers hält MetaLib nun einen Moment inne, lädt die Seite neu und zeigt die Anzahl der Suchergebnisse an. Gleichzeitig wird das present-Skript aufgerufen, um die konkreten Suchergebnisse abzurufen.
#!/usr/bin/perl use LWP::Simple; use LWP::UserAgent; $|++; while ( $line=<STDIN> ) { if ( $line=~ /^FIND-REQUEST/ ) { ($find_request) = $line =~ /FIND-REQUEST=(.*)/; } if ( $line=~ /^SET-ENTRY/ ) { ($set_entry) = $line =~ /SET-ENTRY=(.*)/; } if ( $line=~ /^MAX-RECORD/ ) { ($max_record) = $line =~ /MAX-RECORD=(.*)/; } } $ua = new LWP::UserAgent; $ua->agent('HUIB/METALIB'); $url = "http://eprints.rclis.org/perl/search/advanced?_cache=$find_request&_offset=$set_entry"; $request = new HTTP::Request('GET', $url); $response = $ua->request($request); $rawhtml = $response->content;
Bis hierher geschieht nichts, was nicht schon im find-Skript vorgekommen ist: Module werden initialisiert, die Standardeingabe gelesen, diesmal drei Zeilen, und mit der übergebenen Cache-Nummer ($find_request) und der Nummer des ersten zu holenden Ergebnisses ($set_entry) wird eine URL erzeugt. Die Anfrage wird abgeschickt und die HTML-Antwort der Variablen $rawhtml zugewiesen.
$rawhtml =~ s/[\r\t\n]//g; @felder = split(/<\/p><p>/, $rawhtml);
Nun werden irrelevante und problembehaftete Zeichen (Wagenrücklauf, Tabulator, Zeilenumbruch) entfernt und der HTML-Quelltext in einzelne Datensätze aufgeteilt und diese dem Array @felder zugewiesen. Hier kommt es sehr gelegen, dass die Macher von E-LIS sauber gekapseltes HTML anbieten. Alle Datensätze sind in eigenen Absätzen zu finden und die Datenfelder sind mit Klassenbezeichnungen in <span>-Tags semantisch ausgezeichnet - ein mustergültiges Repository.
foreach (@felder) { if ($i++ >= ($max_record - $set_entry)) { last; } $feld = $_; ($titel) = $feld =~ /<span class="field_title">(.+?)<\/span>/; ($jahr) = $feld =~ /<span class="field_year">(.+?)<\/span>/; ($url) = $feld =~ /<a href="(.+?)">/; ($konferenz) = $feld =~ /<span class="field_conference">(.+?)<\/span>/; ($ort) = $feld =~ /<span class="field_confloc">(.+?)<\/span>/; ($autor) = $feld =~ /<span class="field_authors">(.+?)<\/span> \(/; $autoren = join('; ', ($autor =~ /<span class="person_name">(.+?)<\/span>/g));
Die einzelnen Datensätze werden nun wieder in einer foreach-Schleife durchlaufen. Darin wird zunächst überprüft, ob bereits die durch $max_record festgelegte Höchstanzahl an zurückzugebenden Datensätzen erreicht wurde und bricht in diesem Fall die Schleife ab. Ansonsten werden die bibliographischen Daten aus dem HTML-Quelltext mittels einfacher regulärer Ausdrücke ausgelesen und mnemotechnisch benannten Variablen zugewiesen.
print "RECORD\n"; print "100 \$a$autoren\n"; if ($konferenz) { print "110 $konferenz\n"; } print "245 10\$a$titel\n"; print "856 \$u$url\n"; print "362 0#\$a$jahr\n"; print "END-RECORD\n"; } print "END-OF-DATA\n";
Schließlich erfolgt wieder die Ausgabe der Daten an STDOUT und somit an MetaLib. Das Schema wird von MetaLib intern als PLAIN bezeichnet, entspricht aber USMARC (siehe 3.1. Protokoll und 3.3. Konfiguration der MetaLib-Software). Damit ist der Ablauf vollständig: MetaLib hat die Ergebnisse zur gestellten Suchanfrage in verwertbarem Format erhalten und gibt sie nun als Tabelle an den Benutzer aus.
MetaLib_Version_3.13_-_Ressource_Management_Guide_2005_Oct._.pdf]external_hooks_luettgau.pdf]MetaLib_external_programms_Ere_Maijala.ppt]Www_Targets.ppt]l_huib_elis_find]l_huib_elis_present]l_huib_delicious_json_find]l_huib_delicious_json_present]unittest1.sh]unittest2.sh]delicious2connotea.pl]bibliothek AND wissenschaft OR metalib und bibliothek OR wissenschaft AND metalib gleichermaßen falsch wie bibliothek OR wissenschaft OR metalib behandelt, obwohl boolesche Operatoren eigentlich zweistellig sind. Da aber E-LIS nur die Auswertung der gesamten Suchbegriffe eines Termes anbieten, nämlich entweder "es müssen alle gleichzeitig vorhanden sein" (=_merge=ALL) oder "es muss mindestens ein Begriff enthalten sein" (=_merge=ANY, ließe sich das nur sehr aufwändig über mehrere Suchanfragen und anschließende Auswertung lösen, was gleichermaßen traffic- und rechenintensiv wäre.$ua→agent("HUIB/METALIB")), da viele WWW-Dienste Clients mit Standard-Useragents als Angreifer betrachten und ihnen den Zugriff verweigernFIND-REQUEST steht mal für die Suchanfrage des Nutzers, mal für die URL des gecacheten Ergebnisses (siehe auch 3.1. Protokoll)SET-ENTRY, der letzte mit MAX-RECORD bezeichnet. Konsequent (und intuitiver) wäre es, statt SET-ENTRY MIN-RECORD zu verwenden.