JavaRush /Java-Blog /Random-DE /Von Hello World bis Spring Web MVC und was Servlets damit...
Viacheslav
Level 3

Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben

Veröffentlicht in der Gruppe Random-DE
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 1

Einführung

Wie wir wissen, ist der Erfolg von Java genau der Entwicklung von Software zu verdanken, die eine Verbindung zum Netzwerk anstrebt. Daher nehmen wir die übliche Konsolenanwendung „ Hello World “ als Grundlage und verstehen, was sie braucht, um von einer Konsolenanwendung zu einer Netzwerkanwendung zu werden. Daher müssen Sie zunächst ein Java-Projekt erstellen. Programmierer sind faule Leute. In prähistorischen Zeiten, als einige auf Mammutjagd waren, saßen andere da und versuchten, sich nicht in der ganzen Vielfalt der Java-Bibliotheken und Verzeichnisstrukturen verwirren zu lassen. Damit der Entwickler den Prozess der Anwendungserstellung steuern und einfach schreiben kann: „Ich möchte eine Bibliothek dieser oder jener Version 2“, haben sie spezielle Tools entwickelt – Build-Systeme. Die beiden bekanntesten sind Maven und Gradle . Für diesen Artikel verwenden wir Gradle. Hätten wir früher die Verzeichnisstruktur selbst erstellen müssen, können wir jetzt mit Gradle mithilfe des Gradle Init Plugins ein Java-Projekt mit einer Verzeichnisstruktur und einer Basis-Main-Klasse in einem Befehl erstellen: gradle init --type java-application Dieser Befehl führt die Initialisierung (init) für durch uns eine Java-Anwendung (java-application) mit Konsole Hello World. Nach Abschluss erscheint eine Datei im Verzeichnis - build.gradle . Dies ist unser Build-Skript – also ein bestimmtes Skript zum Erstellen einer Anwendung mit einer Beschreibung, welche Aktionen dafür ausgeführt werden müssen. Öffnen wir es und fügen die Zeile hinzu: Mit jar.baseName = 'webproject' Gradle können Sie verschiedene Aktionen an einem Projekt ausführen. Diese Aktionen werden als Aufgaben bezeichnet . Durch Ausführen eines Befehls (Aufgabe) wird eine JAR-Datei gradle buildim Verzeichnis /build/libs erstellt . Und wie Sie vermutet haben, lautet der Name jetzt webproject.jar . Aber wenn wir ausführen java -jar ./build/libs/webproject.jar, erhalten wir eine Fehlermeldung: no main manifest attribute. Dies liegt daran, dass Sie für eine Java-Anwendung ein Manifest anhängen müssen – dies ist eine Beschreibung, wie mit der Anwendung gearbeitet wird und wie sie wahrgenommen wird. Dann weiß die JVM, die die Java-Anwendung ausführt, welche Klasse der Einstiegspunkt für das Programm ist und erhält weitere Informationen (z. B. Klassenpfad). Wenn wir uns den Inhalt des Build-Skripts genauer ansehen, sehen wir, dass die Plugins verbunden sind. Beispiel: apply plugin: 'java' Wenn wir zur Gradle Java Plugin-Seite gehen , können wir sehen, dass wir das Manifest konfigurieren können:
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
Die Hauptklasse, der Einstiegspunkt in das Programm, wurde für uns vom Gradle Init Plugin generiert. Und es wird sogar im Parameter mainClassName angegeben. Aber das hat uns nicht gepasst, denn... Diese Einstellung bezieht sich auf ein anderes Plugin, Gradle Application Plugin . Wir haben also eine Java-Anwendung, die „Hello World“ auf dem Bildschirm anzeigt. Diese Java-Anwendung ist in einem JAR (Java ARchive) verpackt. Es ist einfach, konsolenbasiert und nicht aktuell. Wie kann man daraus eine Webanwendung machen?
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben - 2

Servlet-API

Damit Java mit dem Netzwerk zusammenarbeiten konnte, erschien bereits in der Antike eine Spezifikation namens Servlet API . Diese Spezifikation beschreibt die Client-Server-Interaktion, den Empfang einer Nachricht von einem Client (z. B. einem Browser) und das Senden einer Antwort (z. B. mit dem Text einer Seite). Natürlich hat sich seitdem viel geändert, aber der Punkt ist, dass die Servlet-API verwendet wird, damit aus einer Java-Anwendung eine Webanwendung wird. Um nicht unbegründet zu spekulieren, greifen wir genau diese Spezifikation auf: JSR-000340 JavaTM Servlet 3.1 . Zunächst interessiert uns „ Kapitel 1: Überblick “. Es beschreibt die Grundkonzepte, die wir verstehen müssen. Erstens: Was ist ein Servlet? Im Kapitel „ 1.1 Was ist ein Servlet? “ heißt es, dass ein Servlet eine Java-Komponente ist, die von einem Container verwaltet wird und dynamische Inhalte generiert. Wie andere Java-Komponenten ist ein Servlet eine Java-Klasse, die in Bytecode kompiliert wird und mithilfe der Java-Technologie auf einen Webserver geladen werden kann. Es ist wichtig, dass Servlets mit einem Web-Client (z. B. einem Browser) im Rahmen des Anforderungs-/Antwort-Paradigmas interagieren, das vom Servlet-Container implementiert wird. Es stellt sich heraus, dass Servlets in einer Art Servlet-Container leben. Was ist das? Im Kapitel „ 1.2 Was ist ein Servlet-Container? “ heißt es, dass ein Servlet-Container ein Teil eines Webservers oder Anwendungsservers ist, der Netzwerkdienste bereitstellt, über die Anforderungen gesendet und Antworten gesendet werden. Dieser Servlet-Container verwaltet den Lebenszyklus von Servlets. Alle Servlet-Container müssen mindestens das HTTP-Protokoll unterstützen, können aber auch andere unterstützen. Zum Beispiel HTTPS. Wichtig ist auch, dass der Servlet-Container der Umgebung, in der Servlets ausgeführt werden, beliebige sicherheitsrelevante Einschränkungen auferlegen kann. Wichtig ist auch, dass gemäß „ 10.6 Web Application Archive File “ die Webanwendung in eine WAR-Datei (Web ARchive) gepackt werden muss. Das heißt, jetzt müssen wir unsere JAR- und Anwendungs-Plugins für etwas anderes entfernen. Und das ist das Gradle WAR-Plugin . Und statt jar.baseName geben Sie war.baseName an. Weil Da wir das JAR-Plugin nicht mehr nutzen, haben wir auch die Manifest-Einstellungen entfernt. Als wir das JAR starteten, musste der Java Virtual Machine (JVM) über das Manifest mitgeteilt werden, wie sie mit unserer Anwendung arbeiten soll. Weil die JVM es ausgeführt hat. Die Webanwendung wird offenbar von einer Art Webserver ausgeführt. Es stellt sich heraus, dass er ihm irgendwie sagen muss, wie er mit unserer Webanwendung arbeiten soll? Und es stellt sich heraus, dass ja. Webanwendungen haben ihr eigenes spezielles Manifest. Es heißt Deployment Descriptor . Ihm ist ein ganzer Abschnitt gewidmet: „ 14. Deployment Descriptor “. Es gibt einen wichtigen Abschnitt: „ Kapitel 10:". Es geht darum, was eine Webanwendung aus Sicht der Servlet-API ist. Im Kapitel „ 10.5 Verzeichnisstruktur “ wird beispielsweise angegeben, wo sich der Bereitstellungsdeskriptor befinden sollte: /WEB-INF/web.xml. Wo soll die WEB-INF platziert werden? Wie im Gradle WAR-Plugin angegeben, fügt es ein neues Layout hinzu : src/main/webapp. Erstellen wir daher ein solches Verzeichnis, darin erstellen wir ein WEB-INF-Verzeichnis und darin eine web.xml-Datei. Es ist wichtig, dass das Verzeichnis heißt WEB-INF und nicht META-INF! Kopieren wir es aus dem XML-Beispiel „ 14.5.1 Ein einfaches Beispiel “:
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben - 3
Wie wir sehen, wird zur Konfiguration ein XML-Dokument verwendet. Um als gültig (gültig) zu gelten, muss ein XML-Dokument einem „Schema“ entsprechen. Man kann sich das als eine Art Schnittstelle für ein XML-Dokument vorstellen. Das Schema gibt an, welche Elemente in einem XML-Dokument enthalten sein können, welche Art von Daten das Element, die Reihenfolge, die Anforderung und andere Aspekte definieren können. Das aus der Dokumentation kopierte Beispiel weist auf Version 2.5 hin, wir wollen jedoch Version 3.1 verwenden. Natürlich änderte sich die Spezifikation mit dem Versionswechsel und es kamen neue Funktionen hinzu. Daher müssen Sie ein anderes Schema als das für Version 2.5 verwendete verwenden (web-app_2_5.xsd). Welches Schema sollte ich für Version 3.1 verwenden? Dabei hilft uns die Dokumentation, Kapitel „ 14.3 Bereitstellungsdeskriptor “, in der es heißt: specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd Das heißt, wir müssen den Link zum Schema überall durch das angegebene xsd ersetzen, nicht vergessen, es version="2.5"auf 3.1 zu ändern, und auch den Namespace überall ändern ( xmlns und in xsi:schemaLocation). Sie geben an, in welchem ​​Namensraum wir arbeiten werden (um es ganz einfach auszudrücken, welche Elementnamen wir verwenden können). Wenn Sie die Schemadatei öffnen, enthält der targetNamespace denselben Namespace, den wir angeben sollten:
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 4
Wie wir uns erinnern, haben wir im Manifest der Jar-Datei geschrieben, welche Klasse wir verwenden möchten. Was ist hier zu tun? Hier müssen wir angeben, welche Servlet-Klasse wir verwenden möchten, wenn wir eine Anfrage von einem Web-Client erhalten. Die Beschreibung kann im Kapitel „ 14.4 Deployment Descriptor Diagram “ nachgelesen werden. Es wird so aussehen:
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 5
Hier ist alles einfach. Das Serverlet wird deklariert und dann einer bestimmten Vorlage zugeordnet. In diesem Fall auf /app. Wenn die Vorlage ausgeführt wird, wird die Servlet-Methode ausgeführt. Der Schönheit halber sollte die App-Klasse in das Paket übertragen werden, wobei nicht zu vergessen ist, die XML-Konfiguration zu korrigieren. Aber das ist nicht alles. Die App muss ein Servlet sein. Was bedeutet es, ein Servlet zu sein? Das bedeutet, dass wir von HttpServlet erben müssen . Ein Beispiel ist im Kapitel „ 8.1.1 @WebServlet “ zu sehen. Demnach sieht unsere App-Klasse folgendermaßen aus:
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
Aber unser Projekt ist noch nicht fertig. Weil wir jetzt auf die Servlet-API-Version 3.1 angewiesen sind. Das bedeutet, dass wir in unserem Build-Skript eine Abhängigkeit von der Servlet-API angeben müssen. Die JVM muss wissen, dass das, was Sie in den Code geschrieben haben, korrekt ist und wie es verwendet wird. Wie wir uns erinnern, handelt es sich bei der Spezifikation im Wesentlichen nur um Schnittstellen, die beschreiben, wie alles funktionieren soll. Und die Implementierungen liegen auf der Webserverseite. Daher gibt es ohne die Servlet-API die Möglichkeit, die erforderliche Bibliothek in Maven Central zu finden: javax.servlet-api . Und fügen Sie einen Eintrag zum Abhängigkeitsblock hinzu . Im Maven-Repository heißt es, wie Sie gesehen haben, bereitgestellt. Bevor Sie eine Abhängigkeit verwenden, müssen Sie den Bereich angeben. Gradle hat keinen Bereich namens „provided“, aber einen Bereich „ nur kompilieren “. Deshalb werden wir darauf hinweisen: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Pfui, scheint alles in Ordnung zu sein? Gradle Build erstellt unser Projekt in eine WAR-Datei. Und was sollen wir als nächstes damit machen? Zuerst benötigen wir einen Webserver. In Google schreiben wir „ Webserver-Java-Liste “ und sehen eine Liste von Webservern. Wählen wir aus dieser Liste zum Beispiel TomCat aus . Gehen Sie zur Apache Tomcat- Website und laden Sie die neueste Version (derzeit Version 9) als Zip-Archiv herunter (falls für Windows). Entpacken Sie es in ein Verzeichnis. Hurra, wir haben einen Webserver. Aus dem Webserververzeichnis im Unterverzeichnis bin führen wir catalina über die Befehlszeile aus und sehen uns die verfügbaren Optionen an. Lass es uns tun: catalina start. Jeder Webserver verfügt über ein Verzeichnis, das der Webserver überwacht. Wenn dort eine Webanwendungsdatei angezeigt wird, beginnt der Webserver mit der Installation. Diese Installation wird Deployment oder Deployment genannt . Ja ja, deshalb „ Bereitstellungsdeskriptor “. Das heißt, wie die Anwendung ordnungsgemäß bereitgestellt wird. In Tomcat ist dieses Verzeichnis webapps . Kopieren wir dort den Krieg, den wir mit Gradle Build erstellt haben. Danach sehen wir im Protokoll so etwas wie: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Um es noch besser zu verstehen, bearbeiten wir die Datei im Tomcat-Verzeichnis \conf\tomcat-users.xmlund fügen die folgenden Zeilen hinzu:
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 6
Nun starten wir den Server neu (Catalina Stop, Catalina Start) und gehen zur Adresse. http://127.0.0.1:8080/manager Hier sehen wir die Pfade aller Anwendungen. Unser Webprojekt erhielt höchstwahrscheinlich den Pfad /webproject. Was ist dieser Weg? Die Spezifikation im Kapitel „ 10.1 Webanwendungen innerhalb von Webservern “ besagt, dass eine Webanwendung einem Pfad innerhalb der Anwendung zugeordnet ist (in diesem Fall /webproject). Alle Anfragen über diesen Pfad werden demselben ServletContext zugeordnet. Dieser Pfad wird auch contextRoot genannt . Und gemäß „ 10.2 Beziehung zu ServletContext “ verknüpft der Servlet-Container die Webanwendung und den ServletContext eins zu eins. Das heißt, jede Webanwendung hat ihren eigenen ServletContext. Was ist ein ServletContext ? Wie in der Spezifikation angegeben, ist ein ServletContext ein Objekt, das Servlets eine „Ansicht der Anwendung “ bietet, in der sie ausgeführt werden. Der Servlet-Kontext wird in Kapitel 4 der Servlet-API-Spezifikation ausführlicher beschrieben. Überraschenderweise erfordert die Servlet-API in Version 3.1 nicht mehr, dass web.xml vorhanden ist. Sie können beispielsweise ein Servlet mithilfe von Annotationen definieren:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
Auch zum Thema empfohlen: „ Java EE Interview – JEE Servlet API (Fragen und Antworten) “. Wir haben also ein Servlet – es ist dafür verantwortlich, welche Antwort dem Webclient gegeben wird. Wir haben einen ServletContainer, der Anfragen vom Benutzer empfängt, den Pfad, auf den zugegriffen wurde, mit dem Pfad zum Servlet abgleicht und bei einer Übereinstimmung das Servlet ausführt. Bußgeld. Welchen Platz nimmt der Frühling in diesem Weltbild ein ?

Spring Web MVC

Großartig, wir haben eine Webanwendung. Jetzt müssen wir Spring verbinden. Wie können wir das machen? Zunächst müssen Sie herausfinden, wie Sie Spring richtig mit Ihrem Projekt verbinden. Es stellt sich heraus, dass dies früher gemäß der Dokumentation des Spring-Plattformprojekts möglich war , aber jetzt „ wird die Plattform am 9. April 2019 das Ende ihrer unterstützten Lebensdauer erreichen “, das heißt, es ist nicht ratsam, dies zu tun Benutze es, weil es wird bald nicht mehr unterstützt. Der einzige Ausweg besteht darin, dass „ Benutzer der Plattform aufgefordert werden, mit der Verwendung des Abhängigkeitsmanagements von Spring Boot zu beginnen “. Fahren wir daher mit der Spring Boot- Dokumentation fort . Lassen Sie mich klarstellen, dass wir nicht Spring Boot selbst verwenden, sondern nur das Abhängigkeitsmanagement von Spring Boot. Das heißt, das Spring Boot-Projekt kann Wissen darüber liefern, welche Versionen von Bibliotheken verwendet werden sollen (einschließlich Spring MVC). Dort finden wir 3.2. Isolierte Verwendung des Abhängigkeitsmanagements von Spring Boot . Fügen Sie laut Dokumentation Folgendes zum Build-Skript hinzu:
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
Und
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
Wie Sie sehen, haben wir angegeben apply false, d.h. Wir verwenden nicht Spring Boot selbst, sondern verwenden von dort aus das Abhängigkeitsmanagement. Dieses Abhängigkeitsmanagement wird auch BOM – „ Bill Of Materials “ genannt. Jetzt können wir das Spring Web MVC-Projekt selbst verbinden. Spring Web MVC ist Teil des Spring Framework- Projekts und wir interessieren uns für den Abschnitt „ Web Servlet “. Fügen wir die Abhängigkeit zum Build-Skript hinzu: compile 'org.springframework:spring-webmvc'. Wie wir sehen können, legen wir den Umfang der Kompilierung fest, weil Der Webserver stellt uns Spring nicht zur Verfügung. Unser Projekt ist gezwungen, die Spring-Bibliothek in sich aufzunehmen. Als nächstes ist es für uns wichtig, den Abschnitt „ 1.2. DispatcherServlet “ zu lesen, in dem es heißt, dass Spring MVC um das „ Front Controller “-Muster herum aufgebaut ist, bei dem es eine Art zentrales Servlet gibt, das die Konfiguration und Delegation an andere Komponenten bereitstellt . Dispatcher kann als Dispatcher übersetzt werden. Also deklarieren wir zunächst in web.xml:
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 7
Wie wir sehen können, handelt es sich tatsächlich um einen regulären Listener, der in der Servlet-API-Spezifikation definiert ist. Genauer gesagt ist dies ein ServletContextListener, das heißt, er wird ausgelöst, um den Servlet-Kontext für unsere Webanwendung zu initialisieren. Als Nächstes müssen Sie eine Einstellung angeben, die Spring mitteilt, wo sich seine spezielle XML-Konfiguration mit Einstellungen befindet:
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 8
Wie Sie sehen, handelt es sich lediglich um eine reguläre Einstellung, die auf der Ebene des Servlet-Kontexts gespeichert wird, die jedoch von Spring bei der Initialisierung des Anwendungskontexts verwendet wird. Jetzt müssen Sie anstelle aller Servlets einen einzigen Dispatcher deklarieren, der alle anderen Anfragen verteilt.
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 9
Und hier gibt es keine Magie. Wenn wir hinschauen, handelt es sich um ein HttpServlet, bei dem Spring viele Dinge tut, die es zu einem Framework machen. Jetzt müssen Sie nur noch eine bestimmte URL-Vorlage mit dem Servlet korrelieren (zuordnen):
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 10
Alles ist wie zuvor. Jetzt erstellen wir etwas, das unser Webserver anzeigen soll. Erstellen wir beispielsweise ein Unterverzeichnis „pages“ in unserem WEB-INF, und es wird eine Datei „hello.jsp“ geben. Der Inhalt kann der primitivste sein. Innerhalb von HTML-Tags gibt es beispielsweise ein h1-Tag mit dem Text „ Hello World “. applicationContext.xmlUnd vergessen Sie nicht , die zuvor angegebene Datei zu erstellen . Nehmen wir ein Beispiel aus der Spring-Dokumentation: „ 1.10.3. Klassen automatisch erkennen und Bean-Definitionen registrieren “.
Von Hello World bis Spring Web MVC und was Servlets damit zu tun haben – 11
Weil Wenn wir auf diese Weise die automatische Erkennung aktivieren, können wir jetzt zwei Klassen erstellen (sie werden aufgrund der Verwendung spezieller Spring-Annotationen als Spring Beans betrachtet), die Spring nun selbst erstellt und mit ihrer Hilfe unsere Anwendung anpasst:
  1. Webkonfiguration, zum Beispiel eine Konfiguration im Java-Stil:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    Dieses Beispiel wird in der Spring Framework-Dokumentation beschrieben: „ 1.11. MVC Config “.

    Hier registrieren wir einen ViewResolver, der dabei hilft, festzustellen, wo sich die JSP-Seiten befinden. Die zweite Methode stellt sicher, dass das „ Standard-Servlet “ aktiviert ist.

    Mehr dazu können Sie hier lesen: „ Was ist die Notwendigkeit und Verwendung des Standard-Servlet-Handlers ?“.

  2. HelloController-Controller zur Beschreibung der Zuordnung von Anforderungen zu einer bestimmten JSP

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    Hier haben wir die @Controller-Annotation verwendet, die in der Dokumentation im Kapitel „ 1.4. Annotierte Controller “ beschrieben ist.

Wenn unsere Anwendung nun bereitgestellt wird und wir eine Anfrage senden /webproject/hello(wobei /webproject das Kontextstammverzeichnis ist), wird zuerst das DispatcherServlet verarbeitet. Als Haupt-Dispatcher stellt er fest, dass wir /* mit der aktuellen Anfrage übereinstimmen, was bedeutet, dass das DispatcherServlet etwas tun muss. Anschließend werden alle gefundenen Zuordnungen durchgegangen. Es wird erkannt, dass es einen HelloController mit einer Handle-Methode gibt, die /hello zugeordnet ist, und führt diese aus. Diese Methode gibt den Text „Hallo“ zurück. Dieser Text wird vom ViewResolver empfangen, der dem Server mitteilt, wo er nach den JSP-Dateien suchen soll, die dem Client angezeigt werden müssen. So erhält der Kunde letztendlich die sehr geschätzte Seite.

Abschluss

Ich hoffe, aus dem Artikel geht klar hervor, dass das Wort „Kontext“ nicht beängstigend ist. Diese Spezifikationen erweisen sich als sehr nützlich. Und Dokumentation ist unser Freund, nicht unser Feind. Ich hoffe, dass klar wird, worauf Spring basiert, wie es sich verbindet und was die Servlet-API damit zu tun hat.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION