Blog: Latest Entries (15):


SEO, wtf?

Ich guck mir jeden Tag mit den Google Web-Tools die Klicks und Impressions von http://www.annonyme.de, http://www.mp4togif.com und http://www.webm-maker.com.
Dabei fällt mir immer wieder auf wie ungelaublich und durchsichtig diese ganze Sache mit dem Ranking in Google ist. Wärend ich mit der WebM-Maker-Seite das letzte mal sehr viel Glück hatte, weil die von Anfang an auf mobile Geräte ausgerichtet war. Da war die Bewertung nach Eignung für Mobile-Devices dann kein Problem. Aber die Schwankungen bei den Impressions sind teilweise oder eher oft, kaum nach zu vollziehen. Gerade mit entsprechenden Suchbegriffen auch nicht wirklich nachvollziehbar. Alle Optimierungen mit Title, Description, H-Tags und so, sind gemacht und die Begriffe stimmen auch. Aber dennoch stürzten die Impressions in den letzten Tagen sehr ab. Bei der Analyse der echten Zugriffszahlen gingen die Zugriffe aber sogar leicht nach oben. Ok wenn man die Seite gefunden hat und ein Bookmark benutzt oder den Link direkt eingibt geht es natürlich an Google vorbei. Das spricht für Benutzer die wieder kommen. Das ist sehr schön, aber ich hätte auch gerne noch neue Benutzer.

Ich werde dann wirklich mal die WebM-Maker App versuchen im Firefox Marketplace unter zu bringen. Mit etwas Glück bringt dass noch ein paar neue Benutzer. Und Links auf die Seite wären auch nicht schlecht.
Da steckt noch viel Arbeit hinter....


Firefox OS auf beliebigen Android-Phones

Die Entwicklung, wie sie hier beschrieben wird http://www.golem.de/news/mozilla-firefox-os-auf-android-smartphones-installierbar-1507-115315.html.
Da gute und Leistungsfähigere Smartphones mit Firefox OS selten sind, wäre ich doch sehr interessiert Firefox OS mal auf einem wirklich schnellen Smartphone zu sehen und dann vielleicht auch WebM-Maker mal in den Firefox Marketplace einzustellen. Dann wäre es auch mal interessant zu versuchen live Video zu capturen und in GIF oder WebM umzuwandeln.
Vielleicht auch dann mit einem Filtern, um die auch in schwarz-weiss umzuwandeln zu können. Wäre eine nette Spielerei, wenn auch nur selten wirklich interessant. Es ist ja kein Bild-/Video-Bearbeitungsprogramm, sondern nur ein einfacher kleiner Converter für zwischendurch. Und das wäre auf einem Smartphone ja genau richtig aufgehoben.

Wenn ich mal wieder etwas mehr Zeit habe, werde ich mich auf jeden Fall wieder mehr mit Firefox OS beschäftigen.

bbcode-image

Jobs, Bewerbungen und der ganze Rest

Heute geht es mal nicht um technologischen Kram sondern mal um Gefühle und persönliches Befinden.. klingt komisch.. ist aber so. Denn meine Zeit in meinem jetzigen Job geht dem Ende entgegen und.. es fühlt sich doch seltsam an. Die eigenen Projekte sind abgegeben und man begleitet nur noch die Kollegen, wie sie versuchen damit weiter zu kommen, aber so wirklich helfen und was neues einbauen lohnt sich auch nicht mehr, weil dass wäre dann ja auch nicht mehr in der Dokumentation drin und dann will man die ja auch nicht mit so etwas alleine lassen. Außerdem will man ja auch sehen, ob die damit wirklich zu recht kommen.. also nicht gleich helfen sondern beobachten und dann nur wenn es wirklich nicht mehr anders geht eingreifen. In ein paar Wochen müssen die ja auch alleine dadurch.

Menschen und Veränderungen passen ja nie wirklich so gut zusammen. Wenn etwas läuft, tut man sich schwer es zu ändern und ein Risiko einzugehen. Hier ist es gerade genau das, was mich bewegt. Wenn man 10 Jahre bei einer Firma war, hat man sich doch stark an vieles gewöhnt. Die Abläufe, der Tagesrhythmus, die Kommunikationskultur.. alles wird danach anders sein und man muss sich umgewöhnen. Am Ende wird es kein Problem sein, da man doch sehr viel flexibler ist als man denkt, aber man denkt doch immer wieder darüber nach, ob man da nicht auffällt, weil es anders gewohnt ist.

Das Ende hier kommt jetzt auch nicht wirklich plötzlich und überraschend. Ich will es ja so. Ich möchte andere Strukturen, Umgebungen und neue Möglichkeiten mich weiter zu entwickeln. Ich weiß, dass ich dafür weiter ziehen muss und allein diese Erkenntnis und es in Angriff zunehmen hat mir in den letzten Monaten schon viel gebracht.
Ich gehörte nie zu denen, die sich mit einer Technologie zufrieden geben. Ich halte es für vollkommen falsch eine Technologie zu wählen und dann zu glauben man könnte Jahre damit arbeiten, weil alles ja so zukunftssicher ist und dann alles neue kategorisch ablehne. Neben meiner Arbeit mit Java EE und Desktop-Clients, habe ich den Web-Bereich nie aus den Augen verloren und dann auch privat viel Zeit investiert um mich dort auf einem aktuellen Stand zu halten. Neue Konzepte und Technologien... aber nichts was ich in meinem Job gebrauchen konnte oder durfte.

Also kam die Entscheidung, dass ich mich mehr mit neuen als mit alten Dingen beschäftigen möchte und dafür einen neuen Job brauche. Damit fing die erste Phase an. Denn man muss sich bewerben. Ich brauchte einen aktuellen Lebenslauf, dafür musste ich erst einmal wissen, was ich kann, was ich in den letzten Jahren in meinem Job eigentlich alles gemacht habe. Dann musste ich mir erst einmal darüber klar werden was ich privat alles gemacht habe, gelernt habe, was ich wie gut kann und was davon vorzeigbar ist.
Während man die Projekte im Job, wenn man nur firmeninterne Software entwickelt, nicht wirklich zeigen kann (ich als Chef hätte keine Lust, um einen neuen Bewerber zu beurteilen, mir eine Oracle DB, einen Application Server und alle möglichen Libs einzurichten, um dann mit etwas Glück ein fremdes Projekt deployen zu können). Also mussten als Beispiele eher die privaten Projekte herhalten.

* lieber wenige gute Dinge zeigen, als viele wo die Hälfte nicht funktioniert oder nur ein einfaches Beispiel einer Funktion ist * Der Zweck und die Benutzung muss sich innerhalb weniger Sekunden erschließen, keiner will erst einmal 20 Seiten eines Handbuchs lesen müssen
* Es sollte modern sein und zeigen, dass man ausbaufähig ist

Dann das Problem mit der Homepage. Hübsch machen, aktualisieren und es wenigstens so aussehen lassen, als hätte man sie nicht die letzten 2 Jahre total vernachlässigt. Also alle alten Blog-Einträge entfernen bzw. nur die wirklich guten, die einen was bringen, behalten. Alles mehr so ausrichten, dass der Besucher sieht, in welche Richtung man will. Alle Konzepte, Ideen und halb-fertigen Dinge einfach löschen bzw. ausblenden. Das bringt einen schon einmal was, da man die Anzahl der Projekte verringert und sich klar wird, was einen wirklich was bringt und wo es sich lohnt mehr Zeit zu investieren.
Dann muss man auch gefunden werden. Weil Chefs sollen ja laut Medien bei Google nach den Bewerbern suchen... oft lesen sie die Bewerbung nicht mal wirklich durch.. aber falls es doch mal passiert, sollen sie einen auch gut bei Google finden können und genau darauf gestoßen werden, dass man genau das macht und kann, was die brauchen. SEO. Ein leidiges Thema, aber nötig. Google Webmaster-Tools einrichten für alle Websites und Projekte. Hätte man vorher schon mal machen sollen, auch wenn der Erfolg der eigenen Projekte an sich egal war, ist es doch gut zu wissen was Menschen mögen und verwenden. Es gibt einen auch ein gutes Gefühl etwas geschaffen zu haben, was von anderen verwendet wird.. aber Vorsicht... am erfährt eher, dass es keinen interessiert was man so gebaut und entwickelt hat.
Erst nach dem ich versucht habe gefunden zu werden, sind bei einigen Projekten auch wirklich mal Zugriffe zu verzeichnen. Nicht viele, aber endlich mal wenigstens regelmäßig.

Den Blog wieder aufleben lassen. Über Dinge schreiben, die man kann und macht. Es ist egal, ob es nur Grundlagen sind oder einfache Anleitungen. Es zeigt, dass man in sein Wissen Vertrauen hat und etwas erklären kann bzw. es wenigstens versucht. Man fasst sein Wissen auch mal strukturiert zusammen und erschafft sich damit auch sein eigenes kleines Nachschlagewerk auf das man selbst mal wieder zurück greifen kann, wenn man sich denkt: "Wie war das noch mal mit ....?".
Also auch wenn es niemand liest, kann man es immer noch zur Selbstreflektion verwenden.

Wenn man dann nach ca. 2 Wochen mit dem Aufräumen und Verschönern seiner Präsenz im Internet fertig ist, kann man den Lebenslauf schreiben. Ich bin ja immer noch dafür es selbst zu machen und niemanden dafür zu bezahlen. Gerade im IT-Bereich, wo man doch sein können gerne mal darin an den Arbeitgeber anpasst, um spezialisierter zu wirken. Wie man einen Lebenslauf schreibt lernt man in der Schule und jeder hat eine andere Meinung über Form und Inhalt. Ich habe immer gut damit gelebt, dass es reicht wenn er gut zu lesen ist und alles enthält was der Arbeitgeber wissen soll und dafür auch gerne mal gegen ein oder zwei Form-Regeln verstoßen kann.
Das Foto sollte gut sein. Da sonst zu einem echten Fotografen oder selbst mal wieder die Blitze, Schirme, Softboxen und Funkauslöser raus kramen. Kann auch mal ganz erfrischend sein ein andere Hobby kurz wieder aufleben zu lassen und mehr Kontrolle über das Ergebnis zu haben.

Ich geh mal davon aus, dass man schon vorher mindestens ein Stellenangebot gefunden hat, dass einen interessiert. Jetzt wo die Unterlagen fertig sind und man im Internet hoffentlich zu finden ist, kann man anfangen die erste Bewerbung zu verschicken. Wenn die draussen ist, dann gleich weiter machen und mehr Angebote suchen. Mir hat es immer viel gebracht, die Homepages der Firmen mir sehr genau anzusehen, da konnte man dann schon relativ gut sehen, ob es etwas ist, wo man sich bewerben kann oder es doch nichts für einen ist.
Die Hälfte der Firmen wird sich nie melden, einige werden Absagen schicken (teilweise auch erst Monate später) und einige wenige werden einen einladen.

Das Gespräch.. man weiß nie wie es sein wird. Soll man einen Anzug anziehen? Wenn man sich dann besser fühlt oder alle dort so rumlaufen sicher. Ansonsten muss man ein Gefühl bekommen, wie die Firma wohl läuft und funktioniert.
Eben von der Homepage oder aus Emails und Telefonaten. Hier in der Firma wo ich gerade meine letzten Tage hier verbringe, erkannt man Bewerber immer sofort. Es sind die einzigen Leute, die mit einem Anzug herum laufen und dann wenn sie eingestellt wurden auch nie wieder mit einem Anzug zu sehen sind. Aber am Ende ist immer besser zu gut als zu schlecht angezogen zu sein.

Bis man soweit ist, kann es einen Monat dauern. Aber ich hatte einfach die Zeit und es war insgesamt gut diese auch zu investieren und nichts zu überstürzen. Der Blog läuft auch weiterhin noch, auch wenn ihn wohl kaum einer liest... vielleicht sollte ich doch mal anonyme Kommentare ermöglichen und auch share-Buttons für soziale Netzwerke.. aber auch da gilt: Ich habe die Zeit und auch wenn es langsam voran geht, geht es doch stetig voran.

REST-Services (mit APF2 und aoop)

REST-Services sind ja nicht mehr neu und haben gerade die klassischen WebServices (SOAP) an vielen Stellen ersetzt. Wer mal mit SOAP, auch auf einen niedrigeren Level, gearbeitet hat, wird dies nicht als Verlust sehen. Es gibt viele Frameworks für REST-Services und auch lassen sich die meisten anderen Frameworks ohne Probleme so anpassen, dass sie keine HTML-Seite zurück liefern sondern das Ergebnis als JSON. XML wäre auch möglich als Ausgabeformat, aber ist sehr sehr wenig verbreitet. XML ist meistens sehr viel größer, da "name":"value" viel kürzer ist als <name>value</name> oder <attribute name="name" value="value"/>.

Meine Erfahrungen mit dem Parsen von XML und JSON in PHP ist aber, dass beide eigentlich gleich schnell sind. Ein Wechsel bei Config-Files von XML auf JSON hat keine Verbesserung der Ladezeiten ergeben. Auch die Serialisierung von Objekten bei Java war mit JSON nur minimal schneller als mit XML, wobei es dort auch sehr vom Verwendeten Framework abhängig war. Also kann es sein, das es in neueren PHP-Versionen doch Unterschiede geben wird, wenn dort noch mehr optimiert wird.

Mit das größte Problem auf dass ich bei REST-Services in PHP gestoßen bin ist das Parsen der URLs. Hier besteht einfach das Problem, dass es immer viele Methoden pro Service gibt und das einlesen der Mapping-Patterns somit doch relativ aufwendig sein kann. Während man in einem Java-Servlet nur einmal am Anfang alles parsen muss, muss man in PHP bei jeden Request von vorne beginnen. Eine Lösung wäre das Speichern der Ergebnisse in der Session, wobei hier dann aber das Problem bleibt, dass man bei Änderungen diese erkennen und den Inhalt der Session erneuern muss. Man kann das XML als String lesen und einen MD5-Hash bilden. Damit könnte man verschiedene Versionen des Services in der Session identifizieren. Am schönsten wäre natürlich den Zugriff auf das Dateisystem komplett einsparen zu können, aber dass scheint erstmal so nicht machbar zu sein.
Das Gleiche gilt auch für das Objekt, dass die Methoden enthält auf die die URLs gemappt werden. Man muss sich auch deutlich machen, dass ein REST-Service im Grunde kein Konstrukt der objekt-orientierten Programmierung ist, sondern die Facade im Grund klassischen prozedualen Prinzipen folgt. Gerade wenn man Sicherheits-Token und so verwendet und nicht die Session für die Speicherung des eingelogten Benutzer verwendet, merkt man dass man auch ohne Klasse mit einfachen functions hier genau so gut arbeiten könnte. Aber wir verwenden eine Klasse, die den Service beschreibt, um auch keinen Stilbruch zu haben. Ein Objekt zu erzeugen ist teuer und theoretisch könnte man sich dieses hier ersparen.

Mein AFP2-Framework benutzt die selben Klassen wie mein älteres aoop-Framework. Es implementiert momentan noch kein wirkliches Routing sondern nutzt einfach Regex-Ausdrücke um die gesamte URL mit einem Match auf die richtige Methode zu mappen. Hier wird also einfach von oben nach unten die Liste durch laufen und immer ein preg_match durchgeführt. Routing mit beliebig tiefer Verschachtelung und Sub-Routen, würde hier viel Zeit ersparen, da damit die Anfangs-Liste relativ klein wäre und schnell abzuprüfen wäre und erst dann in der nächsten Liste weiter geprüft wird. Das kommt dann später nochmal um etwas schneller zu werden.

Ein ganz einfacher REST-Service kann so aussehen:

rest.xml

<service name="walls" class="PostWallREST">
<method name="loadWall" pattern="/^\/rest\/\w+\/wall\/(\d+)(\?.+)?$/">
<argument type="int" requestvalue="false" group="1"/>
<argument type="int" pattern="userId" requestvalue="true"/>
<argument type="string" pattern="_XW_SESSION_SEC" requestvalue="true"/>
</method>
</service>


PostWallREST.php

class PostWallREST{

private $security=null;
private $conv=null;

private $am=null;

public function PostWallREST(){
$this->security=new XWSearchStringParser();
$this->conv=new PWDTOConverter();
$this->am=XWAddonManager::instance();
}

public function loadWall($id,$userId,$secToken){
$resp=new XWRESTResponse();
$resp->singleResult=true;
if(isset($_SESSION["XWUSER"]) && $this->am->getAddonByName("XWUserSession")->checkSessionSecToken($secToken)){
if($_SESSION["XWUSER"]->getId()==intval($userId) && intval($id)>0){
$wall=new PWWall();
$wall->load($id);
if($wall->checkRight($_SESSION["XWUSER"])){
$dto=$this->conv->convertWall($wall,$_SESSION["XWUSER"],true);
$resp->results[0]=$dto;
}
else{
$resp->restMethodSuccess=false;
}
}
else{
$resp->restMethodSuccess=false;
}
}
else{
$resp->error=true;
$resp->errorMessage="no valid session";
}
$resp->newSecToken=$this->am->getAddonByName("XWUserSession")->getURLParameterWithSessionSecTokenValueOnly();
return $resp;
}
}


Wie man erkennt wird im service-Tag die Klasse angeben, auf die die URLs gemappt werden sollen. Das Mapping der einzelnen Methoden wird dann innerhalb genauer definiert. Jede Method einen grundlegenden Pattern, der für ein Match auf die URL verwendet wird. Wenn dieser passt wird versucht die Argument (die Reihenfolge in der XML muss der Reihenfolge in PHP entsprechen!) aus der URL oder dem Request zu rekonstruieren. Jedes Argument kann ein eigenes Pattern haben. Wenn es ein Request-Value ist, entspricht das Pattern dem Namen im Request. Sollte kein Pattern angeben sein, wird dass Pattern der Method verwendet. Das erspart viel Text, weil man so nur ein allgemein gültiges Pattern schreiben muss. Mit group wird die Gruppe beim preg_replace angeben. Also die die den benötigten Wert enthält, der als Argument übergeben werden soll. Im Pattern der Method können also schon alle Werte in Gruppen markiert werden und bei den Arguments muss immer nur noch angegeben werden, welche Gruppe verwendet werden soll.

URLs zu REST-Services folgen in aoop und APF2 folgender Regel:

http://..../rest/{modulename}/.....


Über den Modul-Namen wird gesteuert welche rest.xml geladen wird. /rest/ ist die Markierung, dass hier ein REST-Service angesprochen werden soll. Der Rest der URL kann nach Belieben definiert werden.

Für unser Beispiel wäre z.B. folgende URL lauffähig:

http://..../rest/PostWall/wall/1?userId=1&_XW_SESSION_SEC=XXXXX


Das Ergebnis der Methode wird dann in JSON umgewandelt und auch der Header entsprechend auf "text/json" gesetzt.

Damit lässen sich nun pro Modul REST-Services definieren und deployen. Bei PHP hat man zum Glück nicht das Problem, dass sich ändernde Klasse von anderen REST-Services anderer Module zu Laufzeitfehlern führen können. Wenn man ein Modul ändert, wird die Klasse ja beim nächsten Request erneut geladen und nicht wie bei Java in der VM gehalten. Probleme kann es nur geben, wenn man die Session verwendet und versucht dort gespeicherte serialisierte Daten einer alten Version in ein Objekt der neuen Version der Klasse zu deserialisieren.
Das macht diese Umgebung in PHP für Microservices interessant. Da man hier wirklich einzelne Module zur Laufzeit austauschen kann, ohne dabei die anderen Module zu beeinflussen (wenn man natürlich vorher drauf geachtet hat, dass Klassen, die auch von anderen Modulen verwendet werden, nicht zu Fehlern bei diesen führen). Zu überlegen wäre auch diese Klassen zwischen den Services als JSON und nur über eine URL dem anderen Service verfügbar zu machen. Also die URL auch zu Intra-Service Kommunikation zu verwenden.

Also sowas wie:

$req=$_REQUEST;
$req["userId"]=0;
$req["_XW_SESSION_SEC"]="XXXXX";
$json=XWRESTServicesLocal::call("/rest/PostWall/wall/1",$req);


Und den JSON-String dann auf eine eigene Klasse zu mappen. So etwas werde ich für die nächste Version dann einbauen.

Das war jetzt ein grober Überblick wie meine Frameworks REST-Services implementieren und versuchen dabei möglichst flexibel zu sein, aber auch nicht dabei zu sehr auf Kosten der Performance zu arbeiten. Die Arbeiten an APF2 sind noch in Gange und deswegen kann sich da auch noch einiges Ändern und vielleicht lerne ich von anderer Seite noch einiges, wie sich auch die letzten Probleme lösen lassen, um so REST-Services noch besser implementieren zu können.

Eine VMDK für VirtualBox vergrößern

Heute mußte ich eine VirtualBox VM mit Oracle Linux 5.5 (Developers Day Variante) anpassen. Die Partition mit dem Home-Verzeichnis war voll und mußte angepasst werden.

Also erstmal über den Manager für die Laufwerke eine Kopie als VDI erzeugt. Dauert .. geht aber gut.
Dann aus dem VirtualBox Verzeichnis heraus die neue Größe setzen:


vboxmanage "c:\.....\disk2.vdi" --resize 20000


Also auf 20GB vergrößert.

Nun muss nur noch die Partition angepasst werden. Nach einige Versuchen und googeln habe ich diese Anleitung gefunden:

http://derekmolloy.ie/resize-a-virtualbox-disk/

GParted lief bei mir in der 0.17.0-4 Version. Eine aktuelle wollte nicht starten. Aber damit ging es leicht.
Erstmal eine neue VM mit der ISO und der disk2.vdi erstellt. Dann starten und alles so lassen wie vorgeschlagen. Resize wählen. Per Drag and Drop die Größe anpassen und dann auf "apply" drücken. Warten.

VM ausschalten und die Oracle Linux VM wieder starten. Alles super!

eigener PHP ClassLoader

Das Geheimnis beim Class-Loading in PHP ist die Funktion __autoload, diese Funktion wird immer aufgerufen bevor eine Instanz einer Klasse erzeugt wird.


function __autoload($classname){

}


Hier kann man also anhand des Klassennamens den Code der Klasse nachladen. Man kann natürlich auch per Hand in jeder Datei über include() die Klassen laden, die man braucht. Meistens läuft es dann darauf hinaus, dass man zu viel lädt und Zugriffe auf das Dateisystem ist eben sehr teuer. Mit der autoload-Funktion ist sicher gestellt nur das zu laden, was auch wirklich benötigt wird und sort dafür, dass möglichst wenige Dateizugriffe durch geführt werden. Wenn also eine Klasse in einem Code-Block verwendet wird der nie ausgeführt wird, wird der Code der Klasse nicht aus dem Dateisystem nachgeladen.

Ein sehr simpler Class-Loader kann also so aussehen:


function __autoload($classname){
if(!class_exists($classname)){
include_once("classes/".$classname.".php");
}
}


Alle Klassen-Dateien müssen an einem bestimmten Ort legen, hier im Order "classes/". Wenn man nun mit Modulen arbeitet, wo ein Modul die Klassen eines übergeordneten Modules nutzen kann, hat man das Problem, dass man nicht immer genau weiß wo eine Klasse liegt und man muss diese Suchen. Bekannt ist das Problem beim Class-Loading in Java, wo ein Package über mehrere JAR-Files verteilt sein kann und man also in allen JAR-Files nach einer Klasse muss, weil man durch Package und Klassennamen nicht auf die JAR schliessen kann in welcher die Klassen-Datei liegt.
Suchen ist natürlich auch sehr teuer, gerade wenn man rekursiv durch Verzeichnisstrukturen laufen will. Wenn man nun bei jeder Klasse alle Verzeichnisse durchläuft bis man eine Klasse gefunden hat, würde alles sehr lange dauern.

Eine Lösung ist, bekannte Pfade sich zu merken, damit man nicht immer wieder das selbe Suchen muss. Wenn man in einem Modul unterwegs ist, hat man es mit maximal zwei Händen voll von Klassen zu tun, die immer und immer wieder verwendet werden. Wenn ich aber durch eine Liste von Dateien laufe und bei jeden Item in der Liste prüfe, ob es das passende ist (ok.. man könnte auch file_exists() innerhalb des Verzeichnisses verwenden...) kann man sich die gefunden Klassen und deren Pfade bis zum Pfund der gesuchten Klasse merken.


function __autoload($classname){
if(!class_exists($classname)){
if(!isset($_SESSION["XWCLASSCACHE"])){
$_SESSION["XWCLASSCACHE"]=array();
}

$cache=$_SESSION["XWCLASSCACHE"];
$seeker=null;
if(!isset($cache[$classname])){
$seeker=new XWRecursiveClassSeeker();
$cache=$seeker->createCache("system/classes/",$classname,$cache);
}

if(isset($cache[$classname])){
try{
$classPath=$cache[$classname];
if(is_file($classPath)){
include_once($classPath);
$_SESSION["XWCLASSCACHE"]=$cache;
}
else{
unset($cache[$classname]);
$_SESSION["XWCLASSCACHE"]=$cache;
}
}
catch(Exception $e){
if(isset($cache[$classname])){
unset($cache[$classname]);
}
echo "<!-- ".$e->getMessage()." -->";
}
}
}
}


Hier wird alles in einen Cache geladen. XWRecursiveClassSeeker durchläuft alle Verzeichnisse rekursiv und vermerkt jeden Pfad zu jeder Klasse auf die er trifft. Wenn die gesuchte Klasse gefunden wurde bricht er ab. Dann wird der Code der Klasse nachgeladen. Alles wird auch in de Session gespeichert so dass man pro Request kaum noch Dateisystemzugriffe zum Suchen hat und nur noch für die includes der Klassen-Datein.
Das Problem ist, wenn man Klassen-Dateien umkopiert, weil dann die Pfade im Cache nicht mehr stimmen. Hier werden diese in dem Fall aus dem Cache entfernt, so dass beim nächsten Versuch die Klasse zuladen wieder neu gesucht wird und es dann auch klappen sollte.

Über dieses Vorgehen, kann man auch in PHP einen relativ peformanten und flexiblen Class-Loader schreiben und muss sich nie wieder selbst um includes von Klassen kümmern!

APF2 - Annonyme PHP Framework 2

Lange Jahre war aoop mein CMS und PHP-Framework für die meisten Projekte, die etwas mehr brauchten als nur JavaScript. Es begann als CMS für meine Homepages. Deswegen unterstützte es die Verwaltung von Pages, konnte mit einer Installation mehrere Domains bedienen und verhielt sich dabei jeweils wie eine komplett eigene Installation. Benutzer-Verwaltung und Logins waren mit im Kern implementiert. Es konnten Module erstellt und für jede Instanz einzelnd deployed werden (jede Instanz konnte sogar eine eigene Version haben, da man später auch pro Instanz eine eigene Datenbank verwenden konnte). Es wurde zu einem doch sehr großem System Vergleich mit einem einfachen kleinen App-Server. Ein Server und eine Installation für alles. Module und Pages wurden immer wieder direkt eingelesen und Menüs automatisch erstellt. Also Modul rein kopieren und damit stand es gleich zur Verfügung.

In letzter Zeit ging ich aber auch dazu über für jeden Bedarf eine eigene Installation mit eigener Datenbank anzulegen, da man bei Problemen so nur eine Installation anfassen musste und bei Updates nur eine Seite für paar Minuten anfassen musste. Die letzten Projekte waren auch Web-Apps die das Framework nutzen aber die Verwaltungsfunktionen des CMS-Teils nicht benötigten. Das Framework ist gut und ich komme damit super zurecht, aber für Web-Apps war dies alles nur Ballast. Web-Apps bestehen auch bei mir momentan aus AngularJS und einem REST-Service. Der REST-Service-Teil von aoop ist relativ neu und nutzt nur wenige andere Klassen des Kern-Systems. Dann kam ich noch mit dem Zend Frameworks in Berührung, die viel hatten, was mir in aoop noch fehlte (Seiten-Aufrufe über URL-Parsing und so.. was aber für die REST-Services schon existierte).

Ich überlegte also mal den REST-Teil heraus zu lösen, um einfacher meine Apps bauen zu können. Ein Problem bei den Apps waren aber die Auslegung auch von AngularJS auf OnePage-Apps. Beruflich hatte ich mit Clients von einem ERP-System zu tun, die nicht ein paar Views und Dialoge hatten sondern pro Modul gerne mal 50 oder mehr und im Client waren auch gerne mal 20 Module installiert. So eine Anzahl an Views und Controllern ist für eine einfache AngularJS-Anwendung zu viel. Also kam ich auf die Idee jeden View/Dialog und seinen Controller als einzelne Komponente zu betrachten und weiterhin Seiten per Request aufzurufen, aber in diesen die Oberfläche aus fertigen Komponenten zusammen zu stellen. Das Laden der Daten und auch Speichern und so sollte die Komponenten dann nur über REST erledigen. Im Grund ein System für 2 Schichten einer 3 Schichten Architektur. REST und Client-View Erzeugung in einem System zusammen gefasst.

Also habe ich mir meine Ideen mal aufgeschrieben und dann angefangen es umzusetzen. Einiges wurde direkt aus aoop übernommen (was auch weiter existieren wird für normale Homepages und so etwas, wo man eben ein CMS braucht). Class-Loader ist eine 1:1 Kopie wo nur ein paar Pfade angepasst wurden.

Damit fing die Entwicklung dann auch an. Class-Loader, neue Modul-Klasse und Klasse zum einlesen aller installierten Module. Dann wurde der REST-Service-Teil kopiert wobei nur ein Klasse ersetzt werden musste, weil eben die Modul-Liste geändert hatte. Damit liefen die REST-Services auf jeden Fall schon mal wieder. PDBC für Datenbankzugriffe musste auch nur
Kopiert werden.

Dann folgte der Teil wo aus Komponenten eine Seite zusammen gebaut werden sollte. Diesmal sollte erst die Page geladen und erzeugt werden bevor angefangen wird das Template zu füllen, was den Vorteil hat, dass jede Seite ihren <title> selbst bestimmen kann und man damit sehr viel flexibler ist. Bei einer Blog-Anwendung können also auch Tags mit in den Title geschrieben werden, obwohl das Template auch für Übersichten und so verwendet werden kann. Also am Ende werden Platzhalter ersetzt.. um es mal genauer zu erklären.

Das URL-Schema ist eigentlich das des REST-Teils geworden:


http://localhost/apf2/rest/testmodul/bla/blubb/1


Eine Page-URL sieht so aus


http://localhost/apf2/content/testmodul/bla/blubb/1


Also Basis-URL, dann folgt der Type an dem entschieden wird ob eine Page oder ein Service gemeint ist, dann der Modulname und nun folgt etwas beliebiges, das durch das Routing oder Mapping der entsprechenden Teile des Frameworks interpretiert wird und am Ende auf einen Service oder eine Page zeigt.

Meine Planung für den Blog ist momentan, dass ich parallel zur Entwicklung ein paar Beiträge schreibe:

* Class-Loader
* REST-Service
* Pages und Komponten
* ein kleines Beispiel
* Zugriff auf Datenbank und automatisches Mapping von Resultsets auf Objekte

Es ist ein kleines Framework an dem man eigentlich leicht verstehen sollte wie man so etwas bauen kann und wie man bestimmte Dinge darin umsetzen kann (nicht muss.. aber meiner Erfahrung nach funktionieren die Dinge sehr gut :-) )

Wlan Repeater

Da in einem Gartenhaus Wlan benötigt wurde habe ich erste Erfahrungen mit einem Netgear Wlan Repeater gesammelt. Installation ging sehr einfach. In die Steckdose stecken, mit dem Wlan des Repeater verbinden. Im Webbrowser das Gerät öffnen über eine URL und das eigene Wlan darin einrichten.. fertig. Der 404-Fehler am Ende war dann auch kein Problem mehr :-)

Netgear WN1000RP-100PES WLAN-Repeater

File-Upload - Part 3 - Daten senden

Nun haben wir unsere Daten in entsprechend aufbereiteter Form. Was noch fehlt ist, dass wir die Daten an unser Script senden, das dann alles Speichert. Das ist an sich der elementare Teil hier und wie wohl erwartet an sich auch der einfachste Teil. Da wir einfach mit append immer hinten an die Datei ran schreiben, bleiben wir synchron. Sollte man asynchron werden wollen, müßte man die Nummer des aktuellen Teils und die gesamt Zahl der Teile mitsenden. Dann och die Größe der Teile und man könnte eine Dummy-Datei erzeugen mit der gesamten Größe und dann die Teile immer an den entsprechenden Bereichen einfügen. Sollte an sich auch nicht so kompliziert sein und am JavaScript-Code würde sich kaum was ändern, ausser den paar mehr Infos im Request.

Aber erstmal alles Synchron, weil wir dann auch einfach mit einer for-Schleife durch unser Array durch laufen können.


var result;
for(var i=0;i<chunks.length;i++){
var last=false;
if(i==(chunks.length-1)){
last=true;
}
result=uploadFileChunk(chunks,filenamePrefix+file.name,url,params,last);
}
return result;


Es wird nur das Result des letzten Teil-Uploads zurück geliefert. Weil dort dann meistens auch die Datei nochmal umkopiert wird und entsprechende Datenbankeinträge vorgenommen werden. Es ist gut die Datei erstmal in einem separaten Verzeichnis als *.part oder so zu speichern und erst wenn der letzte Teil (last Variable) angekommen und gespeichert die DB-Einträge und an einen entsprechenden Ort zu kopieren. Über den Ordner mit den *.part Dateien kann dann ein Cron-Job laufen der alle dort vorhanden *.part Dateien entfernt die länger als 20min nicht geändert wurden.

Ich übergebe noch einen Prefix für den Filename, dann kann am Server userid+filenname+prefix+parterweiterung als Dateiname verwendet werden. Damit ist es auch für einen User möglich Dateien mit selben Dateinamen hochzuladen ohne dass es am Server zu Verwechselungen kommt.

Idealer Weise sollte noch mal der eigentliche Dateiname zusätzlich noch mal mit übergeben werden. Ist hier im Beispiel nicht so hat direkt ersichtlich, weil der Name mit in den "params" steht wo auch noch alle möglichen anderen Daten für das Request mit drin stehen können.


function uploadFileChunk(chunk,filename,url,params,lastChunk) {
var formData = new FormData();
formData.append('upfile', chunk, filename);
formData.append("filename",filename);

for(key in params){
formData.append(key,params[key]);
}

if(lastChunk){
formData.append("lastChunk","true");
}
else{
formData.append("lastChunk","false");
}

var xhr = new XMLHttpRequest();

xhr.open("POST", url,false); //false=synchron;
xhr.send(formData);
console.log("upload response-text: "+xhr.responseText);
return xhr.responseText;
}


Hier der eigentliche Uplaod-Code. Ansich entspricht es einer Form nur eben rein in JavaScript. Die Daten der Form werden dann per AJAX-Request an das Script geschickt.


File-Upload - Part 2 - Die File-API

Nach dem wir nun unser File-Object haben, wollen wir es in kleine Teile zerlegen, die wir dann einzelnd hoch laden
können. JavaScript kann seit einiger Zeit super mit Dateien umgehen. Direkter Zugriff auf das Dateisystem ist natürlich nicht möglich aber Öffnen- und Speicherdialoge reichen ja auch. Um anders Datenabzulegen gibt es noch die indexeddb von JavaScript auf dich in vielleicht in einem weitern Eintrag mal eingehe. Aber ansosnten kommen wir mit Öffnen/Drag and Drop und Speichern vollkommen aus.


function createChunksOfFile(file,chunkSize){
var chunks=new Array();

var filesize=file.size;

var counter=0;
while(filesize>counter){
var chunk=file.slice(counter,(counter+chunkSize));
counter=counter+chunkSize;

chunks[chunks.length]=chunk;
}

return chunks;
}


Die Methode arbeitet an sich ganz einfach. Die Schleife läuft so lange wie die kopierte Größe kleiner ist als die Gesamtgröße der Datei. Bei slice(from,to) gibt man den Anfang und das Ende an. Wenn das Ende hinter dem realen Ende der Datei liegt wird nur das was noch vorhanden war kopiert und kein Fehler geworfen, was es uns hier sehr einfach macht. Wir addieren also z.B. bei jeden Durchlauf 250000 auf die aktuelle Kopie-Größe rauf, bis wir über der Dateigröße liegen. Bei jedem dieser Durchläufe wird von der Position der Größe der Kopie bis zu der Größe + Größe des zu kopierenden Teils, der Teil der Datei mit in ein Array kopiert.

Am Ende haben wir also ein Array mit den Teilen der Datei in der korrekten Reihenfolge.

Man hätte natürlich vorher die Anzahl der Teile ausrechenen können und dann mit einer for-Schleife und für jeden Teil die Position in der Datei berechnen können.. ich fand es so aber erstmal einfacher.


var chunks=createChunksOfFile(file,250000);


Damit erhalten wir Array mit alleien Teilen der Datei zu je ~250KB. Eine Datei von 1MB hätte also ~4 Teile. Alles ungefähr weil eben 250000 keine exakten 250KB sind (1024Byte wären ja 1KB... aber das will uns hier mal nicht interessieren).

File-Upload - Part 1 - Grundlagen

Ein einfacher Fileupload ist einfach zu erstellen. Ein <input> vom Typ "file" in eine Form. "method" auf "post" und "action" auf die Zielseite. enctype="multipart/form-data" nicht vergessen und schon ist alles erledigt.

Nun hat diese Implementierung natürlich ihre Grenzen und bei heuten Web-Anwendungen will man oft nicht mehr nur einfach eine Datei hochladen. Man will Bilder vorher bearbeiten und skalieren. Den Fortschritt des Uploads sehen und mehrere Dateien in einer Queue hochladen lassen. Früher nutzte man für sowas Flash.. aber Flash ist tot. Heute hat man HTML5 und JavaScript. Damit kann man alles realisieren was man sich für einen File-Upload wünscht. Die File-API hilft die Dateien zu Laden (aus einem <input> oder auch per Drag and Drop). Per Notification kann man den Benutzer über den Zustand der Uploads informieren, z.B. eine Benachrichtigung ausgeben wenn eine besonders große Datei fertig hochgeladen wurde.

Der hier entworfene File-Upload ist natürlich auch nicht perfekt, aber er funktioniert gut in mehreren Projekten und Scripte um die Hochgeladenen Dateien entgegen zunehmen und zu Speichern, lassen sich gut in PHP oder als Servlet realsieren.

Für Tests reicht ein einfaches PHP-Script:

PHP:

<?php
//see $_REQUEST["lastChunk"] to know if it is the last chunk-part or not
if(isset($_FILES["upfile"])){
file_put_contents($_REQUEST["filename"],file_get_contents($_FILES["upfile"]["tmp_name"]),FILE_APPEND);
}
?>


oder für Java (FileIOToolKit ist eine eigene Klasse wo append einfach das byte[] an eine Datei ran hängt oder damit eine neue Datei erzeugt, wenn diese noch nicht existieren sollte):

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
try {
String appPath = request.getServletContext().getRealPath("");

String filename = request.getParameter("filename");
for (Part part : request.getParts()) {
if (part.getName().equals("upfile")) {
byte[] out = new byte[part.getInputStream().available()];
part.getInputStream().read(out);

FileIOToolKit.append(appPath + File.separator + filename, out);
System.out.println(appPath + File.separator + filename + " saved!");
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}


Wie man hier schon sieht wird die Datei nicht als ganzes hochgeladen sondern in mehreren Stücken. Das hat den Vorteil, dass man sehr sein den Fortschritt beim Upload bestimmen kann.

Das Laden einer Datei ist relativ einfach. Das Drag and Drop oder das onChange einer <input> vom Typ "file" liefern jeweils ein Event, dass die Dateien enthält (es können immer mehrere sein!).

Hier ein einfaches Beispiel, wobei ein boolean verwendet wird, um die beiden Arten zu unterscheiden:


var files = null; // FileList object
if(!nodragndrop){
files=evt.dataTransfer.files; //input
}
else{
files=evt.target.files; //drag and drop
}


Wir werden aber das Laden ignorieren und davon ausgehen, dass man ein File-Object hat, egal ob aus einer Datei oder vielleicht auch vom JavaScript-Code erzeugt (eine Anleitung wie man die Data-URL in sowas umwandelt findet man in einem älteren Beitrag, wo man lernt wie man ein Bild vor dem Upload automatisch verkleinert).

Im nächsten Teil wird das File dann in kleine Teile zerlegt.

Native-Events und AngularJS

Ich hatte zwar schon mal eine Variante dieser AngularJS Directive gepostet, aber das hier ist die verbesserte Version und sie funktioniert sehr erfolgreich im http://www.schoo-toi.net Projekt.

Anstelel von ng-change="reload()" ( das auf $scope.reload() geht) kann man nge-event="change:reload" schreiben und es geht auch $scope.reload(event) und gibt das native JavaScript Event an die Methode weiter. Um mehrere Event-Listener zu definieren kann man diese mit ; trennen. nge-event="click:open;change:reload".


var nativeEvent=function(){
return {
link:function($scope,el,attrs){
var value=attrs.ngeEvent;
console.log("init nge-event: "+value);
if(el){
var parts=value.split(/[,;]/);
for(var i=0;i<parts.length;i++){
if(parts.length>0){
var func=function(controller,method){
return function(event){
console.log("call method: "+method);
$scope[method](event);
};
};

var subparts=parts.split(/:/);
if(subparts.length==2){
var m=subparts[1].replace(/\(event \)/,"");
m=subparts[1].replace(/\(\)/,"");

var ang_element = angular.element(el);
var raw_element = ang_element[0];

raw_element.addEventListener(subparts[0],func($scope,m),false);
}
}
}
}
else{
console.log("el is null");
}
}
};
};

angular.module('xxx.directives', []).directive("ngeEvent",nativeEvent);
angular.module("xxx",["xxx.directives"]);


xxx ist hier natürlich nur ein Platzhalter.

JavaFX, FXML vs HTML5, AngularJS

Ich durfte jetzt doch einige Monate mit JavaFX für den Client meiner Anwendung verbringen. JavaFX wurde mir gegenüber jeden Falls als der große Heilbringer beworben. Es wäre Swing, SWT und HTML5 weit überlegen. Es wäre schneller und würde Oberflächen ermöglichen die mit HTML nie möglich wären (im besten Falle mit Flash) und würde auch einfach Dinge können, die mit HTML5 unmöglich wären.. Drag and Drop zum Beispiel.

Weitere Vorteile wären, dass man es im Web verwenden könnte, weil es ja als Applet liefe und man könnte es ohne Probleme auf Tablet laufen lassen. Mit VMs von Drittanbietern sogar auf iOS. Android sowie so.

Die Logik meiner Anwendung ist komplett in einem REST-Service implementiert und somit macht der Client wirklich nur eine Sache: Daten und Masken darstellen.

Ein weiterer Vorteil soll auch sein, dass man super einfach selbst Oberflächen zeichnen könnte und man somit nicht auf fertige Komponenten angewiesen wäre. Und das aller Beste wäre FXML wo man die Oberflächen mit einem GUI-Designer bauen könnte und keinen Code schreiben müsse. MVC und Templates. Auch gäbe es super viele Hilfen im Internet.

Also wäre noch auf HTML5 setzen würde, würde auch schlechte und veraltete Technologie setzen.

Jetzt nach dieser Zeit mit JavaFX weiß ich eins, der viel beschworene HTML5-Killer ist JavaFX bei weitem nicht. JavaFX hat noch unglaublich viele Bugs und Probleme. Es killt sich eher mal selber als HTML5. Aber gehen wir die Argumente einfach mal durch:

* HTML5+CSS5 sind sehr viel flexibler was GUIs angeht und der Code ist immer noch vom Menschen lesbar. FXML ist alles andere gut lesbar. JavaFX als Java Code ist da deutlich angenehmer als FXML.
* Flash ist tot und das aus gutem Grund. Selbst Oracle sieht in JavaFX keinen Flash-Ersatz mehr.
* Drag and Drop.. funktioniert in HTML5 sehr viel besser und mit einigen wenigen Zeilencode. Wenn mir jemand erzählt, dass es mit JavaFX in gerade mal 1000 Zeilen-Code nun alles super läuft und ich sehe dass man dafür in HTML5 vielleicht 20 Zeilen gebraucht hätte, kommt mir der Aufwand in JavaFX unglaublich groß vor.
* Applets.. ja.. wer hat das Java Plugin im Browser noch aktiv? Kaum jemand. Applets waren um 2000 rum beliebt. Wer heute noch Applets als tolle Möglichkeit verkauft Java und das Web zusammen zubringen hat die letzten 15 Jahre verschlafen.
* JavaFX und Tablets... HTML5-Apps auf Mobilengeräten sind seit einige Jahren alltag und verdrängen immer mehr native Apps. JavaFX-Unterstützung wurde erst Anfang des Jahres aus der ARM-Distribution des JDK entfernt. RoboVM soll gut laufen, aber an die optimierten Render-Engines wie Gecko oder WebKit wird eine einzelne kleine VM nicht so schnell rankommen. Da zählt auch einfach Erfahrung+Man-Power und da sind Web-Browser sehr weit vorne.

Oft hat man das Gefühl als die Wahl für JavaFX vieler nicht darauf basierend, dass JavaFX besser wäre sondern einfach auf der Ablehnung von JavaScript. Dann kommen Argument wie dass es nativ ja viel einfacher wäre auf System-Resourcen zu zugreifen... mit HTML5 ist der Zugriff auf die Kamera eines Devices sehr viel einfacher als in JavaFX (dank WebRTC).

http://de.slideshare.net/rcuprak/javafx-versus-html5-javaone-2014

JavaFX ist ein tolles Grund-Framework, aber man muss jeden kleinen Furz mehr sich selbst implementieren. Bei HTML5 ist einfach sehr viel schon mit bei (datalist für autocomplete Inputs, Inputs vom Typ "number", AJAX, gegen CSS3 ist das CSS von JavaFX garnichts). Auch das Argument "man muss ja nur einmal implementieren" ist an sich unsinnig, weil auch es kein Mal implementieren zu müssen immer noch weniger Zeit braucht als es einmal zu implementieren.
Eine vorhandene Maske hübscher zu machen dauerte bei jemanden 6h. Mit dem Vorwissen meinte er, er bräuchte nun nur noch 2h. Mit HTML hätte man ca. 20min gebraucht. Ich habe viele Masken auch noch mal in HTML nachgebaut und habe jeweil nicht Stunden sondern Minuten dafür benötigt und sie sahen dafür dann gleich gut aus.

Ein weiteres Argument für JavaFX ist immer wieder dass es eben Java ist. Java ist nicht dynamisch Typisiert was in JavaScript-Anwendungen ja immer zu Problemen führen würde. Diese Probleme konnte ich in den letzten 2 Jahren nicht mehr groß erleben. Besonders bei einem Client für einen REST-Service wo sowie so alles als String in der URL oder im Request landet ist es Ausgabe des Services die Konvertierung in die Datentypen zu übernehmen. Ich hatte bei dem Projekt nicht an einer Stelle ein Problem, dass JavaScript Zahlen und Strings verwechselt hätte.

Für JavaScript gibt es unendlich viele Frameworks. Besonders AngularJS ist hier zu erwähnen. Nun gibt es ja die Behauptung von JavaFX als HTML5-Killer, der HTML5 schon "sehr bald" überflüssig machen werde. Auch hier laufen die Argumentation meist darauf hinaus, dass JavaScript schlecht sein und Java soviel besser. Auch ansonsten sind die Argumentationen sehr interessant.

https://jaxenter.de/javafx-ist-stabiler-als-html5-und-angular-18526

Wenn man diese Argumentation nun nach vollzieht, ist die Aussage grob, dass JavaFX in seiner jetzigen Form länger bestehen bleibt, als bis AngularJS 2.0 released wird (AngularJS 2.0 wird AngularJS 1.x nicht direkt ersetzen), weil einfach bei JavaFX keine große Weiterentwicklung in nächster Zeit erwartet wird.
Ob das jetzt ein Vorteil ist.. da bin ich mir nicht sicher. Weil dann wären nur tote Technologien stabile Technologien.

Seit neusten unterstützt JavaFX nun auch endlich Dialoge. Es hat lange gedauert.

FXML... das ist noch etwas, wo ich mir nicht sicher bin was es überhaupt bringen soll. Es ist keine brauchbare Template-Lösung, da man die Bindings immer noch im Controller machen muss und auch das Befüllen der Komponenten nur im Java-Code möglich ist. Was man mit AngularJS direkt zusammen mit dem HTML-Code schreibt, muss man bei JavaFX also einmal zum Teil im FXML und zum anderen Teil im Java-Code machen. Einträge aus ResourceBundles lassen sich nur sehr schlecht verwenden, weil man diese nicht mit einfachen Strings mischen kann. Ausdrücke wie title="%time (%time_format)" funktionieren einfach nicht. title="%time_and_time_format" wäre mögich.

$http.get().success() durfte man sich so halb-brauchbar nachbauen. Aber immer hin hat JavaFX eine gute Lösung für asynchrone Vorgänge implementiert (Task), damit man Änderungen an der GUI relativ einfach trotz Berechnungen und Abfragen in einem anderen Thread durchführen kann.

Und Hilfe findet man für JavaFX meistens nicht so einfach. Für FXML sowie so so gut wie gar nicht. Wo für HTML5 oder AngularJS Probleme spätestens der 2 Eintrag bei Google eine Lösung liefert, kann es sein dass man bei JavaFX nur Verweise auf deren Bug-Tracker findet.

Insgesamt fühlt sich Entwickeln mit JavaFX an, als würde man gerade an einem Beta-Test teilnehmen. Vieles geht schon, aber es gibt noch grundlegende Bugs und einiges Fehlt auch einfach. Dokumentation ist oft nichtssagend und bei Problemen sind Lösungen oft noch einfach unbekannt.

Am Ende würde ich allein der Entwicklungszeit möglichst immer HTML5+AngularJS JavaFX vorziehen. Wer JavaScript immer noch aus einem Blickwinkel von vor 10 Jahren betrachtet, verpasst viele Entwicklungen, die JavaScript Ding ermöglichen, die vor einigen Jahren niemand für möglich gehalten hat. JavaFX hat mich nur unnötig viel Zeit gekostet und war nicht ansatzweise der Heilsbringer, der es sein sollte. Wäre JavaFX 2 rausgekommen als JavaFX 1 rauskam, wäre es eine echte Alternative für RIAs gewesen. Aber so kommt es einfach viel zu spät und würde schon von vielen anderen Technologien überholt. Wenn man eine vorhandene Java-Anwendung in SWT oder Swing um neue Oberflächen ersetzen will, ist JavaFX toll, aber eine neue Anwendung, die nicht nativ laufen muss, sollte eher portabel in HTML5 entwickelt werden und kann dan auch Desktops und Tablets laufen.

Das ist jeden Falls meine Meinung zu JavaFX.

Older posts:

Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?