Heute habe ich meinen Arbeitsplatz abgebaut.. bzw nur den Rechner, weil der wird dann noch formatiert, bevor jemand anderes ihn dann weiter verwenden darf. Der Rest wird erstmal dort verweilen bis es von den Technik-Leuten entweder abgebaut wird oder die Person, die dann auf meinem Platz sitzen wird. Dabei kommt man irgendwann an den Punkt festzustellen wie dreckig doch die Maus und die Tastatur sind. Und wie es der Zufall natürlich will, hat man auch nichts zum Putzen dabei.
Zuhause wäre das kein Problem, dann dort habe ich das ultimative Reinugungs-Mittel für sowas und noch viel mehr... nicht für alles.. die Einzugsrollen von Druckern sollte man damit nicht versuchen zu säubern. Sonst muss man mit den Fingern die darauf kleben gebliebene Schicht mühseelig abbreiben. Sauber waren sie am Ende dann doch.. aber als gut gemeinter Tipp.. es einfach nicht machen!
Die indexedDB ist eine tolle API, um Daten auf über eine Sitzung hinaus im Browser speichern zu können. Da komplette JavaScript-Objekte dort persistiert werden können, bieten sich viele Anwendungsfälle, wo sie sehr hilfreich sein kann. Z.B. auch das automatische Zwischenspeichern von längeren Eingaben.
Hier wird erklärt wie man ein Objekt mit einer einfachen Id speichern und wieder laden kann.
Dieser Podcast-Beitrag von Heise Developer ist ein wirklich guter Einstieg in AngularJS und seine Begrifflichkeiten. Besonders auch für Leute, die HTML+JavaScript für "richtige" Anwendungen sehr skeptisch gegenüber stehen und diese Technologien für sowas als zu schlecht und ungeeignet ansehen. Es wird dort extra nochmal darauf eingegangen, dass das JavaScript aus IE5 Zeiten lange der Vergangenheit angehört und moderenes JavaScript sehr gut auch für sehr komplexe Anwendungen im online und auch offline Betrieb geeignet ist.
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....
Ich stand mal vor dem Problem, dass ich wie in AngularJS einen REST-Service mit JavaFX abfragen wollte. Es sollte asynchron laufen und dabei die Oberfläche nicht blockieren, so dass alles noch sehr "responsive" auf dem Benutzer wirkt.
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.
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 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.
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:
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.
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.
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:
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!
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();
}
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!
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:
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 :-) )
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 :-)
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]);
}
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.
Das was PDT immer fehlte war die Erzeugung von Gettern und Settern. Bei Klassen verbrachte man viel zu viel Zeit damit diese Methoden hinzu zufügen.. es war unglaublich monoton und fehleranfällig. Aber es gibt Rettung!
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).