Upload
horacio-gonzalez
View
1.020
Download
1
Embed Size (px)
Citation preview
The Serverstrikes back
Servlet, jsp, web server and so on
La ServletThe Web War
Je dois obéir à mon maître Perl
Tu souséstime le pouvoir de CGI
Ton simple language est bien faible comparé au coté obscur de Perl
Robuste je suis grâce à Java
Toujours en mouvement le Web est
Du conteneur émane l'énergie de la Servlet
La ServletGénéralitée
La Servlet est une classe Java permettant de créer dynamiquement des données au sein d'un serveur HTTP.Une instance unique de la Servlet s'execute à chaque requête HTTP reçu par le conténeur (Serveur Web).Elle produit du code (HTML, XML, JS...) compréhensible par un navigateur Web.
● Crée en 1997 (v1.0) par Sun Microsystems● la servlet est un singleton● interface définie dans le package javax.servlet● normée par une JSR (Java Specification Requests)● support natif dans un conténeur web java (Tomcat, Jetty, etc...)
La ServletHistorique
Servlet 3.0 12/2009 JavaEE/SE 6 Async Servlet, File Uploading
Tomcat 7.x, Jetty 8.x
Servlet 2.5 09/2005 JavaEE/SE 5 Ajout des annotations Tomcat 6.x, Jetty 6.x-7.x
Servlet 2.4 11/2003 J2EE 1.4, J2SE 1.3 XML Schema pour le web.xml
Tomcat 5.x, Jetty 5.x
Servlet 2.3 08/2001 J2EE 1.3, J2SE 1.2 Gestion des filtres Tomcat 4.x, Jetty 4.x
Servlet 2.2 08/1999 J2EE 1.2, J2SE 1.2 Intégration dans J2EE Tomcat 3.x, Jetty 3.x
Servlet 2.1 11/1998 Spécification officielle Jetty 2.x
Servlet 2.0 JDK 1.1
Servlet 1.0 06/1997
Web BrowserJava Web Containeur
La ServletDesigned to eat HTTP requests
GET http://localhost:8080/myservlet HTTP/1.0
Decode et affiche la résponse HTML
HTTP/1.1 200 OKServer: Apache-Coyote/1.1Content-Type: text/htmlContent-Length: 122Date: Fri, 02 Nov 2012 14:40:51 GMT
<html><head> <title>Hello Enib!</title> </head> <body> <h1>Welcome in the Java server side!</h1> </body> </html>
CAI Webappp (war)
MyServlet.class
Produit du code HTML
La ServletControlled by the container
Copyright : http://marakana.com/s/post/106/tomcat_architecture_diagram
La ServletLife cycle
Web Container Servlet Class Servlet Object
BeerServlet.class
Class Loading
Instantiation (Default contructor)
Servlet ready to work
init()
service()
Servlet clean up
Servlet working
destoy()
Handle client HttpRequest
doGet() doPost() each request in a separate Thread
Call once by the container
Call once by the container
The servlet should only have the default contsructor
Copyright : http://www.lifecyclesfilm.com/
La ServletSimple ? ... or not ?
public class HtmlServlet extends HttpServlet {private static final long serialVersionUID = 1L;
public HtmlServlet() {
super(); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html") ;PrintWriter out = response.getWriter() ;out.println("<html>") ;out.println("<head>") ;out.println("<title>Hello Enib!</title>") ;out.println("</head>") ;out.println("<body>") ;out.println("<h1>Welcome in the Java server side!</h1>") ;out.println("</body>") ;out.println("</html>") ;
}}
Really so simple ?
Copyright : http://www.lifecyclesfilm.com/
POST : Permet de réaliser une transaction afin de mettre à jour le modèle. Les données sont transmises via l'entête de la page Web.
Attention que se passe-t-il si la page est rejouée ?
La ServletGet or Post Madness
GET...
POST...
Servlet
Servlet Model
<html>...
<html>...
GET permet de récupérer un document, ne doit RIEN changer sur le serveur. Peut être rejoué (F5) sans effets de bords
Copyright : http://www.lifecyclesfilm.com/
La ServletLa guerre des clones
Je vais cloner mes Servlets !
Une pour chaque page !
La ServletAu modèle MVC vous fier, il faut !
Le contrôleur n'effectue aucun traitement, ne modifie aucune donnée. Il analyse la requête du client et se contente d'appeler le modèle adéquat et de renvoyer la vue correspondant à la demande.
Controller(Servlet)
Modèle(Java)
Vue(JSP)
Ce avec quoi l'utilisateur interagit. - présente les résultats renvoyés par le modèle.
- reçoit toute action de l'utilisateur (clic de souris, etc...)
- envoie les évènements au contrôleur (HTTP Request)
La vue n'effectue pas de traitement, elle se contente d'afficher les résultats des traitements effectués par le modèle et d'interagir avec l'utilisateur.
Le modèle représente le cœur (algorithmique) de l'application : traitements des données.
- décrit les données manipulées par l'application. - responsable de l'intégrité des données métier.
Les résultats renvoyés par le modèle ne s'occupent pas de présentation. Le modèle ne contient aucun lien direct vers le contrôleur ou la vue.
BDD
La ServletRequête HTTP et modèle MVC
1. Requête du navigateur
2. Mapping url / servlet
3. Appel du modèle métier
4. La servlet ajoute la réponse du modèle à la requête
5. Forward vers la JSP
6. La JSP récupére la réponse
7. Génération de la page HTML
8. Le conteneur envoie la page au navigateur
Container logic
Servlet
Modèle(Java)
Vue(JSP)
2
1
3
8
7
5
4
6request
La ServletMy best work
1. Requête du navigateur
2. Mapping url / servlet
3. Appel du modèle métier
4. La servlet ajoute la réponse du modèle à la requête
5. Forward vers la JSP
6. La JSP récupére la réponse
7. Génération de la page HTML
8. Le conteneur envoie la page au navigateur
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Call the businessList<Beer> beers = beerBusiness.getBeers();
// Adds the model response to the requestrequest.setAttribute("beers", beers);
// Forward to the JSPRequestDispatcher dispatch = request.getRequestDispatcher("/WEB-INF/views/beer.jsp");dispatch.forward(request, response);
}
beers.jspHead / Body....
<table class="table table-condensed"><thead>
<tr> <th>Name</th> <th>Brewery</th> <th>Country</th> <th>Alcohol</th> </tr> </thead> <tbody>
<c:forEach items="${beers}" var="beer"><tr>
<td>${beer.name}</td> <td>${beer.brewery}</td> <td>${beer.country}</td> <td>${beer.alcohol}</td> </tr> </c:forEach>
</tbody></table>...
...
La ServletLiens Servlet / JSP
1. Requête du navigateur
2. Mapping url / servlet
3. Appel du modèle métier
4. La servlet ajoute la réponse du modèle à la requête
5. Forward vers la JSP
6. La JSP récupére la réponse
7. Génération de la page HTML
8. Le conteneur envoie la page au navigateur
La ServletGestion de la session
Une session web est une période délimitée pendant laquelle un serveur est en communication et réalise des opérations au service d'un client HTTP.
Session implicite : Lié à un browserSession explicite : Liée a des credentials (login / password)
Création d'une session :
HttpSession session = request.getSession(true);
La ServletHttpSession
Principales methodes
Configuration de la durée de session
void setAttribute(String name, Object Value)Object getAttribute(String name) Ajoute ou récupère la valeur d'un attribut en session
Enumeration getAttributeNames() Retourne la liste de tous les attriuts
String getId() Retourne l'identificant de sessions
void invalidate() Invalide la session courante
Dans le web.xml -global à toutes les sessions- : <session-config>
<session-timeout>30</session-timeout></session-config>
A la création de la session -local pour chaque session- :session.setMaxInactiveInterval(30);
La ServletListener de Session
Interfacesà implémenter
HttpSessionListerner HttpSessionAttributeListerner
Déclaration dans le web.xml
Notification new session createdvoid sessionCreated( HttpSessionEvent hse )
Notification session invalidatedvoid sessionDestroyed( HttpSessionEvent hse )
Notification new attribute added :void attributeAdded( HttpSessionBindingEvent hsbe )
Notification attribute removed :void attributeRemoves( HttpSessionBindingEvent hsbe )
Notification attribute replaced:void attributeRemoves( HttpSessionBindingEvent hsbe )
<listener> <description>HttpSessionListener, HttpSessionAttributesListener</description> <listener-class>fr.enib.cai.servlet.SessionListener</listener-class>
</listener>
JSPIntroduction
JSP = JavaServer Pages
Technologie Java crée par Sun
Permettent la génération de pages web dynamiques.
Mélange de Java côté serveur et d'HTML côté client.
Adore se transformer !
Adepte du ménage à 3 (la JSP n'existe pas sans container ni Servlet)
JSPTransformation
<html><body><%= new String("Hello!") %></body></html>
package ...
import javax.servlet...
byte code0010 00101001 00010001 11000101 1010
Servlet
Hello.jsp Hello_jsp.java Hello_jsp.class Hello_jsp servlet
translated compiled loaded
JSPScriptlet
UglyScriptlet.jsp<html><body>Pourquoi suis-je si moche ?<br><%out.println(plain.old.java.Reason.getText() );%></body></html>
Reason.javapackage plain.old.java;
pubic class Reason {public static synchronized String getText() {
return new String("Parce que tu as un problème de circulation sanguine.");}
}
JSPLes directives
UglyScriptlet.jsp<%@ page import="plain.old.java.*" %><html><body>Pourquoi suis-je toujours aussi moche ?<br><% out.println(Reason.getText() ); %></body></html>
<%@ page[language="java"] [extends="package.class"][import="{package.class|package.*}, ..."] [session="true|false"][buffer="none|8kb|sizekb"] [autoflush="true|false"][isThreadSafe="true|false"] [info="text"][errorPage="relativeURL"][contentType="mimeType [charset=characterSet]" | "text/html;charset=ISO-8859-17"][iserrorPage="true|false"]%>
<%@ include file="relativeURL" %>
<%@ taglib uri="URIToTagLibrary" prefix="tagPrefix" %>
JSPLes expressions
UglyScriptlet.jsp<%@ page import="plain.old.java.*" %><html><body>Pourquoi suis-je toujours aussi moche ?<br><%= (Reason.getText() ); %></body></html>
Les expressions JSP permettent d'insérer simplement des chaînes de caractères générées dynamiquement dans la page HTML.
JSPLes objets implicites
Java API jsp ObjectJspWriter outHttpServletRequest requestHttpServletResponse responseHttpSession sessionServletContext applicationThrowable exceptionPageContext pageContextObject page
Les expressions JSP permettent d'insérer simplement des chaînes de caractères générées dynamiquement dans la page HTML.
JSPLes scopes
Page scope - portée : une JSP
Request scope - portée : une http request
Session scope - portée : la session d'un utlisateur du site - n'est pas thread safe (plusieurs onglet d'un browser peuvent partager la même session)
Application/Context scope - portée : globale (Servlet, Jsp, listener) - n'est pas thread safe
JSPExpression Language (EL)
Inclus à partir de JSP 2.0
Un seul but : Code less
Too long expression<%= ((fr.enib.cai.core.Beer) request.getAttribute("beers").get(0).getName() %>
Much better no ?${beers[0].name}
Attention EL implicit objects sont des Maps - pair(name / value)pageScope - requestScope - sessionScope - applicationScopeparam - paramValuesheader - headerValuecookieinitParam
Il faut toujours un exception : pageContext
Oracle tutorial : http://docs.oracle.com/javaee/1.4/tutorial/doc/JSPIntro7.html
JSPExpression Language (EL) - Implicit objects
Autre exemple : Method is : <%= request.getMethod() %>
Avec les EL essayez :
Method is : ${request.method}
Method is : ${requestScope.method}
Method is : ${pageContext.request.method}
Oracle tutorial : http://docs.oracle.com/javaee/1.4/tutorial/doc/JSPIntro7.html
Ouch
● JSTL = JSP Standard Tag Library
● 4 Bibliothèques de tags utilisables dans les JSP
● Objectif : faciliter le développement des JSP
● Anéantir les scriptlets
● Depuis servlet API 2.3
JSTLRelookez vos JSP
Avec le Jstl on peut faire :
des valeurs par défaut<c:out value='${user}' default='invité' />
des boucles <c:forEach var="beer" items="${beers}" >
Beer name : ${beer.name}</c:forEach>
Ne jamais laisser trainer les cadavres ! <c:remove var="kenny" scope="request" /> Le scope est optionnel
JSTLLes gammes - core library
des if... sans else<c:if test="${beer.name eq 'Karmeliet'}">
Yes ${beer.name} have an incredible flavour ! </c:if>
mais avec des choose on peut faire de choses !<c:choose>
<c:when test="${beer.name eq 'Karmeliet'}">Yes ${beer.name} have an incredible flavour !
</c:when><c:when test="${beer.name eq 'Rochefor 8'}">
${beer.name} is another best choice !</c:when><c:otherwise>
Dammed give me a Kro !</c:otherwise>
</c:choose>
JSTLLes gammes - core library
un set vraiment open !
des variables <c:set var="username" scope="" value="moi" />Pour les variables c'est basic même des Maps <c:set target="${PrettyMap}" property="prettyKey" value="prettyValue" />
JSTLLes gammes - core library
Focus sur les includes!
<%@ include file="header.html" %> Static include content added at translation time
<jsp:include page="header.jsp" /> Dynamique include, compiled on time executed for each request
<c:import url="http://www.beertender.com/product.html /> Dynamique (comme jsp:include)Je peux inclure des ressources en dehors de mon container ???
Même avec des paramètres !<c:import url="header.jsp>
<c:param name="subTitle" value="Wy I drink so many beers ?" /></c:import>
File header.jsp<em><strong>${param.subTitle}</strong></em>
JSTLLes gammes - core library
Créer des liens dans vos pages !
<c:url value='/signin.jsp' />
Avec des paramètres
<c:url value='/signin.jsp' ><c:param name="promocode" value="${enib}" />
</c:url>
Dans la page HTTP vous aurez un lien du genre :-> http://myServer.enib.fr:8080/mycontext/signin.jsp?promocode=enib
JSTLLes gammes - core library
catcher les exceptions (NOTE : Ne pas coder avec des moufles) !
<c:catch var="myException"> <!-- doing here a very dangerous operation --></c:catch>
<c:if test="${myEception != null}"> Dammed, what is going wrong ? ${myException.message}</c:if>
JSTLLes gammes - core library
La librairie de formatage (fmt):http://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/fmt/tld-summary.html
● internationalisarion● formatage des chaines de caractère
La "SQL" library :Hum, du Sql dans les JSP, je suis confus... ce n'est pas vraiment MVC compliant... mais ça existe.
La "XML" library :A utiliser en dernier recours, à dose homeopathique !
JSTLL'aventure continue
Peut vous sauver la vie :
● réduire la duplication de code dans vos JSP
● intégrer la vue d'un composant complexe
● coder coté java quand le langage JSP / JSTL est aux limites
Helas trop long pour le parcourir !
JSTLYeah je fais ma lib de tags
Plein de noms pour la même chose
-> Conteneur de servlets (Servlet container en anglais) -> Conteneur web (web container en anglais) -> moteur web -> moteur de servlets...
Bref, il exécute des servlets.
La Star : Tomcat de la fondation Apache (59% du marché).
Les poids lourds :Jboss ou WildFly (redhat), Jetty (Codehaus), WebLogic (IBM), WebSphere (IBM), GlassFish (Oracle), ... et une multitude d'autres
Le Conteneur
● contenir des web apps
● recevoir des requêtes HTTP et les faire traiter par la bonne web app
● faire le mapping vers les servlets
● gérer les ressources de l'application web (base de données, etc...)
● gérer la session
● faire le mapping url / servlet, filtres,valves
● gérer le realm (royaume de la sécurité, un peu obsolète)
● gérer les logs
Le ConteneurSon rôle
Le ConteneurSchema général
Copyright : http://marakana.com/s/post/106/tomcat_architecture_diagram
Le Conteneurserveur.xml
Copyright : http://marakana.com/s/post/106/tomcat_architecture_diagram
● Configuration des services (généralement 1, : Catalina)
● Configuration pour chaque service ○ connecteur (port d'écoute)○ valves (intercèpte toutes les requêtes)○ realm (gère l'authentification)○ host (les applications web du container)
Intercèpte TOUTES les requêtes
Définie dans le server.xml ou context.xmlApplicable pour un : engine, host ou context
exemple :<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
Ecrit un fichier de logs contenant une ligne pour chaque requête HTTP
10.144.167.233 - - [10/Oct/2012:04:31:17 +0200] "POST /servau/thrift/acces/ HTTP/1.1" 200 325 "null" "Java/THttpClient" 3
Le ConteneurLa valve
Intercèpte les requêtes en amont d'une servlet pour un url pattern donné
Définie dans le web.xmlLe filtre est une classe Java
Souvent utilisé pour traiter vérifier la session et les habilitations d'une requête.
exemple : <filter> <filter-name>UserContextFilter</filter-name> <filter-class> fr.enib.cai.filter.UserContextFilter </filter-class> </filter>
<filter-mapping> <filter-name>UserContextFilter</filter-name> <url-pattern>/prive/*</url-pattern> </filter-mapping>
Le ConteneurLe filtre
Execute une requête pour un mapping d'url donné
Définie dans le web.xmlLa servlet est une classe Java
exemple : <servlet> <servlet-name>authentication_servlet</servlet-name> <servlet-class> fr.enib.cai.servlet.SignInServlet </servlet-class> </servlet>
<servlet-mapping> <servlet-name>authentication_servlet</servlet-name> <url-pattern>/signin</url-pattern> </servlet-mapping>
Le ConteneurLa Servlet
WAR = un fichier jar (donc un zip...) contenant :○ JSP○ Servlet○ classes Java○ fichiers (xml, html, js, css)○ taglibs (tld)
Cette archive est utilisée pour déployer une application web sur un serveur d'application.
Le WarWeb Application ARchive
Le WarStructure
mywebapp.war
WEB-INF
libclasses tags
fr
foo
MyTag.tag
fr.foo.Foo.class
web.xml
META-INF
MANIFEST.MF
(public)resources
CSS
JS
PNG
(public)content
JSP
HTML(private)views
JSPs
PRIVATE : fichiers exposés par l'intermédiaire d'une servlet / filtre / web.xmlPUBLIC : fichiers exposés directement par le container
Contener needs infrastructure !
Un service web est un programme informatique permettant la communication et l'échange de données entre applications et systèmes hétérogènes dans des environnements distribués.
Il s'agit donc d'un ensemble de fonctionnalités exposées sur internet ou sur un intranet, par et pour des applications ou machines, sans intervention humaine, et de manière synchrone.
Source Wikipedia
Les web services utilisent comme medium de communication HTTP/S
Web service
anciènement : Simple Object Access Protocol : protocol RPC orienté objet bâti sur XML
● Définit initialement par Microsoft et IBM.● recommandé par le W3C● utilisé dans les architecture SOA (Service Oriented Architecture)
Avantages : ● basé sur HTTP● indépendant de la plate-forme● "presque" indépendant du langage
Inconvénients : ● formalisme lourd (verbosité de l'xml)● couplage fort client / serveur● volume de données transactionnel
Web serviceSOAP
<?xml version="1.0"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Header> <m:User xmlns:m="http://www.exemple.com/rights/" soap:actor="http://www.exemple.com/rights/RightsManager"> Charles </m:User>
<m:Session xmlns:m="http://www.exemple.com/session/" soap:mustUnderstand="1">12AE3C </m:Session><m:Lang xmlns:m="http://www.exemple.com/lang/" soap:actor="http://schemas.xmlsoap.org/soap/next" soap:mustUnderstand="0">
FR </m:Lang> </soap:Header>
<soap:Body> <m:GetPrice xmlns:m="http://www.exemple.com/prices"> <m:Item>Pomme</m:Item> </m:GetPrice>
<soap:Fault> <faultcode>soap:Server</faultcode> <faultstring>Impossible de router le message.</faultstring> <faultactor>http://www.exemple.com/messageDispatcher</faultactor> <detail> <m:error xmlns:m="http://www.exemple.com/errors">E_NO_ROUTE</m:error> </detail> </soap:Fault> </soap:Body></soap:Envelope>
Web serviceSOAP - Exemple
Web Service Description Language: grammaire XML décrivant un Service Web
● v2.0 approuvée en 2007● recommandé par le W3C● décrit une interface publique dans les architecture SOA
Le WSDL sert à décrire :● le protocole de communication (SOAP RPC ou SOAP orienté message)● le format des messages● les méthodes que le client peut invoquer● la localisation du service
Le wsdl est compilé (wsdl2java) pour obtenir les couches client / serveurDes implementation sont disponibles avec les frameworks :
○ cxf○ axis ○ un conteneur Web Profile (tomEE entre autres)
Web serviceWSDL
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <!-- Définition d'un message getTermRequest contenant une chaîne de caractères term --> <wsdl:message name="getTermRequest"> <wsdl:part name="term" type="xs:string"/> </wsdl:message>
<!-- Définition d'un message getTermResponse contenant une chaîne de caractères value --> <wsdl:message name="getTermResponse"> <wsdl:part name="value" type="xs:string"/> </wsdl:message>
<!-- Description du WebService glossaryTerms contenant une opération (action) qui prend en entrée un message getTermRequest et retourne un message getTermResponse --> <wsdl:portType name="glossaryTerms"> <wsdl:operation name="getTerm"> <!-- On peut rajouter une description textuelle de l'opération --> <wsdl:documentation .... /> <wsdl:input message="getTermRequest"/> <wsdl:output message="getTermResponse"/> </wsdl:operation> </wsdl:portType>
<!-- Association de l’accès SOAP pour le WebService glossaryTerms --> <wsdl:binding type="glossaryTerms" name="b1"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation> <soap:operation soapAction="http://www.example.com/getTerm"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding></wsdl:definitions>
Web serviceWSDL - Exemple
REST (REpresentational State Transfer)● un style d'architecture logicielle permettant de construire une application
devant fonctionner sur des systèmes distribués● très proche du Web (définit par Roy Fielding, un des rédacteurs de la norme
HTTP 1.1)● aux sources du Web - STATELESS -
Bref, REST est donc le style d'architecture soutenant le Web.
Web serviceREST
● PUT créer des ressources / urls sur un serveur● GET récupérer une ressource● POST créer ou modifier une ressource sur le server● DELETE supprimer une ressource
Exemple : http://myIncredibleRestServer:8080/context/rest/articles?prix=3.05
[ { "nom": "article a", "prix": 3.05, "disponible": false, "descriptif": "mon article a ..." }, { "nom": "article b", "prix": 3.05, "disponible": true, "descriptif": null }]
Web serviceREST - The Web Spirit !
● Un langage de description d'interfaces (IDL) pour les données et services
● Un compilateur qui génère le code pour mettre en place les servicesMulti-langage : Java, Python, C/C++, PHP...
● Du code pour la description des données (beans)
● la sérialisation/désérialisation
● le transport (HTTP, TCP...)
● le client et le serveur
Web serviceC'est quoi Thrift
enum PhoneType { HOME, WORK, MOBILE, OTHER,} struct Phone { 1: i32 id, 2: string number, 3: PhoneType type,}
struct Contact { 1 : i32 id, 2 : string name, 3 : string surname, 4 : list<Phone> phones,}
ThriftLes idls - décrire les données
struct GetContactByNameRequest { 1 : string name, 2 : string surname,}struct GetContactByNameResponse { 1 : GetContactByNameRequest request, 2 : Contact contact,}
service GetContact { //Ici une belle description du service GetContactByNameResponse getContactByName( 1 : GetContactByNameRequest request );}
● Un et un seul paramètre en entrée NomDeLaMethodeRequest● Un et un seul objet en sortie NomDeLaMethodeResponse
● Le premier champ de l'objet response est toujours la request
Web serviceServices
CloudYou did this really by youself ?
CloudEnjoy, the Cloud save you life
This afternoon...