Dies ist ein Projekt, das als Teil eines Kurses erstellt wurde, den ich an der University of Warschau absolvierte. Es handelt sich um eine einfache Implementierung eines Servlet -Containers, der Java -Servlets unterstützt, die mit der Java -Servlet -API konform sind. Obwohl es ziemlich Barebone ist, kann es einfache Anwendungen laden und ausführen, die grundlegende Funktionen von Servlets wie Versand-, JSP- und Async -Servlets verwenden.
Ich habe nicht viele andere kleine Implementierungen von Servlet -Containern gefunden. Verwenden Sie dies daher als Beispiel dafür, wie ein solcher Container erstellt wird. Denken Sie daran, dass es aufgrund zeitlicher Beschränkungen ein ziemlich unvollkommenes, fehlerhaftes Projekt ist, und während ich versuchte, treu zu bleiben, um API zu servlet, habe ich hier und da einige Freiheiten genommen.
Die Idee ist, dass dieser Container auf ähnliche Weise wie Tomcat funktioniert. Sie können Servlet -Klassen mit ServletContainer :: Addroute hinzufügen, aber die bevorzugte Möglichkeit besteht darin, die Komponenten -Scan -Funktionalität zu verwenden. Wenn die Anwendung in warName.war -Datei eingebunden und in das Verzeichnis deploy wird, wird sie in server/src/main/resources auf dem Serverstart geladen und unter localhost:8000/warName verfügbar.
Standardmäßig wird der Server auf Port 8000 ausgeführt. Er kann in Main geändert werden. Diese Funktion enthält auch und Beispiele für die Starten und Konfiguration dieses Servers.
Das Projekt wurde mit Gradle gebaut. Es enthält zwei Unterprojekte: Server- und Demo -Anwendung (einfache Buchdatenbank). Es wurde unter Linux getestet und ich weiß nicht, ob es überhaupt unter Windows/MacOS funktioniert.
Zum Ausführen von Serveranwendungen:
./gradlew server:run
Für den Startserver müssen wir eine Instanz von ServletContainer erstellen.
ServletContainer(int threads) Erstellt eine neue Instanz von ServletContainer . mit gegebener Anzahl von Threads, die zum Erstellen von Threadpoolen verwendet werden. Dieser Pool wird verwendet, um HTTP -Anfragen zu bearbeiten.
void ServletContainer::start(int port) Startet Servlet Container am gegebenen Port. Denken Sie daran, dass .war -Dateien zu diesem Zeitpunkt nicht geladen werden. Sie werden nur geladen, wenn ServletContainer:servletScan aufgerufen wird.
void ServletContainer::stop()Stoppt anmutig den Behälter.
void ServletContainer::servletScan() Laden Sie alle .war -Dateien vom deploy Verzeichnissen in server/src/main/resources . Es wird auch .JSP -Dateien geladen und wandern sie sofort in .Class -Dateien. Nur mit @WebServlet kommentierte Klassen werden geladen. Eine Einschränkung ist, dass von mir verwendete .war -Dateien eine bestimmte Verzeichnisstruktur haben, von der ich nicht so sicher bin, dass sie in den meisten anderen. Kriegsdateien mithalten. Sie können die Aufgabe war in der Demo -Anwendung überprüfen, um zu sehen, wie diese Struktur aussehen soll. Diese Methode sollte höchstens einmal vor Start () aufgerufen werden.
void ServletContainer::servletScan(String url) Gleich wie ServletContainer::servletScan() kann jedoch. Warendateien aus der angegebenen URL laden.
void ServletContainer::addRoute(Class<? extends HttpServlet> cls, String path)Fügt dem Behälter Servlet hinzu. Servlet wird nur die angegebene URL verarbeiten (siehe FAQ für weitere Details).
Der Server unterstützt die gleichzeitige Behandlung mehrerer Clients gleichzeitig. Jeder Client wird mit separatem Threadpool behandelt. Die Standardpoolgröße beträgt 4 und kann in Main geändert werden.
Der Server unterstützt jede Klasse, die von httpServlet erbt. Eine solche Klasse kann mit ServletContainer::addRoute hinzugefügt werden.
Die wichtigsten Funktionen dieser Klassen werden implementiert. Sie können den Client mit (nur) PrintWriter , den Header und den Antwortstatus einstellen. HttpServletRequest kann die Anforderungs -URL, die HTTP -Methode (Get, Post, Löschen, Patch), Abfrageparameter und Parameter aus dem Körper (im Fall von Post) extrahieren. Daten werden nach dem Spülen des Puffers oder dem Schließen der HttpServletResponse an den Client gesendet. Mit RequestDispatcher können Sie Abfragen an andere Servlets senden, einschließlich JSP -Servlets.
Es gibt Unterstützung für asynchronisierte Servlets. Nachdem HttpServletRequest::startAsync ausgeführt wurde, geht die Anfrage in den asynchronen Modus. Es gibt zwei Möglichkeiten, diesen Modus zu verwenden. Jeder Code wird so lange ausgeführt, wie AsyncContext::complete irgendwann ausgeführt wird, was die Verbindung zum Client beendet. Sie können auch AsyncConext::start(Runnable) AsyncContext::complete . Letzteres ist mit der Java Servlet -API kompatibel. Es unterstützt auch Timeout ( AsyncContext::setTimeout ) und AsyncListener . Async -Servlets werden mithilfe von CompletableFuture implementiert, damit sie Threadpools von der für synchronen Clients verwendeten Threadpools verwenden.
Wenn die Anwendung in .War eingebaut und in den deploy verschoben wurde, wird sie automatisch geladen. Alle mit @WebServlet und Erben von HttpServlet kommenden Klassen werden dem Servlet -Container hinzugefügt und sind unter localhost:port/warName/servletUrl erhältlich. Jede solche Klasse muss genau einen servletUrl haben, der in der Annotation @WebServlet im Wertattribut angegeben ist. Wenn die Anwendung JSP verwendet, wird sie automatisch in .Class zusammengestellt und geladen. Viele Anwendungen können geladen werden. Ich weiß jedoch nicht, was passiert, wenn in zwei Anwendungen die gleichen Klassennamen verwendet werden.
Der Server kann .JSP -Dateien in .Class transpilieren. Es gibt Unterstützung für fast alle Syntaxe in JSP. Dazu gehören:
<%@ page import/include=... %><% ... %><%! ... %><%= ... %><%-- %>${...} ${} Syntax unterstützt die Ausdruckssprache teilweise. Einfache arithmetische Operationen können durchgeführt werden, und jeder Ausdruck der instance.property1.property2 wird in request.getAttribute("instance").getProperty1().getProperty2() . In <% ... %> gibt es auch out.println(...) , das direkt an den Kunden schreibt. JSP kann mit RequestDispatcher::forward angezeigt werden oder ist direkt bei localhost:8000/warName/jspFileName.jsp erhältlich. Eine Beispiel -JSP -Aktion ist bei localhost:8000/library/jsp erhältlich. Denken Sie daran, dass ich mich selbst analysiert habe, damit die seltsame Codeformatierung/Syntax es brechen kann.
Um den Servlet -Container zu demonstrieren, habe ich eine einfache Anwendung implementiert, die eine Buchdatenbank simuliert. Bücher können mit HTML -Formularen hinzugefügt, entfernt und aktualisiert werden. Das gesamte Buch kann auch in der HTML -Tabelle angezeigt werden. Das Frontend wurde mit JSP vollständig entwickelt. Die Anwendung wird in die Bibliothek eingebunden. Krieg mit war -Aufgaben, zur deploy verschoben und beim Start auf den Server geladen.
Endpunkte:
Es gibt über 30 Tests, um den größten Teil der Funktionalität und der Demo -Anwendung zu überprüfen. Es wird bevorzugt, sie aufgrund von Problemen, die in den FAQ erklärt wurden, von Intellij zu stammen. Alternativ können sie mit:
./gradlew test
Dieses Projekt war Teil eines Universitätskurs und ich durfte außer Jungit keine Bibliotheken der Drittanbieter verwenden.
Es gibt Probleme mit Socket -Ports, die bereits verwendet werden. Ich bin nie gesendet, um es zu beheben, aber die meiste Zeit funktioniert es, wenn es aus Intellij geführt wird.
Derzeit ist die Auflösung, bei der Servlet die angegebene URL verarbeiten sollte, ziemlich primitiv. Grundsätzlich sucht der url -Server nach einem Servlet, dessen servletUrl ein Präfix von url ist. Wenn es viele gibt, die mit dem längsten Präfix ausgewählt sind. Dies bedeutet, dass, wenn ein Servlet servletUrl entspricht /app/home hat, auch /app/home/nonexisting/ , /app/home/12345 usw. usw.
Jeder ist herzlich eingeladen, zu diesem Projekt beizutragen. Ich habe ein paar Probleme mit dem ersten Mal erstellt. Schauen Sie sich sie also gerne an :)