JDBC-Treiber für mobile Endgeräte

Mobile Endgeräte können nicht auf JDBC-Datenbanken zugreifen, da bisher keine JDBC-Schnittstelle in der J2ME vorgesehen ist. Künstliche Implementierungen kopieren daher die Schnittstellen von Sun. Für DB2 gibt es allerdings JdbcMe for DB2. Der Treiber besteht aus der kleinsten sinnvollsten Untermenge von JDBC 2.0. Der Treiber ist etwa 32 k groß.

Für die Connected Device Configuration (CDC) gibt es ein optionales Paket, welches in der JSR-169 die JDBC Optional Package for CDC/Foundation Profile API beschreibt.

Einen allgemeineren Weg geht ReqwirelessDB. Es arbeitet mit beliebigen Datenbanken zusammen, denn die Bibliothek implementiert die JDBC-Schnittstelle (die als Kopie dem Paket beiliegen) und delegiert die Anfragen zu einem Servlet. Jede Anfrage ist also entfernt, so dass nicht wirklich das mobile Endgerät die Datenverbindung zum Server aufnimmt. Der Server verbindet zum Datenbank-Server, holt von dort die Daten und schickt diese dem mobilen Endgerät zurück. Die Daten sind in XML (sic!), werden über kXML geparst und dann als ResultSet angeboten.

Das Fazit ist: JDBC ist bisher für mobile Endgeräte keine Lösung.

Labels:

Das oberste Stack-Element duplizieren

Die Klasse Stack besitzt zwar die Basisfunktionalität, die ein Stapel besitzen sollte, aber auch nicht mehr. Hin und wieder wünschen wir uns aber eine Funktion, die das oberste Stack-Element dupliziert, kurz dup().

Bei der Implementierung treten allerdings zwei Fragen auf, mit denen zwei völlig unterschiedliche Lösungsansätze verbunden sind. Da die Klasse Stack wie die anderen Datenstrukturen auf Objekte ausgelegt ist, müssen wir uns darüber Klarheit verschaffen, wie das obere Objekt dupliziert werden soll. Soll eine Kopie der Objekt-Referenz neu auf den Stapel gelegt werden oder etwa das gesamte Objekt geklont werden?

Die einfache Lösung

Die einfachste Lösung besteht darin, das oberste Objekt einfach mittels der schon vorhandenen Stack-Methoden push() und peek() draufzulegen. Nehmen wir an, wir haben eine Unterklasse DupStack, dann sieht die erste Variante zum Clonen so aus:

void dup() /* throws EmptyStackException */
{
push( peek() );
}

peek() gibt aber lediglich eine Referenz auf das Objekt zurück. Und das anschließende push() speichert diese Referenz dann auf dem Stapel. Nehmen wir an, wir haben zwei StringBuffer-Objekte auf dem Stapel. Wenn wir nun dup() aufrufen und den String ändern, der oben auf dem Stapel liegt, so ändern wir automatisch das zweite Element gleich mit. Dies ist aber nicht unbedingt beabsichtigt, und wir müssen uns Gedanken über eine alternative Lösung machen. Wir sehen, dass dup() in der Klasse Stack fehlt, weil seine Implementierung davon abhängt, ob eine Referenz- oder eine Wertsemantik für Kellerelemente gewünscht ist.

Die kompliziertere Lösung mit Klonen

Um das oberste Stack-Element zu kopieren, bietet sich die clone()-Methode von Object an. All die Objekte, die sich klonen lassen, und das sind längst nicht alle, implementieren das Interface Cloneable. Nun ließe sich einfach folgern: Wenn das zu duplizierende Objekt ein Exemplar von Cloneable ist, dann können wir einfach die clone()-Methoden aufrufen und das zurückgegebene Objekt mittels push() auf den Stapel bringen.

void dup2() throws CloneNotSupportedException
{
try
{
Object top = peek();

if ( top instanceof Cloneable )
push( top.clone() );

}
catch ( EmptyStackException e ) { }
}

Beziehungsweise

void dup3() throws CloneNotSupportedException /*, EmptyStackException */
{
push( peek().clone() );
}

Dies funktioniert für die meisten Objekte, allerdings nicht für Objekte der Klasse Object. Denn clone() der Klasse Object ist protected - wir dürfen also von außen nicht dran, nur eine Unterklasse und die Klasse selbst. Hier haben wir also zwei Probleme.

  • Leider lässt sich nur mit Aufwand überprüfen, ob das Objekt auf dem Stapel auch wirklich ein pures Object ist, denn alle Objekte sind instanceof Object. Glücklicherweise gibt es kaum eine Anwendung, wo reine Object-Elemente gesichert werden müssen.
  • Was machen wir mit Objekten, die nicht klonbar sind? Leider gibt es für diese Frage keine direkte Antwort. Eine universelle Stack-Klasse mit einer uneingeschränkten dup()-Methode gibt es nicht. Wir müssen als Stack-Benutzer festlegen, dass das oberste Element Clonable ist, um zumindest eine eigene Implementierung nutzen zu können. Oder wir bleiben dabei, bei nicht klonbaren Objekten doch nur die Referenz zu duplizieren. Das wäre zumindest für eineindeutige Objekte mit Wertsemantik die ideale Lösung.

Insel: Einen Webserver mit der com.sun.net.httpserver.HttpServer Klasse aus Java 6

Java bietet seit der Version 6 eine API, um Web-Services anzusprechen und auch neue Web-Services zu definieren und am eigenen Rechner anzumelden. Doch um Web-Services anbieten, und entfernten Clients Zugriff gewähren zu können, ist immer ein Web-Server nötig. Aus diesem Grund hat Sun einen einfachren HTTP-Server integriert, der sich auch eingeständig nutzen lässt; die Klassen sind zwar mehr oder weniger privat im Paket com.sun.net.httpserver deklariert, dennoch wollen wir ein Beispiel wagen.

Im Mittelpunkt steht die Klasse HttpServer – eine abstrakte Oberklasse, von der die Fabrikmethoden create() und create(InetSocketAddress, int) ein konkretes Exemplar liefern. Im nächsten Schritt verbindet die Methode createContext() einen Pfad (wie etwa /webapp1/) mit einem bestimmten HttpHandler, der die Anfrage an den Pfad übernimmt.

public class HttpServerDemo
{
public static void main( String[] args ) throws IOException
{
HttpServer server = HttpServer.create( new InetSocketAddress( 80 ), 0 );
server.createContext( "/", new DateHandler() );
server.start();
}
}


Jeder Handler implementiert die Schnittstelle HttpHandler mit der Funktion handle(HttpExchange), die über den Parameter HttpExchange Zugriff auf Header, Anfragekörper und Ergebnis ermöglicht.

Ein einfacher HttpHandler, der Anfragen mit einer HTML-Seite beantwortet, die Datum und den Anfragepfad enthält, kann so aussehen:

class DateHandler implements HttpHandler
{
public void handle( HttpExchange httpExchange ) throws IOException
{
httpExchange.getResponseHeaders().add( "Content-type", "text/html" );
String response = "<b>" + new Date() + "</b> for " + httpExchange.getRequestURI();
httpExchange.sendResponseHeaders( 200, response.length() );

OutputStream os = httpExchange.getResponseBody();
os.write( response.getBytes() );
os.close();
}
}


Die Methode getResponseHeaders() liefert ein Headers-Objekt (eine Map<String,List<String>>), zum Setzen der Response-Header. Das Beispiel setzt den Content-type auf text/html. Die Methode sendResponseHeaders schließt die Header-Angaben mit einem Statuscode (Response-Code) und der Content-Länge ab. getRequestURI() gibt den Pfad und ermöglicht uns das Zerlegen der Elemente nach Verzeichnis, Datei, Dateiendung, Ankern und Parametern.

HttpExchange liefert mit getResponseBody() einen OutputStream, dass das Ergebnisdokument formuliert. Die Methode getRequestBody() liefert einen InputStream für das, was der Client sendet. Das Beispiel schreiben die Bytes vom String in den OutputStream und schließt ihn anschließend.

Nach dem Start des Server können wir im Webbrowser URLs wie http://localhost/, oder http://localhost/webapp/bla eingeben und wir erhalten das Datum und die Pfadangabe.

Insel: Fließkommazahlen im Hashcode

Anhängig von den Datentypen sehen die Berechnungen immer etwas unterschiedlich aus. Während Ganzzahlen direkt in einen Ganzzahlausdruck für den Hashcode eingebracht werden können, ist im Fall von double die Konvertierungsfunktion Double.doubleToLongBits() beziehungsweise Float.FloatToIntBits() im Einsatz.

Die Datentypen double und float habe eine weitere Spezialität, da NaN und das Vorzeichen der 0 zu beachten sind, wie das Kapitel 5 näher ausführt. Zusammenfassend gesagt: Sind x = +0.0 und y = -0.0, gilt x == y, aber Double.doubleToLongBits(x) != Double.doubleToLongBits(y). Sind x = y = Double.NaN gilt x != y aber Double.doubleToLongBits(x) == Double.doubleToLongBits(y). Wollen wir die beiden Nullen nicht unterschiedlichen behandeln, sondern als gleich werten, ist ein übliches Idiom:

x == 0.0 ? 0L : Double.doubleToLongBits( x )

Es liefert Double.doubleToLongBits(0.0) die Rückgabe 0, aber Double.doubleToLongBits(-0.0) würde -9223372036854775808 geben.