Blog: Latest Entries (15):


Einarbeitung in schon bekannte Programme

Wenn man die Überschrift liest, klingt es erst einmal seltsam, da man das Programme und Anwendungen ja schon kennt. Das Problem bei komplexen Anwendungen, die in Projekten und der alltäglichen Arbeit verwendet werden, um alles zu managen, dass nicht die eine Art gibt, wie man die verwendet.

Gerade bei sehr kleinen Teams von 2-3 Personen, die eine solche Anwendung einführen und benutzen, entstehen schnell Strukturen und Abläufe, die sich stark an der Arbeitweise und dem Projekt orientieren. Wenn man nun als neue Person hinzukommt und es bekannt ist, dass man mit einer Anwendung schon gearbeitet hat, ist das Erstaunen oft groß, dass man nicht gleich zu 100% in dem System steckt und auch mal nachfragen muss, wie etwas gehandhabt wird.
Ob es nun das behanedeln voon Tickets in Youtrack oder JIRA ist oder auch das Management von Branches in GIT. Es gibt nicht die eine Art und gerade in kleinen Teams, wo es keine Schnittstellen zu anderen Teams und Einblicke in deren Arbeitweisen gibt, entwickelt sich irgendwie ganz schnell die Ansicht, dass man die richtige Arbeitweise erkannt hätte und alle anderne die produkttiv mit der Anwendung arbeiten, zur gleichen Arbeitsweisse gekommen sein müssen.

Deswegen muss man sich immer im klaren sein dass sich das Einarbeiten nie nur auf die grundlegende Bedienung von Anwendungen und dem Kennenlernen von Code beschränkt, sondern viel mehr das Verstehen der Strukuren, Workflows und Ansichten bzw Dennkweisen der Teams bezieht. Wenn man plötzlich mit einer anderen Programmiersprache arbeiten soll ist auch der Syntax das gerinigste Problem und wenn man deren Konzepte verstanden hat, kommt der Syntax fast von allein hinterher.
Man darf also nicht erwarten, dass bloss weil jemand mit einer Anwendung gearbeitet hat, auch genauso damit gearbeitet hat wie man selbst. Das bietet natürlich auch die Gelegenheit mal über den Tellerrand zu gucken und seine eigenen Abläufe noch mal neu zu überdenken und zu optimieren.

Sepia-Filter für MP4toGIF.com

Nachdem längere Zeit bei MP4toGIF.com nichts mehr passiert ist, habe ich heute mich noch mal mit neuen Filtern auseinander gesetzt und mir eine Umgebung zusammen gebaut, in der ich mit neuen Filtern experimentieren kann. Es ist schon interessant, wie man mit dem Ändern weniger Parameter sehr verschiedene Effekte erzielen kann.

bbcode-image


bbcode-image


Die Test-Umgebung findet man unter http://www.annonyme.de/js/filters/. Da kann jeder gerne mal herum experimentieren und versuchen selbst Einstellungen für einen tollen Effekt zu finden.

In den nächsten Tagen und Wochen, werden dann also wohl noch ein paar Filter mehr für MP4toGIF.com entstehen und dort integriert werden.

Die zentrale Render-Function ist sehr einfach aufgebaut:

function render(src, trg, rm, ra, gm, ga, bm, ba, useGreyScale){
var srcData=src.getImageData(0,0,src.canvas.width,src.canvas.height);
var data=srcData.data;
for(var i=0;i<data.length;i+=4){
if(useGreyScale){
var avg = (data[i+0]*rm) + (data[i+1]*gm) + (data[i+2]*bm);
data[i+0]=avg;
data[i+1]=avg;
data[i+2]=avg;
}
else{
data[i+0]=data[i+0]*rm;
data[i+1]=data[i+1]*gm;
data[i+2]=data[i+2]*bm;
}

data[i+0]=Math.abs(data[i+0]+parseInt(ra));
data[i+1]=Math.abs(data[i+1]+parseInt(ga));
data[i+2]=Math.abs(data[i+2]+parseInt(ba));
}
trg.canvas.width=src.canvas.width;
trg.canvas.height=src.canvas.height;
trg.putImageData(srcData,0,0);
}


Invertieren des Bildes ist zum Beispiel: Multiplicator auf 1 und darauf -255 addieren.

Komplexe Probleme und komplexer Code

Komplexe Probleme führen zu komplexen Code. Einfache Probleme führen zu einfachen Code. Das sind die Gesetzmässigkeiten, die immer gelten. Sollte mal ein einfaches Problem zu komplexen Code führen, muss man den einfach wegwerfen und noch mal von Vorne anfangen, weil man irgendwas falsch gemacht hat. Das passiert, aber man erkennt den Fehler schnell und kann ihn korrigieren.

Schwierig wird es wenn ein Problem ein komplex ist. Komplexität ist sowie so ein Problem für sich. Jeder der schon mal Planning Poker gespielt hat, weiß wie schlecht sich Komplexität erkennen und bewerten läßt. Ich habe für mich eine kleine Staffelung erstellt, nach der ich mich richte. Der Knackpunkt bei der Bewertung sind Schnittstellen. Egal ob es andere Systeme sind oder andere Menschen.. sobald Schnittstellen da sind hat man mehr Komplexität und Overhead. Jeder muss das Problem verstehen, seine Aufgaben ausführen und Zeiten einhalten. Wenn das nicht klappt ist es oft auch nicht mal das Problem der anderen Menschen oder Systeme. Fehlerhafte Dokumentation und unklare oder unteschiedlich verwende Begriffe sind fast immer am Anfang dabei und müssen ereinmal geklärt werden.
Deswegen ist zum Beispiel Dokumentation für mich eine hoch komplexe Sache, weil dort immer gleich 2 Schnittstellen existieren. Einmal müssen die Infos rein und dann wieder verständlich für andere raus. Jeder der mal eine 70 Seiten Dokumentation über eine System für andere Entwickler geschrieben hat, wird mir hoffetnlich beipflichten können.

Aber nun zur Tabelle:

* 0: nichts zu tun, da schon erledigt oder obsolete
* 1/2: es müssen Texte oder andere statische Werte geändert werden
* 1: es muss vorhandere Code in der Logik minimal angepasst werden (if-Bedinungen anpassen oder Try-Catch Blöcke setzen)
* 2: Es muss in einer vorhanden Struktur Code erzeugt werden (eine Methode hinzufügen oder Hilfs-Klasse erstellen)
* 3: Es werden Daten verarbeitet (Einfacher Importer oder Ein- und Ausgaben)
* 5: Daten werden um gewandelt oder einfache einfache Schnittstellen entgegen genommen oder ausgegeben (Importer wo die Daten aufbereitet werden müssen)
* 8: Es wird Logik erstelle (Berechnungen und Daten operationen, einfache Planung ohne große Konzeption)
* 13: Logik mit Schnittstellen, wo die andere Seite beeinflusst werden kann, z.B. Fehler wirft, wenn fehlerhafte Daten geliefert werden (Man muss Planen und die Konzeption muss besprochen werden)
* 20: Die Schnittstellen oder Funktionsweisen sind noch zu erarbeiten (Die Realisierung ist machbar, aber es ist noch nicht klar, wie man zum Ziel kommt)
* 40: Man weiß, was man will, aber die Realiserung benötigt Forschung (Viele Meetings und es gibt Ansätze wobei diese auch sich noch als nciht zielführend erweisen können)
* 100: Die Realiserbarkeit kann angenommen werden, aber nicht mit bekannten Mitteln (man muss also richtige Grundlagenforschung betreiben und sich viel Rat einholen)
* unendlich: Man geht von einer Realiserbarkeit aus, aber weiß, dass bis jetzt niemanden gelungen ist so etwas umzusetzen.... z.B. wenn man vorhat einen Fusionsreaktor zu bauen

Da komplexe Probleme zu komplexen Code führen und für diesen gilt, dass er schwer zu warten, lesen und zu erweitern oder ändern ist, muss man den Code vereinfachen. Wer mal versucht hat ohne Neibewertung des Problems von gleichen Ausgangslagen den Code zu verbessern wird gelernt haben, dass man nur wieder anderen komplexen Code geschrieben hat. Man kann Codenicht vereinfachen ohne die Komplexität des Problems runter zu brechen. Das sollte man tun. Probleme in kleinere und einfachere Probleme aufbrechen. Lieber eine Woche länger das Problem analysieren, als Code schreiben der andere und einen selber später noch viel mehr Zeit kosten wird.

bbcode-image


Deswegen.. nie versuchen den Code zu vereinfachen sondern immer das Problem vereinfachen und dann den Code anpassen oder besser noch einfach auch neuschreiben.

Neues Projekt mit aoop

Nach 9 Jahren geht es mit aoop doch mal los. Ein weiters Projekt mit meinem Framework startet morgen. Es basiert auf der neusten Version mit dem Blog-Modul.
Ich bin auch technischer Admin und betreue alles auf technischer Ebene. Logos und Design stammen von meiner Frau und sind wirklich toll. Wer es sich mal ansehen möchte findet es unter http://www.tentacle-news.net.
bbcode-image

Neo4J: CSV Import aus MySQL

bbcode-image


Oft werden NoSQL für sehr spezielle Fälle eingesetzt. Die normale Datenhaltung bleibt weiter hin den SQL-Datenbanken überlassen. Also müssen regelmäßig die Daten aus dem SQL-Bestand in die NoSQL Datenbank kopiert werden. Das dauert oft und viele aufbereitungen der Daten wird schon hier erledigt. die NoSQL Varianten sind deswegen auch oft schneller, weil man eine Teil der Arbeit in den Import-Jobs erledigt, die sonst bei jedem Query als Overhead entstehen. Natürlich haben die NoSQL auch ohne das ihre Vorteile, aber man sollte immer im Auge behalten, ob die Performance von der Engine kommt oder auch von der Optimierung der Daten, weil die Optimierungen der Daten könnte man auch in die SQL-Struktur zurück fließen lassen und diese in die Richtung hin verbessern.

So ein Import dauert... wenn man in der Nacht ein Zeitfenster von einer Stunde hat, ist alles kein Problem. Will man aber auch in kurzen Abständen importieren, muss der Import schnell laufen. Auch wenn man als Entwickler öfters mal den Import braucht, ist es wichtig möglichst viel Performance zu haben.
Hier geht es darum wie man möglichst schnell und einfach Daten aus einer MySQL Datenbank in eine Neo4j Graphen-Datenbank importieren kann, ohne viel Overhead zu erzeugen. Ich verwende hier PHP, aber da an sich keine Logik in PHP implementiert werden wird, kann man ganz leicht auf jeden andere Sprache, wie Java, JavaScript mit node.js und so übertragen. Es werden keine ORMs verwendet (die extrem viel Overhead erzeugen und viel Performance kosten) sondern nur SQL und Cypher.

Wie man einfach sich eine oder mehrere Neo4J-Instanzen anlegt (unter Linux) kann man hier sehr gut sehen:


Wir verwenden bei Neo4j den Import über eine CSV-Datei. Wir werden also nicht jeden Datensatz einzeln Lesen und Schreiben, sondern immer sehr viele auf einmal. Ob man alles in einer Transaktion laufen lässt und erst am Ende commited hängt etwas von der Datenmenge ab. Bis 200.000 Nodes und Relations ist alles kein Problem.. bei Millionen von Datensätzen sollte man aber nochmal drüber nachdenken.
PERIODIC COMMIT ist da eine super Lösung, um alles automatisch laufen zu lassen und sich nicht selbst darum kümmern zu müssen, wann commited wird. Alle 1000 bis 10_000 Datensätze ein Commit sollte gut sein, wobei ich eher zu 10_000 raten würde, weil 1000 doch noch sehr viele Commits sind und so mit der Overhead noch relativ groß ist.

Unsere Beispiel Datenbank sieht so aus:


CREATE TABLE USERS(
USER_ID INT(11) UNSGINED NOT NULL,
USER_NAME VARCHAR(255) NOT NULL,
PRIMARY KEY (USER_ID)
);

CREATE TABLE MESSAGES(
MESSAGE_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
MESSAGE_TITLE VARCHAR(255) NOT NULL,
FROM_ID INT(11) UNSIGNED NOT NULL,
TO_ID INT(11) UNSIGNED NOT NULL,
CC_ID INT(11) UNSIGNED NOT NULL,
PRIMARY KEY (MESSAGE_ID)
);


Wir legen uns 50.000 User an dann noch 100.000 Messages mit jeweils einen FROM, einem TO und einem CC (hier hätte man über eine Link-Table sollen, aber das hier ist nur ein kleines Beispiel, wo das so reicht). Das sollten erst einmal genug Daten sein. (Offtopic: da ich das gerade neben bei auch in PHP schreibe.. warum kann ich für eine 100000 nicht wie in Java 100_000 schreiben?)

Die erste Schwierigkeit ist es die Daten schnell zu exportieren. Ziel ist eine CSV. Wir könnten entweder über PHP die Daten lesen und in eine Datei schreiben oder aber einfach die OUTFILE-Funktion von MySQL nutzen, um die Datenbank diese Arbeit erledigen zu lassen. Wir werden es so machen und erstellen für jede Art von Nodes und Relations eine eigene CSV. Weil wir Header haben wollen fügen wir diese mit UNION einmal oben hinzu


$sql="
SELECT 'user_id', 'user_name'
UNION
SELECT USER_ID,USERNAME
FROM USERS
INTO OUTFILE ".$exchangeFolder."/users.csv
FIELDS TERMINATED BY ','
ENCLOSED BY ''
LINES TERMINATED BY '\n'
";


Damit schreibt MySQL das Ergebnis des Queries in die angegebene Datei. Falls ein Fehler auftritt, muss man gucken, ob der Benutzer unter dem die MySQL-DB läuft in das Verzeichnis schreiben darf und ob nicht eine Anwendung wie apparmor unter Linux nicht den Zugriff blockiert. Es darf keine Datei mit diesen Namen schon vorhanden sein, sonst liefert MySQL auch nur einen Fehler zurück. Wir müssen
die Dateien also vorher löschen und dass machen wir einfach über PHP. Also muss auch der Benutzer unter dem die PHP-Anwendung läuft entsprechende Rechte haben.
Man kann das gut einmal direkt mit phpmyadmin oder einem entsprechenden Programm wie der MySQL Workbench testen. Wenn die Datei erzeugt und befüllt wird ist alles richtig eingestellt.

Mit dem Erstellen der CSV-Datei ist schon mal die Hälfte geschafft. Damit der Import auch schnell geht brauchen wir einen Index für unsere Nodes. Man kann einen Index schon anlegen, wenn noch gar kein Node des Types erstellt wurde. Zum Importieren der User benutzen wir folgendes Cypher-Statement:


$cyp="USING PERIODIC COMMIT 10000
LOAD CSV WITH HEADERS FROM "file:///".$exchangeFolder."/messages.csv" AS row
MERGE (m:message{mid:row.msg_id,title:row.msg_title});";


Der Pfad zur Datei wird als File-URL angegeben. Hier merkt man auch Neo4J seine Java-Basis an. Wenn man mal in eine Temp-Verzeichnis schaut sieht man dort auch Spuren von Jetty.

Am Ende wird der Importer nur eine Reihe von SQL und Cypher Statements ausführen. Wir benötigen um komfortabel zu arbeiten 3 Hilfsmethoden. Dass alles in richtige Klassen zu verpacken wäre natürlich besser, aber es reicht zum erklären erst einmal ein Funktionsbasierter Ansatz.

Da MySQL keine Dateien überschreiben will, brauchen wir eine Funktion zum Aufräumen des Verzeichnisses über das die CSV-Dateien ausgetauscht werden. Wir räumen einmal davor und einmal danach auf. Dann ist es kein Problem den Importer beim Testen mal mittendrin zu stoppen oder wenn er mal doch mit einem Fehler abbricht.


function cleanFolder($folder){
$files=scandir($folder);
foreach($files as $file){
if(preg_match("/\.csv$/i", $file)){
unlink($folder."/".$file);
}
}
}


Für SQL wird wieder mein PDBC verwendet.


include_once("PDBC/PDBCDBFactory.php");
PDBCDBFactory::init("PDBC/dbclasses/","PDBC/conffiles/");
$db=PDBCCache::getInstance()->getDB("embdoop");


Für Neo4J bauen wir uns eine eigen kleine Funktion.


use Everyman\Neo4j\Client;
use Everyman\Neo4j\Cypher\Query;

$client = new Everyman\Neo4j\Client();
$client->getTransport()->setAuth("neo4j","blubb");

function executeCypher($query){
global $client;
$query=new Query($client, $query);
$query->getResultSet();
}


Der Rest ist nun sehr einfach und linear. Ich glaube ich muss da nicht viel erklären und jeder Erkennt sehr schnell wie alles abläuft. Interessant ist wohl das Cypher-Statement für die Receive-Relations, da neben der Relation diese auch mit einem Attribute versehen wird im SET Bereich.


//clear for export (if a previous import failed)
cleanFolder($exchangeFolder);

//export nodes
echo "create users.csv\n";
$sql=" SELECT 'user_id', 'user_name' UNION
SELECT USER_ID,USER_NAME
FROM USERS
INTO OUTFILE '".$exchangeFolder."/users.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY ''
LINES TERMINATED BY '\n'";
$db->execute($sql);

echo "create messages.csv\n";
$sql=" SELECT 'msg_id', 'msg_title' UNION
SELECT MESSAGE_ID, MESSAGE_TITLE
FROM MESSAGES
INTO OUTFILE '".$exchangeFolder."/messages.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY ''
LINES TERMINATED BY '\n'";
$db->execute($sql);

//export relations
echo "create relations_etc.csv\n";
$sql=" SELECT 'user_id', 'msg_id', 'type' UNION
SELECT TO_ID, MESSAGE_ID, 'TO'
FROM MESSAGES
UNION
SELECT CC_ID, MESSAGE_ID, 'CC'
FROM MESSAGES

INTO OUTFILE '".$exchangeFolder."/relations_etc.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY ''
LINES TERMINATED BY '\n'";
$db->execute($sql);

echo "create relations_from.csv\n";
$sql=" SELECT 'user_id', 'msg_id', 'type' UNION
SELECT FROM_ID, MESSAGE_ID, 'FROM'
FROM MESSAGES

INTO OUTFILE '".$exchangeFolder."/relations_from.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY ''
LINES TERMINATED BY '\n'";
$db->execute($sql);

//create indexes for fast import
echo "create index's in neo4j\n";
$cyp="CREATE INDEX ON :user(uid);";
executeCypher($cyp);
$cyp="CREATE INDEX ON :message(mid);";
executeCypher($cyp);

//import nodes
echo "import users.csv\n";
$cyp="USING PERIODIC COMMIT 10000\n
LOAD CSV WITH HEADERS FROM "file:///".$exchangeFolder."/users.csv" AS row\n
MERGE (u:user{uid:row.user_id,name:row.user_name});";
executeCypher($cyp);

echo "import messages.csv\n";
$cyp="USING PERIODIC COMMIT 10000
LOAD CSV WITH HEADERS FROM "file:///".$exchangeFolder."/messages.csv" AS row
MERGE (m:message{mid:row.msg_id,title:row.msg_title});";
executeCypher($cyp);

//import relations
echo "import relations_from.csv\n";
$cyp="USING PERIODIC COMMIT 10000
LOAD CSV WITH HEADERS FROM "file:///".$exchangeFolder."/relations_from.csv" AS row
MATCH(u:user{uid:row.user_id})
MATCH(m:message{mid:row.msg_id})
MERGE (u)-[r:send]->(m);";
executeCypher($cyp);

echo "import relations_etc.csv\n";
$cyp="USING PERIODIC COMMIT 10000
LOAD CSV WITH HEADERS FROM "file:///".$exchangeFolder."/relations_etc.csv" AS row
MATCH(u:user{uid:row.user_id})
MATCH(m:message{mid:row.msg_id})
MERGE (m)-[r:receive]->(u)
SET r.type=row.type;";
executeCypher($cyp);

//clear after import
cleanFolder($exchangeFolder);


Hier sieht man wie der Importer die 50.000 User, 100.000 Messages und insgesamt 300.000 Relations von einer MySQL in die Neo4J Instanz importiert.



Die Festplatte ist nur über SATA-2 Angeschlossen und nicht besonders schnell. Eine SSD, wie für Neo4J empfohlen, würde alles sehr beschleunigen.

Zum Löschen aller Daten aus der Neo4J kann man diese Statement verwenden:


MATCH (n)
DETACH DELETE n

Linux zum Arbeiten

In meinem Job und auch privat, bin ich jetzt teilweise auf Linux umgestiegen. Auch den Post hier schreibe ich auf meinem Notebook mit Linux Mint. Ok.. trotzdem benutze ich dafür noch Notepad++. Für den habe ich unter Linux noch keinen Ersatz gefunden, bei dem ich den Editor einfach schliesen kann, ohne alles speichern zu müssen und bei dem alles wieder so ist wie als ich ihn geschlossen habe, wenn ich den Editor wieder öffne.

Aber insgesamt, habe ich festgestellt, dass ich mit Linux sehr gut auskomme. Die meisten Programme die ich verwende gibt es für Linux und für fast alle anderen gibt es einen guten Ersatz. Sowohl Eclipse als auch PHPStorm laufen perfekt unter Linux. In PHPStorm macht das Konsolen-Fenster jetzt auch richtig Sinn, weil die Linux Konsole doch sehr sinnvoller da ist als die primitive Konsole von Windows.
GIT läuft wie gewohnt. SourceTree vermisse ich auf Linux etwas, wenn auch die GIT-Integration von Eclipse sehr gut funktioniert und für das normale Arbeiten vollkommen ausreicht.

Firefox, MySQL, Apache.. alles unter Linux genau so wie unter Windows.

Wärend ich bei der Arbeit Ubuntu benutze, verwende ich privat Linux Mint. Wenn ich wählen kann, würd ich immer zu Linux Mint greifen. Es scheint mir etwa runder zu sein und der Cinnemon-Desktop gefällt mir sehr viel besser als die Unity-GUI von Ubuntu.

Auf meinem PC läuft weiterhin Windows 10 und ich werde dort auch erstmal dabei bleiben, weil um mal ein Spiel zwischen durch zu spielen ist Windows doch noch die bessere Wahl.

Auf jeden Fall ist Linux eine gute Umgebung um darin zu entwickeln und ich habe momentan mit Job und anderen Projekten sehr viel zu tun. Deswegen werden es im Blog wohl auch in den nächsten Monaten weniger Beiträge werden. Dafür werde ich versuchen mal wieder ein oder zwei längere Post zu verfassen und auch mal ein oder zwei kleine Tutorial-Videos dazu reinstellen.

Hier der erste Versuch: Wie man Notepad++ unter Linux verwendet.


Sei Professionell.. oder so

Ich habe in den letzten Tag Soft Skills etwas weiter gelesen. Eines der Kapitel dreht sich darum professionell zu sein. Aber die Frage, was genau professionell ist wird da nicht wirklich ausreichend beantwortet.
Es wird das Beispiel gegeben, dass eine Person bei einem morgendlichen Scrum Stand-up Meeting (was eine super Sache ist und wirklich von jedem Team gemacht werden sollte!) immer schriftlich festgehalten hat, was die Person gemacht hat und was sie vorhat an dem Tag zu machen. Ich nehme auch täglich an so einem Meeting teil und finde, dass in so einer Situation als professionell gilt.

Mir kann niemand erzählen, dass man sich nicht an alles was man am vorherigen Tag getan hat erinnern könnte, wenn man es nicht aufschreibt. Auch ohne Notizen kann man dabei sehr professionell sein. Ich würde die Professionalität eher an solchen Dingen fest machen:

* Man kann direkt und klar sagen, was man getan hat und was man vor hat zu tun
* Man beginnt nicht mit "Ja.. was hab ich eigentlich gestern gemacht..."
* Man sagt nicht nur ein Schlagwort. Wenn man ein Thema über Tage hat (was ja nicht selten ist), sollte man einmal kurz Unterpunkte nennen und den anderen damit auch einen kleinen Einblick geben, was durch das Thema alles betroffen ist
* Direkte Antworten auf direkte Nachfragen geben können
* Aufgaben und Todos notieren und nicht einfach nur abnicken

Man muss einfach direkt und klar kommunizieren, wenn man mit 8 Personen ein 15 Minuten Stand-up Meeting nicht immer überziehen will.

Auch in anderen Situationen ist Professionalität hauptsächlich das Auftreten und das Kommunizieren. Man sagt konkrete Termine und hält diese ein, sollte ein Termin nicht zu halten sein, sagt man rechtzeitig Bescheid. Man verschiebt nicht immer alles auf den letzten Augenblick.

Professionell zu sein, bedeutet auch nicht, dass man alles Weiß oder keine Fehler macht. Aber man Fragt rechtzeitig und gezielt nach und wenn man einen Fehler macht, streitet man diesen nicht ab oder versucht in als unbedeutend hinzustellen, sondern man fragt nach wo der Fehler genau lag und wie man in am Besten beheben kann und zeigt sich interessiert und lernbereit.

Man sieht schon, man braucht keine Führungsposition um professionell zu sein. Auch der neue Azubi kann das sein. Wenn man verlässlich ist, hat man schon 90% auf dem Weg hin zum Professional geschafft.

PS: Ein Anzug kann helfen, aber hilft allein nicht. Aber ein Anzug kann gut sein, um sich selbstsicherer zu fühlen und damit kommt man schon mal sehr viel besser rüber, als wenn man unsicher ist.

bbcode-image

Der Softwareentwickler: der Problemlöser

Was ist eigentlich die Aufgabe eines Softwareentwicklers? Natürlich Software entwickeln.. das ist ja klar. Aber warum entwickelt man Software.
Ok.. erst einmal natürich, weil es der Beruf ist man von irgendwas leben muss. Aber warum entwickelt man Software in seiner Freizeit? Geld? Ansehen? Aber in den meisten Fällen einfach um ein Problem zu lösen.
Selbst wenn es um Geld geht sollte an sich das Problemlösen im Mittelpunkt stehen. Wenn ein Kunde gerne eine Homepage mit einem entsprechenden System hätte, um seinen Job besser erledign zu können, schreibt man ihm ein Angebot. Individuelle SYsteme sind aber teuer. Wenn der Kunde sich es also nicht leisten kann, sollte man nicht einfach dann sagen "Pech gehabt!", sondern überlegen wie man das Problem lösen kann.
Die Lösung heißt nicht auf Geld zu verzichten, man muss ja auch bezahlt werden, aber es gibt immer eine Lösung. Wieso nich ein System entwickeln, dass der Kunde verwenden kann un sich trotzdem noch an andere verkaufen läßt?

Die Software die man entwickelt ist nur da Werkzeug, um die Probleme zu lösen. Daran sollte man immer denken. Eine gute Software ist nicht gut, weil sie viele Features hat oder hübsch aussieht, sie st gut weil sie ein Problem löst.

Darum nutzen viele noch Jahre alte Software, weil sie einfach das Problem gut löst. Es geht nicht um Technik oder immer das modernste zu haben. Die Umsetzung sollte gut sein, damt die Software lange stabil läuft.. aber sie mus kein Meiterwerk an Abstraktion und Programmierkünsten sein.

Das macht einen guten Softwareeentwickler aus.. Software zu schreiben, die die Probleme der Leute löst. Oder sie soweit ablenkt, dass sie die Probleme mal vergessen können :-)

CSV direkt aus MySQL exportieren

Auch heute in Zeiten von JSON und XML ist einer der Haupt Import- und Export-Formate immer noch CSV. Der Vorteil ist eben, dass es sich einfach erstellen lässt, einfach einlesen und zur Kontrolle in einem Texteditor laden lässt. Excel kann es auch irgendwie und OpenOffice bzw LibreOffice kann super damit umgehen.

Meistens erstellt man die Dateien ja in dem man Daten aus der Datenbank lädt und dann das Resultset durchläuft und direkt ausgibt oder in einen String schreibt, den man in eine Datei schreibt. Der Vorteil ist, dass man die Daten noch mal bearbeiten kann. Aber wenn man nicht zu komplexe Bearbeitungen vornehmen will und man diese auch mit SQL erledigen kann, gibt es auch die Möglichkeit CSV-Dateien direkt in der Datenbank (hier MySQL) zu erstellen.

Gerade wenn man die Datei nicht ausgeben will über eine PHP Datei sondern sie direkt in einer Verzeischnis kopiert (auch über FTP oder SSH) und die Datei dann von dort von einem anderen System eingelesen wird (z.B. von Neo4J.. so bin ich darauf gekommen) ist dieses Vorgehen sehr viel performanter (gerade wenn OR-Mapper im Spiel sind) als das normale Vorgehen.


SELECT ID,NAME
FROM TEST
WHERE ID>5
INTO OUTFILE '/var/www/app/data/export/out_test.csv'
FIELDS TERMINATED BY ','


Wenn man das FILE-Right hat, kann man so eine CSV in ein beliebiges Verzeichnis schreiben.

Man muss nur sicher stellen, dass man die nötigen Rechte im Verzeichnis hat und dass keine Programme wie apparmor das Schreiben verhindern. ERRCODE 13 wäre der Fehlercode der in so einem Fall angezeigt werden würde.

Wenn man nun alle Rechte erteilt hat und es nicht geht und apparmor läuft, kann man kontrollieren, ob MySQL davon überwacht wird.

sudo aa-status


Um den Zugriff auf das Verzeichnis zu erlauben muss man folgendes tun. Man muss /etc/apparmor.d/usr.sbin.mysql.d bearbeiten und einfach den Pfad an die vorhanden anfügen. Dann mit noch mal apparmor neu laden und es sollte gehen.


sudo /etc/init.d/apparmor reload

Firefox OS - Friedhof

Da leider auch die WebRuntime des Firefox Desktop-Browsers bald eingestellt werden so und es so nicht mehr möglich sein wird Apps aus dem Marketplace auf PCs oder Android Geräten zu installieren habe ich beschlossen in die Richtung keine Weiterentwicklung meiner Apps mehr zu betreiben oder noch etwas in den Firefox Marketplace einzustellen.

Ich habe meine nun toten Apps daher auf den Friedhof verband :-)

bbcode-image


Da finden sich erst einmal nur die Links zum Öffnen. Die Möglichkeit die Apps von dort auf Firefox OS Smartphones zu installieren, folgt demnächst.

Performance und Anführungszeichen in PHP

Immer wenn ich jemanden Fragen höre "Weißt du, was der Unterschied zwischen einfachen und doppelten Anführungszeichen in PHP ist?" Denke ich immer: "Gleich.. gleich kommt es wieder mit der Performance". Und es kommt immer die selbe Antwort am Ende raus, dass doppelte Anführungszeichen langsamer sein als die einfachen.
Ich habe es getestet und die doppelten waren meistens minimal schneller, aber nicht so sehr dass man nicht sagen kann, dass eine der beiden Varianten schneller als die andere wäre. Der Opcode sieht auch gleich aus. Die meisten vergessen einfach dabei, dass auch bei PHP der Code erst einmal geparst und optimiert wird und dann erst der erzeugte Opcode ausgeführt wird. Bei der Ausführung hat sich das mit den verschiedenen Anführungszeichen schon erledigt, weil String intern immer gleich abgebildet werden.

Also wenn keine Variablen im String vorkommen, macht es keinen Unterschied, welche Art von Anführungszeichen verwendet werden. Und der Punkt als Concat-Zeichen schneller ist als Variablen direkt im String anzusprechen ist wieder eine andere Frage.

Ausführlich und sehr gut wird es hier erklärt: https://nikic.github.io/2012/01/09/Disproving-the-Single-Quotes-Performance-Myth.html

und auch hier gibt es einen guten Beitrag zu dem Thema: https://www.soeren-hentzschel.at/sonstiges/single-quotes-vs-double-quotes-in-php-strings/


Das Ende von Firefox OS

Ging dann ja doch schneller als erwartet. Der Marketplace wird wohl 2017 geschlossen.
Alle nicht Firefox OS Apps haben sich wohl schon 29. März erledigt. Ich werde dann also meine Einreichungen, die abgelehnt wurden, auch auf dem Stand belassen und keine weitere Arbeit mehr in die Richtung investieren.

Mich würde aber wirklich mal interessieren wie viele Firefox OS Smartphones es auf der Welt wirklich gibt und wie viele diese noch weiter verwenden würden. Einen eigenes Repository zu schreiben, wo Entwickler ihre Anwendungen hochladen oder verlinken könnten. So etwas ist bestimmt in 2-3 Tagen zu machen. Bei nicht gepackten Anwendungen ist es sehr einfach, weil man nur die URL zur Manifest-Datei an eine JavaScript-Funktion übergeben muss. Bezahl-Apps oder Support für In-App Käufe wäre da natürlich nicht drin. Keine großen Freigaben oder so.. jeder lädt einfach den Link oder die Zip-Datei hoch... und andere können sich die App über einen Klick auf einen Button installieren.

So etwas wäre ein lustiges kleines Projekt und Firefox OS ist eigentlich eine tolle Umgebung für Entwickler, weil man keine großen Entwicklungsumgebungen oder ähnliches braucht und ein Firefox-Browser zum Testen vollkommen reicht.
Dafür Apps zu entwickeln hat immer viel Spaß gemacht und ging immer sehr schnell.

Am Ende glaube ich nicht, dass es sich lohnen würde, in das gesamte Firefox OS Ökosystem noch mal Zeit zu investieren. Es ist wirklich schade um das System.

Bewerben bei einer bestimmten Firma

Einige Ansichten in Soft Skills sind recht interessant. Zum Beispiel wenn man den Wunsch hat bei einer bestimmten Firma zu arbeiten. Den Tipps im Buch nach sollte man erst einmal heraus finden wer da so arbeitet und ob diese Personen Blogs oder ähnliches haben. Dann anfangen deren Blogs zu lesen und auch zu kommentieren. Wichtig dabei den echten Namen zu verwenden und möglichst konstruktiv beim Kommentieren sein. Mit etwas Glück folgen dann ein paar der Leute zum eigenen Blog. Das ganze führt dazu, dass wenn man sich bei der Firma bewirbt, einigen der Name schon etwas sagt und schon eine gewisse Vertrautheit begleitet.
Das selbe ist es auch vor einem Bewerbungsgespräch oder einer Bewerbung schon mal Kontakt mit der Firma aufzunehmen und sich persönlich über die Firma und die Stelle zu informieren. Dabei geht es auch nur darum, dass wenn man dort zum Gespräch auftaucht schon der Gegenüber einen schon kennt, was einen doch einen großen Vorteil einbringt und einen auch sicherer im Auftreten macht.

bbcode-image


Ich hatte auch mal eine Zeit überlegt, mich bei PHP-Treffen sehen zu lassen nur, um vielleicht da in Kontakt mit einer Firma zu kommen, die sehr viel versprechend aussah.. am Ende habe ich es nicht gemacht. Aber an sich ist so ein Vorgehen bestimmt ganz gut.

Wer gerade einen Job such kann, dass ja mal ausprobieren.

IDE einrichten für Einsteiger - Teil 2 - Tomcat

Java + Tomcat (Teil 2)

Nachdem wir uns im letzte Teil schon eine Entwicklungsumgebung für Java eingerichtet haben, können wir Desktop Anwendungen schreiben. Aber für Serveranwendungen benötigen wir einen Servlet Container oder einen Application Server. Für Microservices gibt es auch andere Frameworks, aber wir beschränken uns hier erstmal auf eine einfache JSP-Seite.
Ein kleines Servlet kommt auch noch hinzu. Man könnte auch direkt mit JSF anfangen, aber um sich mit dem Thema Webdevelopment in Java vertraut zu machen fangen wir ganz einfach an. Mit JSP und Servlet kann man auch alles bauen. Von einfachen Webanwendungen mit einzelnen Pages, über MVC-Frameworks bis hin zu REST-APIs. Für das meiste gibt natürlich fertige Frameworks, aber es in einem kleinen Beispiel mal selbst zu versuchen, bringt einen oft einen besseren Einblick in diese Bereiche und man versteht, die vorhandenen Frameworks besser und auch warum sie so funktionieren wie sie funktionieren.

Zuerst downloaden wir uns einen aktuellen Tomcat 8. Den Tomcat Servlet-Container gibt es schon seit vielen Jahren.. eigentlich seit Anfang an und entsprang dem Jarkata-Projekt von Apache. Ich bin 2004/2005 mit dem Tomcat 4 angefangenm wo noch alles unter Apache Jarkata tomcat lief und erst später dann mit der 5er Version zum Apache Tomcat wurde. Man wird aber den Begriff Jarkata noch oft genug bei den Libs und Klassen des Tomcat finden.

Download Apache Tomcat 8.0.x

Einfach die passende ZIP-Datei downloaden und an einen beliebigen Ort entpacken. Da wir alles über Eclipse steuern, müssen wir erst einmal da nichts weiter machen. Wer den Tomcat auch mal so ausprobieren möchte muss JAVA_HOME bei den Umgebungsvariablen von Windows setzen und auch den Pfad zum JRE (zum Bin-Verzeichnis) im Path von Windows hinterlegen. Dann kann man im Tomcat-Verzeichnis mit bin/startup.bat den Tomcat starten. Die einfache Startseite sollte dann unter http://localhost:8080/ aufrufen.

bbcode-image


Aber jetzt zurück zu Eclipse. Wir laden unseren Workspace aus dem letzten Teil oder einen anderen den wir benutzen möchten. Wir öffnen Window -- Preferences -- Server - Runtime Environments.
Dort clicken wir auf Add..., um den Tomcat hinzu zufügen.

Wir wählen den Tomcat 8 aus und clicken auf Next. Dann wählen wir unser Tomcat Verzeichnis aus.

bbcode-image


bbcode-image


Wenn noch keine Server-View angezeigt wird, müssen wir uns diese nochmal hinzufügen.

bbcode-image


Dort clicken wir auf den Link, um einen neuen Server hinzu zufügen. Da wir nur eine Runtime haben, wird uns diese auch direkt vorgeschlagen. Wir übernehmen alle Vorgaben und wählen Finish.

bbcode-image


Damit haben wir unseren Server fertig eingerichtet und können nun zu unserem kleinen Beispiel-Projekt übergehen. Wir fangen ganz einfach mit einer JSP-Seite an, bei der wir ein Input-Feld für einen Namen haben, der dann an die Seite übergeben wird und diesen mit Hallo {name}! wieder ausgibt. Klassisch, einfach und die wichtigsten Dinge wie Forms, Request und Ausgabe sind dabei.

Wir brauchen ein Dynamic Web Project:

bbcode-image


Das Projekt fügen wir auch gleich zu den Projekten hinzu die automatisch bei Änderungen neu auf dem Tomcat deployed (wichtiges Wort in der Java-Welt!) werden. Wenn also etwas geändert wird, wird Projekt einmal auf dem Tomcat entfernt und neu hinzugefügt, so dass die Änderungen über einen Webbrwoser betrachtet werden können. So etwas kann bei größeren Projekten paar Sekunden dauern.. aber zum Glück muss nur bei Java-Dateien neu deployed werden. HTML, CSS oder JavaScript Dateien erfordern kein erneutes Deployment und Änderungen sind einfach so verfügbar, weil direkt auf die Dateien zu gegriffen wird und nichts kompiliert werden muss.

bbcode-image


bbcode-image


Nun fügen wir uns eine index.jsp hinzu. JSP-Seiten werden im WebContent angelegt, um gefunden zu werden. Die index.jsp ist wie eine index.html und wird beim Aufruf verwendet, wenn keine andere Seite angegeben wurde. Das Verhalten kann man über eine web.xml definieren. Dort kann man auch Servlet-Mappings und Resources definieren. Aber so eine brauchen wir erst einmal nicht.

bbcode-image


Auf die Seite kommt eine einfache Form und die Ausgabe des Namen, wenn im Request ein Name gefunden wird. Wir übergeben einfach mal den Namen per GET damit man sehen kann, wie der Parameter über die URL übergeben wird. Normal sollte man Form-Eingaben über POST übergeben. Aber die Änderung ist ja im Form-Tag schnell gemacht.


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Blog-Beispiel</title>
</head>
<body>
<%
if(request.getParameter("name")!=null && request.getParameter("name").length()>0){
%>
Hallo <strong><%=request.getParameter("name") %></strong>! <a href="index.jsp">reset</a>
<%
}
else{
%>
<form method="get">
<input type="text" name="name" /><br/>
<input type="submit" value="ok"/>
</form>
<%
}
%>
</body>
</html>


Aufrufen können wir diese Seite mit der URL nach dem Schema http://localhost:8080/{projectname}/ bei meinem Beispiel als http://localhost:8080/BlogTomcat/.

bbcode-image

bbcode-image


Eine JSP-Seite ist ja nur die Ausgangsdatei und diese wird in ein Servlet kompiliert. Servlets sind gerade für Dinge noch sehr gut, wenn man keine große Ausgaben hat, z.B. für Uploads, Downloads (mit vorherigen Benutzercheck) oder REST-APIs, die einfach immer ein Object in JSON umwandeln. Um schon mal ein Servlet gesehen zu haben erstellen wir unser Beispiel noch mal direkt als Servlet.


package de.hannespries.blog.ides.tomcat;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name="nameservlet",urlPatterns={"/name"})
public class NameServlet extends HttpServlet{
private static final long serialVersionUID = 609957577169396811L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String out="<html><head><title>Blog-Beispiel</title></head><body>";
if(req.getParameter("name")!=null && req.getParameter("name").length()>0){
out+="Hallo <strong>"+req.getParameter("name")+"</strong>! <a href="/name">reset</a>";
}
else{
out+="<form method="get"><input type="text" name="name" /><br/><input type="submit" value="ok"/></form>";
}
out+="</body></html>";
resp.getWriter().println(out);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}


Das URL-Pattern ist hier sehr einfach gehalten. Man kann auch Wildcards und ähnliches setzen, was sehr praktisch ist, wenn man Werte in URLs einbauen möchte, was sehr gut für SEO ist. Beipsiel /service/item/2/ wobei 2 die Id des Item in der Datenbank ist und darüber geladen werden kann.

bbcode-image


Wobei wir zum Thema Datenbanken im nächsten Teil kommen, wo wir uns eine Entwicklungsumgebung für PHP einrichten. Da auch dort Eclipse zum Einsatz kommt, sollte man auch wenn man sich nur für Java interessiert den Teil doch mal durchlesen und gerade der Bereich mit der Einrichtung einer MySQL-Datenbank ist interessant, wenn man eine Webanwendung im Tomcat entwickelt. Das Anlegen einer Datasource in der Config des Tomcats werde ich dort auch nochmal kurz erläutern.


Older posts:

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