Metadaten, oder: Warum man Volt und milliVolt nicht einfach addieren kann.

Tobias Nebel Dieser Artikel wurde von geschrieben. Tobias Nebel hat sein Studium der Telekommunikationsinformatik an der Hochschule für Telekommunikation in Leipzig absolviert. Er arbeitet als Softwareentwickler bei der Firma ubigrate mit dem Schwerpunkt Geräteintegration.

In vielen Bereichen der Softwareentwicklung kommt ein Entwickler früher oder später an den Punkt, an dem er bei Berechnungen zusätzliche Informationen zu Daten berücksichtigen muss. Diese zusätzlichen Informationen, auch Metadaten genannt, können das Ergebnis eines Verarbeitungsschrittes gravierend verfälschen: Wenn beispielsweise ein Messgerät sowohl Volt als auch Milli-Volt liefern kann, so muss diese Einheit bei Operationen zwischen zweier solcher Werte berücksichtigt werden.

Ein Mechanismus, der solch zusätzliche Informationen in einem System berücksichtigt, wird auch Metadatenverwaltung genannt. Bislang existieren verschiedene – mehr oder minder einfache – Möglichkeiten eine solche Verwaltung von Metadaten durchzuführen. Alle diese Ansätze haben jedoch entscheidende Schwachpunkte, die eine allgemeine Anwendbarkeit verbieten.

Der einfachste Weg zur Berücksichtigung von Metadaten ist die Umrechnung aller Werte in eine vorher festgelegte Einheit, wie z.B. mV. Damit kann dann an allen Punkten im System von der gleichen Einheit ausgegangen werden. Hier existiert jedoch das Problem, dass nur eine einzige Einheit verwendbar ist und keine zusätzlichen Informationen, wie etwa ein Zeitstempel oder diverse Messumstände, dem Wert zugeordnet werden können.

Ein anderer Weg ist die Kapselung von Werten und zugehörigen Metadaten in Werteobjekten. Das hat den Vorteil, dass beliebige Arten von Metadaten in ein solches Objekt gepackt werden können. Ein Problem dieses Ansatzes besteht darin, dass (zumindest in Java) Standard-Operatoren, wie beispielsweise +, -,* oder / nichtmehr verwendet werden können. Es müssen vielmehr spezielle Objekt-Methoden implementiert werden, die genau solche einfachen Operationen kapseln. Eine Berücksichtigung der zugehörigen Metadaten würde in diesem Falle in diesen Methoden stattfinden und das Ergebnis gegebenenfalls korrigieren.
MetaInteger a = new MetaInteger(123, metaInformation_a); MetaInteger b = new MetaInteger(456, metaInformation_b); MetaInteger c = a.add(b);

Der große Nachteil bei desem Ansatz ist die Notwendigkeit der Umstrukturierung des kompletten Codes. Alle vorhandene Operationen auf relevanten Daten müssten durch entsprechende Meta-Operationen ersetzt werden. Dies ist je nach Umfang eines existierenden Softwaresystems unzumutbar.

Beide Ansätze haben neben den entscheidenden Schwachstellen, auch jeweils große Vorzüge. Optimal wäre hier doch eine Kombination aus den jeweiligen Vorteilen: Der Einfachheit der Verwendung von Standard-Operatoren mit der Erweiterbarkeit von Werteobjekten. Eine solche Lösung wäre dann fast vollständig transparent für den Programmierer nutzbar.

Na, Interesse geweckt?…hier geht’s weiter:

Doch wie kann man mit Java soetwas erreichen? Das Zauberwort heißt Bytecode-Engineering. Prinzipielles Ziel ist, dass der Programmierer weiterhin mit primitiven Datentypen wie z.B. int, float oder double arbeiten kann. Die Verwaltung der zu den primitiven Daten gehörigen Metadaten läuft dann im Hintergrund ab. Die Logik zur Verwaltung wird erst zur Ladezeit einer Klasse mittels eines geeigneten Mechanismus’ (siehe <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/instrument/package-summary.html">java.lang.instrument</a>) eingewoben. Eine Einführung in diese Thematik ist hier zu finden. Um nun die Konsistenz zwischen Daten und Metadaten zu wahren muss einfach jeder Bytecode-Befehl analysiert und gegebenenfalls um Funktionalität ergänzt, ersetzt oder auf Meta-Ebene nachgebildet werden.

Bei einer solchen Analyse liegen allerdings so einige Stolpersteine im Weg, da viele Eigenheiten der Java Virtual Machine berücksichtigt werden müssen. So spielen die Unterschiede zwischen Variablenarten eine große Rolle (statische und nichtstatische Variablen einer Klasse, lokale Variablen eines JVM-Frames). Ebenso existieren ja entsprechend dem Operanden-Stack eines aktuellen JVM-Frames auch zugehörige Informationen zu genau diesen Stack-Werten.

Ein JVM-Frame

Ein JVM-Frame

Grundsätzlich ist es also notwendig, Vorgänge der Bytecode-Ebene auch auf der Metadaten-Ebene nachzubilden. Dazu gehören insbesondere:

  • Stack-Operationen: Transfer von Werten zwischen Local Variable Table, statischen Feldern, nicht-statischen Feldern auf und vom Operanden-Stack
  • Zugriffe auf Variablen allgemein
  • (arithmetische) Operationen auf und zwischen Variablen erkennen und auf externe Methoden umleiten
  • Aufrufe von Methoden

Zusätzlich müssen externe Methode implementiert werden, die Funktionen der Bytecode-Ebene übernehmen und um die Berücksichtigung der registrierten Metadaten ausführen. So wird beispielsweise der Bytecode-Befehl <a href="http://javaseiten.de/jvmisi.html#iadd">IADD</a> auf eine entsprechende Methode umgeleitet, die ebenfalls zwei Stack-Werte addiert, jedoch zusätzlich noch Metadaten dieser Stack-Werte berücksichtigt.

Eine im Rahmen meiner Diplomarbeit entwickelte Softwarekomponente zeigt, dass der Einsatz einer solchen Lösung zwar Geschwindigkeitseinbußen nach sich zieht, aber durchaus praktikabel ist.

Post to Twitter

Schlagworte:

1 Kommentar bisher »

  1. Martin Knechtel sagt

    am 6. August 2009 @ 15:00

Komentar RSS · TrackBack URI

Hinterlasse einen Kommentar

Name:

eMail:

Website:

Kommentar:

 

google

google

asus