Manchmal möchte man aus einem Tomcat heraus Processe und Programme aufrufen. Wie bei SQL-Injections muss man das natürlich stark absichern, aber das Konvertieren von Bildern und Video geschieht meistens auf diese Art und Weise.
Was aber wenn der Tomcat per ProcessBuilder nichts mehr aufrufen kann außerhalb seines Verzeichnisses, obwohl er alle Recht haben sollte? Wenn es ein System mit systemd ist (z.B. ein Ubuntu) ist, kann es einfach eine Security-Einstellung sein, die den Aufruf verhindert.
Hier muss man die Start-Config für den Service anpassen. "ProtectSystem=false" ist zum Prüfen der Lösung ganz gut, sollte aber später durch eine genauere Anpassung der ReadWritePaths geöst werden.
An sich ist es garnicht sooo schwer Nuxeo mit einem Keycloak zu verbinden und dann die Benutzerverwaltung allein über das Keycloak abzuwinkeln. Leider ist die Dokumentation dazu sehr dürftig und zu großen Teilen einfach veraltet und lückenhaft. Hier wird einmal in kurzer Form erklärt wie man das mit einer aktuellen Version von Nuxeo 10.10 bewerkstelligen kann. Man sollte das 10.10 Repository von Github einmal per Maven komplett selbst gebaut haben. Wir hatten die HF53-Version und ein Grundsetup als Docker-Image ist unter annonyme/nuxeo:HF53 zu finden. Besser ist aber wenn man sich das vollständig selbst baut. Das Docker-Repository hilft beim Bauen.
Die Erweiterung für Nuxeo
Das Repository für die Nuxeo Platform Login Keycloak Erweiterung ist Teil des Nuxeo Mono-Repository und kann direkt mitgebaut werden. Die Anleitung dazu ist vollkommen veraltet, aber ich nehme sie hier als Basis. Man braucht um dieses benutzen zu können:
Die Dateien aus der Zip der Adapter-Dist, die JAR vom Nuxeo Platform Login Keycloak sowie die JAR des UserMapper Services müssen alle in das selbe plugin/ Verzeichnis kopiert werden wie in der Anleitung erklärt wird. Das config/ Verzeichnis wie im Repository einfach auch rüber kopieren. Der Inhalt der JSON-Datei kann direkt aus dem Nuxeo kopiert werden und
ist die Config-Datei für den Keycloak Tomcat-Adapter und hat also an sich nichts mit Nuxeo zu tun. Dem entsprechend ist die Dokumentation zu der Datei auch um Welten besser als bei den Nuxeo Komponenten.
In der Anleitung wird alles in ein Template-Verzeichnis kopiert. Ein Template ist ein Profile für verschiedene Nuxeo-Konfigurationen und es können mehrere davon gleichzeitig verwendet werden. Den Docker-Container muss man dann also mit NUXEO_TEMPLATES: docker,keycloak starten.
Das war es dan nauch. Beim Login in Nuxeo einfach einen Account aus dem Keycloak verwenden und der Benutzer sie wie die im Keycloak zugeordneten Rollen/Gruppen werden ins Nuxeo übernommen.
Wenn man nochmal mit dem Administrator-Konto ins Nuxeo will und dieser noch nicht im Keycloak angelegt ist, muss man nur direkt /nuxeo/login.jsp aufrufen und bekommt die Nuxeo-Anmeldung ohne auf die Realm-Anmeldeseite des Keycloak weiter geleitet zu werden.
Es sind keine weiteren Konfigurationen an Nuxeo nötig. Wenn man sich ein Docker-Image baut muss also nur die keycloak.json aus dem config-Verzeichnis des Templates ersetzt werden können.
Wer sich sonst immer für Wildfly/Tomcat und JAX-RS für seine REST-API Lösungen entschieden hat wird sich mit Apache Meecrowave sehr schnell zu Hause fühlen. Im Grunde ist es auch nichts anderes als ein Tomcat mit JAX-RS nur dass die Setup-Phase fast komplett entfällt. Für Microservices und schnelle Lösungen hat man in wenigen Minuten eine funktionsfähige REST-API.
Für eine einfache REST-API braucht man die pom.xml, eine Klasse mit einer Main-Methode und einen REST-Endpoint.
@Path("test")
@ApplicationScoped
public class TestController {
@GET
@Produces(MediaType.APPLICATION_JSON)
public TestModel action(){
TestModel model = new TestModel();
model.setId(23);
model.setName("Test");
return model;
}
}
Der Endpoint ist jetzt erreichbar:
http://localhost:8080/test
Der Vorteil bei dieser Lösung ist, dass man sehr einfach ein Docker-Image mit dieser Anwendung erstellen kann, das man dann direkt deployen kann.
Durch das Meecrowave-Maven-Plugin wird eine meecrowave-meecrowave-distribution.zip im Target-Verzeichnis erstellt.
RUN apk --no-cache add bash
EXPOSE 8080
ENTRYPOINT["sh /app/meecrowave.sh start"]
Auch für Test gibt fertige Meecrowave-Packages, die man nutzen kann. Sonst geht natürlich auch einfach JUnit.
Wer sich jetzt fragt ob Spring Boot besser oder schlechter ist.. ich hatte jetzt mit beiden zu tun und am Ende ist beides an sich das Selbe mit ein jeweils anderen Libs. Beides ist für Microservices sehr gut geeignet.
Ich und NAS war immer ein schwieriges Thema. Eines von Buffalo mit 1TB Festplatte war mir viel zu laut. Ich habe die Festplatte dann in meinen PC eingebaut und den Rest des NAS entsorgt. Die NAS-Funktion der Fritzbox war ganz nett aber ich wollte keine Festplatte unter dem Schrank im Wohnzimmer haben. In der Wohnung war leider der Telefonanschluss nicht mit Arbeitszimmer und unter dem Bücherschrank war es immer extrem staubig. Also es dann mit dem Haus begann und ich die Idee hatte mit IP-Kameras den Gartenbereich zu überwachen, wurde das Thema NAS wieder aktuell. Ich hatte mir da verschiedene Lösungen überlegt:
- Billiger 19" Server
- Billiger HP Micro-Server
- ein NAS
- ein Rasberry Pi mit USB-Festplatte
Ich bestellte mir ein günstiges NAS aus China. Dieses mal eines, wo die Festplatte austauschbar war und das etwas mehr nach einem NAS aussah. Die UI war auch sehr viel besser als bei dem Buffalo NAS. Aber es war wirklich sehr laut (allein der Lüfter) und so wirklich toll fühlte es auch nicht an. Man merkte einfach den Preis und Updates und Patches konnte man da auch nicht erwarten.
Gehen wir ein paar Jahre zurück. Ich weiß nicht mehr wie ich darauf gekommen bin, aber irgendwann fand ich heraus, dass man bestimmte QNAP NAS als VM-Hosts verwenden konnte. Diese waren nicht günstig, aber brauchten an sich alles mit was eine kleine Firma brauchte.
Vor so 4 Jahren kam es dann dazu, dass ich um Hilfe bei einer kleineren Firma gebieten wurde. Da schien ein QNAP-NAS perfekt zu passen. Die brauchten eben nur zentralen Speicher und Dinge wie eine zentrale Groupware, die nicht in der Cloud lief (wegen Datenschutzbedenken). Verbindung per VPN, um auf die Groupware zugreifen zu können, wäre nicht wirklich das Problem gewesen.
Damals hätte so ein NAS ca. 2000 Euro gekostet, das genug Festplatten- und Arbeitsspeicher für den Zweck gehabt hätte. Das war dann für die kleine Firma zu viel.
Dezember 2017. 10 PCs ein Server. Domain-Controller. Zentraler Speicher. Zuerst dachte ich man könnte eine VM mit Samba als Domain-Controller nutzen und auf so einem NAS laufen lassen, bis ich heraus fand, dass diese Größe von NAS's sogar einen entsprechenden Service schon mit brachte. Die Preise waren auch in der Zeit stark gefallen. Ein passendes NAS mit 2x 3GB und 4GB RAM kostete gerade mal etwas über 600 Euro. Bezahlbar und alles in einem.
Auch ganz praktisch an einer QNAP TS-253A ist auch die Größe, die ohne Probleme auf dem Boden eines 19" Schranks platz findet. USB 3.0, 2x 1GBit LAN, RAID.. Microphone-Anschlüsse für Karaoke (ohne wäre teurer gewesen).
Das NAS wurde gekauft und ich hatte endlich mal die Möglichkeit mit so einem System zu arbeiten. Fazit: Toll! UI ist super zu bedienen. Es ist leise und schnell. Netzwerkfreigaben
funktionieren natürlich sofort und problemlos. Der Domain-Controller funktionierte auch sofort, wobei ich etwas mit den Logon-Scripten zu kämpfen hatte bis die liefen. Man durfte kein ".bat" hinter schreiben. Der Rest ist wirklich einfach. Eine Domain anlegen, einen Domain-Admin anlegen ,paar Benutzer und es kann los gehen. PC mit Hilfe des Admin-Kontos in die Domain aufnehmen. Neustart und sich mit einem der Benutzer anmelden. Läuft und bringt kleinen Firmen sehr viel mehr Kontrolle (als PCs wo jeder als Admin drauf arbeitet).
Das Geld für eine TS-253A wollte ich privat dann doch nicht ausgeben. Aber es gibt günstigere gebrauchte NAS von QNAP wie die TS-120. Dieses hat nur einen HDD-Slot und einen 1GBit LAN-Anschluss. Aber auch USB 3.0 und sogar E-SATA. Als CPU ist eine ARM-CPU und keine x86 von Intel verbaut. Deswegen hat es wohl auch keine VM-Host Funktion, wobei es auch beim RAM eng werden würde. Leider fehlt da auch der Domain-Controller, den ich sonst doch gerne gehabt hätte.
Wenn man sich aber mal durch die Funktionen und Zusatzdienste der dort installierten QTS-Version klickt, merkt man schnell, dass QNAP ein NAS auch eher als sehr kompakten Server sieht, als eine Festplatte mit LAN-Anschluss. QTS ist dabei ein wirkliches tolles System, dass schnell ist und trotz den ganzen Funktionen auch für denen übersichtlich erscheint, der vorher noch nie mit QTS zu tun hatte.
Auf dem Startbildschirm sind schon verschiedene Dienste wie ein eigens kleines Cloud-Storage-System zu sehen.
Ich zeige hier mein paar Dienste, die wirklich praktisch sein können (ich aber auch nicht nicht wirklich verwendet habe). Wie oben schon erwähnt, fehlt mir etwas der Domain-Controller.
Eine ActiveDirector/Domain Integration als Client ist aber möglich, so dass man die Benutzer einer Windows Domain direkt im NAS nutzen kann und nicht alles doppelt pflegen muss. Aber es gibt auch einen LDAP-Server. Wie weit man damit kommt, habe ich noch nicht getestet, werde ich aber in den nächsten Monaten wohl mal ausprobieren.
Wenn man eine einfache Groupware oder ein Wiki im Intranet laufen lassen möchte, braucht man an sich auch keine VMs. Denn das NAS bringt einen WebServer (sieht wie ein Apache aus), einen MySQL-Server und PHP mit. Ein Media-Wiki kann man auch sich direkt so als "App" installieren. Der Fall von vor 4 Jahren hätte sich also auch so lösen lassen ohne VM.
Wer eine Java-Lösung einer PHP-Lösung vorzieht hat Glück, denn es kann sogar ein Tomcat installiert werden. Es fehlt node.js, aber mit Apache + PHP und Tomcat sollte 90% aller Webanwendungen darauf laufen lassen können.
Performance... da würde ich nicht zu viel erwarten, aber sicher ist es für eine Firma mit so 10-15 Mitarbeitern, die dort Emails (Roundcube) checken und ihre Termine und Aufgaben verwalten ausreichend schnell. Mit DynDNS/fester IP und Port-Forwarding oder VPN, hat man eine kleine unbahängige Lösung. Ein VServer zu mieten ist aber da doch vielleicht die bessere Lösung, wenn man von extern Zugreifen möchte und keine wiederkehrenden Probleme mit Verbindungsausfällen der Internetverbindung hat.
Wegen den IP-Kameras überlege ich nochmal zu prüfen, ob die nicht die vom NAS mitgelieferte Lösung verwenden sollte. Ich müsste nochmal prüfen ob meine Kamera damit kompatibel ist und wie teuer kompatible Lösungen sind. Weil direkt die Kamera über das NAS steuern zu können ist natürlich sehr viel schöner, als nur eine Kamera per FTP Bilder und Videos auf einem NAS ablegen zu lassen. Da wäre wohl keine Lösung mit dabei Videos die älter als 1 Woche sind automatisch löschen zu lassen und ähnliches.
Auch wollte ich noch mal Testen, ob ich eine Festplatte einfach per E-SATA anschließen kann und diese wie die interne dann verwaltet wird. Eine externe für Backups wäre nett, aber ich hätte doch gerne die Option mehr Festplatten nachrüsten zu können. QNAP bietet hier leider keine Erweiterungen an wie beim TS-253A an.
Am Ende ist QNAP zwar teurer als einfache NAS-Systeme, bietet aber auch ein sehr stabiles System mit vielen Funktionen und kann in kleineren Firmen ohne Probleme einen klassischen Server ersetzen. Ein klassischer Server wäre dabei zu meist auch teurer. Die Geräte ab 300 Euro (nur das Gehäuse) bieten schon wirklich und sind ihr Geld meinen kleinen Erfahrungen nach auch wert.
Leute die viel mit Video-Streaming arbeiten, sollten sich bestimmte Funktionen wie
DJ2 live mal ansehen. Bei Karaoke im kleinen Stil sollte man aber vielleicht doch erst einmal bei UltraStar bleiben.
Bevor es dann wirklich mal um die Installation von PDT in Eclipse geht, wollen wir uns erst einmal eine MySQL Datenbank einrichten, damit wir auch richtige kleine Anwendungen schreiben können und nur ganz selten kommt man da ohne Datenbank aus. Auch wenn NoSQL Datenbanken wie Neo4J wirklich toll und momentan sehr in sind, bleiben wir bei einem klassischen RDBMS. Weil MySQL gerade im Web sehr verbreitet ist und sich einfach lokal einrichten lässt, bleiben wir auch bei MySQL.
Wer ein Linux benutzt, kann MySQL immer ganz einfach über den Paket-Mananger installieren. Für Windows kann man schnell zu XAMPP greifen. XAMPP enthält alles vom Apache, PHP7 und MySQL bzw MariaDB.
XAMPP funktioniert am Besten wenn man es direkt unter C:\ installiert.
Zum Verwalten der Server-Anwendungen von XAMPP gibt es das XAMPP Control Panel. Hier müssen nur der Apache und der MySQL Server gestartet
werden.
Damit auf die Datenbank zugegriffen werden kann bringt XAMPP phpMyAdmin mit. phpMyAdmin ist eine Datenbankverwaltung die in PHP geschrieben ist und auch von den meisten Hostern angeboten wird.. wenn nicht sogar von allen. Man kann direkt über die URL http://localhost/phpmyadmin darauf zugreifen.
Zuerst erstellen wir eine Datenbank mit dem Namen blog_test.
Damit wir ein paar Daten haben, legen wir uns eine Tabelle mit ein paar wenigen Daten an. Hier ist das SQL-Script dafür:
CREATE TABLE TEST_ITEMS(
ITEM_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
ITEM_VALUE VARCHAR(255) NOT NULL,
PRIMARY KEY(ITEM_ID)
);
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_01');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_02');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_03');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_04');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_05');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_06');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_07');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_08');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_09');
Die IDs werden automatisch hochgezählt und müssen deswegen nicht extra angegeben werden.
Nun gehen wir wieder in Eclipse zurück und erstellen uns eine kleine JSP mit einer Datenbankabfrage. Und hier wird es etwas komplizierter mit den verschiedenen ClassLoader des Tomcats und den verschiedenen Config-Dateien. Ich hab die einfachste aber nicht beste Variante gewählt. Die JAR-Datei mit dem JDBC-Treiber kommt direkt in das lib-Verzeichnis des Tomcat und wir passen die zentrale context.xml Datei, die uns von Eclipse zur Verfügung gestellt wird.
Die aktuelle JAR mit dem MySQL-Treiber findet man auf dev.mysql.com.
In die Context-Datei definierten wir die DataSource als Resource.
Nun können wir über JNDI uns diese DataSource in eine JSP holen. Wir erstellen eine ganz einfach Abfrage. Das Ergebnis liefert uns ein Statement in Form eines ResultSets. Ich benutzt hier die Methode über den Index die Ergebnisse zu bekommen, z.B. getString(1) wobei aber auch getString("ITEM_ID") funktioniert und für den zielgerichteten Einsatz sehr viel besser ist, weil man so das SQL-Statement ändern kann und auch die Reihenfolge der Columns ändern, ohne dabei auf den Java-Code achten zu müssen. Hier wird aber nicht zielgerichtet ein Wert ausgelesen und z.B. in ein anderes Object geschrieben sondern einfach alles ausgegeben. Deswegen auch nur getString() und keine anderen Methoden, die einen passenden Datentyp zurück liefern und ein eigenes Casten der Werte unnötig machen.
Der Vorteil die Connection über eine DataSource zu bekommen und nicht jedes mal selbst zu initiieren ist, dass die DataSource ein Pooling der Connections vornimmt und Datenbankverbindungen zur Wiederverwendung offen hält, um den Overhead für Verbindungsaufbauten zu verringern.
SQL-Abfragen direkt in einer JSP-Seite zu ist aber eine schlechte und man sollte so etwas in DAO-Klassen auslagern und in der JSP nur die Ansicht mit schon fertigen Objekten erstellen, die dann vom DAO geliefert werden.
Außerdem werden immer mehr JPA verwendet, wo die SQL-Statements automatisch erzeugt werden. Handgeschriebenes SQL ist in komplexen Fällen meistens schneller und besser, aber ORM-Frameworks erleichtern einen die Arbeit schon sehr und man sollte sich JPA auf jeden Fall einmal
ansehen, bevor man noch direkt mit JDBC und SQL arbeitet.
Im nächsten Teil geht es dann wirklich mit PHP weiter.
Nachdem ich feststellen musste, dass man Stash/BitBucket zwar für $10 für ein kleines Team kaufen kann, aber man dafür eine Kreditkarte benötigt, habe ich mit auf die Suche nach einer kostenlosen Alternative.
Das Problem der meisten Alternativen wie Gerrit oder Apache Allura ist doch die immer sehr aufwendige und auch umständliche Installation. Auch gerade wenn man Windows benutzt und nicht Linux. Auch hat nicht jeder Windows-Benutzer der Zuhause etwas entwickeln möchte eine Windows 2008 Server-Version. Ich hatte mir etwas vorgestellt, was ich einfach im Tomcat deployen kann als WAR und dann ist es da (vielleicht noch Einstellungen wie Speicherort und Datenbank.. aber nicht viel mehr). Die beiden genannten Systeme können
zwar sehr viel und gerade Gerrit ist wohl noch mal einen zweiten Blick wert, aber ich wollte erst einmal schnell was kleines und einfaches.
Ich habe länger gesucht und habe am Ende Gitblit gefunden. Die WAR downloaden, ins webapp Verzeichnis kopieren und den Tomcat starten... und es war da. Eingeloggt mit dem default-User und alles erschloss sich gleich.
Und mit das Beste ist die vorhandene Integration von SourceTree, die bei mir langsam war aber an sich sauber und gut funktionierte. So kann man sich auf jeden Fall für Testzwecke in wenigen Minuten einen GIT-Server einrichten.
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.
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.
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.
Wenn noch keine Server-View angezeigt wird, müssen wir uns diese nochmal hinzufügen.
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.
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:
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.
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.
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.
Aufrufen können wir diese Seite mit der URL nach dem Schema http://localhost:8080/{projectname}/ bei meinem Beispiel als http://localhost:8080/BlogTomcat/.
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.
@WebServlet(name="nameservlet",urlPatterns={"/name"})
public class NameServlet extends HttpServlet{
private static final long serialVersionUID = 609957577169396811L;
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.
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.
Als ich mit PHP anfing war es noch Alltag, dass man wenn eine Datenbank-Abfrage notwendig war diese auch einfach direkt an Ort und Stelle einbaute. Kapselung in einer eigenen Funktion oder Klasse war nicht wirklich verbreitet. Es wurde meistens direkt mit den entsprechenden Datenbank Funktionen gearbeitet und keine Abstraktion verwendet.
Nur in großen Anwendungen war es anders. Vorher hatte ich mit Java, JDBC und DataSources im Tomcat gearbeitet, wo es am Ende egal war welche Datenbank dahinter lief und man dies nur beim Anlegen der DataSource abgeben musste. Zuhause war ich auf Oracle Datenbanken und hatte nur so am Rande mit MySQL zu tun. Ich sollte ein Projekt von MySQL auf Oracle portieren und merkte schnell wie doof es war keine Abstraktion zu haben. Also nahm ich mir vor, sollte ich mal ein PHP anfangen, dass ich es besser machen würde. Genau so einfach wie mit einem Tomcat und den DataSources.
Dann kam in der Berufsschule das Thema Datenbanken, dass wir mit einer XAMPP-Installation bearbeiten sollten. Ein kleines Shop-"System". CRUD, eine Liste laden und wenn wir voll gut waren sogar Joins oder mal ein COUNT mit GROUP BY.. also alles was die meistens aus ihrem normalen Arbeitstag in und auswendig kannten. Ich wollte es dann auch gerne so schreiben, dass ich SQL über eine zentrale Klasse ausführen konnte und Schleifen einfach ausführen konnte und das Problem das bei Oracle ein Array so aussah [column]
und nicht wie bei MySQL
[column] direkt dort in der Implementierung ausgeglichen wurde und man so einfach zwischen den beiden Systemen switchen kontne ohne Code ändern zu müssen.
Aber fangen wir mal an. Zuerst erstellen wir uns eine einfache Datenbank Tabelle mit ein paar Daten. Diese werden dann im Verlaufe dieses Artikels mit PDBC aus der Datenbank laden und einmal ausgeben. Unsere Tabelle sieht wie folgt aus:
CREATE TABLE tests (
test_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
test_name VARCHAR(255) NOT NULL,
PRIMARY KEY (test_id)
);
INSERT INTO tests(test_name) VALUES ('TEST_01');
INSERT INTO tests(test_name) VALUES ('TEST_02');
INSERT INTO tests(test_name) VALUES ('TEST_03');
INSERT INTO tests(test_name) VALUES ('TEST_04');
INSERT INTO tests(test_name) VALUES ('TEST_05');
Diese Tabelle repräsentiert Daten für eine einfache Klasse mit einer Id und einem Namen. Also das Grundmodel auf dem bestimmt fast 98% aller Entitäten basieren. Das normale vorgehen wäre jetzt diese Daten über ein einfaches SQL Query einzulesen. Dabei enthält man ein ResultSet oder Array mit allen Zeilen, die man dann in einer Schleife durchläuft. Während jedem Durchlauf erzeugt man ein neues Objekt der Klasse und befühlt dieses über die Setter mit den Daten aus dem Zeilen Array.
$result=array();
foreach($data as $row){
$obj=new Test();
$obj->setId($row["test_id"]);
$obj->setName($row["test_name"]);
$result[]=$obj;
}
Man muss viel per Hand schreiben, was sehr zeitintensiv, stupide und fehleranfällig ist. Wenn man an mehreren Stellen so lädt z.B. einmal laden eines Objekt anhand der Id, einmal die Liste und noch die Liste mit einem Pattern für den Namen, muss man alle Methoden und Funktionen wo man die Daten in das Objekt füllt ändern, wenn man mal eine Column der Datenbank hinzufügt, ändert oder entfernt.
Wenn man diesen Teil nun automatisiert, erleichtert einen das Leben schon sehr. Bei einer Klasse mit 10 Attributen sind es dann nicht mehr 12 Zeilen Code sondern nur noch 2. PDBC erledigt für einen diese Schritte. Vom erzeugen der Objekt, die Abfrage der Daten und dann das befüllen der Objekte.
Um nun zu wissen, welches Datenfeld aus der Datenbank in welches Klassen-Attribute gehört werden Annotationen genutzt. Es ist ganz leicht JPA und Hibernate beeinflusst. Wobei PDBC sehr viel weniger kann.
FKs werden nicht in Objekte abgebildet, also ein lazy oder eager Loading. Es werden nur wirklich Daten kopiert. Das SQL muss man komplett per Hand schreiben, was ich aber als Vorteil sehe, weil HQL/JPQL doch mich immer sehr eingeschränkt haben und viele wichtige SQL-Funktionen wie Nested-Queries und gute Joins oft nicht möglich waren. SQL bietet dort sehr viel mehr und performantere Möglichkeiten.
Aber hier geht es erst einmal um einfaches und schnelles Laden der Datensätze.
Dafür brauchen wir erst einmal unsere Klasse mit den entsprechenden Annotationen, die bestimmen zu welcher Column das Klassen-Attribute gehört. Das Schlüsselwort hierbei ist "@dbcolumn". Diese Annotationen werden mit Hilfe der Refelection Klassen von PHP und den DocComments gelesen und dann geparst.
Damit man hier performant bleibt werden die Properties einmal pro Klasse eingelesen und in einer Map gespeichert, so was man schnell über den Column-Name zum Property gelangt. Wenn man dann für die nächste Klasse die Zuordnung braucht hat man diese schon. Da die Map static ist, geschieht das Einlesen auch nur einmal pro Request für eine Klasse.
setAccessible(true) ist hier sehr wichtig, weil wir so private Attribute lesen und schreiben können und dazu bringt es noch einem wirklich spürbaren Performance-Vorteil. Ja.. auch bei PHP. Bei Java gilt genau das Selbe. Die Reflection-Klassen von PHP sind von den Bezeichnungen und Methoden auch sehr an Java angelehnt und wer mit den Relfections in Java sich auskennt, kann sofort in PHP weiter machen, da die wichtigen Methodennamen wirklich zu 100% übereinstimmen.
public function getId(){
return $this->id;
}
public function setId($id){
$this->id=$id;
}
public function getName(){
return $this->name;
}
public function setName($name){
$this->name=$name;
}
}
Man sieht hier die Annotationen an den Attributen der Klasse. Die Werte werden direkt in die Attribute geschrieben. Die Nutzung der Getter und Setter ist hier nicht implementiert. Wobei das große Vorteile hätte da man so z.B. eine "1" aus der Datenbank auf ein "true" mappen könnte. Dafür werde ich mal eine Implementierung mit eignen Properties schreiben, die zusammen mit dem Property auch die Accessor-Methoden mitliefert, wenn diese vorhanden sind.
Jetzt geht es aber weiter mit dem konkreten Beispiel. Zuerst werden wir uns unsere Datenbank-Klasse holen. Dafür muss die Datenbankverbindung in der Config-Datei angegeben sein. Der Name muss eindeutig sein und mit dessen Hilfe wird die Datenquelle auch identifiziert. Die Bezeichnungen in der Config-Datei orientieren sich Oracle, so nennt sich ein Feld "SID", also Service-ID, das entspricht bei MySQL dem Datenbanknamen. Sollte hier der Standard-Port von MySQL verwendet werden, ist dieser normaler weise nicht anzugeben.
Wir haben hier also unsere Datasource (wie im Tomcat) unter dem Namen "ds_test", die auch die lokale MySQL-Instanz und die Datenbank verweist. Wie man am Passwort schon vermuten kann, habe ich bei mir einfach den Bitnami WAMP-Stack verwendet, aber XAMPP oder eine vollständige Installation gehen natürlich genau so gut.
Wir müssen noch angeben wo die Config-Datei und wo die Datenbank-Klassen zu finden sind.
Wenn man PDBC in sein System integrieren will, kann man so die Config-Datei einfach mit im Config-Verzeichnis des Systems unterbringen.
Nun folgt ein kleines und einfaches SQL-Statement:
$sql="SELECT test_id,test_name FROM tests ORDER BY test_name ASC";
Und nun beginnt der spannende Teil, wir übergeben unser Datebank-Object, den SQL-String und den Klassennamen an den PDBCObjectMapper. Dieser nutzt das Interface der DB-Klasse, weswegen ihm egal ist welche Implementation verwendet wird. Dann wird die Klasse analysiert und deren Properties eingelesen und die passenden Spalten wie schon weiter oben beschrieben eingelesen. Der Rest ist eigentlich relativ primitiv. Erst wird eine Instanz der Klasse erzeugt und dann das Array mit dem Datensatz des Resultsets wir mit $key (Spaltenname) und $value (Wert aus der DB) durchlaufen.
Wird ein Property zu dem $key gefunden wird in dieses das $value geschrieben.
private function fillObject($row,$ref){
$obj=$ref->newInstance();
$props=$this->loadProperties($ref);
foreach($row as $key => $value){
if(isset($props[$key])){
$props[$key]->setValue($obj,$value);
}
}
return $obj;
}
public function queryList($db,$sql,$className){
$db->executeQuery($sql);
$ref=new ReflectionClass($className);
$result=array();
for($i=0;$i<$db->getCount();$i++){
$result[count($result)]=$this->fillObject($db->getRow($i),$ref);
}
return $result;
}
Die loadProperties-Methode habe ich ja weiter oben schon beschrieben. Hier wieder sich daran erinnern, dass wir setAccessible(true) für jedes Properties gesetzt haben und somit ohne Probleme in private-Properties schreiben können.
"Test" ist unser Klassename, $sql unser String mit dem Query-Statement und $db unsere oben in der config-Datei definierten Datasource.
Als Rückgabe erhalten wir, wie alle schon sicher richtig vermutet haben, ein Array mit unseren gefüllten Test-Objekten. Um zusehen, dass alles richtig befüllt wurde, geben wir diese einfach mal aus.
foreach($tests as $test){
echo $test->getId()." - ".$test->getName()."<br>\n";
}
Der gesamte Code für unser kleines Test-Script sieht dann also so aus:
Ich hoffe ich konnte einen kleinen Einblick geben, wie man sich die Arbeit mit Datenbanken in PHP einfacher machen kann und einen Teil der Konzepte aus der Java-Welt in PHP implementieren kann. Man kann natürlich noch sehr viel mehr implementieren und ein kompletes und komplexes ORM-System hier draus bauen, aber das würde hier zu weit führen und ich brauchte es auch so erst einmal nicht. Auch Inserts und Updates darüber zu realisieren und Getter und Setter benutzen zu können wäre toll und steht noch auf dem Plan, aber wohl alles erst im Laufe von 2016.
Schade dass es die HHVM nicht für Windows gibt, aber dafür sieht Quercus sehr gut aus. Ein kleines Script zum Hochladen von Dateien lief sofort.. demnächst wird mal aoop damit getestet. Damals mit Java-Bridge lief es mal ganz gut auf einem JBossWeb. DB Connection-Pools mit PHP wären schon eine tolle Sache.
Blog-entries by search-pattern/Tags:
Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?