Peter Rieger

Werbung
Peter Rieger
Implementierung einer
Client-Server-Optimierung zum
Austausch von Multimedia-Dokumenten
Diplomarbeit
im Studiengang Diplom-Mathematik
an der Universitat Augsburg
Aufgabensteller: Prof. Dr. Werner Kieling
Zweitgutachter: Prof. Dr. Bernhard Moller
Betreuer: Dipl.-Inf. Matthias Wagner
Augsburg, November 1997
Inhaltsverzeichnis
Inhaltsverzeichnis
i
Abbildungsverzeichnis
iii
1 Einleitung
1
2 Grundlagen
5
3 Architektur
9
2.1 Internet und WWW . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Das Hypertext Transfer Protocol (HTTP) . . . . . . . . . . . .
3.1 Der Client . . . . . . . . . .
3.2 Der Proxy-Server . . . . . .
3.3 Der Storage-Server . . . . .
3.3.1 Der Web-Server . . .
3.3.2 Das CGI-Programm
3.3.3 Die Datenbank . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
6
10
10
17
17
19
23
4 Optimierung
30
5 Messungen
41
6 Zusammenfassung und Ausblick
48
Literaturverzeichnis
50
4.1 Theoretische Grundlagen . . . . . . . . . . . . . . . . . . . . . .
4.2 Realisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i
30
34
INHALTSVERZEICHNIS
ii
A Abbildungen
A-1
B Tabellen
B-1
C Programme
C-1
C.1
C.2
C.3
C.4
C.5
C.6
C.7
converter.c . . . . . . . . . . . .
dynform.sqc . . . . . . . . . . .
db interface.sqc . . . . . . . . .
optimierer.sqc . . . . . . . . . .
insert copy.sqc . . . . . . . . . .
mklter.c . . . . . . . . . . . .
Eingabeformular (HTML-Code)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
C-1
C-4
C-7
C-17
C-35
C-43
C-45
Abbildungsverzeichnis
1.1 Anzahl der Hostadressen im Internet . . . . . . . . . . . . . . .
1
3.1
3.2
3.3
3.4
3.5
3.6
3.7
Systemarchitektur . . . . . . . .
CACHEMISS . . . . . . . . . .
CACHEHIT . . . . . . . . . . .
Ablauf der Formatumwandlung
Datenbankschema . . . . . . . .
Das relationale Schema . . . . .
Die Log-Tabelle . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
11
12
21
26
27
27
4.1
4.2
4.3
4.4
Menge der Attribute . . . . .
Zusammenhangskomponenten
Auswahl der Attributmengen
Auswahl der Basiselemente . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
36
37
38
39
A.1
A.2
A.3
A.4
A.5
Proxy-Konguration 1
Proxy-Konguration 2
Proxy-Konguration 3
Benutzerschnittstelle .
Anfrageergebnis . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
A-1
A-2
A-2
A-3
A-4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
iii
Kapitel 1
Einleitung
Die zunehmende Popularitat des World Wide Web (WWW) bestimmt heute das
Wachstum des Internet. Es ist aber kaum moglich, die Groe des Internet oder die
Anzahl der Benutzer exakt zu bestimmen. Bezuglich der Anzahl der Hostadressen
kann man jedoch einen Trend erkennen. In Abbildung 1.1 1 spiegelt sich die
Entwicklung der Anzahl wieder, wobei erwahnt werden sollte, da die Zahl der
Rechner im Internet nicht mit der Anzahl der Hostadressen ubereinstimmt.
Abbildung 1.1: Anzahl der Hostadressen im Internet
Mit der wachsenden Benutzergemeinde steigt auch die Zahl der Anbieter im
WWW, welche heute den groten Anteil an den Internetdiensten haben. Immer
1
Quelle: http://nw.com/zone/WWW/report.html
1
KAPITEL 1: EINLEITUNG
2
mehr Firmen, Organisationen oder Privatpersonen sind im WWW vertreten und
bieten dort ihre Informationen an. Diese Daten konnen in den unterschiedlichsten
Formen vorliegen, z.B. als:
Texte
Bilder
Videosequenzen
Audiodaten
Diese Daten werden als Multimedia-Dokumente bezeichnet. Die Speicherung dieser Dokumente kann in unterschiedlichen Formaten und Qualitaten erfolgen. Beispielsweise gibt es eine Reihe von Grakformaten, um Bilder zu speichern.
jpeg
ti
gif
png
Ebenso konnen unterschiedliche Auosungen oder Farbtiefen verwendet werden.
Bei Videos gibt es die Formate avi und mpeg die Qualitat wird hier zusatzlich
durch die Anzahl der Bilder pro Sekunde bestimmt.
Obwohl sich Formate zum Teil entsprechen mussen solche Daten in verschiedenen
bereitgestellt werden, sei es, weil der Anwender nur ein bestimmtes Format verarbeiten kann (Er besitzt nur einen Audioplayer fur das SUN-Audio-Format (au),
jedoch nicht fur wav-Dateien.2), oder weil er fur eine Sichtung3 der Daten mit
geringerer Qualitat (dadurch kurzere Ladezeit, weniger Speicherbedarf) zufrieden
ist.
Um die Vielzahl der Dokumente verwalten zu konnen, bietet sich die Speicherung
in einer Datenbank an. Es ist allerdings nicht sinnvoll, alle Formate und Qualitaten zu speichern, da zwischen den einzelnen Formaten funktionale Abhangigkeiten bestehen. So kann z.B. mit dem Tool convert aus dem ImageMagick-Paket
eine png-Datei in das gif-Format uberfuhrt werden.4 (Das ImageMagick-Paket
von John Cristy ist zum Anzeigen oder Bearbeiten von Bildern geeignet und ist
im WWW unter http://www.wizards.dupont.com/cristy/ImageMagick.html
zu nden.) Um die Rechenzeit fur die Konvertierung moglichst gering zu halten,
siehe z.B.: http://www.swf3.de/studio/comixarchiv.htm
siehe z.B.: http://www.ulistein.de/CARTOONS/uebers.html
4 Die genaue Syntax lautet: convert datei.png datei.gif
2
3
KAPITEL 1: EINLEITUNG
3
ist es sinnvoll, Formate die oft angefragt werden, direkt in der Datenbank zu
speichern. Ein anderes Ziel jedoch ist es, den Speicherbedarf gering zu halten.
Durch ein sich anderndes Anfrageverhalten (Queryprol) konnen sich allerdings
auch die in der DB zu speichernden Formate andern.
Die groe Popularitat des WWW ist wohl auf die einfache Benutzerschnittstelle
(Web-Browser) und das stark zunehmende Informationsangebot zuruckzufuhren.
Der Anwender kann mit einfachen Maus-Klicks seine Daten erhalten und mu
keine komplizierten Kommandos beherrschen. Dies aber hat zur Folge, da die
Netze immer starker ausgelastet werden. Fruher, als FTP (File Transfer Protocol)
noch den groten Anteil an den Internetdiensten hatte, reichte es aus, den Inhalt
von wichtigen Servern zu spiegeln (d.h. Kopien des Inhalts auf Servern in anderen
Landern anzulegen), um den Benutzern lange U bertragungszeiten zu ersparen und
die Netze zu entlasten.
Um im World Wide Web die Ladezeiten zu verkurzen, verwendet man Caches. Die
Web-Browser besitzen meist Speicher- und Platten-Caches, die es ihnen ermoglichen aufgerufene Seiten und deren Bestandteile (z.B. Grakelemente) zwischenzuspeichern, um sie bei einem erneuten Aufruf der Web-Seite aus dem lokalen
Speicher holen zu konnen. Dadurch entfallt der Verbindungsaufbau zum entfernten Server, die Ladezeit wird verkurzt und das Netz entlastet.
Diese lokalen Caches haben den Nachteil, da sie nur von einem Anwender verwendet werden konnen. Diese Einschrankung wird durch die Einfuhrung von
Proxy-Servern aufgehoben. Wird ein Proxy-Server { kurz Proxy { verwendet
(der Browser mu entsprechend konguriert werden), so wird die Anforderung
(Request) eines Dokuments vom Proxy und nicht vom Web-Server abgewickelt.
Der Proxy-Server kann somit fur viele Benutzer Daten uber seinen Cache bereitstellen, die er nur beim ersten Request direkt vom Web-Server laden mu. Fur den
Anwender ergibt sich somit eine kurzere Ladezeit, und sowohl Netz als auch WebServer werden entlastet. Es gibt allerdings ein Problem bezuglich der Aktualitat
der Daten. Hierauf wird in Abschnitt 3.2 naher eingegangen. Wunschenswert
ware auch fur den Proxy-Server eine Formatumwandlung und eine zusatzliche
Optimierung aufgrund des Queryprols und der Netzlast.
W. Kieling, W. Kowarschick und G. Kostler haben in KKK96] ein Konzept
zur Optimierung eines solchen Systems vorgestellt. Diese Diplomarbeit befat
sich mit dessen Implementierung, wobei nach Rucksprache die Optimierung nur
fur den Storage-Servers realisiert wurde beim Proxy-Server wurde lediglich eine
Formatumwandlung implementiert. Es werden fur die Realisierung hauptsachlich
(frei verfugbare) Standardsoftwarekomponenten verwendet:
Squid Version 1.1.5 | Proxy-Server
Squid ist unter http://squid.nlanr.net/Squid zu beziehen.
KAPITEL 1: EINLEITUNG
4
DB2 for Common Servers Version 2 | relationale Datenbank
DB2 ist ein kommerzielles Datenbanksystem von IBM.
Apache Version 1.2b4 | Web-Server
http://dev.apache.org/
ImageMagick Version 3.9.0 | Konvertierungs-Tool fur Bildformate
http://www.wizards.dupont.com/cristy/ImageMagick.html
Sox Version 12.12 | Konvertierungs-Tool fur Audiodateien
http://pardis.sas.upenn.edu/softlist.de/sox.html
Netscape Gold Version 3.0.1 | Web-Browser
http://home.netscape.com/
Allerdings wurden am Quellcode des Proxy-Servers einige A nderungen und Erganzungen vorgenommen, um die zusatzlichen Aufgaben zu erfullen. Die Anbindung
der Datenbank an den Web-Server sowie die Optimierung der Speicherformate,
der Datenbank erfolgt uber C-Programme mit Embedded SQL. Dies wird ausfuhrlich in Kapitel 3 besprochen.
Kapitel 2
Grundlagen
In diesem Kapitel erfolgt zunachst ein kurzer geschichtlicher U berblick uber die
Entstehung des Internet und des World Wide Web, sowie eine kleine Einfuhrung
in das im WWW verwendete HTTP-Protokoll.
2.1 Internet und WWW
Der Name Internet ist eine Abkurzung fur Interconnected Networks. Es handelt
sich um einen Zusammenschlu von lokalen, nationalen und globalen Computernetzen, die durch das TCP/IP-Protokoll (Transmission Control Protocol/Internet
Protocol) untereinander verbunden sind.
Das Internet verdankt seine Entstehung dem US-Verteidigungsministerium, das
Ende der sechziger Jahre ein dezentral funktionierendes Netzwerk fur militarische
Zwecke forderte, das selbst bei groeren Ausfallen noch eine Datenubermittlung
gewahrleisten sollte.
1969 entstand ein Computernetz mit dem Namen ARPANET (Advanced Research Projects Agency-NET). Als dieses Netz 1972 der O entlichkeit vorgestellt
wurde, schlossen sich auch Universitaten diesem Netz an. 1983 trennte sich der
militarische Teil ab es entstand das MILNET, und aus dem ARPANET wurde
das INTERNET. Anfang der neunziger Jahre begann die Kommerzialisierung des
Internet.
Im Internet gibt es zahlreiche Dienste:
e-Mail
Newsgroups
IRC (Internet Relay Chat)
5
2.2 Das Hypertext Transfer Protocol (HTTP)
6
FTP (File Transfer Protocol)
Telnet (Teletype Network)
Gopher
WAIS (Wide Area Information Servers)
WWW (World Wide Web)
Das World Wide Web ist der jungste, aber spektakularste aller Internet-Dienste.
Das WWW wurde 1993 am CERN-Institut in der Schweiz entwickelt und war
ursprunglich nur fur interne Zwecke bestimmt. Aufgrund seiner einfachen und
exiblen Nutzbarkeit wurde es weltweit verfugbar gemacht und hat sich seither
zum erfolgreichsten Internet-Dienst entwickelt.
2.2 Das Hypertext Transfer Protocol
Das Hypertext Transfer Protocol (vgl. FGM+97]) ist das Kommunikationsprotokoll, das im WWW zwischen Clients (Browsern) und Servern benutzt wird. Es
nutzt das TCP (Transmission Control Protocol) als Transport-Protokoll. Bei der
Verwendung eines Web-Browsers bleibt dem Endanwender verborgen, was sich
auf HTTP-Ebene abspielt. Deshalb wird hier anhand eines kleinen Beispiels der
Ablauf eines Request aufgezeigt. Dies ist fur das Verstandnis des Proxy-Servers
und der CGI-Programme (Common Gateway Interface) von Vorteil.
Beispiel (HTTP-Request)
Ruft man von einem Web-Browser aus die Homepage des Instituts fur Informatik
an der Universitat Augsburg auf, so genugt es, den URL (Uniform Resource
Locator)
http://www.informatik.uni-augsburg.de/
einzugeben, und man erhalt das Ergebnis angezeigt.
Baut man aber eine Telnet-Verbindung zum Institutsserver auf, so erhalt man
den HTML-Code (Hypertext Markup Language) der Web-Seite mit zusatzlichen
Protokolldaten (HTTP). Dazu mu zunachst einmal eine Verbindung zum HTTPPort (standardmaig Port 80) des Web-Servers aufgebaut werden. Dies geschieht
mit dem Befehl:
telnet www.informatik.uni-augsburg.de 80
2.2 Das Hypertext Transfer Protocol (HTTP)
7
Um die Seite anzufordern, genugt die Eingabe von
GET http://www.informatik.uni-augsburg.de/ HTTP/1.0
gefolgt von einer Leerzeile1, um den Befehl abzuschlieen.
Das Ergebnis sieht dann wie folgt aus:
HTTP/1.1 200 OK
Date: Wed, 24 Sep 1997 07:11:42 GMT
Server: Apache/1.2b4
Connection: close
Content-Type: text/html
Last-Modified: Fri, 29 Aug 1997 11:42:19 GMT
ETag: "c85-1391-3406b59b"
Content-Length: 5009
Accept-Ranges: bytes
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html charset=iso-8859-1">
<META NAME="GENERATOR" CONTENT="Mozilla/4.01 de] (Win95 I) Netscape]">
<TITLE> Universit&aumlt Augsburg - Institut f&uumlr Informatik </TITLE>
</HEAD>
...
Hier ist deutlich zu erkennen, da zunachst der HTTP-Header und anschlieend,
durch eine Leerzeile getrennt, die HTML-Seite ubertragen wird. Die wichtigsten
Header-Zeilen haben dabei folgende Bedeutung:
HTTP/1.1 200 OK
Typ und Version des Protokolls gefolgt vom Ruckgabecode.
Date: Wed, 24 Sep 1997 07:11:42 GMT
Systemzeit des Servers wird zusammen mit Last-Modied oder Expires fur
die Bestimmung der Gultigkeitsdauer einer Web-Seite verwendet.
Content-Type: text/html
MIME-Type (vgl. FB96]) des Dokuments bestehend aus einem media-type
(z.B.: text, image, sound oder application) und einem subtype. Anhand des
Content-Type entscheidet der Web-Browser, wie er die Daten zu behandeln
hat. Eine Auswahl gultiger Typen ist in Tabelle B.1 zu nden.
Last-Modied: Fri, 29 Aug 1997 11:42:19 GMT
Zeitpunkt der letzten A nderung wird zusammen mit Date fur die Bestimmung der Gultigkeitsdauer der Web-Seite verwendet.
1
Im HTTP wird der Header durch eine Leerzeile von den Daten getrennt
2.2 Das Hypertext Transfer Protocol (HTTP)
8
Content-Length: 5009
Die Lange des anschlieenden Datenbereichs in Bytes.
Aus historischen Grunden ist die Verwendung von Headern optional (in HTTP
0.9 gab es sie noch nicht), sie dienen aber dazu, den Funktionsumfang der beteiligten Anwendungen zu erhohen. Darauf wird in Abschnitt 3.2, bei Erklarung
des Squid, naher eingegangen. Um eine einfache Erweiterbarkeit von HTTP zu
gewahrleisten, mussen alle Programme ihnen unbekannte Header ignorieren.
Kapitel 3
Architektur
In diesem Kapitel werden die Grundlagen und die Einzelkomponenten des Systems beschrieben, und es wird auf die Probleme eingegangen, die sich bei der
Realisierung ergeben haben.
Client
Storage-Server
Proxy-Server
Web-Server
CGI-Programm
B
D
e
ch
Ca
Abbildung 3.1: Systemarchitektur
Wie aus Abbildung 3.1 hervorgeht, besteht das System aus:
Client
Proxy-Server mit Cache
Storage-Server
Der Storage-Server umfat dabei folgende Komponenten:
9
3.1 Der Client
10
Web-Server
CGI-Programm
Datenbank
3.1 Der Client
Der Client stellt die Schnittstelle zum Anwender dar. Er hat nur die Aufgabe,
dem Benutzer die Auswahl der gespeicherten Dokumente und der zur Verfugung
stehenden Formate zu ermoglichen.
Hierzu kann jeder gangige Web-Browser (z.B.: Netscape Navigator, Internet Explorer, . . . ) verwendet werden. Die Anbindung der Datenbank wird dabei durch
ein CGI-Programm realisiert, das dynamisch eine HTML-Seite erzeugt. Diese
Seite enthalt die in der Datenbank gespeicherten Daten innerhalb von Listenfeldern und bietet eine Auswahlmoglichkeit, um die Dokumente anzufordern (siehe
Abb. A.4). Desweiteren ist der Browser auch in der Lage, einige der verwendeten
Formate anzuzeigen (Abb. A.5), externe Programme zu starten, die dies ermoglichen, oder das Dokument auf die Festplatte zu speichern. Fur die Beispiele wurde
Netscape Gold V3.01 als Client-Software verwendet.
3.2 Der Proxy-Server
Hier werden zunachst die Vorteile des Einsatzes von Proxy-Servern erlautert. Anschlieend wird anhand der verwendeten Software auf die Probleme eingegangen,
die durch zusatzliche Anforderungen entstehen, und die Losungsmethoden werden
vorgestellt.
Wird die Anfrage eines Clients an den Storage-Server uber einen Proxy-Server
abgewickelt, so sind folgende Ablaufe moglich:
1. Der Client stellt seine Anfrage an den Proxy der Proxy stellt fest, da das
Dokument nicht im Cache liegt (CACHEMISS) und leitet die Anfrage an
den Storage-Server weiter. Dieser liefert das gewunschte Dokument an den
Proxy-Server. Der Proxy-Server speichert das Dokument im Cache ab und
leitet es an den Client weiter (siehe Abbildung 3.2).
2. Der Client stellt seine Anfrage an den Proxy der Proxy hat das geforderte
Dokument im Cache (CACHEHIT) und liefert es direkt an den Client (siehe
Abbildung 3.3).
3.2 Der Proxy-Server
CLIENT
11
PROXY/CACHE
STORAGE-SERVER
Zeit
Anfrage
Antwort
Abbildung 3.2: CACHEMISS
Die Vorteile eines Proxy-Servers werden hieraus gut ersichtlich. Bei einem `cachemiss' treten fur den Client kaum spurbare Verzogerungen auf. Bei einem `cachehit'
erfolgt jedoch eine wesentlich schnellere Antwort, die Netzlast zwischen Proxyund Storage-Server wird reduziert und der Storage-Server entlastet.
Es ist allerdings auch zusatzlicher Verwaltungsaufwand erforderlich. So mu der
Proxy-Server beispielsweise die Gultigkeit seines Cache-Inhalts uberprufen. Dies
kann durch einen mitgelieferten Expires-Header erfolgen oder durch eine Anfrage
beim Web-Server, ob das Dokument seit dem letzten Zugri geandert wurde. Da
ein Web-Server auch einen Date-Header sendet, wirkt sich die lokale Systemzeit
nicht auf die Gultigkeitsdauer eines Dokuments aus. Die Gultigkeitsdauer entspricht der Dierenz aus Expires- und Date-Header. Erfolgt eine Anfrage an ein
Dokument im Cache des Proxy-Servers, uber dessen Gultigkeit nichts bekannt ist,
so stellt der Proxy einen bedingten GET-Request, der einen If-Modified-SinceHeader enthalt, an den Web-Server dieser liefert das Dokument nur, wenn es
neuer ist als das des Proxy-Servers. Dies ist bei dynamisch erzeugten Seiten immer der Fall. Deshalb mu das in Abschnitt 3.3.2 beschriebene CGI-Programm
db interface einen Expires-Header erzeugen, um den Proxy-Servers die M
oglichkeit zu geben, gespeicherte Dokumente aus dem Cache zu lesen und nicht
erneut uber das Netz anzufordern.
Fur einen Proxy-Server ergibt sich auch die Frage, welche Web-Seiten in den
Cache ubernommen werden sollen. Als Grundsatz gilt, da nur solche URLs
`gecached' werden, die bei wiederholtem Aufruf ein identisches Ergebnis liefern.
Also insbesondere:
1. keine POST-Requests (nur GET-Requests)
3.2 Der Proxy-Server
CLIENT
12
PROXY/CACHE
STORAGE-SERVER
Anfrage
Zeit
(kein Netzverkehr)
Antwort
Abbildung 3.3: CACHEHIT
2. keine URLs, die cgi-bin oder `?' enthalten
3. keine Dokumente mit einem Authorization-Header
-Requests enthalten Parameter im Rumpf somit kann die Anfrage an einen
URL ganz unterschiedliche Ergebnisse liefern.
URLs, die cgi-bin oder `?' enthalten, deuten auf eine von einem CGI-Programm
erzeugten Web-Seite hin. Diese kann bei wiederholter Anfrage unterschiedlich
sein, wenn sich beispielsweise der Inhalt der Datenbank andert, auf die durch das
Programm zugegrien wird.
Fur Dokumente, die einer Autorisierung bedurfen, mu diese naturlich vom WebServer durchgefuhrt werden und nicht vom Proxy.
POST
Squid
Squid ist ein hierarchischer Proxy-Server, der neben Eltern (parents) auch Nachbarn (neighbors) besitzen kann dies ermoglicht den Aufbau von Cache-Verbunden
wie de-cache1 . Die Eltern und Nachbarn werden bei einem `cachemiss' in die Suche nach dem Dokument integriert. Dadurch erhoht sich die virtuelle Cache-Groe
und damit auch die Treerrate. Desweiteren ist Squid ereignisgesteuert, d.h.
Squid hort alle Kanale nach eingehenden Daten ab. Anfragen werden dann durch
Handler-Funktionen bearbeitet dies geschieht aber nur in kleinen Teilstucken,
um alle Requests gleichwertig zu behandeln. Diese Vorgehensweise ist zwar sehr
Informationen zu de-cache gibt es in World Wide Web unter
http://www.informatik.uni-bonn.de/dv/www/de-cache/
1
der Adresse
3.2 Der Proxy-Server
13
sinnvoll, erschwert aber das Verstandnis des Programmablaufs und somit das
Erweitern des Programms, zumal auch keine verwendbare Dokumentation fur
Entwickler vorliegt.
Wie eben gezeigt, vermeidet man es, dynamisch erzeugte Web-Seiten in den Cache aufzunehmen. Squid bietet dazu die Moglichkeit, in der Kongurationsdatei
hinter dem Schlusselwort cache stoplist Zeichenketten anzugeben, deren Vorkommen im URL ein `cachen' verhindert.
cache_stoplist cgi-bin ?
Dies wirft allerdings ein groes Problem auf, da die vom Storage-Server gelieferten
Daten das Ergebnis eines CGI-Programmes sind und somit cgi-bin und `?' im
URL aufweisen. Um die Dokumente trotzdem `cachen' zu konnen, gibt es zwei
Losungsmoglichkeiten.
`cachen' aller dynamisch erzeugten Seiten
nur die dynamisch erzeugten Seiten eines speziellen Servers `cachen'
Die erste Moglichkeit ware zwar bequem und durch einfaches A ndern der Kongurationsdatei realisierbar es kann aber nicht das Ziel sein, alle dynamisch erzeugten
Seiten zu `cachen'.
Die zweite Alternative erfordert zwar einige A nderungen am Programmcode, bietet jedoch eine prazise Auswahl der `cachbaren' Seiten. Die Realisierung besteht
aus der Einfuhrung eines neuen Schlusselwortes (multimediaserver) und einer
damit verbundenen Zeichenkette in der Kongurationsdatei, die notwendige Unterstutzung in der Kongurationsroutine und einem zusatzlichen Stringvergleich
bei der Entscheidung der `Cachebarkeit'. Steht die Zeile
multimediaserver http://www.informatik.uni-augsburg.de:1082/
cgi-bin/db interface
in der Kongurationsdatei des Proxy-Servers, so werden alle Web-Seiten, die vom
CGI-Programm db interface des Storage-Servers erzeugt werden, in den Cache
ubernommen, obwohl sie cgi-bin im URL enthalten.
Die Integration von Konvertierungsprogrammen in den Proxy-Server besteht aus
mehreren Teilaufgaben.
Verwaltung der Abhangigkeiten unter den Formaten incl. Umwandlungsfunktionen
Erkennen von Dokumenten, die aus einem im Cache vorhandenen Dokument
durch Konvertierung erzeugt werden konnen
3.2 Der Proxy-Server
14
Austausch der Daten zwischen Proxy-Server und Konverterproze
Generierung und Verwaltung von Kindprozessen, die die Konvertierung
durchfuhren
Bemerkung
Im folgenden mu streng zwischen Konverter- und Konvertierungsproze (bzw.
-programm) unterschieden werden.
Ein Konverterproze ist ein Kindproze des Proxy-Servers er regelt die
Verarbeitung der Header und ruft den Konvertierungsproze auf.
Ein Konvertierungsproze ist der Kindproze eines Konverterprozesses er
fuhrt die eigentliche Formatumwandlung durch. Dazu wird er von einem
Konvertierungsprogramm, das ein Filter2 sein mu, uberlagert.
Die Verwaltung der Umwandlungsfunktionen wird durch Eintrage in der Kongurationsdatei ermoglicht, wobei das Schlusselwort convert am Zeilenanfang einen
solchen Eintrag einleitet. Die weiteren Felder stehen fur das Quellformat, das
Zielformat, den MIME-Typ des Zielformats und die Umwandlungsfunktion.
convert
jpeg
gif
image/gif
convert - gif:-
Die vorangegangene Zeile z.B. bedeutet, da das jpeg-Format mit dem Kommando convert - gif:- ins gif-Format umgewandelt werden kann. Der neue
MIME-Typ ist image/gif dieser mu im Content-Type-Header an den Client
ubergeben werden.
Diese Daten werden im Programm in einer einfach-verketteten Liste abgespeichert. Die einzelnen Knoten haben dabei folgende Struktur:
typedef struct convert
{
char *src_type
char *dest_type
char *dest_mime
char *command
struct convert *next
}convert_t
2
/*
/*
/*
/*
/*
Quellformat
Zielformat
MimeType des Zielformats
Konvertierungsfunktion
naechster Eintrag
*/
*/
*/
*/
*/
Programme, die von Standardeingabe lesen und auf Standardausgabe schreiben
3.2 Der Proxy-Server
15
Diese Liste wird auch bei der Suche nach Dokumenten im Cache verwendet. Wird
ein Dokument angefordert, dessen URL mit der durch multimediaserver spezizierten Zeichenkette beginnt, ist es moglich, dieses Dokument durch Formatumwandlung aus einem im Cache bendlichen Dokument zu generieren. Dazu wird,
falls der URL nicht im Cache gefunden wird, nach Cache-Eintragen gesucht, deren Format in das angeforderte konvertiert werden kann. Dies geschieht durch
Generierung neuer URLs, bei denen gegenuber des alten die Formatangabe ausgetauscht wurde, wobei das Format des ursprunglichen URL in der dest type Komponente eines Listenelements stehen mu. Der Inhalt der zugehorigen src type
Komponente wird im neuen URL verwendet. Erfolgt z.B. eine Anfrage an den
URL
http://www.informatik.uni-ausgburg.de:1082/cgi-bin/
db interface?name=dilbert2&format=jpeg
und stehen in der Kongurationsdatei die Zeilen
convert
convert
convert
convert
tiff
gif
ppm
png
jpeg
jpeg
jpeg
jpeg
image/jpeg
image/jpeg
image/jpeg
image/jpeg
convert
convert
convert
convert
-
jpeg:jpeg:jpeg:jpeg:-
so wird im Cache auch nach URLs mit folgender Endung gesucht:
.../cgi-bin/db
.../cgi-bin/db
.../cgi-bin/db
.../cgi-bin/db
interface?name=dilbert2&format=tiff
interface?name=dilbert2&format=gif
interface?name=dilbert2&format=ppm
interface?name=dilbert2&format=png
Die Suchreihenfolge entspricht dabei der Reihenfolge der convert-Eintrage in der
Kongurationsdatei. Somit existiert eine Praferenzordnung unter den Konvertierungsfunktionen.
Die im Cache abgelegten Dokumente enthalten auch den vom Web-Server gelieferten HTTP-Header dieser mu im Fall eines `cachehit' mit dem Dokument an
den Client weitergegeben werden. Wird ein Dokument aus dem Cache geholt und
konvertiert, mu der Content-Type-Header an das neue Format angepat werden.
Diese Aufgabe wird vom Konverterproze ubernommen. Die dazu benotigten Informationen werden durch einen zusatzlichen Header, der im weiteren Verlauf als
Convert-Header bezeichnet wird, ubertragen. Dieser besitzt die beiden Eintrage
Command und Mime-Type, welche folgende Bedeutung haben:
Command: convert - gif:-
Programm zur Formatumwandlung incl. Kommandozeilenparameter.
3.2 Der Proxy-Server
16
Mime-Type: image/gif
Content-Type
des vom Konvertierungsproze zu liefernden Formats.
Der Convert-Header wird durch eine Leerzeile vom HTTP-Header getrennt dies
kann folgendes Aussehen haben, falls das Dokument aus dem Cache das jpegFormat besitzt und die Anfrage fur das gif-Format erfolgt:
Command: convert - gif:Mime-Type: image/gif
HTTP/1.1 200 OK
Date: Thu, 18 Sep 1997 14:01:39 GMT
Server: Apache/1.2b4
Connection: close
Content-Type: image/jpeg
Expires: Fri, 14 Nov 1997 14:01:39 GMT
Der Konverterproze erhalt uber seine Standardeingabe den Convert-Header, den
HTTP-Header und die zu konvertierenden Daten. Der Inhalt des Convert-Headers
wird ausgewertet und fur die spateren Aktionen gespeichert. Der HTTP-Header
wird unverandert ausgegeben, soweit es sich nicht um den Content-Type-Eintrag
handelt, welcher durch den im Convert-Header ubergebenen Mime-Type ersetzt
und ausgegeben wird. Der anschlieend erzeugte Kindproze (Konvertierungsproze) verarbeitet die restliche Eingabe { also die eigentlichen Daten. Dabei wird
der Konvertierungsproze von dem im Command-Eintrag des Convert-Header angegebenen Kommando (in diesem Beispiel: convert - gif:-) uberlagert dieses
fuhrt dann auch die Konvertierung durch und liefert das Ergebnis auf stdout. Da
Unix uber eine gepuerte Ausgabe verfugt, mu darauf geachtet werden, da der
Header komplett ausgegeben wurde, bevor die Ausgabe der Daten beginnt. Der
Konverterproze liefert fur das aktuelle Beispiel den folgenden HTTP-Header,
gefolgt von dem ins gif-Format konvertierten Dokument:
HTTP/1.1 200 OK
Date: Thu, 18 Sep 1997 14:01:39 GMT
Server: Apache/1.2b4
Connection: close
Content-Type: image/gif
Expires: Fri, 14 Nov 1997 14:01:39 GMT
Die Formatumwandlung ist bei der vorliegenden Implementierung auf einfache
Filterfunktionen beschrankt. Im Abschnitt 3.3.2 wird die Konvertierung im Bereich des Storage-Servers mittels einer Reihe von durch Pipes verbundene Filterfunktionen realisiert. Dies kann beim Proxy-Server durch Erweiterungen des
Programms converter (Quellcode siehe Anhang C.1 Seite C-1) erreicht werden.
3.3 Der Storage-Server
17
Fur die Verwaltung der Konverterprozesse wurde die Kongurationsdatei um zwei
zusatzliche Eintrage erweitert. Die Zeile:
convert program /home/wiss/db2ex/squid/bin/converter
gibt den Pfad und den Namen des zu verwendenden Konverterprogramms an,
wahrend die Zeile:
conv children 5
die Anzahl der zu startenden Konverterprozesse festlegt.
Die Konverterprozesse werden beim Start oder bei einer Neukonguration des
Squid gestartet. Alle Konverterprozesse laufen parallel zum Proxy-Server ab. Sie
fragen in einer Endlosschleife die Eingabe ab und geben nach erfolgter Konvertierung die Daten an den Proxy zuruck.
Bemerkung (Proxyeinsatz)
Soll ein Proxy-Server verwendet werden, mu der Client (Web-Browser) entsprechend konguriert werden. In den Abbildungen A.1 bis A.3 sind diese Schritte fur den Netscape Navigator dargestellt. Dabei wird der Proxy des Rechners
ls2cip01.informatik.uni-augsburg.de unter Port 3128 f
ur HTTP-Requests
eingetragen.
Ist die Verwendung eines Proxy-Servers eingestellt, gehen alle Anfragen an diesen
und nicht mehr an den im URL spezizierten Web-Server. Der Proxy leitet die
Requests an andere Proxy-Server oder den Web-Server weiter, oder aber holt das
Dokument aus seinem Cache.
3.3 Der Storage-Server
Der Storage-Server ist nur ein theoretisches Konstrukt. Er speichert die Dokumente und bietet dem Benutzer Zugrismoglichkeiten darauf. Dabei bleibt die
Implementierung dem Endanwender weitgehend verborgen. Die Komponenten
des Storage-Servers umfassen einen Web-Server, eine Datenbank und ein CGIProgramm, das die Verbindung zwischen beiden herstellt.
3.3.1 Der Web-Server
Der Web-Server bearbeitet HTTP-Anfragen des Clients und liefert entsprechende
Dateien zuruck.
Der Apache Server wird uber zwei Dateien (httpd.conf und srm.conf) konguriert. Die wichtigsten Eintrage in httpd.conf sind dabei:
3.3 Der Storage-Server
18
Port 1082
Die Nummer des Ports, unter der der Web-Server erreichbar ist. Der Standardport fur HTTP ist 80, aber fur Ports kleiner als 1023 sind Root-Rechte
erforderlich.
User db2ex
Group db2grp
Benutzer- und Gruppenname, unter denen der Web-Server laufen soll.
ServerAdmin [email protected]
E-Mail Adresse des Administrators, an den Probleme gemeldet werden sollen.
ServerRoot /home/wiss/db2ex/mywork
Das Verzeichnis, das Kongurations-, Fehler- und Log-Dateien enthalt.
ServerName www.informatik.uni-augsburg.de
Der Servername, der an den Client zuruckgeliefert werden soll. Es mu sich
dabei um einen gultigen DNS Namen des Rechners handeln.
Die Datei srm.conf enthalt unter anderem folgende Eintrage:
DocumentRoot /home/wiss/db2ex/html dir
Das Verzeichnis, unter dem die Web-Seiten abgelegt sind.
UserDir public html
Der Verzeichnisname, unter dem Benutzer in ihrem Homeverzeichnis eigene
Webseiten ablegen konnen.
DirectoryIndex index.html Index.html welcome.html Welcome.html
Standarddateien, die geladen werden, wenn in der Anfrage keine Datei speziziert wurde.
ScriptAlias /cgi-bin/ /home/wiss/db2ex/mywork/cgi-bin/
Erkennungsmerkmal fur CGI-Programme und Pfad, unter dem sie abgelegt
sind.
An den Web-Server konnen drei Arten von Anfragen gestellt werden.
1. Anfragen an eine Datei im DocumentRoot-Verzeichnis oder einem Unterverzeichnis davon.
http://www.informatik.uni-augsburg.de:1082/Index.html
Index.html
/home/wiss/db2ex/html dir
www.informatik.uni-augsburg.de
Liefert die Datei
des Rechners
unter Port 1082 lauft.
im Verzeichnis
uber den Web-Server der
3.3 Der Storage-Server
19
2. Anfragen an eine Datei im UserDir-Unterverzeichnis des Homeverzeichnisses eines Benutzers.
http://www.informatik.uni-augsburg.de:1082/~db2ex/Index.html
Index.html
/home/wiss/db2ex/public html
/home/wiss/db2ex
db2ex
Die Datei
im Verzeichnis
wird
ubertragen, wobei
das Homeverzeichnis des Benutzers
ist.
3. Anfragen an ein CGI-Programm.
http://www.informatik.uni-augsburg.de:1082/cgi-bin/dynform
dynform
/home/wiss/db2ex/mywork/cgi-bin
Dadurch wird auf dem Web-Server das Programm
im Verzeichnis
gestartet und dessen Ausgabe uber
den Web-Server an den Client ubertragen.
3.3.2 Das CGI-Programm
Das Common Gateway Interface (CGI) ist eine Schnittstelle, die es dem Client
erlaubt, Programme auf dem Web-Server auszufuhren3 und auch Parameter an
diese zu ubergeben. Damit lassen sich dynamisch Web-Seiten generieren oder Datenbankzugrie ermoglichen. Die dabei verwendete Programmiersprache ist nicht
von Bedeutung. Haug wird Perl verwendet, da es uber machtige Zeichenkettenoperationen verfugt. Wegen der notigen Datenbankanbindung und der benotigten
Betriebssytemkomponenten wird hier C benutzt.
Es werden zwei CGI-Programme verwendet, um die Verbindung zwischen Datenbank und Web-Server zu realisieren. Da ein CGI-Programm vom Web-Server
gestartet wird, stehen nicht alle Umgebungsvariablen zur Verfugung, was der
Fall ware, wenn der Aufruf aus einer Shell erfolgt. Fur Zugrie auf eine DB2Datenbank mu die Environment-Variable DB2INSTANCE gesetzt sein. Dies geschieht durch die Zeile
putenv("DB2INSTANCE=db2ex")
in den C-Programmen. Das Programm dynform (Quellcode in Anhang C.2) erzeugt als Ausgabe eine HTML-Seite, die ein Formular enthalt. Das Formular
spiegelt den Inhalt des Storage-Server wider und dient zur Auswahl des Dokuments. In Anhang C.7 ist die Ausgabe des CGI-Programms im HTML-Code
(vgl. Bor96]) dargestellt. Die von Netscape geladene Seite ist in Abbildung A.4
zu sehen.
Wird vom Benutzer die Abschicken-Schaltache betatigt, so wird das zweite CGIProgramm gestartet und mit den ausgewahlten Listenelementen als Parameter
versorgt. Die dafur erforderliche HTTP-Anfrage lautet:
3
Der Web-Server mu naturlich entsprechend konguriert sein, um dies zu erlauben.
3.3 Der Storage-Server
20
GET http://www.informatik.uni-ausgburg.de:1082/cgi-bin/
db interface?name=dilbert2&format=jpeg HTTP/1.0
Neben dem hier verwendeten GET-Request gibt es noch den POST-Request. Sie
unterscheiden sich hauptsachlich in der Art der Parameterubergabe. Die Vorund Nachteile dieser Requestarten kann in Gun96] nachgelesen werden.
Mit dem uncgi-Tool von Steven Grimm, das im World Wide Web unter der Adresse http://www.hyperion.com/~koreth/uncgi.html zu nden ist, lassen sich
POST- und GET-Requests einheitlich behandeln. Die Parameter werden dabei verarbeitet und in Umgebungsvariablen gespeichert, deren Name aus WWW gefolgt von
Parameternamen besteht. Fur das Programm db interface bedeutet das, da
Name und Format des angeforderten Dokuments in den Environment-Variablen
WWW name bzw. WWW format abgelegt sind und mit der Funktion getenv() ausgelesen werden konnen.
Um ein Dokument aus der Datenbank zu holen, wird zunachst eine einfache SQLQuery benutzt. Das Attribut Inhalt wird dabei wegen der Fahigkeiten von DB2
(siehe Abschnitt 3.3.3) beim Verarbeiten von BLOBs in einer temporare Datei
abgelegt.
SELECT a.inhalt,f.mimetype,f.fid
FROM artikel a, formate f
WHERE a.fid=f.fid AND
a.name=:docname AND
f.name=:doctype
Ist diese Anfrage erfolgreich, so wird ein HTTP-Header auf stdout ausgegeben
und durch eine Leerzeile getrennt4 der Inhalt der temporaren Datei hinterher
geschickt. Der Header kann dabei folgendes Aussehen haben:
Content-type: image/gif
Expires: Fri, 14 Nov 1997 13:07:31 GMT
Content-length: 8728
Der Expires-Header ist erforderlich, um den Proxyserver das `Cachen' des Dokuments zu ermoglichen. Darauf wurde in Abschnitt 3.2 schon eingegangen.
Falls das Dokument nicht im gewunschten Format abgespeichert ist, sondern
durch Umwandlung erzeugt werden mu, liefert die erste Anfrage einen Fehler.
In diesem Fall wird die etwas komplexere zweite Anfrage verwendet.
4
Darauf wurde in Abschnitt 2.2 schon eingegangen.
3.3 Der Storage-Server
21
SELECT a.inhalt, u.funktion, f.mimetype, a.fid, f.fid
FROM artikel a, umwandlung u, formate f
WHERE a.fid=u.quellformat AND
u.zielformat=f.fid AND
a.name=:docname AND
f.name=:doctype AND
u.praeferenz=(SELECT max(u1.praeferenz)
FROM umwandlung u1, artikel a1
WHERE a1.name=a.name AND
u1.zielformat=f.fid AND
u1.quellformat=a1.fid)
U ber die Spalte praeferenz wird diejenige Konvertierungsfunktion ausgewahlt,
die in der optimalen Basis (siehe Abschnitt 4.1) enthalten ist und ein physikalisch
reprasentiertes Format in das vom Client geforderte umwandeln kann. Gleichzeitig wird das gewunschte Dokument in dem fur die Konvertierung benotigten
Format in eine temporare Datei ausgelesen.
Fur die erforderliche Formatumwandlung werden Kindprozesse gestartet und ihre
Ein- und Ausgabekanale uber Pipes verbunden (vgl. Her96]). Dies ist auch der
Grund, fur die Konvertierungsprogramme zu fordern, da sie als Filter arbeiten.
Wurde die SQL-Query als Ergebnis fur die Umwandlungsfunktion z.B. Filter 1
| Filter 2 | Filter 3 liefern, so h
atte das einen Ablauf wie in Abbildung 3.4
zur Folge.
Pipe
CGIProgramm
Filter_1
Kindprozeß 1
Pipe
Filter_2
Kindprozeß 2
Pipe
Filter_3
Kindprozeß 3
Pipe
Abbildung 3.4: Ablauf der Formatumwandlung
Falls die Umwandlungsfunktion Pipe-Symbole (`|') enthalt, mu sie in ihre einzelnen Befehle aufgespaltet werden. Dann mussen der Befehlsname und die zu-
3.3 Der Storage-Server
22
gehorigen Parameter in die entsprechenden Variablenkonstrukte fur die UNIXSystemaufrufe gebracht werden. Anschlieend wird die entsprechende Anzahl an
Kindprozessen gestartet diese werden dabei durch Pipes verknupft. So wird die
Ausgabe des einen Prozesses zur Eingabe des nachsten. Zu diesem Zeitpunkt
konnen die Kindprozesse durch die Umwandlungsprogramme uberlagert werden.
Erfolgt nun die Ausgabe des Dokuments (Inhalt der temporaren Datei) in die Pipe, so kann an deren Ende das umgewandelte Format entnommen werden. Dieses
wird im Anschlu an einen HTTP-Header an den Web-Server weitergeleitet.
Bemerkung zu den Konvertierungsprogrammen
Die Forderung nach Filtern als Konvertierungsprogramme hat mehrere Grunde:
einheitliche Aufrufsyntax: Die Programme mussen nicht mit Datein-
amen versorgt werden die Ein- und Ausgabe erfolgt immer uber dieselben
Kanale.
einfache Verknupfung: Sind mehrere Konvertierungen nacheinander erforderlich, so konnen die einzelnen Programme uber Pipes miteinander verbunden werden.
keine zusatzlichen Kosten: Es sind keine zeitraubenden Plattenzugrie
erforderlich, da die Daten im Hauptspeicher von einem Proze zum anderen
weitergegeben werden. Diese Tatsache wirkt sich auch auf die Optimierung
im Kapitel 4 aus.
Fur die Konvertierung von Bilddaten wurde das eingangs schon erwahnte Programm convert aus dem ImageMagick Paket von John Cristy ausgewahlt, da
es eine Vielzahl von Formaten unterstutzt und wie gefordert als Filter eingesetzt
werden kann. Um stdin und stdout als Ein- bzw. Ausgabekanal zu verwenden,
mu beim Programmaufruf der sonst verwendete Dateiname durch ein Minuszeichen `-' ersetzt werden. Das Format der `Zieldatei' wird durch vorangestelltes
und durch Doppelpunkt abgeschlossenes Formatkennzeichen vor dem `Dateinamen' angegeben. Ein konstruiertes Beispiel soll die Verwendung von convert als
Filter verdeutlichen:
cat dilbert.gif | convert - jpeg:- | xv -
Hier wird die Datei dilbert.gif uber die Standardeingabe von convert gelesen
und vom gif- ins jpeg-Format umgewandelt, bevor sie uber den Standardausgabekanal an stdin des Viewers xv weitergeleitet wird.
Fur die Formatumwandlung der Audiodaten wird das Programm sox verwendet.
Laut Handbuch kann sox statt auf Dateien auch auf stdin und stdout operieren.
3.3 Der Storage-Server
23
Versuche, sox als Filter zu verwenden, waren allerdings bisher nicht von Erfolg
gekront.
Mit dem Programm mkfilter (der Quellcode bendet sich im Anhang C.6) kann
jedes Programm, das fur die Ein- und Ausgabe Dateien benutzt, als Filter verwendet werden. Das zu verwendende Programm wird inklusive seiner Aufrufparameter als Parameter an mkfilter ubergeben. Durch das Minuszeichen `-' werden
die Optionen fur die Ein- und Ausgabedatei getrennt, d.h. Optionen vor dem
Minuszeichen werden beim Programmaufruf vor der Eingabedatei angegeben, die
anderen danach. mkfilter liest die Daten von stdin in eine temporare Datei.
Anschlieend wird das ubergebene Kommando mit dieser Datei als Eingabedatei
und einer weiteren temporaren Datei fur die Ausgabe aufgerufen. Die Ausgabedatei wird nach stdout kopiert, und die temporaren Dateien werden geloscht.
Somit ist der Aufruf von
mkfilter sox - -t .au
mit folgender Befehlsfolge vergleichbar.
cat >tmpfile1
sox tmpfile1 -t .au tmpfile2
cat tmpfile2
rm tmpfile1
rm tmpfile2
Letztere Methode hat allerdings den Nachteil, da teure Plattenzugrie durchgefuhrt werden. Aus Grunden der Performance sind immer Filter vorzuziehen,
die keine Plattenzugrie benotigen.
3.3.3 Die Datenbank
DB2 { eine "objekt-relationale\Datenbank
DB2 von IBM ist eine relationale Datenbank mit objekt-relationalen Erweiterungen. Zu diesen Erweiterungen zahlen:
Trigger
User-Dened Functions (UDF)
User-dened Distinct Types (UDT)
Large Objects (LOB)
3.3 Der Storage-Server
24
Ein Trigger besteht aus einer Menge von Aktionen (nicht notwendigerweise Datenbankoperationen), die ausgefuhrt werden, wenn auf der Datenbank Veranderungen stattnden dies geschieht durch Insert-, Delete- oder Update-Anweisungen.
Da Select-Anweisungen keine Trigger auslosen, konnen diese nicht zum Protokollieren der Datenbankzugrie verwendet werden.
Durch User-Dened Functions ist der Anwender in der Lage, eigene Erweiterungen
fur SQL zu entwickeln. UDFs erlauben auch das U berladen von Funktionen die
konkrete Auswahl der passenden Funktion erfolgt uber die Signatur.
UDFs werden in dieser Implementierung nicht verwendet, da sie nur auf den
Ergebnistupeln der Anfrage arbeiten und selbst keinen weiteren Datenbankzugri
erlauben. Es wurde jedenfalls grote Schwierigkeiten bereiten, nach erfolgter
Konvertierung sowohl das Dokument im neuen Format als auch die benotigte
Rechenzeit als Ergebnis einer UDF zuruckzugeben.
UDTs erlauben es auf, bestehenden atomaren Typen (auch auf LOBs) eigene
Datentypen zu denieren. Durch User-Dened Functions, die auf diesen UDTs
operieren, kann das Verhalten dieser Datentypen genau festgelegt werden. Starke Typisierung gewahrleistet, da UDFs nur auf die UDTs angewendet werden
konnen, auf denen sie deniert wurden. Das Casting zwischen UDTs kann, falls
dabei zusatzlich eine Umrechnung stattndet soll (z.B. Umwandlung zwichen Millimeter und Inch), mit User-Dened Functions realisiert werden.
DB2 verfugt uber drei Arten von LOBs:
Character Large Objects (CLOB)
CLOBs verwendet man fur Texte, falls die Groe von VARCHAR (4 KByte)
uberschritten wird. Der Vergleich von CLOBs mit anderen Texttypen wird
von DB2 unterstutzt.
Double-Byte Character Objects (DBCLOB)
DBCLOBs sind speziell fur die Aufnahme von Texten mit Zeichensatzen,
deren einzelne Zeichen durch zwei Bytes kodiert sind.
Binary Large Objects (BLOB)
BLOBs konnen die verschiedensten Daten beinhalten (z.B.: Bild-, Videooder Audio-Daten) und werden auch fur UDTs verwendet.
LOBs konnen bis zu 2 Gigabyte (GB) an Daten aufnehmen es mu aber im Datenbankschema immer die Obergrenze der LOB-Groe angegeben werden. Bei
wachsendem Speicherbedarf sind dann A nderungen im Datenbankschema erforderlich.
In dieser Implementierung werden die Dokumente, deren Inhalt vielfaltige Formate umfat, die keiner Einschrankung unterliegt, in BLOBs abgespeichert.
3.3 Der Storage-Server
25
Es gibt drei Moglichkeiten, mittels Embedded SQL auf LOBs zuzugreifen. Das
LOB kann durch eine Select-Anweisung einer der folgenden Variablen zugewiesen
werden:
Hostvariable
File-Reference-Variable
LOB-Locator-Variable
Bei einer Zuweisung an eine Hostvariable wird das komplette Objekt innerhalb
eines Verbundes im Hauptspeicher des Rechners abgelegt. Dies ist bei groeren
LOBs nicht praktikabel, da der Hauptspeicher sehr rasch an seine Grenzen stot.
Die Zuweisung an eine File-Reference-Variable erzeugt eine Datei, die den Inhalt
des LOBs aufnimmt. Der Dateiname mu zuvor in der name-Komponente der
File-Reference-Variable abgelegt werden.
Eine Zuweisung an eine LOB-Locator-Variable belat das LOB auf dem Datenbankserver er kann uber diese Variable lediglich referenziert werden. Es ist
moglich durch Locator-Variablen Operationen auf den LOBs durchzufuhren und
die so veranderten LOBs, wieder in die Datenbank einzufugen. Es besteht auch
die Moglichkeit, uber Locator-Variablen, LOBs in kleinen Teilstucken aus der
Datenbank auszulesen.
Durch das anschlieende Programmsegment wird ein BLOB in 8 KByte groen
Blocken aus der Datenbank gelesen und in einen Ausgabekanal geschrieben.
EXEC SQL BEGIN DECLARE SECTION
long db_len
long first
long b_len
char docname21]
char doctype11]
SQL TYPE IS BLOB(8192) blobbuf
SQL TYPE IS BLOB_LOCATOR blobloc
EXEC SQL END DECLARE SECTION
EXEC SQL DECLARE c2 CURSOR FOR
SELECT a.inhalt,
FROM artikel a, formate f
WHERE a.fid = f.fid AND
a.name = :docname AND
f.name = :doctype
EXEC SQL OPEN c2
3.3 Der Storage-Server
26
EXEC SQL FETCH c2 INTO :blobloc
EXEC SQL VALUES(LENGTH(:blobloc)) INTO :db_len
for(i = 0 i < db_len i += 8192)
{
first = i + 1
b_len = (db_len - i > 8192) ? 8192 : db_len-i
EXEC SQL VALUES( SUBSTR( :blobloc, :first, :b_len ) )
INTO :blobbuf
write(fd, blobbuf.data, blobbuf.length)
}
Dies ware die optimale Methode, um im CGI-Programm auf die BLOBs zuzugreifen, aber durch die verwendete Programmstruktur mit Kindprozessen ist
dies nicht moglich. Deshalb werden File-Reference-Variablen verwendet. Bei einer grundlegenden U berarbeitung des CGI-Programms kann die Verwendung von
Locator-Variablen ermoglicht werden.
Das Datenmodell
Fur die Speicherung der Daten genugt ein recht einfaches Schema, wie aus Abbildung 3.5 ersichtlich ist. Es handelt sich um zwei Entitaten und zwei Relationen,
wobei Artikel eine schwache Entitat ist. Die Darstellung entspricht der aus
LL95].
FID
Name
hat Format
Artikel
Formate
Name
Inhalt
wandelt um
Funktion
MimeType
Präferenz
Abbildung 3.5: Datenbankschema
Ein Artikel besitzt die Attribute Name und Inhalt. Inhalt ist ein BLOB, das
die eigentlichen Daten enthalt. Name ist Schlusselattribut und bezeichnet den Dateninhalt. Da ein Artikel aber gleichzeitig in verschiedenen Formaten gespeichert
3.3 Der Storage-Server
27
sein kann, ist fur den tatsachlichen Schlussel noch das Schlusselattribut FID der
Entitat Formate notig.
In der Entitat Formate werden alle unterstutzten Formate gespeichert. Der kunstliche Schlussel FID (Integer) ist in den Anwendungsprogrammen ezienter als das
Attribut Name und kann bei langen Formatbezeichnern auch Speicherplatz einsparen. In Name ist die Typbezeichnung (evtl. Dateiendung) gespeichert, wahrend
MimeType den HTTP-Typen des Formats enth
alt.
Die Relation wandelt um enthalt im Attribut Funktion die komplette Aufrufsyntax des passenden Konvertierungsprogramms. Das Attribut Pr!aferenz dient zur
Auswahl der Konvertierungsfunktion im CGI-Programm (siehe Abschnitt 3.3.2)
und wird bei der Optimierung (siehe Abschnitt 4.2) gefullt.
Die Relation hat Format kann im relationalen Datenbankschema mit in die Tabelle fur Artikel aufgenommen werden. Genau genommen ergibt sich das schon
durch die Aufnahme des Schlusselattributs FID. Das sich daraus ergebende relationale Datenbankschema ist in Abbildung 3.6 dargestellt.
Artikel
Name
FID
Inhalt
Formate
varchar(30)
FID
integer
varchar(10)
Name
MimeType
BLOB(200M)
Umwandlung
integer
varchar(100)
Quellformat
Zielformat
Funktion
Praeferenz
integer
integer
varchar(200)
integer
Abbildung 3.6: Das relationale Schema
Die Datenbankzugrie werden in einer Tabelle protokolliert, deren Schema in Abbildung 3.7 wiedergegeben ist. Die Bedeutung der einzelnen Attribute (Spalten)
Log_Tabelle
CL_Name
Artikel
varchar(100)
varchar(30)
CL_FID
integer
DB_FID
integer
CL_Size
integer
DB_Size
UW_Dauer
Datum
integer
dec(7,3)
timestamp
Abbildung 3.7: Die Log-Tabelle
wird im folgenden erlautert.
3.3 Der Storage-Server
28
CL Name
Rechnername mit Domane oder IP-Adresse des Clients, der ein Dokument
anfordert. Datensatze die vom Optimierer eingefugt werden, haben die Eintrage 'optimierer' oder 'converter'.
Artikel
Name des angeforderten Dokuments. Wurde der Datensatz vom Optimierungsprogramm erzeugt, ist hier der Wert 'dummy' eingetragen.
CL FID
Das Schlusselattribut FID des angeforderten Formats.
DB FID
Das Schlusselattribut FID des aus der Datenbank gelesenen Formats. Sind
CL FID und DB FID verschieden, dann wurde das angeforderte Format CL FID
durch Konvertierung aus DB FID erzeugt.
CL Size
Groe in Bytes des angeforderten Formats.
DB Size
Groe in Bytes des dazu aus der Datenbank gelesenen Formats.
UW Dauer
CPU-Zeit in Sekunden, die fur die Konvertierung benotigt wurde. War keine Konvertierung notig (CL FID = DB FID), ist der Wert 0.
Datum
Datum und Zeitpunkt des Zugris mit Auosung in Mikrosekunden.
Die in der Log-Tabelle enthaltenen Informationen bilden die Grundlage der statistischen Daten fur die Optimierung. Darauf wird in Abschnitt 4.2 noch genau
eingegangen.
Somit kann die gesamte Datenbank durch die folgenden DDL-Befehle erzeugt
werden.
create table
(
FID
name
mimetype
formate
integer not null primary key,
varchar(10),
varchar(100))
create table artikel
( name
varchar(30) not null,
fid
integer not null,
inhalt BLOB(200000k),
3.3 Der Storage-Server
29
primary key(name,fid),
foreign key(fid)
references formate )
create table umwandlung
( quellformat integer not null,
zielformat integer not null,
funktion
varchar(200),
praeferenz integer,
primary key(quellformat,zielformat),
foreign key(quellformat)
references formate(fid),
foreign key(zielformat)
references formate(fid) )
create table log_tabelle
( cl_name
varchar(100) not null,
artikel
varchar(30) not null,
cl_fid
integer not null,
db_fid
integer not null,
cl_size
integer,
db_size
integer,
uw_dauer dec(7,3),
datum
timestamp not null primary key,
foreign key(cl_fid)
references formate(fid),
foreign key(db_fid)
references formate(fid))
Bemerkung
Wird ein neues Dokument in die Datenbank aufgenommen, so mu dieses nicht in
allen zur Zeit physikalisch reprasentierten Formaten geschehen. Es mu nicht einmal in einem dieser Formate gespeichert werden. Die einzige Forderung an das/die
Speicherformat/e ist, da sich alle anderen Formate durch die unterstutzten Konvertierungsfunktionen daraus generieren lassen. Dadurch ergeben sich fur die
Benutzer keine Einschrankungen. Das CGI-Programm verwendet bei Anfragen
auf dieses Dokument eine geeignete Umwandlungsfunktion, die nicht in der optimalen Basis enthalten sein mu. Nach einer Optimierung ist das neue Dokument
wie alle anderen in allen physikalisch reprasentierten Formaten vorhanden.
Kapitel 4
Optimierung
Ziel der Optimierung ist es, fur die Clients alle unterstutzten Formate bereitzustellen, jedoch bei minimalem Aufwand, d.h. bei einem ausgewogenen Verhaltnis
von Speicherbedarf und Rechenzeit. Konkret bedeutet das zu entscheiden, welche
Formate abgespeichert und welche berechnet werden. Diese Entscheidung wird
durch das Anfrageverhalten der Benutzer beeinut und mu somit regelmaig
erneuert werden.
In diesem Kapitel werden zunachst die theoretischen Grundlagen der Optimierung
des Storage-Servers erlautert. Anschlieend erfolgt die Beschreibung der Implementierung. Auch hier wurde groer Wert auf Flexibilitat gelegt eine Erweiterung der unterstutzten Formate oder der Abhangigkeiten erfolgt durch Einfugen
in die Datenbank.
4.1 Theoretische Grundlagen
In KKK96] wurden Grundlagen und ein Algorithmus zur Optimierung der Speicherformate aufgrund des Query-Prols eines objekt-orientierten Schemas vorgestellt. Dieser Algorithmus wird hier in abgewandelter Form auf ein relationales
Modell angewandt.
A ist eine Menge von Attributen (Speicherformaten)
Aphys sind die zu speichernden Attribute
Acomp sind die zu berechnenden Attribute
Es gilt desweiteren A = Aphys _ Acomp.
30
4.1 Theoretische Grundlagen
31
Denition (Functional Constraints)
Seien A B Attribute und sei f eine Funktion. Dann nennt man fc (A = f (B ))
functional constraint (funktionale Abhangigkeit). A heit Kopfattribut von fc
(A = head(fc)). B heit Rumpfattribut von fc (B = body(fc)).
Sei F eine Menge von functional constraints. Dann gilt:
A hangt direkt von B ab () 9fc 2 F : fc (A = f (B ))
Bemerkung
Im Gegensatz zu KKK96] hat fc hier nur ein Rumpfattribut. Das ist keine groe
Einschrankung, da in der Praxis nur Umwandlungen von einem Speicherformat
in ein anderes erfolgen.
Denition (Komposition)
Seien (A = f (B )) (B = g(C )) 2 F functional constraints. Dann ist auch (A =
f (g(C ))) eine functional constraint.
Denition (Transitivitat, Irreexivitat)
F heit transitiv genau dann, wenn A B C 2 A (A = f (B )) (B = g(C )) 2 F
=) (A = h(C )) 2 F
F heit irreexiv genau dann, wenn 8A 2 A gilt (A = f (A)) 2= F . (D.h. kein
Attribut hangt direkt von sich selbst ab.)
Denition (Functional Base)
Sei F transitiv und irreexiv und Fcomp eine Teilmenge von F . Dann heit F
functional base, falls folgendes gilt:
1. Die Mengen der Kopf- und Rumpfattribute sind disjunkt.
(Ah = fhead(fc) j fc 2 Fcompg Ab = fbody(fc) j fc 2 Fcompg
=) Ah \ Ab = )
2. Fur alle Attribute A 2 A gibt es hochstens eine functional constraint mit
A als Kopfattribut (j ffc j fc 2 Fcomp A = head(fc)g j 1).
Bemerkung
Diese Denition ist gleichwertig mit der aus KKK96], aber durch Punkt 1 und
der Forderung nach Transitivitat und Irreexivitat von F entfallt der Test auf
Zyklenfreiheit.
Die Transitivitat und Irreexivitat kann beim Einfugen neuer Constraints sichergestellt werden und mu nicht Bestandteil der Optimierung sein.
4.1 Theoretische Grundlagen
32
Denition (Optimal Funktional Base)
Sei cost eine Kostenfunktion und sei Fopt eine functional base, dann ist Fopt
optimal, falls cost(Fopt ) = minfcost(Fcomp) j Fcomp ist functional baseg
Algorithmus (Optimierung)
Im Algorithmus wird zunachst Aphys (und somit auch Acomp) bestimmt, damit
sind auch die functional constraints eingeschrankt (head(fc) 2 Acomp body(fc) 2
Aphys). Die Basis mu jetzt nur noch aus dieser Menge gebildet werden.
(A F cost): (Aopt Fopt,min)
OFB
begin
min
:= 1
Aopt := A
Fopt := for
do
Aphys A
Acomp := A n Aphys
Fcomp := ffc j fc 2 F ^ head(fc) 2 Acomp ^ body(fc) 2 Aphysg
for Fbase Fcomp where 8A 2 Acomp : 9_ fc 2 Fbase mit A = head(fc )
do
if min
then
min
fi
done
done
end
< costs(Aphys Fbase)
:= costs(Aphys Fbase)
Fopt := Fbase
Aopt := Aphys
Erklarung des Optimierungsalgorithmus
Der Optimierungsalgorithmus entscheidet mittels einer Kostenfunktion, welche
Formate (Attribute) zukunftig abgespeichert werden sollen und durch welche
Funktionen (functional constraints) die restlichen Formate zu berechnen sind.
Dazu werden alle Teilmengen aus den Attributen gebildet. Die Elemente dieser Teilmenge sind die physikalisch reprasentierten Attribute alle anderen sind
zu berechnen. Diese Einteilung reduziert die Anzahl der functional constraints,
die als Kandidaten fur eine functional base in Betracht kommen. Um eine Basis
zu erhalten, mu fur jedes zu berechnende Attribut eine functional constraint
gewahlt werden, so da dieses Attribut Kopfattribut ist und das Rumpfattribut
physikalisch reprasentiert ist. Fur jede gefundene Basis wird die Kostenfunktion
4.1 Theoretische Grundlagen
33
ausgewertet. Die Basis mit den geringsten Kosten ist optimal und entscheidet
uber die zukunftigen Speicherformate und die zu verwendenden Konvertierungsfunktionen.
Komplexitat
Sei n die Anzahl der Attribute (n = j A j). Im schlimmsten Fall { falls jedes Atn
tribut von allen anderen abhangt { ergeben sich fur die Optimierung P ni in;i
i=0
Alternativen.
n
n
n
P n n;i
i
n P ni in;i
n P ni in;i
n
i=0
i=0
i=0 i
2
3
6
1 057
10
2 237 921
3
10
7
6 322
11
18 210 094
4
41
8
41 393
12 157 329 097
5
196
9
293 608
13 1 436 630 092
In der Realitat werden diese Zahlen aber kaum erreicht, da die Anzahl der Abhangigkeiten geringer sein wird.
Bemerkung
In KKK96] wird gezeigt, da eine Zerlegung der functional constraints in Zusammenhangskomponenten moglich ist1, und der Algorithmus auf diesen Komponenten durchgefuhrt werden kann. Genau betrachtet ndet dabei eine Zerlegung der
Attributmenge statt.
Denition (Kostenfunktion)
Die Kostenfunktion entspricht weitgehend der Denition aus KKK96] mit Veranderungen bei den Zugriskosten. Die Kosten setzen sich aus Speicher- und
Zugriskosten zusammen. Die Konstante z 2 0 1] dient zur Gewichtung der
beiden Komponenten.
cost(Aphys Fbase) = (1 ; z) store cost(Aphys) + z acc cost(Fbase)
Denition (Speicherkosten)
Die Speicherkosten sind proportional zur durchschnittlichen Groe des entsprechenden Speicherformats (avg size). avg size wird aus statistischen Daten ermittelt. Durch die Konstante cS wird diese Groe auf einen geeigneten Wert
skaliert.
X
avg size(A)
store cost(Aphys) = cS A2Aphys
1
Bilder konnen nicht in Audiodaten umgewandelt werden und umgekehrt
4.2 Realisierung
34
Denition (Zugriskosten)
Die Zugriskosten (acc cost) hangen direkt von der Anzahl der Zugrie auf das
jeweilige Format (Queryprol) ab. Ist ein Format in der Datenbank gespeichert,
so ist acc cost proportional zur durchschnittlichen Groe. Mu ein Format durch
Umwandlung erzeugt werden, werden die Zugriskosten durch die Rechenzeit fur
die Konvertierung und die Groe des Quellformats bestimmt. Die Konstante cA
dient auch hier zum Skalieren. Die Werte fur avg acc, avg size und comp cost
werden dabei aus der Zugrisstatistik entnommen.
acc cost(Fbase) =
wobei
X
A2A
avg acc(A) acc cost(A)
(
falls A 2 Aphys
cA avg size(A)
acc cost(A) = comp
cost(A = f (B )) + cA avg size(B ) falls A 2 Acomp
Dabei ist (A = f (B )) 2 Fbase und somit auch B 2 Aphys.
Bemerkung
Im Gegensatz zu KKK96] werden auch die Berechnungskosten (comp cost) aus
statistischen Daten ermittelt. Dadurch werden verbesserte Konvertierungsalgorithmen und leistungsfahigere Hardware bei der Kostenbestimmung automatisch
berucksichtigt.
Fur die Konstanten cS , cA und z haben sich nach ersten Tests folgende Werte als
gunstig erwiesen.
cS = 0 000 002 cA = 0 000 000 1 z = 0 01
4.2 Realisierung
In Abschnitt 4.1 wurde ein Algorithmus zur Optimierung der Speicherformate des
Storage-Servers vorgestellt. Dazu werden statistische Daten uber die Groe der
Speicherformate, die Dauer der Formatumwandlung und die Anzahl der Zugrie
verwendet. Die Speicherformate entsprechen den Attributen im theoretischen
Teil und die Konvertierungsfunktionen den functional constraints. Im weiteren
Verlauf werden beide Bezeichnungen verwendet.
Statistische Daten
In der in Abschnitt 3.3.3 eingefuhrte Tabelle log tabelle wird jeder Zugri auf
den Storage-Server protokolliert. Diese Datensatze werden vom Optimierungsprogramm zur Bestimmung der statistischen Werte benutzt. Allerdings werden nur
4.2 Realisierung
35
die Daten des letzten Monats berucksichtigt. A ltere Datensatze werden geloscht,
um durch einen konstanten Auswertungszeitraum die Gesamtentwicklung der Zugrie erkennen zu konnen. Auerdem werden nach jedem Optimierungslauf Informationen uber die Umwandlungsdauer und die Formatgroe in dieser Tabelle
abgelegt. Naturlich werden auch die Daten der letzten Optimierung geloscht, bevor die neuen eingefugt werden.
Diese Vorgehensweise ist sinnvoll, da die Datenbank-Tabelle somit aktuelle Statistikdaten enthalt und trotzdem recht klein bleibt. Andernfalls ware folgendes
Szenario vorstellbar:
Sind beispielsweise aller Bilder im gif-Format gespeichert, wahrend die Anfragen nur fur das jpeg-Format erfolgen, dann sind in der Log-Tabelle Datensatze
vorhanden, die den Speicherbedarf fur das gif- und das jpeg-Format sowie die
Dauer der Konvertierung angeben. Wird jetzt durch Optimierung jpeg als einziges Speicherformat verwendet, und verlieren die alten Log-Daten an Aktualitat
und werden dadurch geloscht, dann gibt es auch keine Informationen uber die
Umwandlungsdauer vom gif- ins jpeg-Format. Findet auch kein Zugri im gifFormat mehr statt, so geht auch die Information uber dessen Groe verloren.
Die statistischen Daten konnen durch einfache SQL-Queries aus der Datenbank
extrahiert werden. Die folgende Query liefert beispielsweise die Anzahl der Requests und die durchschnittliche Groe der Formate:
SELECT cl_fid, avg(cl_size), count(cl_fid)
FROM log_tabelle
GROUP BY cl_fid
ORDER BY cl_fid DESC
Anfanglich sind die statistischen Daten etwas sparlich, sie werden aber durch
die zunehmende Anzahl der Client-Anforderungen und der Optimierungen immer besser und aussagekraftiger. In Kapitel 5 wird anhand eines Beispiels der
Fortschritt durch wiederholte Optimierung aufgezeigt.
Liegen noch keine Angaben uber die Groe eines Formats vor, so wird diese auf
null gesetzt, und es entstehen keine Speicherkosten. Die vom letzten Optimierungslauf erzeugten Eintrage in der Log-Tabelle sorgen fur (geringe) Zugriskosten, was zur Folge hat, da das Format durch die Optimierung abgespeichert
wird. Dafur ist aber eine Umwandlung aus einem bisher gespeicherten Format
notig. Die dabei anfallenden Daten uber die Dateigroe des Quell- und Zielformats und der durchschnittlichen Rechenzeit werden wieder in die Log-Tabelle
eingetragen, wodurch sich die Qualitat der statistischen Daten verbessert.
Ist nichts uber die Dauer der Konvertierung zwischen zwei Formaten bekannt,
so wird der Mittelwert aus den durchschnittlichen Rechenzeiten fur das entsprechende Zielformat und das entsprechende Quellformat als Abschatzung genommen. Diese Strategie wirkt sich nur bei den ersten beiden Optimierungen aus.
4.2 Realisierung
36
Die gewonnenen Werte sind in der Regel erheblich kleiner als die Rechenzeiten,
die tatsachlich erforderlich sind. Dadurch werden solche Formatumwandlungen
bei der Optimierung bevorzugt, also eher in die optimale Basis aufgenommen.
Somit fuhren spatere Zugrie auf den Storage-Server haug zu Konvertierungen,
folglich auch zu neuen, besseren statistischen Daten.
Da vom Optimierungsprogramm nur ein Datensatz pro Umwandlungsfunktion
eingefugt wird, ist dessen Gewichtung fur die Berechnung der Kosten gering,
falls viele Anfragen genau diese Formatumwandlungen erzwingen. Wurde eine
Umwandlungsfunktion seit geraumer Zeit nicht mehr verwendet, weil sich die
gespeicherten Formate geandert haben, so bleiben durch diesen einen Datensatz
die fruher gewonnenen Informationen erhalten.
Die Datenstruktur
Die grundlegende Datenstruktur fur den Optimierungsalgorithmus bilden einfachverkettete Listen dadurch gibt es im Optimierungsprogramm keine Einschrankungen hinsichtlich der Anzahl der Speicherformate (Attribute) oder der Umwandlungsfunktionen (functional constraints). Die Listen nehmen die Mengen aus dem
Algorithmus auf und werden durch Datenbankanfragen gefullt.
Es werden drei Arten von Listen verwendet. Variablen vom Typ list t enthalten
die Speicherformate (siehe Abbildung 4.1) und statistische Informationen daruber.
typedef struct list
{
long node
long size
long access
double ct_src
double ct_dest
char
is_phys
char
is_opt
struct list * next
} list_t
gif
tiff
jpeg
/*
/*
/*
/*
/*
/*
/*
/*
ps
ID des Speicherformats (Attribut)
durchschnittliche Groesse
Anzahl der Zugriffe
Abschaetzung fuer Konvertierungszeit
Abschaetzung fuer Konvertierungszeit
1 = Attribut ist zu speichern
1 = speichern bei optimaler Basis
naechstes Speicherformat
ppm
pgm
png
au
*/
*/
*/
*/
*/
*/
*/
*/
wav
Abbildung 4.1: Menge der Attribute
Die Verbundkomponente is phys dient zur Unterscheidung zwischen physikalisch
reprasentierten (1) und zu berechnenden (0) Formaten. In is opt wird dieses
4.2 Realisierung
37
Kennzeichen fur die optimale Basis gespeichert, d.h. die Zuweisung Aopt = Aphys
im Algorithmus wird im Programm durch is opt = is phys auf allen Listenelementen realisiert.
In den Variablen vom Typ list2 t werden die Konvertierungsfunktionen, bzw.
deren Quell- und Zielformate gespeichert.
typedef struct list2
{
long
node1
long
node2
double comp_time
char
is_base
struct list2 * next
} list2_t
/*
/*
/*
/*
/*
ID des Quellformats
ID des Zielformats
durchschnittliche Konvertierungszeit
1 = f_c ist Basiselement
naechste Konvertierungsfunktion
*/
*/
*/
*/
*/
Die Komponente comp time enthalt die durchschnittliche Rechenzeit fur die jeweilige Konvertierungsfunktion. In is base wird die Auswahl der in der zur Basis
gehorenden Funktionen getroen.
Der Variablentyp list list t dient zur Zerlegung der Formate in Zusammenhangskomponenten.
typedef struct list_list
{
struct list * first
struct list_list * next
} list_list_t
/* Liste der Attribute */
/* naechste Liste
*/
Die Verbundkomponente first ist ein Zeiger auf Attributmengen (Formatemengen). In Abbildung 4.2 ist die Zerlegung der Attributmenge aus Abbildung 4.1 in
Grak- und Audioformate dargestellt.
gif
tiff
au
wav
jpeg
ps
ppm
Abbildung 4.2: Zusammenhangskomponenten
pgm
png
4.2 Realisierung
38
Der Programmablauf
Bevor die eigentliche Optimierung stattnden kann, mussen die statistischen Daten { wie eben beschrieben { in die geeigneten Datenstrukturen eingefugt werden.
Dabei werden auch die unterstutzten Formate und die vorhandenen Umwandlungsfunktionen aus der Datenbank ausgelesen.
Anschlieend wird die Attributmenge A mit dem Union-Find -Algorithmus aus
Sed83] in Zusammenhangskomponenten zerlegt (siehe Abb. 4.1 und Abb. 4.2)
und die Optimierung auf diesen durchgefuhrt.
Im Algorithmus aus Abschnitt 4.1 werden zunachst alle Teilmengen der Attributmenge A gebildet. Die darin enthaltenen Attribute werden physikalisch reprasentiert alle anderen durch Konvertierung erzeugt. Die Auswahl der physikalisch
reprasentierten Attribute erfolgt durch binare Addition auf der Verbundkomponente is phys. Dabei wird beim ersten Listenelement is phys um eins erhoht
tritt ein U bertrag auf (das Ergebnis ist zwei), wird is phys auf null gesetzt und
die Addition beim nachsten Listenelement fortgefuhrt. Ein U bertrag am Ende
der Liste signalisiert das Ende des Algorithmus. Dies ist in Abbildung 4.3 zu
gif
tiff
jpeg
png
0
1
0
1
0
0
0
1
1
0
0
0
0
0
1
0
0
0
0
0
1
0
1
0
1
0
1
0
Übertrag
Abbildung 4.3: Auswahl der Attributmengen
sehen. Dabei steht in den Zeilen unter den Listenelementen der Inhalt der zugehorigen is phys-Komponente. Der Wert eins bedeutet, da das entsprechende
Format abgespeichert wird. Null steht dagegen bei zu berechnenden Formaten.
Der Anteil der Speicherkosten an den Gesamtkosten kann schon zu diesem Zeitpunkt ermittelt werden, da er nur von der Auswahl der gespeicherten Formate
abhangt.
Im nachsten Schritt werden die noch in Frage kommenden Umwandlungsfunktionen Fcomp (Die Komponente is phys des Quellformats mu gleich eins sein
is phys des Zielformats dagegen null.) selektiert und in eine eigene einfachverkettete Liste kopiert. Durch eine geschickte Datenbankanfrage beim Fullen
4.2 Realisierung
39
der ursprunglichen Liste ist die so entstehende Liste schon nach den Zielformaten sortiert. Da aber in der Basis fur jedes Zielformat nur eine Umwandlungsfunktion existieren darf, werden die Funktionen nach dem Zielformat gruppiert.
Die Verbundkomponente is base des ersten Gruppenelements erhalt jeweils den
Wert eins, alle anderen null. Der Eintrag eins in is base bedeutet, da diese
Konvertierungsfunktion zur Basis gehort. Die Auswahl aller Basen erfolgt durch
Rotation der is base-Komponenten innerhalb der Gruppen. Dabei erfolgt der
Rotationsschritt der nachfolgenden Gruppe erst, wenn in der aktuellen Gruppe
ein kompletter Zyklus durchlaufen wurde, d.h. die Eins wurde wieder nach vorn
geschoben. Wurde auch in der letzten Gruppe ein vollstandiger Rotationszyklus
durchgefuhrt, dann sind alle moglichen Basen gefunden. In Abbildung 4.4 ist die
gif
ps
tiff
ps
jpeg
ps
gif
png
tiff
png
jpeg
png
1
0
0
1
0
1
0
0
0
0
1
0
1
1
1
0
0
0
0
1
0
0
0
0
0
1
0
0
1
0
0
1
0
0
1
0
Abbruchkriterium
Abbildung 4.4: Auswahl der Basiselemente
Bestimmung der Basis dargestellt, falls gif, tiff und jpeg abgespeichert sind
und sich ps und png aus jedem dieser drei Formate berechnen lassen.
Ist eine Basis gefunden, kann dazu aus den statistischen Daten der Wert der
Kostenfunktion ermittelt werden. Ist dieser kleiner als das bisherige Optimum,
werden die Inhalte der is phys-Komponente nach is opt kopiert, um die zu
speichernden Formate Aopt festzuhalten. Um Fopt zwischenzuspeichern wird die
Liste kopiert, die Fcomp enthalt.
Nach der Ausfuhrung des Optimierungsalgorithmus stehen die zu speichernden
Formate und die zu benutzenden Konvertierungsfunktionen fest. Der Inhalt der
Datenbank mu den neuen Anforderungen noch angepat werden. Dies geschieht
in drei Schritten:
1. Die Dokumente mussen in die neu zu speichernde Formate umgewandelt
werden.
2. Nicht mehr zu speichernde Formate mussen aus der Datenbank entfernt
werden.
4.2 Realisierung
40
3. Die zu benutzenden Umwandlungsfunktionen mussen ausgewahlt werden.
Der erste Punkt wird dabei durch folgenden Ablauf erfullt: Fur alle zu speichernden Formate werden alle Dokumente gesucht, die in einem Format aus derselben
Zusammenhangskomponente, aber nicht in dem zu speichernden Format, vorhanden sind. Dies geschieht mit folgender SQL-Query:
SELECT DISTINCT name
FROM
artikel
WHERE fid=:db_fid AND
name NOT IN (SELECT name
FROM
artikel
WHERE fid=:cl_fid)
Dabei enthalt cl fid das zu speichernde Format und db fid der Reihe nach alle
anderen Formate. Fur jedes Format db fid werden dann mit dem Programm
insert copy (der Quellcode ist im Anhang C.5) alle gefundenen Dokumente im
gewunschten Format eingefugt.
Das Programm insert copy verlangt als Parameter den Namen des Dokuments
und das zu speichernde Format. Dann wird mit den Methoden des CGI-Programms db interface (siehe Abschnitt 3.3.2) aus einem in der Datenbank vorhandenen Format das gewunschte erzeugt. Im Gegensatz zum CGI-Programm
erfolgt aber keine Ausgabe, sondern eine Speicherung in der Datenbank.
Punkt zwei ist durch eine einfache SQL-Anweisung zu erledigen:
DELETE FROM artikel
WHERE fid = :cl_fid
Diese Anweisung wird fur alle Formate cl fid ausgefuhrt, die in Zukunft zu
berechnen sind.
Auch der dritte Punkt ist durch eine SQL-Anweisung zu realisieren:
UPDATE umwandlung
SET praeferenz = :pref
WHERE zielformat = :cl_fid AND
quellformat = :db_fid
Die Update-Anweisung wird nur fur die Funktionen durchgefuhrt, deren Quellformate in der Datenbank gespeichert werden, und deren Zielformate zu berechnen
sind andere Funktionen konnen ohnehin nicht verwendet werden. Die Hostvariable pref enthalt hierbei den Wert der is base-Komponente, die Aufschlu
daruber gibt, ob eine Konvertierungsfunktion benutzt wird (zur Basis gehort)
oder nicht.
Kapitel 5
Messungen
In diesem Kapitel wird anhand eines Beispiels das Ergebnis des Optimierungsalgorithmus gezeigt.
In der Datenbank sind 69 Bilder und sieben Audio-Clips abgelegt. Dabei werden
folgende Formate unterstutzt:
Bildformate Audioformate
gif
au
ti
wav
jpeg
ps
ppm
pgm
png
Zwischen den Speicherformaten gelten folgende Abhangigkeiten:
Quellformat
gif
ti
jpeg
ps
ppm
pgm
png
au
wav
Zielformat
gif ti jpeg ps ppm pgm png au wav
41
KAPITEL 5: MESSUNGEN
42
Die dabei verwendeten Konvertierungsfunktionen sind in Tabelle B.2 im Anhang
aufgefuhrt.
Das Beispiel konzentriert sich nur auf die Bilder, die anfanglich alle im jpegFormat gespeichert sind.
Zunachst gibt es auch noch keine statistischen Daten, die als Grundlage fur die
Optimierung herangenommen werden konnten. Diese Daten entstehen erst durch
Zugrie auf den Storage-Server oder durch die Optimierung selbst, falls neue Attribute physikalisch reprasentiert werden. Deshalb wird hier der Optimierungsalgorithmus solange wiederholt, bis sich das Resultat nicht mehr verandert. Sind
die statistischen Daten `vollstandig', dann werden sie durch die Optimierung nur
noch unwesentlich beeinut, und es genugt ein Optimierungslauf.
Fur die Konstanten in der Kostenfunktion werden die nachfolgenden Werte verwendet.1
cS = 0 000 002 cA = 0 000 000 1 z = 0 01
Im Beispiel wird immer eine U bersicht des aktuellen Queryprols gegeben. Dies
ist eine einfache Aufschlusselung der Zahl der Anfragen auf die Formate. Anschlieend erfolgt die Darstellung des Resultats der Optimierung, wobei die zu
speichernden Formate (Attribute) angezeigt werden hier gilt Aopt = Aphys. Auerdem wird die optimale functional base dargestellt. Dabei bedeutet die Notation
`gif ;! ti', da in der optimalen functional base eine funktionale Abhangigkeit
zwischen `gif' und `ti' besteht hierbei ist `ti' Kopf- und `gif' Rumpfattribut.
Mit anderen Worten: Das tiff-Format wird durch Konvertierung aus dem gifFormat erzeugt.
Zu Beginn werden 100 Anfragen fur das gif-Format gestellt.
Format gif P
Anfragen 100 100
Nach dem ersten Optimierungslauf ergibt sich:
Aphys
gif
Basis
gif ;! ti
gif ;! jpeg
gif ;! ps
gif ;! ppm
gif ;! pgm
gif ;! png
Die Konstanten konnen als Parameter an das Optimierungsprogramm ubergeben werden,
z.B.: optimierer -s 0.000002 -a 0.0000001 -z 0.01
1
KAPITEL 5: MESSUNGEN
43
Nach dem zweiten Optimierungslauf ergibt sich:
Aphys
Basis
gif png ;! ti
png png ;! jpeg
png ;! ps
png ;! ppm
png ;! pgm
Nach dem dritten Optimierungslauf ergibt sich:
Aphys
Basis
gif ppm ;! ti
ppm ppm ;! jpeg
ppm ;! ps
ppm ;! pgm
ppm ;! png
Nach dem vierten Optimierungslauf ergibt sich:
Aphys
gif
ti
Basis
ti ;! jpeg
ti ;! ps
ti ;! pgm
ti ;! ppm
ti ;! png
Nach dem funften Optimierungslauf ergibt sich:
Aphys
gif
ti
ps
pgm
Basis
ti ;! jpeg
ti ;! ppm
ti ;! png
Nach dem sechsten Optimierungslauf ergibt sich:
Aphys
Basis
gif png ;! ti
png png ;! jpeg
png ;! ps
gif ;! ppm
png ;! pgm
Weitere Optimierungen andern nichts am Ergebnis.
KAPITEL 5: MESSUNGEN
44
Es erfolgen zusatzlich 50 Anfragen fur das jpeg-Format.
Format gif jpeg P
Anfragen 100 50 150
Nach der Optimierung ergibt sich:
Aphys
gif
Basis
gif ;! ti
gif ;! jpeg
gif ;! ps
gif ;! ppm
gif ;! pgm
gif ;! png
Dieses Ergebnis bleibt auch nach weiteren Optimierungen erhalten.
Nun werden noch 20 Anfragen im png-Format gestellt.
Format gif jpeg png P
Anfragen 100 50 20 170
Die Optimierung fuhrt zu folgendem Ergebnis:
Aphys
Basis
gif
gif ;! ti
ppm ppm ;! jpeg
ppm ;! ps
ppm ;! pgm
ppm ;! png
Auch dieses Ergebnis bleibt schon nach der ersten Optimierung konstant.
Von jetzt an bleibt die Summe der Anfragen konstant. Es ndet lediglich eine
Verschiebung der Zusammenstellung statt. In der Log-Tabelle verlieren 50 Datensatze im gif-Format ihre Gultigkeit. Dafur werden 30 neue Anfragen fur das
jpeg- und 20 f
ur das png-Format gestellt.
Format gif jpeg png P
Anfragen 50 80 40 170
KAPITEL 5: MESSUNGEN
45
Die Optimierung liefert im diesem Fall:
Aphys
Basis
gif png ;! ti
png gif ;! jpeg
png ;! ps
png ;! ppm
png ;! pgm
Weitere Optimierungen beeinussen auch dieses Ergebnis nicht mehr.
Nun verlieren die restlichen 50 Log-Daten fur das gif-Format ihre Gultigkeit.
Dafur erfolgen 10 neue gif-, 10 jpeg- und 30 png-Anfragen.
Format gif jpeg png P
Anfragen 10 90 70 170
Als Ergebnis der Optimierung entsteht:
Aphys
Basis
ti ti ;! gif
png ti ;! jpeg
png ;! ps
ti ;! ppm
png ;! pgm
Das Ergebnis wird durch weitere Optimierungen nicht mehr verandert.
Nun erfolgt eine Aufschlusselung der Kostenfunktion, die als Grundlage fur die
letzte Optimierung verwendet wurde. Die fett gedruckten Zahlen gehen in die
Berechnung der Kosten der optimalen functional base ein.
Die nachste Tabelle enthalt die durchschnittliche Groe (in Bytes) der einzelnen
Formate und die daraus resultierenden Speicherkosten:
A 2 A avg size(A) store cost(A)
gif
271058
0,54212
ti
jpeg
ps
ppm
pgm
png
P
A2Aphys
71377
0,14275
101342
0,20268
0,34544
41838
81222
588465
31594
0,08368
0,16244
1,17693
0,06319
KAPITEL 5: MESSUNGEN
46
Die durchschnittlichen Rechenzeiten (in Sekunden), die von den Konvertierungsprogrammen fur eine Formatumwandlung beansprucht werden, sind in der anschlieenden Tabelle dargestellt:
A2A
gif
ti
jpeg
ps
ppm
pgm
png
gif
0,0000
1,6517
0,0000
1,8380
0,0000
3,2775
comp cost(A = f (B ))
B2A
ti
jpeg ps ppm pgm png
3,0930 6,3250
3,0930
3,0930
3,0930
1,8820
0,4370
0,0000
4,1646
1,1652
0,9610 3,0930
0,0000
0,0000
0,0000 3,0930
1,5940
0,4150 3,0930
0,0000
0,0000
1,4120 3,0930
5,8410
Zu den entsprechenden Konvertierungsfunktionen liegen noch keine
verlalichen Informationen uber die benotigte Rechenzeit vor.
Die nun folgende Tabelle enthalt die Zugriskosten, falls eine Formatumwandlung
notwendig ist (A 2 Acomp):
(A = f (B ) A 2 Acomp B 2 Aphys)
B2A
ti
jpeg ps ppm pgm png
43,4019 88,6086
44,1259
43,4439
12,3887
7,7634
1,7885
0,6709
397,0082
110,4810
4,8407 15,4859
0,2942
0,0507
0,0286 12,3887
6,4165
2,1107 15,4859
0,2942
0,0507
105,0162 229,1916
436,5851
acc cost(A)
A2A
gif
gif
ti
0,1084
jpeg 157,8103
ps
0,1355
ppm
7,4604
pgm
0,1355
png 244,5391
Die letzte Tabelle enthalt neben dem Queryprol (Anzahl der Zugrie), die Zugriskosten fur physikalisch reprasentierte Formate (A 2 Aphys) und auch die
Zugriskosten, die durch die optimale Basis bestimmt sind.
KAPITEL 5: MESSUNGEN
47
A 2 A avg acc(A) acc cost(A) acc cost(A)
A 2 Aphys
A2A
gif
14
0,37948 43,40193
ti
4
0,02855 0,02855
jpeg
94
0,39328
0,67094
ps
5
0,04061
0,05067
ppm
4
0,23539
0,02855
pgm
5
0,01580
0,05067
png
74
0,74993
0,74993
P
A2A
44,98125
Somit ergibt sich fur die optimalen Kosten:
cost(Aopt Fopt) = (1 ; 0 01) 0 34544 + 0 01 44 98125 = 0 79180
Kapitel 6
Zusammenfassung und Ausblick
In dieser Arbeit wurde ein System zur Speicherung und Verwaltung von Datenbestanden mit Formatumwandlung und Optimierung vorgestellt. Dabei wurde
groter Wert auf Flexibilitat gelegt. Erweiterungen der unterstutzten Speicherformate und deren Abhangigkeiten untereinander sind ohne Programmieraufwand
und nur durch Administration des Systems moglich. Die Messungen in Kapitel 5
zeigen, da sich das System selbststandig an neue Anforderungen (wechselndes
Queryprol) anpat und die dazu benotigten statistischen Informationen selbst
sammelt. Die Art der speicherbaren Formate ist vielfaltig, jedoch sollten zwischen
einzelnen Formaten Abhangigkeiten bestehen. Naturlich ist dieses System noch
nicht perfekt es gibt noch eine Reihe von moglichen Erganzungen und Verbesserungen:
1. Bisher wird die Auslastung des Storage-Servers nicht berucksichtigt, da in
die Berechnungskosten nur die reine CPU-Zeit einiet. Wird aber im CGIProgramm (db interface) die Zeitdierenz zwischen Beginn und Ende der
Konvertierungsfunktion ermittelt und in der Log-Tabelle abgespeichert, so
geht uber die Rechenzeit der parallel ablaufenden Prozesse die Serverauslastung ein.
2. Im Bereich des Proxy-Servers ist auch eine Optimierung denkbar. Dies
wurde bedeuten, da die Auswahl des Formats, das beim Storage-Server
angefordert wird, von den Zugris- und Berechnungskosten der einzelnen
Formate abhangt. Die Menge der zu berechnenden Formate kann dabei
in der Kongurationsdatei des Proxy-Servers abgespeichert werden. Eine
Praferenzordnung unter den Konvertierungsfunktionen ware schon durch
ihre Reihenfolge in der Kongurationsdatei gegeben, da die erste passende
Funktion verwendet wird.
3. Das Sammeln von statistischen Daten, die die Grundlage fur die Kostenfunktion bilden, gestaltet sich etwas schwieriger. Die Zugrisinformationen
48
KAPITEL 6: ZUSAMMENFASSUNG UND AUSBLICK
49
muten in Log-Dateien gespeichert werden, deren Auswertung komplizierter ist als die von Datenbanktabellen (Aggregation). Dies fuhrt zu einem
komplexeren Optimierungsprogramm. Nach erfolgter Optimierung mu
das Ergebnis davon in die Kongurationsdatei des Proxy-Servers ubernommen werden. Beim verwendeten Proxy (squid) ist eine Neukonguration
wahrend des Betriebs moglich, indem ein SIGHUP Signal an den Proxy-Server
geschickt wird. Dies kann beispielsweise mit dem folgenden Kommando erreicht werden, falls in der Datei squid.pid im angegebenen Verzeichnis die
Proze-ID des Proxy-Servers abgelegt ist:
kill -HUP `cat ~/squid/logs/squid.pid`
4. Die im Zuge einer Optimierung notwendigen Veranderungen an den physikalisch reprasentierten Formaten { speziell die Aufnahme neuer Speicherformate in die Datenbank { ist bei groen Datenmengen sehr zeitraubend. Deshalb ware es wunschenswert, diesen Vorgang in kleine Teilaufgaben zu zerlegen, die ausgefuhrt werden, wenn die Belastung der Datenbank moglichst
gering ist.
5. In die Datenbank konnten noch zusatzliche Metainformationen zu den einzelnen Dokumenten aufgenommen werden. Dies kann dann in der Anfrageschnittstelle fur Suchanfragen verwendet werden. Es ist auch eine mehrstuge Auswahl beim Request denkbar (1. Auswahl des Dokumenttitels
2. Auswahl des Formats). Damit konnte verhindert werden, da fur ein
Dokument ein Format ausgewahlt wird, in das es nicht konvertiert werden
kann, z.B. ein Audio-Format fur ein Bilddokument.
Literaturverzeichnis
Bor96]
Gunter Born. HTML 2.0/3.2 Reference Guide. Addison-WesleyLongman, Bonn Reading, Mass. u.a.], 1996.
FB96] N. Freed and N. Borenstein. Multipurpose Internet Mail Extensions
(MIME) Part Two: Media Types. Network Working Group, November 1996. http://www.leo.org/pub/comp/doc/standards/rfc/rfc2000-2099/rfc2046.
FGM+97] R. Fielding, J. Gettys, J. Mogul, H. Frystyk, and T. Berners-Lee.
Hypertext Transfer Protocol { HTTP/1.1. Network Working Group,
January 1997. http://www.leo.org/pub/comp/doc/standards/rfc/rfc2000-2099/rfc2068.
Gun96] Shishir Gundavaram. CGI Programming on the World Wide Web.
O'Reilly & Associates, Inc., Sebastopol, 1996.
Her96] Helmut Herold. UNIX-Systemprogrammierung. Addison-Wesley,
Bonn Paris Reading, Mass. u.a.], 1996.
KKK96] W. Kowarschick, G. Kostler, and W. Kieling. Client-Server Optimation for Multimedia Document Exchange. Institut fur Informatik,
Universitat Augsburg, Sep. 1996.
Klu95] Rainer Klute. Das World Wide Web. Addison{Wesley, Bonn Paris Reading, Mass. u.a.], 1995. http://www.nads.de/klute/WWWBuch/1/.
LL95]
Stefan M. Lang and Peter C. Lockemann. Datenbankeinsatz. Springer
Verlag, Berlin Heidelberg, 1995.
Sed83] Robert Sedgewick. Algorithms. Addison{Wesley, Reading, Massachusetts u.a], 1983.
50
Anhang A
Abbildungen
Abbildung A.1: Proxy-Konguration 1
A-1
ANHANG A: ABBILDUNGEN
Abbildung A.2: Proxy-Konguration 2
Abbildung A.3: Proxy-Konguration 3
A-2
ANHANG A: ABBILDUNGEN
Abbildung A.4: Benutzerschnittstelle
A-3
ANHANG A: ABBILDUNGEN
Abbildung A.5: Anfrageergebnis
A-4
Anhang B
Tabellen
MIME-Type
Dateiendung
application/octet-stream bin arj zoo dms exe
application/oda
oda
application/pdf
pdf
application/postscript
ai eps ps
application/rtf
rtf
application/x-bcpio
bcpio
application/x-cpio
cpio
application/x-dvi
dvi
application/x-gtar
gtar
application/x-hdf
hdf
application/x-latex
latex
application/x-mif
mif
application/x-netcdf
nc cdf
application/x-shar
shar
application/x-sv4cpio
sv4cpio
application/x-sv4crc
sv4crc
application/x-tar
tar
application/x-tcl
tcl
application/x-tex
tex
application/x-texinfo
texinfo texi
application/x-tro
t tr ro
application/x-tro-man
man
application/x-tro-me
me
application/x-tro-ms
ms
application/x-ustar
ustar
application/x-wais-source src
application/zip
zip
Fortsetzung nachste Seite
B-1
ANHANG B: TABELLEN
Fortsetzung
MIME-Type
audio/basic
audio/x-ai
audio/x-pn-realaudio
audio/x-wav
image/gif
image/ief
image/jpeg
image/ti
image/x-cmu-raster
image/x-png
image/x-portable-anymap
image/x-portable-bitmap
image/x-portable-graymap
image/x-portable-pixmap
image/x-rgb
image/x-xbitmap
image/x-xpixmap
image/x-xwindowdump
text/html
text/plain
text/richtext
text/tab-separated-values
text/x-setext
video/mpeg
video/quicktime
video/x-msvideo
video/x-sgi-movie
B-2
Dateiendung
au snd
aif ai aifc
ra ram
wav
gif
ief
jpeg jpg jpe
ti tif
ras
png
pnm
pbm
pgm
ppm
rgb
xbm
xpm
xwd
html htm
txt
rtx
tsv
etx
mpeg mpg mpe
qt mov
avi i
movie
Tabelle B.1: MIME-Typen
ANHANG B: TABELLEN
Quellformat Zielformat Umwandlungsfunktion
gif
ti
convert - ti:jpeg
ti
convert - ti:ppm
ti
convert - ti:png
ti
convert - ti:ti
gif
convert - gif:jpeg
gif
convert - gif:ppm
gif
convert - gif:png
gif
convert - gif:gif
jpeg
convert - jpeg:ti
jpeg
convert - jpeg:ppm
jpeg
convert - jpeg:png
jpeg
convert - jpeg:gif
ps
convert - ps:ti
ps
convert - ps:jpeg
ps
convert - ps:ppm
ps
convert - ps:png
ps
convert - ps:gif
ppm
convert - ppm:ti
ppm
convert - ppm:jpeg
ppm
convert - ppm:png
ppm
convert - ppm:gif
pgm
convert - pgm:ti
pgm
convert - pgm:jpeg
pgm
convert - pgm:ppm
pgm
convert - pgm:png
pgm
convert - pgm:gif
png
convert - png:ti
png
convert - png:jpeg
png
convert - png:ppm
png
convert - png:wav
au
mklter sox - -t .au
au
wav
mklter sox - -t .wav
Tabelle B.2: Konvertierungsfunktionen
B-3
Anhang C
Programme
C.1 converter.c
/******************************************************************
*
*
* converter.c
*
*
*
* Konverterprogramm f"ur den Proxy-Server
*
* liest Daten von stdin, schreibt auf stdout
*
* erh"alt die Konvertierungsfunktion und den Content-Type des
*
* Zielformats "uber den Convert-Header
*
******************************************************************/
#include
#include
#include
#include
#include
<stdio.h>
"squid.h"
<string.h>
<strings.h>
<sys/types.h>
typedef struct _cmd
{
char *cmd
char **args
} cmd_t
static const char *const w_space = " \t\n\r"
char input_lineBUFSIZ]
C-1
C.1 converter.c
C-2
int getTokenNumber(char *s)
{
int n=0
while(*s)
{
while(*s && isspace(*s)) s++
if(!*s) break
while(*s && !isspace(*s)) s++
n++
}
return n
}
/* Convert-Header auswerten nach Aufforderung Programm beenden */
void parseMyHeader(cmd_t *f, char **mime)
{
char *token = NULL, **s
while (1)
{
if (fgets(input_line, BUFSIZ, stdin) == NULL) exit(0)
if ((token = strchr(input_line, '\n')))
*token = '\0'
if ((token = strchr(input_line, '\r')))
*token = '\0'
if (strcmp(input_line,"$shutdown") == 0) exit(0)
if (strcmp(input_line,"$hello") == 0)
{
printf("$alive\n")
fflush(stdout)
continue
}
if ((token = strtok(input_line, w_space)) == NULL)
return
if (strcasecmp(token,"Mime-Type:")==0)
*mime=xstrdup(strtok(NULL,w_space))
else if (strcasecmp(token,"Command:")==0)
{
if ((token = strtok(NULL,w_space))==NULL) exit(1)
f->cmd = xstrdup(token)
C.1 converter.c
C-3
s=f->args=(char**)calloc(getTokenNumber(token+
strlen(token+1))+1,sizeof(char*))
*s++=xstrdup(token)
while ((token = strtok(NULL,w_space))!=NULL)
*s++=xstrdup(token)
*s=NULL
}
}
}
/* HTTP-Header lesen, ver"andern und ausgeben */
void parseHTTPHeader(char *t)
{
char *s
while (fgets(input_line, BUFSIZ, stdin))
{
for(s=input_line*s&&isspace(*s)s++)
if (!*s)
{
puts("")
return
}
fprintf(stderr,"<%s>\n",input_line)
fflush(stderr)
if (strncasecmp(input_line,"Content-Type:",12)!=0)
printf("%s",input_line)
else
printf("Content-Type: %s\n",t)
}
}
/* Konvertierungsproze"s starten und Konvertierung durchf"uhren */
void convert(cmd_t *c)
{
int pid, status
switch(pid = fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork %s %s\n",
c->cmd,*(c->args))
exit(1)
C.2 dynform.sqc
case
C-4
0:
execvp(c->cmd,c->args)
fprintf(stderr,"ACHTUNG FEHLER kein Aufruf von %s\n",
c->cmd)
fprintf(stderr,"
%s %s \n",*(c->args),*(c->args+1))
exit(-1)
default: wait(&status)
}
return
}
/* Hauptprogramm */
void main(void)
{
char *mime
cmd_t fkt
setbuf(stdin,NULL)
for()
{
parseMyHeader(&fkt,&mime)
fflush(stdout)
parseHTTPHeader(mime)
fflush(stdout)
convert(&fkt)
fflush(stdout)
}
}
C.2 dynform.sqc
/******************************************************************
*
*
* dynform.sqc
*
*
*
* dynform liefert den Inhalt der Datenbank als HTML-Formular
*
* und erm"oglicht "uber den Aufruf von db_interface den Zugriff
*
C.2 dynform.sqc
C-5
* auf die Dokumente
*
*
*
******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
EXEC SQL INCLUDE SQLCA
#define
CHECKERR(CE_STR)
if (check_error (CE_STR, &sqlca) != 0) \
return 1
int main(int argc, char *argv])
{
int first
char errtxt200]
EXEC SQL BEGIN DECLARE SECTION
char doctype11]
char docname21]
EXEC SQL END DECLARE SECTION
/* Environment-Variable beim Aufruf "uber CGI nicht bekannt! */
putenv("DB2INSTANCE=db2ex")
EXEC SQL CONNECT TO peter_db USER db2ex USING daba2
sprintf(errtxt,"Content-type: text/plain \n\n"
"CONNECT TO PETER_DB\n%s\n",getenv("DB2INSTANCE"))
CHECKERR (errtxt)
EXEC SQL DECLARE c1 CURSOR FOR
SELECT distinct name FROM formate
CHECKERR ("Content-type: text/plain \n\nDECLARE CURSOR c1\n")
EXEC SQL DECLARE c2 CURSOR FOR
SELECT distinct name FROM artikel
CHECKERR ("Content-type: text/plain \n\nDECLARE CURSOR c2\n")
EXEC SQL
CHECKERR
EXEC SQL
CHECKERR
OPEN c1
("Content-type: text/plain \n\nOPEN CURSOR c1\n")
OPEN c2
("Content-type: text/plain \n\nOPEN CURSOR c2\n")
C.2 dynform.sqc
printf("Content-type: text/html\n\n")
printf("<HTML>\n<HEAD><TITLE>Eingabeformular"
"</TITLE></HEAD>\n<BODY>\n")
printf("<H1>Eingabeformular</H1>\n<HR>\n")
/* auszuf"uhrendes CGI-Programm */
printf("<FORM ACTION=\"/cgi-bin/db_interface\"")
/* Formular mit den Dokumentnamen */
printf(" METHOD=\"GET\">\nName des Artikels: <BR>")
printf("<SELECT NAME=\"name\" SIZE=8>\n")
first=1
do
{
EXEC SQL FETCH c2 INTO :docname
if (SQLCODE != 0)
break
else
{
printf ("<OPTION%s>%s\n",first?" SELECTED":"",docname)
first=0
fflush(stdout)
}
} while ( 1 )
printf("</SELECT>\n<BR>\n")
/* Formular mit den unterst"utzten Dokumentformaten */
printf("gew&uumlnschtes Format: <BR>\n")
printf("<SELECT NAME=\"format\" SIZE=8>\n")
first=1
do
{
EXEC SQL FETCH c1 INTO :doctype
if (SQLCODE != 0)
break
else
{
printf ("<OPTION%s>%s\n",first?" SELECTED":"",doctype)
first=0
fflush(stdout)
}
} while ( 1 )
printf("</SELECT>\n<BR>\n")
printf("<P>\n<INPUT TYPE=\"submit\" VALUE=\"Abschicken\">\n")
printf("</FORM>\n<HR>\n</BODY>\n</HTML>\n")
C-6
C.3 db interface.sqc
C-7
EXEC SQL CLOSE c1
CHECKERR ("Content-type: text/plain \n\nCLOSE CURSOR\n")
EXEC SQL CONNECT RESET
CHECKERR ("Content-type: text/plain \n\nCONNECT RESET\n")
return 0
}
C.3 db interface.sqc
/******************************************************************
*
*
* db_interface.sqc
*
*
*
* CGI-Programm zur Datenbankanbindung mit Konvertierung
*
* db_interface liefert ein das angeforderte Dokument im
*
* gew"unschten Format zur"uck, notfalls mit Konvertierung
*
*
*
******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/timers.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sql.h>
#include <fcntl.h>
#include "stat.h"
#ifndef USE_MSG
# ifndef USE_SHM
#
define USE_SHM
# endif
#endif
#ifdef USE_MSG
# include <sys/msg.h>
# define MSG_KEY IPC_PRIVATE
#endif
C.3 db interface.sqc
C-8
#ifdef USE_SHM
# include <sys/shm.h>
# define SHM_KEY IPC_PRIVATE
#endif
#include "util.h"
EXEC SQL INCLUDE SQLCA
#define
CHECKERR(CE_STR)
#define
#define
#define
#define
MAXPIPES 15
MAXOPTS 15
EIN
1
AUS
0
if (check_error (CE_STR, &sqlca) != 0) \
return 1
struct fork_cmd
{
char *cmd
char *opt
struct fork_cmd *next
}
#ifdef USE_MSG
struct SEND_MESG
{
long type
unsigned long bytes
} sendmessage
#endif
typedef struct fork_cmd s_fork_cmd
int pipesMAXPIPES]2]
#ifdef USE_MSG
int msg_queue
#endif
#ifdef USE_SHM
int shmhandle
long *b_p
#endif
s_fork_cmd* split_cmd(char *s)
void ausgabe(int nos)
void eingabe(char *fname, int nos)
C.3 db interface.sqc
C-9
void split_opts(char **spl, char *cmd, char *opts)
unsigned long umwandlung(char *fname, char *cmd)
int main(int argc, char *argv])
{
long len
char errtxt200], expdate200], buf4096]
FILE *fp1
clock_t start_z
struct tm *tp2
struct timespec tp1
struct stat filestat
int fd
EXEC SQL BEGIN DECLARE SECTION
SQL TYPE IS BLOB_FILE tmp_file
short lobind
long cl_fid
long db_fid
long db_len
long cl_len
double r_zeit
char docname21]
char doctype11]
char mime101]
char cl_host101]
char uw_fkt201]
EXEC SQL END DECLARE SECTION
/* Environment-Variable beim Aufruf "uber CGI nicht bekannt! */
putenv("DB2INSTANCE=db2ex")
fp1 = freopen("mycgi.err", "a", stderr)
getclock(TIMEOFDAY,&tp1)
fprintf(stderr,"TimeOfDay:%ld.%06ld\n",tp1.tv_sec,tp1.tv_nsec)
tp1.tv_sec+=5184000
tp2=gmtime(&tp1.tv_sec)
strftime(expdate,199,"%a, %d %b %Y %H:%M:%S GMT",tp2)
fprintf(stderr,"Expires: %s\n",expdate)
/* Parameter des CGI Aufrufs auswerten */
uncgi()
C.3 db interface.sqc
strcpy(docname,getenv("WWW_name"))
strcpy(doctype,getenv("WWW_format"))
strcpy(cl_host,getenv("REMOTE_HOST"))
EXEC SQL CONNECT TO peter_db USER db2ex USING daba2
sprintf(errtxt,"Content-type: text/plain \n\n"
"CONNECT TO PETER_DB\n%s\n",getenv("DB2INSTANCE"))
CHECKERR (errtxt)
fprintf(stderr,"Connect OK\n %s %s\n",docname,doctype)
strcpy(tmp_file.name,tmpnam(NULL))
tmp_file.name_length = strlen(tmp_file.name)
tmp_file.file_options = SQL_FILE_CREATE
EXEC SQL SELECT a.inhalt,f.mimetype,f.fid
INTO :tmp_file :lobind, :mime, :db_fid
FROM artikel a, formate f
WHERE a.fid=f.fid AND a.name=:docname AND
f.name=:doctype
if (SQLCODE != 0)
{
EXEC SQL DECLARE c2 CURSOR FOR
SELECT a.inhalt, u.funktion, f.mimetype, a.fid, f.fid
FROM artikel a, umwandlung u, formate f
WHERE a.fid=u.quellformat AND u.zielformat=f.fid AND
a.name=:docname AND f.name=:doctype AND
u.praeferenz=(SELECT max(u1.praeferenz)
FROM umwandlung u1, artikel a1
WHERE a1.name=a.name AND
u1.zielformat=f.fid AND
u1.quellformat=a1.fid)
EXEC SQL OPEN c2
EXEC SQL FETCH c2 INTO :tmp_file :lobind, :uw_fkt,
:mime, :db_fid, :cl_fid
if (SQLCODE != 0)
{
printf("Content-type: text/plain\n")
C-10
C.3 db interface.sqc
C-11
printf("Status: 404 Not Found\n\n")
printf("FALSCHES FORMAT\nArtikel ")
printf("%s.%s ist nicht vorhanden\n",docname,doctype)
printf("bzw. kann durch Umwandlung nicht erzeugt werden.\n")
return
}
else
{
/* HTTP-Header ausgeben */
printf("Content-type: %s\n",mime)
printf("Expires: %s\n\n",expdate)
fflush(stdout)
stat(tmp_file.name,&filestat)
start_z=clock()
cl_len=umwandlung(tmp_file.name,uw_fkt)
r_zeit=(clock()-start_z)/(double)CLOCKS_PER_SEC
db_len=filestat.st_size
fprintf(stderr,"Umwandlung von %s.%s mit %s nach %s\n",
docname,doctype,uw_fkt,mime)
}
}
else
{
if (lobind < 0)
{
printf("Content-type: text/plain\n\n")
printf("Artikel %s.%s ist leer\n",docname,doctype)
}
else
{
fprintf(stderr,"Tempfile: %s\n",tmp_file.name)
fd=open(tmp_file.name,O_RDONLY)
fstat(fd,&filestat)
/* HTTP-Header ausgeben */
printf ("Content-type: %s\n",mime)
printf ("Expires: %s\n",expdate)
printf ("Content-length: %ld\n\n",filestat.st_size)
fflush(stdout)
/* BLOB ausgeben */
while(len=read(fd,buf,4096))
write(fileno(stdout),buf,len)
C.3 db interface.sqc
C-12
fflush(stdout)
close(fd)
cl_len=db_len=filestat.st_size
cl_fid=db_fid
remove(tmp_file.name)
r_zeit=0.0
}
}
/* Zugriffsdaten in Log-Tabelle schreiben */
EXEC SQL INSERT INTO log_tabelle
VALUES ( :cl_host, :docname, :cl_fid, :db_fid, :cl_len,
:db_len, :r_zeit, CURRENT TIMESTAMP )
CHECKERR ("Content-type: text/plain\n\nINSERT INTO log_tab\n")
EXEC SQL CONNECT RESET
CHECKERR ("Content-type: text/plain \n\nCONNECT RESET\n")
return 0
}
/* Formatumwandlung mittels Kindprozessen */
unsigned long umwandlung(char *fname, char *cmd)
{
int nos=0, status
unsigned long
len
char *optvMAXOPTS]
s_fork_cmd *f1, *f
#ifdef USE_MSG
msg_queue=msgget(MSG_KEY,IPC_CREAT | 0777)
if(msg_queue==-1)
{
perror("Fehler bei msgget()")
exit(1)
}
#endif
f1=f=split_cmd(cmd)
while(f!=NULL)
{
f=f->next
pipe(pipes++nos])
C.3 db interface.sqc
}
nos=0
pipe(pipesnos])
f=f1
eingabe(fname,nos)
while(f!=NULL)
{
nos++
fprintf(stderr,"%d:%s p1: %d p2: %d\n",nos,f->cmd,
pipesnos]0],pipesnos]1])
split_opts(optv,f->cmd,f->opt)
switch(fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork "
"%s %s\n",f->cmd,f->opt)
exit(1)
case 0: dup2(pipesnos-1]AUS],0)
close(pipesnos-1]AUS])
close(pipesnos-1]EIN])
dup2(pipesnos]EIN],1)
close(pipesnos]EIN])
fprintf(stderr,"Aufruf von execvp '%s' '%s'\n",
f->cmd,f->opt)
close(pipesnos]AUS])
execvp(f->cmd,optv)
fprintf(stderr,"ACHTUNG FEHLER kein Aufruf von "
"%s\n",f->cmd)
exit(-1)
default: close(pipesnos-1]AUS])
close(pipesnos]EIN])
}
f=f->next
}
#ifdef USE_SHM
shmhandle=shmget(SHM_KEY, sizeof(long), 0700)
fprintf(stderr,"SHMHANDLE %d\n",shmhandle)
if(shmhandle==-1)
{
perror("Fehler bei shmget()")
exit(1)
}
C-13
C.3 db interface.sqc
b_p=(long *)shmat(shmhandle,0,0)
if(b_p==(void *)-1)
{
perror("Fehler bei shmat()")
exit(1)
}
*b_p=0
#endif
ausgabe(nos)
f=f1
wait(&status)
wait(&status)
while(f!=NULL)
{
f=f->next
wait(&status)
}
len=1
#ifdef USE_MSG
if(msgrcv(msg_queue,(struct msgbuf *)&sendmessage,
sizeof(unsigned long),0,MSG_NOERROR)==-1)
{
perror("Fehler bei msgrcv()")
exit(1)
}
msgctl(msg_queue,IPC_RMID,NULL)
len=sendmessage.bytes
#endif
#ifdef USE_SHM
len=*b_p
shmdt((void *) b_p)
shmctl(shmhandle,IPC_RMID,NULL)
#endif
fprintf(stderr,"Zielformat hat %ld Bytes\n",len)
return len
}
C-14
C.3 db interface.sqc
/* Schreiben des BLOBs in die Eingabepipe des */
/* Konvertierungsprozesses
*/
void eingabe(char *fname, int nos)
{
char buf4096]
int len, fd
switch(fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork %d\n",
nos)
exit(1)
case 0: fd=open(fname,O_RDONLY)
while(len=read(fd,buf,4096))
write(pipes0]EIN],buf,len)
close(fd)
remove(fname)
exit(0)
default: close(pipes0]EIN])
}
}
/* Lesenen des umgewandelten BLOBs aus der Ausgabepipe des */
/* Konvertierungsprozesses
*/
void ausgabe(int nos)
{
char buf4096]
int nob
unsigned long len=0
switch(fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork %d\n",
nos)
exit(1)
case 0: while(nob=read(pipesnos]AUS],buf,4096))
{
write(1,buf,nob)
len+=nob
}
#ifdef USE_MSG
sendmessage.type=1
C-15
C.3 db interface.sqc
C-16
sendmessage.bytes=len
if( msgsnd(msg_queue,(struct msgbuf *) &sendmessage,
sizeof(unsigned long),MSG_NOERROR) == -1 )
{
perror("Fehler bei msgsnd()")
fprintf(stderr,"%d\n",errno)
exit(1)
}
#endif
#ifdef USE_SHM
*b_p=len
shmdt((void *)b_p)
#endif
exit(0)
default: close(pipesnos]AUS])
}
}
/* Aufspalten des Kommandostrings in Befehle und Komponenten */
s_fork_cmd* split_cmd(char *s)
{
char *del, *opt
s_fork_cmd *f_base, *f_runner
f_base=NULL
while(*s && isspace(*s)) s++
while(*s)
{
if(f_base==NULL)
f_runner=f_base=malloc(sizeof(s_fork_cmd))
else
{
f_runner->next=malloc(sizeof(s_fork_cmd))
f_runner=f_runner->next
}
f_runner->next=NULL
del=opt=s
while(*del && *del !='|') del++
while(*opt && !isspace(*opt) && *opt!='|') opt++
f_runner->cmd=malloc(opt-s+1)
strncpy(f_runner->cmd,s,opt-s)
C.4 optimierer.sqc
C-17
f_runner->cmdopt-s]=0
while(*opt && isspace(*opt)) opt++
if (del-opt)
{
f_runner->opt=malloc(del-opt+1)
strncpy(f_runner->opt,opt,del-opt)
f_runner->optdel-opt]=0
}
else
f_runner->opt=""
s=del
while(*s && (isspace(*s)||*s=='|')) s++
}
return f_base
}
/* Aufspalten der Optionen */
void split_opts(char **spl, char *cmd, char *opts)
{
*spl++=cmd
while(*opts)
{
*spl++=opts
while(*opts && !isspace(*opts)) opts++
if(*opts) *opts++=0
while(*opts && isspace(*opts)) opts++
}
*spl=NULL
}
C.4 optimierer.sqc
/******************************************************************
*
*
* optimierer.sqc
*
*
*
* USAGE: optimierer -a value] -s value] -z value]
*
*
*
C.4 optimierer.sqc
C-18
* optimierer entscheidet ausgrund einer Kostenfunktion "uber die *
* zu speichernden Formate und die zu verwendenden Umwandlungs*
* funktionen.
*
* Die Datenbank wird den neuen Anforderungen angepa"st!
*
*
*
******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/timers.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sql.h>
#include <fcntl.h>
#include "stat.h"
EXEC SQL INCLUDE SQLCA
#define
CHECKERR(CE_STR)
#define
#define
MAXPIPES 15
MAXOPTS 15
struct list
{
long node
long size
long access
double ct_src
double ct_dest
char is_phys
char
is_opt
struct list * next
}
typedef struct list list_t
struct list2
{
long
node1
long
node2
if (check_error (CE_STR, &sqlca) != 0) \
return 1
C.4 optimierer.sqc
double comp_time
char
is_base
struct list2 * next
}
typedef struct list2 list2_t
struct list_list
{
struct list * first
struct list_list * next
}
typedef struct list_list list_list_t
/* Liste der Attribute (Formate) */
list_t *attr
/* Liste der functional constraints (Umwandlungsfkts.) */
list2_t *fc
/* Liste der Zusammenhangskomponenten */
list_list_t *compo
/* Konstanten fuer die Kostenfunktion */
double const_ca=0.0000001,
const_cs=0.000002,
const_z=0.01
EXEC SQL BEGIN DECLARE SECTION
long num
long cl_fid
long db_fid
long db_len
long cl_len
long pref
double r_zeit
char docname21]
char doctype11]
char mime101]
char cl_host101]
char uw_fkt201]
EXEC SQL END DECLARE SECTION
int init_lists(list_t **a, list2_t **f)
void split2compo(list_t **a, list2_t **f,
C-19
C.4 optimierer.sqc
list_list_t **c, long size)
int next_base(list_t *a)
int get_fcs(list_t *a, list2_t *fc, list2_t **fcn)
void free_list2(list2_t **l)
int next_fc_base(list2_t *fc)
double get_ctime(long s, long d, list_t *l)
double store_costs(list_t *base)
double acc_costs_phys(list_t *base)
double acc_costs_comp(list2_t *fc, list_t *base)
void copy_fc(list2_t **d, list2_t *s)
long add_phys_type(list_t *base)
void del_comp_type(list_t *base)
void get_args(int argc, char *argv])
int main(int argc, char *argv])
{
long i, len, maxa
char errtxt200], expdate200], buf4096]
clock_t start_z
struct stat filestat
int fd
char *c
unsigned long idx, lines
list_t *runner
list2_t *runner2, *new_fc, *opt_fc=NULL
list_list_t *runnerl, *ll
double cost_s, cost_a_p, cost_a_c, costs
putenv("DB2INSTANCE=db2ex")
/* Optionen auswerten */
get_args(argc,argv)
EXEC SQL CONNECT TO peter_db USER db2ex USING daba2
sprintf(errtxt,"main: CONNECT TO PETER_DB\n%s\n",
getenv("DB2INSTANCE"))
CHECKERR (errtxt)
maxa=init_lists(&attr, &fc)
EXEC SQL DELETE FROM log_tabelle
WHERE cl_name = 'optimierer' OR
cl_name = 'converter'
C-20
C.4 optimierer.sqc
C-21
strcpy(docname,"dummy")
strcpy(cl_host,"optimierer")
for(runner2=fcrunner2!=NULLrunner2=runner2->next)
{
db_fid=runner2->node1
cl_fid=runner2->node2
r_zeit=runner2->comp_time
for(runner=attrrunner->node!=db_fidrunner=runner->next)
db_len=runner->size
for(runner=attrrunner->node!=cl_fidrunner=runner->next)
cl_len=runner->size
EXEC SQL INSERT INTO log_tabelle
VALUES (:cl_host, :docname, :cl_fid, :db_fid,
:cl_len, :db_len, :r_zeit, CURRENT TIMESTAMP)
}
split2compo(&attr, &fc, &compo,maxa)
/* OPTIMIERUNGSALGORITHMUS */
for(ll=compoll!=NULLll=ll->next)
{
costs=1e300
for(i=0!i)
{
i=next_base(ll->first)
cost_s=store_costs(ll->first)
cost_a_p=acc_costs_phys(ll->first)
if(get_fcs(ll->first,fc,&new_fc))
do
{
cost_a_c=acc_costs_comp(new_fc,ll->first)
if(costs>cost_a_c+cost_a_p+cost_s)
{
costs=cost_a_c+cost_a_p+cost_s
for(runner=ll->firstrunner!=NULLrunner=runner->next)
runner->is_opt=runner->is_phys
free_list2(&opt_fc)
copy_fc(&opt_fc,new_fc)
}
}while(!next_fc_base(new_fc))
C.4 optimierer.sqc
free_list2(&new_fc)
}
for(runner=ll->firstrunner!=NULLrunner=runner->next)
printf("->%ld%d]",runner->node,runner->is_opt)
/* Anpassung des Datenbankinhalts */
add_phys_type(ll->first)
del_comp_type(ll->first)
printf("\nfc: ")
for(runner2=opt_fcrunner2!=NULLrunner2=runner2->next)
{
printf("->(%ld/%ld)%d]",db_fid=runner2->node1,
cl_fid=runner2->node2,runner2->is_base)
pref=runner2->is_base
EXEC SQL UPDATE umwandlung
SET praeferenz = :pref
WHERE zielformat = :cl_fid AND
quellformat = :db_fid
}
printf("\nOPT_COST: %f\n",costs)
}
EXEC SQL CONNECT RESET
CHECKERR ("Content-type: text/plain \n\nCONNECT RESET\n")
return 0
}
/* Lesen der statistischen Daten, Formate und Funktionen */
int init_lists(list_t **a, list2_t **f)
{
list_t *l1
list2_t *l2
long
maxattr=0
*a=NULL
*f=NULL
EXEC SQL DECLARE c1 CURSOR FOR
SELECT fid FROM formate ORDER BY fid
CHECKERR ("init_list: DECLARE CURSOR c1\n")
EXEC SQL DECLARE c2 CURSOR FOR
SELECT cl_fid, avg(cl_size), count(cl_fid)
FROM log_tabelle
C-22
C.4 optimierer.sqc
GROUP BY cl_fid
ORDER BY cl_fid DESC
CHECKERR ("init_list: DECLARE CURSOR c2\n")
EXEC SQL DECLARE c3 CURSOR FOR
SELECT db_fid, avg(db_size), count(db_fid)
FROM log_tabelle
GROUP BY db_fid
ORDER BY db_fid DESC
CHECKERR ("init_list: DECLARE CURSOR c3\n")
EXEC SQL DECLARE c4 CURSOR FOR
SELECT cl_fid, avg(uw_dauer)
FROM log_tabelle
WHERE cl_fid <> db_fid
GROUP BY cl_fid
ORDER BY cl_fid
CHECKERR ("init_list: DECLARE CURSOR c4\n")
EXEC SQL DECLARE c5 CURSOR FOR
SELECT db_fid, avg(uw_dauer)
FROM log_tabelle
WHERE cl_fid <> db_fid
GROUP BY db_fid
ORDER BY db_fid
CHECKERR ("init_list: DECLARE CURSOR c5\n")
EXEC SQL DECLARE c6 CURSOR FOR
SELECT quellformat, zielformat
FROM umwandlung
ORDER BY zielformat, quellformat
CHECKERR ("init_list: DECLARE CURSOR c6\n")
EXEC SQL DECLARE c7 CURSOR FOR
SELECT db_fid, cl_fid, avg(uw_dauer)
FROM log_tabelle
WHERE cl_fid <> db_fid
GROUP BY cl_fid, db_fid
ORDER BY cl_fid, db_fid
CHECKERR ("init_list: DECLARE CURSOR c7\n")
EXEC SQL DELETE FROM log_tabelle
WHERE datum < CURRENT TIMESTAMP - 1 MONTH AND
C-23
C.4 optimierer.sqc
C-24
artikel<>'dummy'
EXEC SQL OPEN c1
CHECKERR ("init_list: OPEN CURSOR c1\n")
do {
EXEC SQL FETCH c1 INTO :cl_fid
if (SQLCODE != 0) break
l1=*a
*a=malloc(sizeof(list_t))
(*a)->node=cl_fid
(*a)->size=0
(*a)->access=0
(*a)->ct_src=0.0
(*a)->ct_dest=0.0
(*a)->next=l1
maxattr=(maxattr>cl_fid)?maxattr:cl_fid
} while ( 1 )
EXEC SQL CLOSE c1
CHECKERR ("init_list: CLOSE CURSOR c1")
EXEC SQL OPEN c2
CHECKERR ("init_list: OPEN CURSOR c2\n")
do {
EXEC SQL FETCH c2 INTO :cl_fid, :cl_len, :num
if (SQLCODE != 0) break
for(l1=*al1!=NULLl1=l1->next)
if(l1->node==cl_fid)
{
l1->size=cl_len
l1->access=num
}
} while ( 1 )
EXEC SQL CLOSE c2
CHECKERR ("init_list: CLOSE CURSOR c2")
EXEC SQL OPEN c3
CHECKERR ("init_list: OPEN CURSOR c3\n")
do {
C.4 optimierer.sqc
C-25
EXEC SQL FETCH c3 INTO :db_fid, :db_len, :num
if (SQLCODE != 0) break
for(l1=*al1!=NULLl1=l1->next)
if(l1->node==db_fid)
l1->size=((double)l1->size*l1->access+(double)db_len*num)/
(l1->access+num)
} while ( 1 )
EXEC SQL CLOSE c3
CHECKERR ("init_list: CLOSE CURSOR c3")
EXEC SQL OPEN c4
CHECKERR ("init_list: OPEN CURSOR c6\n")
do {
EXEC SQL FETCH c4 INTO :cl_fid, :r_zeit
if (SQLCODE != 0) break
for(l1=*al1!=NULLl1=l1->next)
if(l1->node==cl_fid)
l1->ct_dest=r_zeit
} while ( 1 )
EXEC SQL CLOSE c4
CHECKERR ("init_list: CLOSE CURSOR c4")
EXEC SQL OPEN c5
CHECKERR ("init_list: OPEN CURSOR c5\n")
do {
EXEC SQL FETCH c5 INTO :db_fid, :r_zeit
if (SQLCODE != 0) break
for(l1=*al1!=NULLl1=l1->next)
if(l1->node==db_fid)
l1->ct_src=r_zeit
} while ( 1 )
EXEC SQL CLOSE c5
CHECKERR ("init_list: CLOSE CURSOR c5")
EXEC SQL OPEN c6
CHECKERR ("init_list: OPEN CURSOR c6\n")
do {
C.4 optimierer.sqc
EXEC SQL FETCH c6 INTO :db_fid, :cl_fid
if (SQLCODE != 0) break
l2=*f
*f=malloc(sizeof(list2_t))
(*f)->node1=db_fid
(*f)->node2=cl_fid
(*f)->comp_time=get_ctime(db_fid,cl_fid,*a)
(*f)->next=l2
} while ( 1 )
EXEC SQL CLOSE c6
CHECKERR ("init_list: CLOSE CURSOR c6")
EXEC SQL OPEN c7
CHECKERR ("init_list: OPEN CURSOR c7\n")
do {
EXEC SQL FETCH c7 INTO :db_fid, :cl_fid, :r_zeit
if (SQLCODE != 0) break
for(l2=*fl2!=NULL && r_zeit>0l2=l2->next)
if(l2->node1==db_fid && l2->node2==cl_fid)
l2->comp_time=r_zeit
} while ( 1 )
EXEC SQL CLOSE c7
CHECKERR ("init_list: CLOSE CURSOR c7")
return maxattr
}
/* Aproximation der Rechenzeit */
double get_ctime(long s, long d, list_t *l)
{
double result=0.0
while(l!=NULL)
{
if(l->node == s)
result+=l->ct_src
if(l->node == d)
result+=l->ct_dest
l=l->next
}
C-26
C.4 optimierer.sqc
return result/2
}
/* UNION-FIND-Algorithmus */
int fastfind(long *dad, int x, int y, int union1)
{
int i, j, t
for(i=xdadi]>0i=dadi])
for(j=ydadj]>0j=dadj])
while(dadx]>0)
{
t=x
x=dadx]
dadt]=i
}
while(dady]>0)
{
t=y
y=dady]
dadt]=j
}
if(union1&&(i!=j))
if(dadj]<dadi])
{
dadj]=dadj]+dadi]-1
dadi]=j
}
else
{
dadi]=dadi]+dadj]-1
dadj]=i
}
return i==j
}
/* Aufteilung in Zusammenhangskomponenten */
void split2compo(list_t **a, list2_t **f,
list_list_t **c, long size)
{
list_t *l, *runner1, *runner3, *last, *runner
list2_t *runner2
C-27
C.4 optimierer.sqc
list_list_t *ll, *runnerl
long *dad, i
ll=NULL
dad=calloc(size+1,sizeof(long))
memset(dad,0,(size+1)*sizeof(long))
for(runner2=*frunner2!=NULLrunner2=runner2->next)
fastfind(dad,runner2->node1,runner2->node2,1)
ll=*c
*c=malloc(sizeof(list_list_t))
(*c)->next=ll
(*c)->first=malloc(sizeof(list_t))
(*c)->first->node=1
(*c)->first->is_phys=0
(*c)->first->next=NULL
for(runner=*arunner->node!=1runner=runner->next)
(*c)->first->size=runner->size
(*c)->first->access=runner->access
for(i=2i<=sizei++)
{
for(ll=*cll!=NULLll=ll->next)
if(fastfind(dad,ll->first->node,i,0))
{
l=malloc(sizeof(list_t))
l->node=i
l->is_phys=0
l->next=(*c)->first
for(runner=*arunner->node!=irunner=runner->next)
l->size=runner->size
l->access=runner->access
(*c)->first=l
break
}
if(ll==NULL)
{
ll=*c
*c=malloc(sizeof(list_list_t))
(*c)->next=ll
(*c)->first=malloc(sizeof(list_t))
(*c)->first->node=i
(*c)->first->is_phys=0
C-28
C.4 optimierer.sqc
(*c)->first->next=NULL
for(runner=*arunner->node!=irunner=runner->next)
(*c)->first->size=runner->size
(*c)->first->access=runner->access
}
}
free(dad)
}
/* bin"are Addition auf den Attributen */
int next_base(list_t *a)
{
int old, add=1
while(add && a!=NULL)
{
old=a->is_phys
a->is_phys=add-old
add&=old
a=a->next
}
return add
}
/* Kandidaten f"ur die Basis aus den Funktionen ausw"ahlen*/
int get_fcs(list_t *a, list2_t *fc, list2_t **fcn)
{
list2_t *l2
list_t *runner, *fastrun
int
status=1
long
last
*fcn=NULL
while(fc!=NULL)
{
for(runner=arunner!=NULLrunner=runner->next)
if(runner->node==fc->node1 && runner->is_phys==1)
for(fastrun=afastrun!=NULLfastrun=fastrun->next)
if(fastrun->node==fc->node2 && fastrun->is_phys!=1)
{
l2=*fcn
*fcn=malloc(sizeof(list2_t))
(*fcn)->next=l2
C-29
C.4 optimierer.sqc
C-30
(*fcn)->node1=fc->node1
(*fcn)->node2=fc->node2
(*fcn)->comp_time=fc->comp_time
(*fcn)->is_base=0
fastrun->is_phys=2
}
fc=fc->next
}
for(l2=*fcn,last=0l2!=NULLl2=l2->next)
{
l2->is_base=(l2->node2!=last)
last=l2->node2
}
for(runner=arunner!=NULLrunner=runner->next)
if(!runner->is_phys)
status=0
else
runner->is_phys&=1
return status
}
/* Rotation auf den Gruppen der Funktionen zur Basisbestimmung */
int next_fc_base(list2_t *fc)
{
list2_t *l2
long
last
int
old=1, h
if(fc!=NULL)
{
last=fc->node2
old=0
}
for(l2=fcl2!=NULL && l2->node2==last l2=l2->next)
{
h=l2->is_base
l2->is_base=old
old=h
}
if(l2!=NULL && old)
{
fc->is_base=old
return next_fc_base(l2)
C.4 optimierer.sqc
}
else
return old
}
void free_list2(list2_t **l)
{
list2_t *runner
while(*l!=NULL)
{
runner=(*l)->next
free(*l)
*l=runner
}
}
/* Kostenfunktion: Speicherkosten */
double store_costs(list_t *base)
{
double result
for(result=0.0base!=NULLbase=base->next)
if(base->is_phys)
result+=base->size
return (1 - const_z) * const_cs * result
}
/* Kostenfunktion: Zugriffskosten f"ur A_phys */
double acc_costs_phys(list_t *base)
{
double result
for(result=0.0base!=NULLbase=base->next)
if(base->is_phys)
result+=base->size*base->access
return const_z * const_ca * result
}
/* Kostenfunktion: Zugriffskosten f"ur A_comp */
double acc_costs_comp(list2_t *fc, list_t *base)
C-31
C.4 optimierer.sqc
{
list_t *l
double result, avg_acc
for(result=0.0fc!=NULLfc=fc->next)
if(fc->is_base)
{
for(l=basel->node!=fc->node2l=l->next)
avg_acc=l->access
result+=fc->comp_time*avg_acc
for(l=basel->node!=fc->node1l=l->next)
result+=l->size*avg_acc*const_ca
}
return const_z * result
}
/* Optimale Basis sichern */
void copy_fc(list2_t **d, list2_t *s)
{
list2_t *l2
while(s!=NULL)
{
l2=*d
*d=malloc(sizeof(list2_t))
(*d)->node1=s->node1
(*d)->node2=s->node2
(*d)->comp_time=s->comp_time
(*d)->is_base=s->is_base
(*d)->next=l2
s=s->next
}
}
/* Neu zu speichernde Formate in die Datenbank "ubernehmen */
long add_phys_type(list_t *base)
{
list_t *comp, *phys
long
n_o_new=0
char
command255]
for(comp=basecomp!=NULLcomp=comp->next)
C-32
C.4 optimierer.sqc
C-33
{
if(comp->is_opt==0) continue
cl_fid=comp->node
EXEC SQL SELECT name
INTO
:doctype
FROM
formate
WHERE fid=:cl_fid
for(phys=basephys!=NULLphys=phys->next)
{
if(phys->node==comp->node) continue
db_fid=phys->node
EXEC SQL DECLARE c CURSOR FOR
SELECT DISTINCT name
FROM
artikel
WHERE fid=:db_fid AND
name NOT IN (SELECT name
FROM
artikel
WHERE fid=:cl_fid)
CHECKERR ("add_phys_type: DECLARE CURSOR c\n")
EXEC SQL OPEN c
CHECKERR ("add_phys_type: OPEN CURSOR c\n")
do {
EXEC SQL FETCH c INTO :docname
if (SQLCODE != 0) break
sprintf(command,"insert_copy %s %s",docname,doctype)
printf("%s\n",command)
system(command)
n_o_new++
} while ( 1 )
EXEC SQL CLOSE c
CHECKERR ("init_list: CLOSE CURSOR c1")
}
}
EXEC SQL INSERT INTO log_tabelle
SELECT cl_name, 'dummy', cl_fid, db_fid, AVG(cl_size),
AVG(db_size), AVG(uw_dauer), CURRENT TIMESTAMP
FROM
log_tabelle
WHERE cl_name='converter'
GROUP BY cl_name, cl_fid, db_fid
C.4 optimierer.sqc
C-34
EXEC SQL DELETE FROM log_tabelle
WHERE cl_name='converter' AND artikel<>'dummy'
return n_o_new
}
/* nicht mehr zu speichernde Formate l"oschen */
void del_comp_type(list_t *base)
{
while(base!=NULL)
{
if(base->is_opt==0)
{
cl_fid=base->node
EXEC SQL DELETE FROM artikel
WHERE fid = :cl_fid
printf("FID:%d deleted\n",cl_fid)
}
base=base->next
}
}
/* Kommandozeile auswerten */
void get_args(int argc, char *argv])
{
int ch
int bflg, aflg, errflg
extern char *optarg
extern int optind, optopt
while ((ch = getopt(argc, argv, ":a:s:z:")) != -1)
switch (ch)
{
case 'a': const_ca=atof(optarg)
break
case 's': const_cs=atof(optarg)
break
case 'z': const_z=atof(optarg)
break
case ':':
/* -a, -s or -z without arguments */
fprintf(stderr,"Option -%c requires an argument\n",
optopt)
errflg++
C.5 insert copy.sqc
C-35
break
case '?':
fprintf(stderr,"Unrecognized option: - %c\n",optopt)
errflg++
}
if (errflg)
{
fprintf(stderr,"USAGE: %s -a value] -s value] -z value]\n",
argv0])
exit (2)
}
}
C.5 insert copy.sqc
/******************************************************************
*
*
* insert_copy.sqc
*
*
*
* USAGE: insert_copy documentname documenttyp
*
*
*
* insert_copy liest das Dokument documentname in einem Format,
*
* das nach documenttyp umgewandelt werden kann, wandelt es um und *
* speichert es wieder in der Datenbank.
*
*
*
******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/timers.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sql.h>
#include <fcntl.h>
#include "stat.h"
#include "util.h"
EXEC SQL INCLUDE SQLCA
C.5 insert copy.sqc
#define
CHECKERR(CE_STR)
#define
#define
#define
#define
MAXPIPES 15
MAXOPTS 15
EIN
1
AUS
0
C-36
if (check_error (CE_STR, &sqlca) != 0) \
return 1
struct fork_cmd
{
char *cmd
char *opt
struct fork_cmd *next
}
typedef struct fork_cmd s_fork_cmd
int pipesMAXPIPES]2]
s_fork_cmd* split_cmd(char *s)
char * ausgabe(int nos)
void eingabe(char *fname, int nos)
void split_opts(char **spl, char *cmd, char *opts)
char * umwandlung(char *fname, char *cmd)
int main(int argc, char *argv])
{
char errtxt200], *fname
clock_t start_z
struct stat filestat
FILE *fp1
EXEC SQL BEGIN DECLARE SECTION
SQL TYPE IS BLOB_FILE tmp_file
short lobind
long cl_fid
long db_fid
long db_len
long cl_len
double r_zeit
char docname21]
char doctype11]
char uw_fkt201]
C.5 insert copy.sqc
C-37
char cl_host101]
EXEC SQL END DECLARE SECTION
/* Environment-Variable beim Aufruf "uber CGI nicht bekannt! */
putenv("DB2INSTANCE=db2ex")
fp1 = freopen("insert.err", "a", stderr)
if(argc!=3)
{
fprintf(stderr,"Number of args should be 3 NOT %d\n",argc)
exit(1)
}
strncpy(docname,argv1],20)
docname20]=0
strncpy(doctype,argv2],10)
doctype10]=0
EXEC SQL CONNECT TO peter_db USER db2ex USING daba2
sprintf(errtxt,"CONNECT TO PETER_DB\n%s\n",
getenv("DB2INSTANCE"))
CHECKERR (errtxt)
strcpy(tmp_file.name,tmpnam(NULL))
tmp_file.name_length = strlen(tmp_file.name)
tmp_file.file_options = SQL_FILE_CREATE
EXEC SQL SELECT a.inhalt
INTO :tmp_file :lobind
FROM artikel a,formate f
WHERE a.fid=f.fid AND
a.name=:docname AND
f.name=:doctype
if (SQLCODE != 0 || lobind < 0)
{
EXEC SQL DECLARE c2 CURSOR FOR
SELECT a.inhalt, u.funktion, a.fid, f.fid
FROM artikel a, umwandlung u, formate f
WHERE a.fid=u.quellformat AND u.zielformat=f.fid AND
a.name=:docname AND f.name=:doctype AND
u.praeferenz=(SELECT max(u1.praeferenz)
C.5 insert copy.sqc
C-38
FROM umwandlung u1, artikel a1
WHERE a1.name=a.name AND
u1.zielformat=f.fid AND
u1.quellformat=a1.fid)
EXEC SQL OPEN c2
EXEC SQL FETCH c2 INTO :tmp_file :lobind, :uw_fkt,
:db_fid, :cl_fid
if (SQLCODE != 0)
{
fprintf(stderr,"Can't create %s.%s\n",docname,doctype)
remove(tmp_file.name)
}
else
{
stat(tmp_file.name,&filestat)
db_len=filestat.st_size
start_z=clock()
fname=umwandlung(tmp_file.name,uw_fkt)
r_zeit=(clock()-start_z)/(double)CLOCKS_PER_SEC
stat(fname,&filestat)
cl_len=filestat.st_size
strcpy(tmp_file.name,fname)
tmp_file.name_length = strlen(tmp_file.name)
tmp_file.file_options = SQL_FILE_READ
EXEC SQL INSERT INTO artikel
VALUES(:docname,:cl_fid,:tmp_file)
CHECKERR ("INSERT")
strcpy(cl_host,"converter")
EXEC SQL INSERT INTO log_tabelle
VALUES (:cl_host, :docname, :cl_fid, :db_fid,
:cl_len, :db_len, :r_zeit, CURRENT TIMESTAMP)
CHECKERR ("log")
EXEC SQL COMMIT
fprintf(stderr,"Added %s.%s to database\n",docname,doctype)
remove(tmp_file.name)
C.5 insert copy.sqc
C-39
}
}
else
{
fprintf(stderr,"Nothing to do!\n %s.%s is in the database\n",
docname,doctype)
remove(tmp_file.name)
}
EXEC SQL CONNECT RESET
CHECKERR ("CONNECT RESET\n")
return 0
}
/* Formatumwandlung mittels Kindprozessen */
char * umwandlung(char *fname, char *cmd)
{
int nos=0, status
char *optvMAXOPTS]
s_fork_cmd *f1, *f
f1=f=split_cmd(cmd)
while(f!=NULL)
{
f=f->next
pipe(pipes++nos])
}
nos=0
pipe(pipesnos])
f=f1
eingabe(fname,nos)
while(f!=NULL)
{
nos++
split_opts(optv,f->cmd,f->opt)
switch(fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork "
"%s %s\n",f->cmd,f->opt)
exit(1)
case 0: dup2(pipesnos-1]AUS],0)
C.5 insert copy.sqc
close(pipesnos-1]AUS])
close(pipesnos-1]EIN])
dup2(pipesnos]EIN],1)
close(pipesnos]EIN])
fprintf(stderr,"Aufruf von execvp '%s' '%s'\n",
f->cmd,f->opt)
close(pipesnos]AUS])
execvp(f->cmd,optv)
fprintf(stderr,"ACHTUNG FEHLER kein Aufruf von "
"%s\n",f->cmd)
exit(-1)
default: close(pipesnos-1]AUS])
close(pipesnos]EIN])
}
f=f->next
}
fname=ausgabe(nos)
f=f1
wait(&status)
wait(&status)
while(f!=NULL)
{
f=f->next
wait(&status)
}
return fname
}
/* Schreiben des BLOBs in die Eingabepipe des */
/* Konvertierungsprozesses
*/
void eingabe(char *fname, int nos)
{
char buf4096]
int len, fd
switch(fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork %d\n",
C-40
C.5 insert copy.sqc
nos)
exit(1)
case 0: fd=open(fname,O_RDONLY)
while(len=read(fd,buf,4096))
write(pipes0]EIN],buf,len)
close(fd)
remove(fname)
exit(0)
default: close(pipes0]EIN])
}
}
/* Lesenen des umgewandelten BLOBs aus der Ausgabepipe des */
/* Konvertierungsprozesses
*/
char * ausgabe(int nos)
{
char buffer4096], *fname
int nob, fd
fname=strdup(tmpnam(NULL))
switch(fork())
{
case -1: fprintf(stderr,"Fehler beim Aufruf von fork %d\n",
nos)
exit(1)
case 0: fd=creat(fname,0666)
while(nob=read(pipesnos]AUS],buffer,4096))
write(fd,buffer,nob)
close(fd)
exit(0)
default: close(pipesnos]AUS])
}
return fname
}
/* Aufspalten des Kommandostrings in Befehle und Komponenten */
s_fork_cmd* split_cmd(char *s)
{
char *del, *opt
s_fork_cmd *f_base, *f_runner
f_base=NULL
C-41
C.5 insert copy.sqc
while(*s && isspace(*s)) s++
while(*s)
{
if(f_base==NULL)
f_runner=f_base=malloc(sizeof(s_fork_cmd))
else
{
f_runner->next=malloc(sizeof(s_fork_cmd))
f_runner=f_runner->next
}
f_runner->next=NULL
del=opt=s
while(*del && *del !='|') del++
while(*opt && !isspace(*opt) && *opt!='|') opt++
f_runner->cmd=malloc(opt-s+1)
strncpy(f_runner->cmd,s,opt-s)
f_runner->cmdopt-s]=0
while(*opt && isspace(*opt)) opt++
if (del-opt)
{
f_runner->opt=malloc(del-opt+1)
strncpy(f_runner->opt,opt,del-opt)
f_runner->optdel-opt]=0
}
else
f_runner->opt=""
s=del
while(*s && (isspace(*s)||*s=='|')) s++
}
return f_base
}
/* Aufspalten der Optionen */
void split_opts(char **spl, char *cmd, char *opts)
{
*spl++=cmd
while(*opts)
{
*spl++=opts
C-42
C.6 mklter.c
C-43
while(*opts && !isspace(*opts)) opts++
if(*opts) *opts++=0
while(*opts && isspace(*opts)) opts++
}
*spl=NULL
}
C.6 mklter.c
/***************************************************************
*
*
* source: mkfilter.c
*
*
*
* USAGE: mkfilter command args1 - args2
*
*
*
* mkfilter f"uhrt den Befehl command als Filter aus.
*
* mkfilter entspricht vom Verhalten einer Folge von Befehlen
*
*
*
*
cat >tmp_file1
*
*
command args1 tmp_file1 args2 tmp_file2
*
*
cat tmp_file2
*
*
rm tmp_file1 tmp_file2
*
*
*
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include "stat.h"
void main(int argc, char *argv])
{
char buffer8192], *out_file_name, *in_file_name
int fd, i, len, flag=0
/* Namen f"ur tempor"are Dateien generieren */
in_file_name=strdup(tmpnam(NULL))
out_file_name=strdup(tmpnam(NULL))
C.6 mklter.c
/* stdin in Datei umleiten */
fd=creat(in_file_name,0666)
while(len=read(fileno(stdin),buffer,8192))
write(fd,buffer,len)
close(fd)
/* Kommandostring erzeugen */
buffer0]=0
for(i=1i<argci++)
{
if(!strcmp(argvi],"-"))
{
strcat(buffer,in_file_name)
flag=1
}
else
strcat(buffer,argvi])
strcat(buffer," ")
}
if(!flag)
{
strcat(buffer,in_file_name)
strcat(buffer," ")
}
strcat(buffer,out_file_name)
fprintf(stderr,"EXECUTE: %s\n",buffer)
/* Kommando ausf"uhren */
i=system(buffer)
/* Ausgabedatei nach stdout kopieren */
fd=open(out_file_name,O_RDONLY)
while(len=read(fd,buffer,8192))
write(fileno(stdout),buffer,len)
close(fd)
/* tempor"are Dateien l"oschen */
remove(in_file_name)
remove(out_file_name)
}
C-44
C.7 Eingabeformular (HTML-Code)
C-45
C.7 Eingabeformular (HTML-Code gekurzt)
Content-type: text/html
<HTML>
<HEAD><TITLE>Eingabeformular</TITLE></HEAD>
<BODY>
<H1>Eingabeformular</H1>
<HR>
<FORM ACTION="/cgi-bin/db_interface" METHOD="GET">
Name des Artikels: <BR>
<SELECT NAME="name" SIZE=8>
<OPTION SELECTED>au
<OPTION>bild1_14
<OPTION>bild1_15
<OPTION>bild1_16
<OPTION>bild1_17
<OPTION>bild1_18
<OPTION>bild1_19
<OPTION>bild1_20
<OPTION>bild1_21
<OPTION>bild1_22
<OPTION>bild1_23
<OPTION>bild1_24
.
.
.
<OPTION>s_eastwood
<OPTION>s_houdini
<OPTION>s_kabwin95
<OPTION>s_schaf
<OPTION>s_zucht
<OPTION>scherneck_karte1
<OPTION>scherneck_karte2
<OPTION>scherneck_schloss
</SELECT>
<BR>
gew&uumlnschtes Format: <BR>
<SELECT NAME="format" SIZE=8>
<OPTION SELECTED>au
<OPTION>gif
<OPTION>jpeg
C.7 Eingabeformular (HTML-Code)
<OPTION>pgm
<OPTION>png
<OPTION>ppm
<OPTION>ps
<OPTION>tiff
<OPTION>wav
</SELECT>
<BR>
<P>
<INPUT TYPE="submit" VALUE="Abschicken">
</FORM>
<HR>
</BODY>
</HTML>
C-46
Herunterladen