Ich stolpere immer wieder über das Unix ln Kommando. Unter Unix ist es ja eigentlich immer so: von ist der erste Parameter, nach ist der Zielparameter. Einfach gesprochen: es ist eigentlich immer von-nach. Beim Link Kommando ln ist das anders. Dort wird erst das Ziel angegeben und dann der zu erzeugende Link. Der erzeugte Vektor ist dann aber genau umgekehrt, da der Link auf die ursprüngliche Datei zeigt. Ich gebe das beim ersten Versuch fast immer falsch an – womit ihr natürlich messerscharf schliesst: oft macht der Kohlmann das nicht.
Im Java 7 API wurde mit dem neuen java.nio.file Package auch die Möglichkeit geschaffen Links im Dateisystem zu erzeugen. Dort wurde es genau anders herum implementiert. Von Link nach Ziel. Für die Java Welt ist das konsistent. Im Zusammenspiel mit unixartigen Systemen nicht. Aber auch das werde ich irgendwann drin haben.
Hardcore sind die Entwickler, die bei einem Fehler eine Exception schmeissen. Einfach so Exception, kein Subtype. Vollkommen scherzbefreit sind aber die, die nicht einfach eine Exception werfen, sonder einen InternalError. Der erbt von VirtualMachineError und die Dokumentation ist recht knapp und klar:
Thrown to indicate some unexpected internal error has occurred in the Java Virtual Machine.
Frei übersetzt heisst das soviel wie: keine Ahnung was passiert ist, aber hier geht gar nichts mehr. Wohlgemerkt, nicht für die Anwendung, sondern für die VM, dem Herz der Runtimeumgebung. Tritt so ein Fehler auf gilt es zu retten was zu retten ist, wenn dies noch irgendwie möglich ist. Ganz bescheuert wird es aber, wenn ich innerhalb von ein paar Minuten den Code so umgebaut habe, dass dieses Sub-System zwar nicht funktioniert, der Rest aber weiter machen kann als sei nichts geschehen.
Habe mir am Wochenende überlegt die ganzen Applikationen auf diesem Server auf aktuelle Versionen anzuheben. Bei der Blogsoftware wird das eine komplexere Geschichte. Dort hat sich fast alles geändert und ich muss neben dem CSS auch noch die eigenen Plugins umschreiben.
Dann habe ich mir das JSPWiki angeschaut. Was hat sich getan zwischen Version 2.0.x (aus 2006) die hier läuft und der aktuellen Version 2.8.2? Sogut wie nichts wichtiges im Markup. Deswegen wurde auch hier beschlossen: never touch a running system.
Wordpress würde für mich viel zu stressig sein ![]()
Java 7 ist nun also erschienen. Nach langen, langen, sehr langen Geburtswehen. Leider nicht für Mac OS X. Und Apple wird es auch nicht mehr offiziell unterstützen - fuck you Steve. Leider wurde auch das von mir sehnlichst erwartete Feature, Type Annotations, auf Release 8 verschoben. Gerade in diesem Bereich sehe ich sehr viel Potential für die Sprache und die Entwicklung stabiler, skalierender Software. Weniger in den Ergebnissen des Project Coin, dem neuen IO Framework für Dateien oder in der invokedynamic Umsetzung.
Vor knapp 8 Monaten habe ich mal geschrieben, was das alles für Konsequenzen hat.
In Java existiert das sogenannte Konzept der checked exceptions. Jede Exception, die nicht vom Typ RuntimeException ist, muss in Methoden deklariert werden, wenn sie geworfen wird. Und sie muss explizit gefangen werden, wenn eine Methode, die die Exception deklariert, aufgerufen wurde. Alternativ kann die aufrufende Methode die Exception – oder einen generelleren Typen – fangen oder weiter werfen. Soweit so bekannt, so langweilig.
Problematisch wird es wenn Methoden Mengen solcher checked exceptions werfen oder in einem try-catch Block mehrere Methoden mit unterschiedlichen checked exception aufgerufen werden. Dann kann es zu elend langen catch Sequenzen kommen. Java 7 wird hier etwas Abhilfe schaffen. Trotzdem kann es nervig sein, sich durch die Exceptionwüsten zu arbeiten.
Der grösste Vorteil von checked exceptions ist meiner Meinung nach aber der dokumentierende Charakter. Dadurch, dass eine checked exception explizit in der Methode dokumentiert wird, weiss der Entwickler was ihn erwartet – und kann entsprechend handeln. Beispielsweise ist eine meiner Lieblingsexceptions die IOException. Sie dokumentiert, dass eine IO-Operation Probleme machen kann. Passiert dies, tritt ein Problem auf, kann ich als Entwickler oft noch versuchen zu reagieren. Ich kann beispielsweise versuchen noch einmal eine Datei anzulegen, in eine Datei oder auf einen Socket zu schreiben. Erst wenn dies ein paarmal nicht funktioniert hat, hat kann aufgegeben werden. Oder ein Subtyp der Exception besagt glasklar: hier geht nichts mehr.
Würde die IOException eine RuntimeException sein, dann muss sie nicht dokumentiert werden. Weder im Code in der Methodensignatur, noch in den Javadocs. Und genau diese Dokumentation wird dann auch ganz gerne einmal vergessen. Ist dies der Fall kann ich nur erahnen, dass ein IO Problem aufgetreten ist, um entsprechend zu reagieren oder ich muss auf Verdacht eine IOException fangen. Geholfen ist mir damit nicht viel.
Leider wird viel zu oft bei Exceptions überhaupt nicht reagiert und z.B. bei Datenbankoperationen stumpf ein Rollback durchgeführt. Das Exceptions signalisieren: versuch es noch einmal oder nimm einen alternativen Weg ist bei der Geschwindigkeit, in der heute Dinge entwickelt werden leider kaum zu vermitteln. Deswegen fehlt meistens eine entsprechende Strategie im Umgang mit Exceptions. Auf Exceptions nur mit einem totalen Abbruch zu reagieren ist ein nach-mir-die-Sintflut programmieren. Qualität kann so nicht entstehen. Ich denke, dass dies einer der Gründe ist, warum sich das Konzept der checked exceptions nicht durchgesetzt hat.
Ich sehe gerade mit Freude, dass in Java 7 eine Methode System.lineSeparator() eingeführt wird. Zwar bin ich kein grosser Freund von convenience methods, aber hier macht es Sinn. Es erleichtert die Abfrage des entsprechenden System Properties. Da der Test für eine SecurityException nicht mehr gemacht werden muss, verringert sich der bisherige Code
final String lineSeparator = AccessController.doPrivileged(
new PrivilegedAction() {
public String run() {
return System.getProperty("line.separator");
}
});
mit Java 7 auf eine Zeile:
final String lineSeparator = System.lineSeparator();
Ersparnis: 4 Lines of Code, eine anonyme innere Klasse und mehr als 100 getippte Zeichen. Und das hundertausendfach.
Der Featureumfang von Java 7 wird aufgespalten. Das ist noch nichts Neues. In Java 7 (soll Mitte 2011 erscheinen) wird es ein paar neue APIs geben, nichts weltbewegendes. Etwas Syntaxsugar aus dem Project Coin. Aus Coin interessiert mich eigentlich nur der Diamond Operator. Weniger Code ist immer gut. Das Closures verschoben werden ist nicht so wichtig. Schmerzhafter ist schon das Jigsaw auf Java 8 (Ende 2012) verschoben wird. Besonders bei grösseren Applikationen würde dies viele Bauchschmerzen vermeiden helfen - komme mir jetzt niemand mit OSGi. Aus meiner Sicht richtig blöde ist, dass JSR 308 (Annotations on Java Types) auf Java 8 verschoben wird. Das ist eine wirklich extrem sinnvolle Erweiterung um stabilere Systeme zu entwickeln. Und nur noch darum geht es bei der Weiterentwicklung von Java: stabilere Systeme entwickeln.
Wenn Java 8 Ende 2012 hoffentlich das Licht der Welt erblickt, dann wird es noch mindestens 2-4 Jahre dauern, bis es den Markt breiter durchdrungen hat. Das heisst also, die Features können erst 2015 oder 2016 in breitem Mass genutzt werden. Vielleicht auch noch später. Gründe: es wird sicherlich einige Unternehmen geben, die auf Java 7 wechseln werden. Sei es weil Java 6 im Jahr 2011 schon in die Jahre gekommen ist - erschienen 2006 - oder sie die neuen Features unbedingt brauchen. Nicht vernachlässigt werden darf auch die Java Enterprise Edition (JEE). Diese hängt der Java Standard Edition immer um 1-2 Jahre hinterher. Dann müssen auch die Application Server angepasst und breiter ausgerollt werden. Normalerweise überspringen Unternehmen einen Release. Ist dies der Fall, werden sie erst mit Java 9 die Java 8 Features anwendbar machen. Also irgendwann 2017 oder später.
Eine andere Strategie: die Unternehmen erkennen, dass Java 7 ist was es ist, nur ein kosmetischer Release. Erst mit Java 8 wird es wirklich interessant. Dann ist 2016 wieder realistisch. So oder so wird sich die nächsten 4 Jahre bei Java in der freien Wildbahn nicht viel tun. Auch so kann Planungssicherheit aussehen.
Die GWT-MATH Artefakte befinden sich im Maven-Repository unter com/google/code/gwt-math. Die groupId in der POM Datei ist allerdings com.googlecode.
Wie geht denn das?
Im Grunde bin ich mit Java als Sprache und als Plattform ganz zufrieden. Hier und da kann es Verbesserungen geben. Beispielsweise in der Abschottung von Subsystemen. Dies hätte ich gerne in Form eines Modulsystems und nicht als OSGi-System aufgepfropft. Bei Generics hätte einiges besser laufen können. Mit Java 7 wird jetzt immerhin der Diamond-Operator eingeführt. Autoboxing ist leider vollkommen verunglückt.
Womit ich inzwischen nicht mehr zufrieden bin ist das Schlüsselwort final und seine Semantik. Mit final wird eine Klasse oder Methode vor überschreiben geschützt. Ein Feld wird vor erneuter Zuweisung geschützt. Das Feld ist dementsprechend mehr eine Konstante als eine Variable. Langjährige Erfahrung hat allerdings gezeigt: überschreiben oder neu zuweisen wird überbewertet. Vererbt wird selten wirklich sinnvoll und echte Variablen gibt es auch eher selten.
Inzwischen halte ich es für eine bessere Idee statt final ein var zu verwenden. Also Verbung und neu zuweisen nicht explizit zu verbieten, sondern explizit zu erlauben. Code würde dadurch robuster werden. Einige sich immer wieder einschleichender Fehler würden beseitigt. Überkomplexe Vererbungshierarchien mehr durchdacht, bevor sie in Code gegossen werden. Einige der neuen Sprachen, die auf der JVM aufsetzten, implementieren var. Leider nicht die Hauptsprache Java. Schade, dass der Zug dafür abgefahren ist.
Für internationalisierte (I10N) Implementierungen verwende ich gerne das CAL10N API. Das API erreicht eine Compiler Assisted Localization durch den Einsatz von Java enums. Der Name des Enumerationsfeldes ist der Schlüssel in einer entsprechenden Properties Datei. Hierdurch wird der Schlüssel innerhalb des Java-Code nicht mehr als String hinterlegt. Durch ein Maven Plugin können die Properties-Dateien überprüft werden. Ich empfehle jedem Java Entwickler sich das API einmal anzuschauen.
Ich habe den Ansatz für den Einsatz in meinen privaten Projekten und bei bei Zimory erweitert. Durch den Einsatz von enums können auch Annotationen zum Einsatz kommen. Zwei Annotationen habe ich definiert. Eine für den enum Typen und eine für die enum Felder. Beide haben als einzigen Wert einen String:
@Target(TYPE)
@Retention(RUNTIME)
public @interface SystemId {
String value();
}
@Target(FIELD)
@Retention(RUNTIME)
public @interface MessageId {
String value();
}
@SystemId definiert grobgranular das Modul/Subsystem – oder wie auch immer die Systemunterteilung namentlich genannt wird – für die Messages. @MessageId definiert eine spezielle feingranulare ID für z.B. einen Fehlerzustand innerhalb eines Moduls. Der Sourcecode einer Message enum sieht dann in etwa so aus:
import ch.qos.cal10n.LocaleData;
import ch.qos.cal10n.Locale;
import ch.qos.cal10n.BaseName;
@SystemId("RT")
@BaseName("messages")
@LocaleData({@Locale("en")})
public enum Messages {
/** Indicates a login failure. */
@MessageId("001001") LOGIN_VALIDATION_FAILED;
}
Für die Auswertung haben ich einen eigenen MessageProducer geschrieben. Dieser kapselt eine Implementierung des CAL10Ns IMessageConveyor Interface. Ausserdem analyisert er per Reflection die Annotationen und baut aus den Werten eine ID zusammen. Diese ID wird vor die internationalisierte Nachricht gestellt. Daneben übernimmt die Implementierung noch einige Dinge wie Caching usw. Eine erzeugte Nachricht sieht in etwa wie die Folgende aus:
RT-001001: Login failed for user "foobar".
Solche IDs sind nicht nur für die Analyse von Fehlermeldungen sinnvoll. Sie bieten auch einen einfachen Ansatz für die Fehlerpropagierung in Systemen, in denen bisher kein durchgängiges Exceptionframework existierte. Damit auch nicht Entwickler die Fehlermeldungen verstehen, haben ich eine Doclet-Implementierung geschrieben. Diese analyisert alle enum Klassen mit @SystemId und generiert aus dem Wert der Annotation, der Javadoc Klassendokumentation, dem Wert von @MessageId und der Javadoc Felddokumentation eine XML Datei mit einer Zusammenfassung der Daten. Durch einfache XSLT Skripte wird das XML Dokument in Docbook und Xdoc umgewandelt und kann somit für die Anwenderdokumentation weiter verarbeitet werden. Da alles aus einer Quelle kommt, werden Fehler durch cut 'n paste reduziert.
Voll reingelaufen. Priorität: low, no workaround. Und ich muss jetzt eine Lösung finden ![]()
Money Quote:
Introspecting returns incorrect PropertyDescriptor for overriden getter in case when child getter has more specific return type. In this case PropertyDescriptor of parent class is returned instead.
Bis Java 1.6.0_18 kein Fix. In Java 7 (b80) gefixt. Leider kann ich mir dafür gar nichts kaufen.
Für meinen Job bei Zimory habe ein wenig mit Weld SE gespielt. Weld ist die CDI Referenzimplementierung und Weld SE eine Anpassung für die Java SE Welt.
Die eigentliche Implementierung war recht einfach. Allerdings wurde beim Ausführen immer eine UnsatisfiedResolutionException geworfen. Die Nachricht war WELD-001308 Unable to resolve managed beans for…
. Nach kurzer Suche war klar, dass Weld eine leere "META-INF/beans.xml" Datei benötigt. Nachdem die Datei angelegt war, funktionierte das Beispiel problemlos.
Es lässt sich streiten, ob die CDI Referenzdokument in Kapitel 12.1 eine solche Datei zwingend vorschreibt. Ich habe es dort nicht heraus gelesen.
Download: Sourcecode (7.897 Bytes)
javac scheint bei package Annotationen nicht zu überprüfen, ob die Annotationen den deklarierten Regeln folgen. Eben habe ich einer solchen Annotation den default Wert weggenommen. Die Verwendung auf type Ebene warf Compiler Fehler. Ist der Wert in der package-info.java Datei nicht vorhanden, läuft der compile Prozess ohne Probleme durch. Die falsche Verwendung auf package Ebene wurde durch nicht angepassten Unit-Test entdeckt. Eine IncompleteAnnotationException wird geworfen, wenn ich den Wert über das Reflection API auslese.
Vorher:
@Documented
@Retention(RUNTIME)
@Target({PACKAGE, TYPE})
public @interface CommandHolder {
String value() default "";
}
Nacher:
@Documented
@Retention(RUNTIME)
@Target({PACKAGE, TYPE})
public @interface CommandHolder {
String value();
}
Verhalten tritt auf mit javac 1.6.0_16 und javac 1.7.0-ea (b80) auf einem aktuellen AMD64 Ubuntu 9.10.
Vielleicht wühle ich mich später mal durch die Spezifikationen.
Ich implementiere gerade privat ein REST API. In dem API werden XML Daten mittels JAXB gewandelt. Dabei habe ich einfach POJOs verwendet und mit den JAXB Annotationen versehen. Eigentlich ganz einfach. Eigentlich… Bis zu dem Zeitpunkt an denen Collections ins Spiel kommen. Naiv wie ich war – he, wer liesst schon Spezifikationen – habe ich Collection Properties einfach annotiert. Und weil ich ein pfiffiger Entwickler bin, habe ich bei den Gettern eine leere Collection (Collections.emptyXyz()) zurück gegeben, wenn die Referenz null war. Ergebniss: unmarshall(…) warf eine UnsupportedOperationException. Das Problem: JAXB erwartet eine mutable Collection. Collections.emptyXyz() liefert aber immutable Objekte. JAXB ruft darauf dann clean() auf und füllt die Collection neu – die Refernzimplementierung mittels add(E). Eine null Referenz wird auch nicht akzeptiert und führt ebenfalls zu einer Ausnahme.
Ich habe mir dann doch mal die JAXB 2.2 Spezifikation von 2009-12-10 durchgelesen. Unter Punkt 5.5.2.2 wird das Verhalten spezifiziert. Mit einer Einschränkung: sie gilt nur für Collections vom Typ List. Die JAXB 2.2 Referenzimplementierung kommt aber auch mit Sets und dem Collection Interface selbst zurecht.
Der Code sieht jetzt so aus:
@Valid
@NotNull
@Size(min=0, max=5)
@OneToMany(fetch=EAGER)
@XmlElement(name=ANSWER_XML_NAME)
@XmlElementWrapper(name=POSSIBLE_ANSWERS_XML_NAME)
public List<Answer> getPossibleAnswers() {
if (this.possibleAnswers != null) {
return this.possibleAnswers;
}
return new ArrayList();
}
Wem JAD nicht mehr genügt, weil er nur bis Java 1.4 sinnvoll decompiliert (kein Generics Support), mag sich JD anschauen. Kein Problem mit inneren Klassen, synthetischen oder Brigde-Methoden/Typen. Annotationen werden aufgelöst und enum Typen decompiliert. Der erzeugte Quellcode ist gut lesbar - ist ja der kniffelige Part bei Decompilern. Wird bei mir JAD ablösen.
Und ich frage mich, warum die Decompiler Jungs immer C++ verwenden.
Manchmal muss es ein grober Klotz sein. Ein Klasse die ich entwickelt hatte, hatte eine bestimmte Signatur. Nix wildes, beispielsweise war die Klasse final. Wenn eine Klasse final ist, muss ich mich nicht um die Probleme kümmern, die Vererbung mit sich bringen kann. Das final war plötzlich verschwunden. Grund: in einem Test wurde eine abgeleitete Version benötigt. Leider wurde nur das final entfernt. Die Klasse aber nicht hinsichtlich Vererbung angepasst. Z.B. habe ich in der equals Methode noch den instanceof Operator verwendet. Geht bei final Klassen. Bei Klassen, die nicht final sind ist das grob fahrlässig.
Ich habe den Test umgeschrieben – es geht auch einfach ohne Vererbung mit nur einer Zeile Code mehr und dann die Tests erweitert. Bisher habe ich immer nur die Erzeugung eines Exemplars über den Konstruktor getestet. Jetzt teste ich auch die Signatur. Den Test habe ich mit einem Kommentar versehen. Hoffentlich hilft es.
@Test
public void creation() {
// If you must change these tests go into yourself and check:
// are the changes are really needed?
new Notifier();
final Constructor>[] cons = Notifier.class.getDeclaredConstructors();
assertThat(cons.length, is(equalTo(1)));
assertThat(Modifier.isPublic(cons[0].getModifiers()), is(equalTo(true)));
final int modifiers = Notifier.class.getModifiers();
assertThat(Modifier.isPublic(modifiers), is(equalTo(false)));
// Implementation safe for inheritance?
assertThat(Modifier.isFinal(modifiers), is(equalTo(true)));
}
Vorher sah der Test so aus:
@Test
public void creation() {
new Notifier();
}
In Beispielen für Datenübertragungen findet sich häufig eine Serialisierung der Daten in eine XML Applikation. Dieser Eintrag soll zeigen, dass XML nicht immer die erste Wahl sein muss. Es soll das Bewusstsein erweitern nicht immer das Erstbeste zu verwenden, was einem in den Sinn kommt.
Für das Sportics Realtime Telemetry API wurde bewusst auf XML verzichtet. Der Grund: Realtime Telemetry Clients arbeiten auf reduzierter Hardware wie z.B. Mobiltelefonen und haben eine instabile Funkanbindung des Netzwerkes (z.B. Sportics J2ME Applikation). In solchen Umgebungen ist XML nicht die erste Wahl. Die Daten werden nicht kompakt genug repräsentiert.
Ein Realtime Telemetry Datensatz besteht aus einer Menge von Messwerten zu einem bestimmten Zeitpunkt. Ein Messwert hat immer auch eine beschreibende Kennung. Mehrere solcher Datensätze werden zu einem Snap zusammengeführt. Ein Realtime Telemetry Client schickt einen solchen Snap z.B. alle 2 Minuten an den Realtime Telemetry Server.
In XML kann ein solcher Datensatz wie folgt dargestellt werden:
<data>
<measurements>
<measurement type='longitude'>12.5675256</measurement>
<measurement type='time'>123456789</measurement>
<measurement type='heartrate'>78</measurement>
</measurements>
<measurements>
<measurement type='longitude'>12.5675456</measurement>
<measurement type='time'>123457813</measurement>
<measurement type='heartrate'>79</measurement>
</measurements>
...
</data>
Es fällt auf, dass ein solches Format sehr viel redundanten Overhead produziert. Dieser kann durch kürzere Namen reduziert werden. Dabei geht allerdings die sprachliche Wiedererkennung verloren.
<data>
<ms>
<m t='lon'>12.5675256</m>
<m t='tme'>123456789</m>
<m t='hrt'>78</m>
</ms>
<ms>
<m t='lon'>12.5675456</m>
<m t='tme'>123457813</m>
<m t='hrt'>79</m>
</ms>
...
</data>
Kompakter, aber auch schwieriger zu lesen. Es ist im Übrigen ein Irrglaube, dass das JSON Format kompakter ist. In der obigen Notation ist es grösser.
Eine weitere Möglichkeit der Reduktion ist auf allgemeine Namen für XML Elemente zu verzichten. An deren Stelle wird der Wert des t-Attributes verwendet:
<data>
<ms>
<lon>12.5675256</lon>
<tme>123456789</tme>
<hrt>78</hrt>
</ms>
...
</data>
Ein ungewöhnliche Schreibweise, aber durchaus machbar. Bei diesem Aufbau wird ein entsprechendes JSON Dokument im Übrigen kleiner als sein XML Pendant. Hierzu folgt weiter unten eine Grafik aus realen Messwerten.
Es geht noch kompakter. Bei Sportics wurde bei der Realtime Telemetry API bewusst gegen SOAP Webservices entscheiden. Der Grund ist einfach: nicht alle Zielgeräte verfügen über SOAP Webservices Support. SOAP Webservices von Hand nachzuprogrammieren ist keine Kleinigkeit. Stattdessen haben wir uns für ein REST API entschieden. Dies vereinfachte die Kommunikationsschnittstelle und auch den Aufwand für die Implementierung auf Clientseite. Zwar wird auch in der REST Literatur viel mit JSON und XML gearbeitet, aber dies muss nicht sein.
Bei den Messwerten setzen wir statt dessen auf das CSV-Format nach RFC 4180. Im CSV-Format wird der oben beschriebene Datensatz wie folgt notiert:
tme,lon,hrt
123456789,12.5675256,78
123457813,12.5675456,79
Noch kompakter ist kaum möglich. Das API schreibt noch einige Dinge vor, beispielsweise muss ein Header vorhanden sein und jede Zeile nach der Headerzeile muss so viele Spalten haben wie die Headerzeile. Ansonsten verwirft der Server die Daten und liefert eine Fehlermeldung.
Die abschliessende Frage lautet: Um wieviel kleiner ist die CSV-Notation?
Aus den realen Daten von einigen tausend Datensätzen lässt sich folgendes Bild gegenüber der CSV Notation ermitteln:
- die XML Notation ist um ca. den Faktor 2 grösser
- die JSON Notation ist um den Faktor 1,5 grösser
In absoluten Zahlen:
| CSV | XML | JSON |
|---|---|---|
| 2.791.662 | 5.831.791 | 4.361.462 |
Ein Bild beschreibt mehr als 500 Worte:
Dieser Inhalt wurde aus einer weiteren Quelle zusammengefügt. Mehr...
- Cost cutter - süchtig machender Tetris Clone.
via Petronella - Weiss gar nicht wieso sich die Sozialverbände und -anbieter so über die geplante Reduzierung der Wehrdienstzeit aufregen. Zivis sollen keine Arbeitsplätze wegnehmen dürfen, also nur Jobs machen, auf die verzichtet werden kann. Wie schlimm steht es eigentlich ums Pflegesystem?
- Android Icons für die Entwicklung unter Creative Commons Attribution Share Alike Lizenz.
- Some Java Concurrency Tips
Auf http://code.google.com/p/sportics-j2me habe ich die Applikation, die beim Frankfurter Helbmarathon gelaufen ist, hochgeladen. Der Code ist unter der GPL 3 freigegeben. Die Version 1.0 ist mit dem Nokia N79 und N85 getestet worden und lief 5 Stunden stabil. Dann war der Akku leer ![]()
