Zeichen setzen: Java unter Linux

(November 2007)
Inhalt dieses Artikels:
Plattformunabhängigkeit | Das Zeilenende | Keine Umlaute | Groß-/Kleinschreibung | Dateinamen | Build-Prozess | File-Handling | Zusammenfassung
Ihre Meinung zum Artikel
sehr gut
1
2
3
4
5
6
schlecht
Autor: Oliver Böhm, Software-Entwickler und Buchautor

Eigentlich sollte man meinen, dass es egal ist, auf welcher Java-Plattform eine Anwendung entwickelt wird, warb doch Sun in der Vergangenheit mit dem Spruch "write once – run anywhere" für ihr Java-Baby. Inzwischen ist Java den Kinderschuhen entwachsen und die Praxis zeigt, dass "write once – test anywhere" trotz der 100 %-Pure-Java-Initiative von Sun immer noch seine Berechtigung hat. Auch unter Windows ist es gar nicht so schwierig, für Linux Java-Anwendungen zu entwickeln. Was dabei berücksichtigt werden sollte, beschreibt dieser erstmals im JavaSPEKTRUM 05/2007 veröffentlichte Fachbeitrag.
Vor allem im Web-Bereich laufen die meisten Java-Anwendungen auf Linux- oder Unix-Servern. Wenn also schon klar ist, dass Ihre Anwendung unter Linux oder Unix zum Einsatz kommen wird, so kann man viele Probleme vermeiden, wenn auch die Entwickler-Plattform der Ziel-Plattform entspricht. Es gibt viele gute Gründe, die dafür sprechen (O. Böhm, Java Software Engineering unter Linux , 2002). Aber nachdem der Großteil der Entwickler sich nur mit der Windows-Plattform auskennt und damit (mehr oder weniger) zufrieden ist, ist ein angeordneter Plattformwechsel eher kontraproduktiv. Hier gilt es, die Fallstricke zu kennen, die beim Einsatz von Java-Anwendungen unter Linux oder Unix (oder auch MacOS-X) auftreten können. Der Informatiker und Buchautor Oliver Böhm hat solche Stolpersteine in der Fachzeitschrift JavaSPEKTRUM (Ausgabe 5/2007) zusammengefasst.
Plattformunabhängigkeit
 
Viele Fallstricke lassen sich schon im Vorfeld der Entwicklung aufdecken, wenn man auf verschiedenen Plattformen gleichzeitig entwickelt. Haben Sie die Möglichkeit, für Ihr Entwicklerteam auch noch einen Linux- oder Mac-Kollegen zu gewinnen, der unter dieser Plattform entwickelt, nutzen Sie diese Chance. Viele Probleme, die sonst während des Betriebs auftreten können, werden damit vermieden.

Voraussetzung dafür ist, dass die benötigte Toolkette ebenfalls plattformunabhängig ist (javatux externer Link). Während dies für die Entwicklungsumgebung meistens der Fall ist (Eclipse, NetBeans, JBuilder, ...), gilt dies nicht immer für alle Plug-Ins. Bei Einsatz von MDA (Modell Driven Architecture) muss man aufpassen, dass nicht nur das Modellierungstool auf verschiedenen Plattformen läuft, sondern auch der verwendete Code-Generator. CVS und Subversion zur Versionierung sind meistens bereits in die Java-Entwicklungsumgebung integriert bzw. als Plug-In vorhanden und auf allen gängigen Plattformen lauffähig. Bei Microsoft SourceSafe sind Sie auf die Windows-Schiene fixiert, so dass Sie hier über ein alternatives Versionierungssystem nachdenken sollten.
Das Zeilenende
 
Ein leidiges Thema für die Versionierung ist das Zeilenende. Während die Unix-Familie mit einem Zeichen (Newline, '\n') auskommt, orientiert sich die DOS- und Windowswelt traditionell am guten alten Fernschreiber, der zwei Befehle benötigte, um eine neue Zeile zu beginnen (Zeilenvorschub + Wagenrücklauf bzw. Newline + Carriage Return, "\n\r").

Fast alle Entwicklungsumgebungen kommen mit beiden Varianten zurecht. Schwierigkeiten ergeben sich eher aus der Ablage auf der Platte – bereits durch die unterschiedlichen Zeilenenden haben Sie zwei unterschiedliche Versionen, auch wenn Sie ansonsten keine Änderung vorgenommen haben. Dies erschwert Vergleiche zwischen unterschiedlichen Versionen. Auch nachgelagerte Tools oder Skripte, die z. B. bestimmte Metriken oder Reports generieren, können plötzlich aus dem Tritt kommen. Bei den meisten Entwicklungsumgebungen können Sie das Zeilenende einstellen. Setzen Sie es auf Newline (‘\n‘). Damit gibt es erfahrungsgemäß die wenigsten Probleme. Vor allem dann, wenn der CVS- oder Subversion-Server auf einem Linux- oder Unix-Rechner liegt, was durchaus zu empfehlen ist.
Keine Umlaute
 
Auch wenn sich hierzulande UTF-8 für die Zeichencodierung eingebürgert hat, ist im europäischen Raum ISO-8859-1 noch weit verbreitet. Glücklicherweise kann man in Eclipse und anderen Java-Entwicklungsumgebungen das bevorzugte Encoding einstellen. Trotzdem sind Sie nicht davor geschützt, dass ein anderer Entwickler ein anderes Encoding verwendet. Wenn Sie Glück haben, merkt er es, weil die Umlaute anders aussehen. Wenn Sie Pech haben, ändert er einige Stellen und checkt den Code wieder ein. Und schon haben Sie seltsame Zeichen in Ihrer Anwendung, die sich ungesund auf das Betriebsklima innerhalb der Mannschaft auswirken. Deswegen: Hände weg von Umlauten!

Während sich für HTML-Dateien die uml-Schreibweise (z. B. "ö "für "ö") anbietet, bringt Java mit native2ascii das nötige Werkzeug mit, um für Umlaute und andere Sonderzeichen die Ersatzdarstellung anzuzeigen:

> native2ascii
Grüß Gott!
Gr\u00fc\u00df Gott!>

Damit könnte eine schwäbische Variante des Hello-World-Klassikers folgendermaßen geschrieben werden:

/**
* Schwaebische Version des hello-world-Klassikers.
*/
public class World {
  public static void main(String[] args) {
     System.out.println("Gr\u00fc\u00df Gott!");
   }
}

Schöner wäre es natürlich, wenn man hier mit ResourceBundles arbeiten würde, um das Programm auch für andere Sprachen offen zu halten, aber nicht immer möchte und kann man diesen Aufwand treiben. Vor allem dann nicht, wenn man von vornherein weiß, dass es keine internationale Version der Anwendung geben wird.
Groß-/Kleinschreibung
 
Während die Unix-oiden Betriebssysteme beim Dateinamen zwischen Groß- und Kleinschreibung unterscheiden, ist dies bei Windows nur manchmal der Fall. So kann es unter Linux die beiden Dateien "Makefile" und "makefile" geben, während Sie unter Windows damit nur eine Datei ansprechen können.

Etwas kritischer wird es, wenn Sie (oder Ihre Entwicklungsumgebung) bei der Vergabe des Dateinamens nicht aufpassen. Der Java-Compiler erwartet für die Definition einer World-Klasse, dass die dazugehörige Datei "World.java" heißt. Haben Sie aus Versehen die Datei "world.java" genannt, beschwert sich der Compiler mit "... should be declared in a file named World.java". Und wenn Sie versuchen, die Datei umzubenennen, beschwert sich Windows, dass es diese Datei bereits gibt (Abhilfe: zweimaliges Umbenennen).

Gehen Sie vorsichtig mit Groß- und Kleinschreibung um. Eine Klasse Helloworld können Sie zwar unter Unix in "HelloWorld" (mit großem "W") umbenennen, aber unter Windows spielt da das Betriebssystem nicht mit.
Dateinamen
 
Neben der unterschiedlichen Behandlung von Groß- und Kleinschreibung treten häufig Probleme mit Dateien und Dateinamen auf, wenn man hier nicht plattformübergreifend denkt. Der Pfad-Trenner, der unter Linux "/" und unter Windows "\" lautet, ist dabei nicht so kritisch. Pfadangaben werden von Java sowohl als "/etc/init.d/" als auch als "\\etc\\init.d\\" akzeptiert (man beachte die doppelten "\\", die die Ersatzdarstellung zu "\" sind). Ärgerlicher sind Laufwerksangaben, wie "c:\\windows", die es unter Unix nicht gibt und die auch nicht auf ein vorhandenes Verzeichnis abgebildet werden können (zumindest nicht ohne Klimmzüge).

Was soll man aber tun, wenn beispielsweise ein Tomcat unter Windows im Verzeichnis "d:\Programme\tomcat" zu finden ist, während es sich unter Linux um das Verzeichnis "/opt/tomcat" handelt? Lagern Sie solche unterschiedlichen Pfadangaben in zwei Propertys- oder andere Konfigurations-Dateien aus – eine für die Windows-Welt und eine für die Linux-Welt. Über die System-Property "os.name" können Sie dann entscheiden, welche von den beiden Property-Dateien Sie laden wollen:

public static Properties loadProperties() {
  String filename = System.getProperty("os.name") + ".properties";
  try {
    return loadProperties(filename);
  } catch (IOException e) {
    log.error("can't load " + filename, e);
    return new Properties();
  }
}


Hier wird einfach der Name des Betriebssystems genommen, um eine Propertys-Datei zu laden. Im Falle von Linux wird damit die Datei "Linux.properties" geladen, unter Sun Solaris muss die Datei "SunOS.properties" und für Windows "Windows XP.properties" heißen. Möchte man diese Propertys-Dateien in ein Versionierungssystem wie CVS, Subversion (oder auch andere) einchecken, kann es u. U. mit dem Leerzeichen in "Windows XP.properties" Probleme geben. Ursache dafür sind meistens Trigger-Skripte, die durch diesen Vorgang angestoßen werden können und als Parameter den Dateinamen enthalten:

check_rules Windows XP.properties


So in etwa könnte der Aufruf eines solchen Skripts lauten. Dummerweise meint das Skript, dass hier zwei Dateinamen "Windows" und "XP.properties" übergeben wurden. Vermeiden Sie also Dateinamen mit Leerzeichen, auch wenn dies unter Unix zulässige Dateinamen sind – die meisten Anwendungen sind nicht darauf vorbereitet.

Um im obigen Beispiel aus diesem Dilemma herauszukommen, können Sie die Leerzeichen durch "_" ersetzen oder eine "default.properties" mit Voreinstellungen verwenden, die dann zum Zuge kommt, wenn eine Betriebssystem-spezifische Datei nicht vorhanden ist. Wenn wir schon bei Sonderzeichen sind: Vermeiden Sie Umlaute auch in Dateinamen, die können auf anderen Systemen manchmal hässlich aussehen. Am besten Sie verwenden für Dateinamen annähernd die gleichen Regeln wie für Java-Variablen: nur Buchstaben, Unterstrich ("_"), Zahlen und noch den Punkt.
Build-Prozess
 
Auch für den Build-Prozess können Sie Betriebssystem-spezifische Propertys-Dateien einsetzen:

<!--    read OS and other dependant properties    -->
<property file="${lib.dir}/ant/${os.name}.properties"/>
<property file="${lib.dir}/ant/default.properties"/>

Dies ist der Ausschnitt einer plattformunabhängigen "build.xml", die von Ant externer Link zum Bau der Anwendung verwendet wird: Unter Linux wird damit zuerst "Linux.properties" eingelesen und anschließend werden die Propertys, die dort nicht definiert sind, aus "default.properties" übernommen.
File-Handling
 
Ärgerlich ist es, wenn das Laden einer Datei mit folgender Fehlermeldung fehlschlägt:

21:12:15,061 ERROR hello.World - can't load Linux.properties
java.io.FileNotFoundException: Linux.properties (
  No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    ...

Offensichtlich wurde die Datei "Linux.properties" nicht gefunden, aber wo wurde sie gesucht? Ist doch klar, werden Sie vielleicht denken, im aktuellen Verzeichnis. Aber können Sie sagen, welches das aktuelle Verzeichnis ist, das von Ihrer Entwicklungsumgebung benutzt wird? Oder stellen Sie sich den armen Administrator vor, der verschiedene Web-Anwendungen im Applikationsserver gestartet hat. Welches ist hier das aktuelle Verzeichnis?

Strengen Sie sich etwas mehr an bei den Fehlermeldungen – nichts ist ärgerlicher als nichtssagende Meldungen der Art "Allgemeine Schutzverletzung". Das Mindeste, was Sie in diesem Beispiel ausgeben sollten, ist entweder das Verzeichnis, in dem die Datei erwartet wird, oder der absolute Dateiname:

22:48:03,293 ERROR hello.World - can't load /local/hello/Linux.properties
java.io.FileNotFoundException: Linux.properties (
  No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    ...


Jetzt ist klar, wo die fehlende Datei gesucht wurde. Es ist übrigens gar nicht so schwer, hier eine aussagekräftige Fehlermeldung zu erhalten – Sie müssen nur die getAbsolutePath()-Methode der File-Klasse verwenden:

public static Properties loadProperties(File file) {
  log.info("loading " + file);
  try {
    InputStream istream = new FileInputStream(file);
    Properties props = loadProperties(istream);
    istream.close();
    return props;
  } catch (IOException e) {
    log.error("can't load " + file.getAbsolutePath(), e);
    return new Properties();
  }
}


Die Probleme mit dem aktuellen Verzeichnis lassen sich auch umgehen, wenn man die Dateien direkt aus dem Classpath liest. Dazu existieren in der Class-Klasse die Methoden getResource() und getResourceAsStream():

...
try {
  props = loadProperties(World.class.getResourceAsStream(filename));
} catch (Exception e) {
  log.error("can't load " + filename + " from classpath", e);
}
...

Dieses Verfahren bietet sich an, wenn Sie beispielsweise eine Standard-Propertys-Datei in Ihrer Anwendung bereitstellen wollen, die mit der Jar-Datei ausgeliefert wird.
Zusammenfassung
 
Solange man keine Swing-Anwendungen entwickelt (hier zeigen vor allem ältere JDK-Versionen bei schlampiger Programmierung teilweise ein unterschiedliches Verhalten – aber dies ist ein eigenes Thema), ist es gar nicht so schwierig, auch unter Windows für Linux Java-Anwendungen zu entwickeln. Achten Sie darauf, dass Fehlermeldungen aussagekräftig sind, dann können auftretende Probleme schnell behoben werden.

Die meisten Schwierigkeiten sind eher im Umgang mit der Zielplattform zu erwarten: Wenn ein Windows-Entwickler sich plötzlich mit einer Kommandozeile unter Linux konfrontiert sieht, kann sehr schnell Frust aufkommen, wenn er nicht die nötigen Grundkenntnisse dafür mitbringt. Nehmen Sie daher auf jeden Fall einen Experten mit ins Boot, der als Ansprechpartner und Berater dienen kann. Und am besten Sie haben mindesten einen Entwickler im Team, der auch auf der Zielplattform entwickelt – viele Probleme lassen sich damit bereits im Vorfeld lösen.
Mehr Informationen zum Thema erhalten Sie im Fachmagazin JavaSPEKTRUM externer Link, herausgegeben vom Verlag SIGS-DATACOM GmbH. Der Autor Oliver Böhm beschäftigt sich mit Java-Entwicklung unter Linux und APO mit AspectJ. Neben seiner hauptberuflichen Tätigkeit als Software-Entwickler und -Coach gibt er Vorlesungen an der FH Ulm, ist Board-Mitglied der JUGS (Java User Group Stuttgart) und leitet die Special Interest Group (SIG) AspectJ. Er ist u. a. Autor der Bücher "JavaSoftware Engineering unter Linux" (SuSE Press) und "Aspektorientierte Programmierung mit AspectJ5" (dpunkt.verlag).

Verlag und Autor behalten sich alle Rechte am Artikel vor. © 2007 SIGS-DATACOM GmbH
 
GULP Membership Area
Noch mehr Fachwissen über neue Entwicklungen und Konzepte rund um die Java- Plattform? GULP Member erhalten 20 % Rabatt auf das JavaSPEKTRUM Jahresabonnement.

Kommentare zu diesem Artikel:

"Genau das, was ich brauche... Hab damit nämlich auch gerade so meine Probleme. (Januar 2008)"

"Sehr gut. (Dezember 2007)"

"Ein großes Kompliment für diesen sehr informativen Artikel. 'Plattformunabhängige' Beiträge sind leider nicht die Regel. Ich vermisse diese Sicht auch öfter in IT-Fachbüchern. (Dezember 2007)"

"Super Artikel, Kompliment an den Autor, bitte mehr davon ;) (November 2007)"

"Sehr gut strukturiert und anschaulich geschrieben. (November 2007)"