Académique Documents
Professionnel Documents
Culture Documents
Comprendre
les servlets et Java Server Pages
simplement
par la pratique
Version 1.2
Version 1.2
Annexe 5 : uploads de fichiers....................................................................................25 Prsentation du paquetage util.more.web.form .................................................25 Utilisation du paquetage util.more.web.form .....................................................25 Annexe 6 : description de la librairie util ................................................................27
Version 1.2
Introduction
Ce document rcapitule les informations essentielles la conception de sites web faisant appel la technologie Java ct serveur par lutilisation de servlets et de scripts JSP. Les servlets constituent la rponse Java aux programmes CGI ( Common Gateway Interface ). Excutes sur un serveur dapplications, elles interceptent les requtes des navigateurs web et gnrent des rponses (souvent en DHTML) construites dynamiquement laide du langage Java. Les JSP sont lextension des servlets et se rapprochent du modle J2EE qui spare : Les contenus statiques et dynamiques La prsentation et la logique mtier, notamment grce aux JavaBeans.
Version 1.2
Servlets
Les servlets font usage de la technologie Java pour gnrer des pages HTML dynamiques (DHTML). Elles permettent lintgration de lensemble des fonctionnalits de J2EE, contrairement dautres langages web et notamment PHP.
Cycle de vie
init doGet service doPost destroy Les mthodes init() et destroy() ne sont appeles quune seule fois par le conteneur de servlets, respectivement la premire fois que la servlet est appele et au moment o le conteneur (donc le serveur dapplications) termine son excution, contrairement aux mthodes service() et doXXX() , appeles chaque invocation de la servlet.
Exemple
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Hello extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>"); out.println("Test de servlet"); out.println("</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Hello World!!!</h1>"); out.println("<p>This is a test servlet</p>");
Version 1.2
out.println("</body>"); out.println("</html>"); } }
Transmis par le biais dun formulaire (principalement GET, POST et POST/multipart, utilis pour les uploads de fichiers sur le serveur).
Les paramtres en entre sont rcuprs par la servlet laide de la mthode getParameter :
String request.getParameter(String paramName)
JSP
Dfinition
La technologie JSP simplifie la mise en uvre de sites faisant appel Java ct serveur. Il sagit de code DHTML dans lequel sinsrent des scriptlets. Les pages JSP sont transformes la vole par un traducteur interne au serveur dapplications lors de leur premier appel. Cette traduction permet de gnrer, compiler puis instancier un objet Java correspondant. Ce dernier est comparable, dans son fonctionnement, une servlet pour laquelle la mthode service a t redfinie laide des instructions JSP. Le code DHTML est ainsi interprt par le traducteur afin de le retourner au client laide de commandes similaires linstruction out.println() des servlets. Le code Java nest soumis aucun traitement avant compilation de la classe Java rsultante.
Version 1.2
Le fichier indiqu par chemin_fichier_local est insr tel quel lendroit de lappel la directive include . Il est galement possible dutiliser la directive jsp:include :
<jsp:include page="URL_relative" flush="true" />
Ou, alternativement :
<jsp:include page="URL_relative" flush="true"> <jsp:param name="paramName" value="paramValue" /> </jsp:include>
Version 1.2
Dans ce cas, les ressources incluses nont accs qu lobjet out (de type JspWriter) JSP et ne peuvent faire usage des cookies. De plus, celles-ci ne peuvent contenir du code JSP gnral. Enfin, la premire directive dinclusion est excute au moment de la traduction du JSP en code Java (inclusion statique) alors que la seconde directive est excute au moment du traitement de la requte (inclusion dynamique).
Autres directives
La directive taglib permet de dfinir des balises personnalises dans des bibliothques de balises :
<%@ taglib uri="tagLibrairie" prefix="tagPrefix" %>
La commande forward permet de rediriger la page courante (JSP) vers une seconde page tout en conservant les informations de session collectes jusqualors (par les beans notamment) :
<jsp:forward page="autre_page.jsp" />
Laction plugin est utilise pour gnrer des balises HTML de type <OBJECT> ou <EMBED> en fonction du navigateur client. Elle a pour effet le tlchargement du plug-in Java (si ncessaire) suivi de lexcution de lapplet ou du bean spcifi dans la balise :
<jsp:plugin type="bean|applet" name="objectName" code="objectCode" codebase="objectCodebase" hspace="hspace" vspace="vspace" width="width" jreversion="jreversion" nspluginurl="url" iepluginurl="url"> <jsp:params> <jsp:param name="param1" value="paramValue1" /> <jsp:param name="paramN" value="paramValueN" /> </jsp:params> <jsp:fallback>Texte alternatif si plugin non charg </jsp:fallback> </jsp:plugin>
Version 1.2
Scriptlets
Dfinition et intgration
Les scriptlets permettent dinsrer du code Java au sein du code DHTML des pages JSP laide des dlimiteurs <% et %> :
<% code Java %>
Il est galement possible dutiliser des balises XML. Les scriptlets sont alors dclares par :
<jsp:scriptlet> code Java </jsp:scriptlet>
Le code rdig est recopi tel quel par le traducteur Java des scripts JSP. Ces derniers autorisent galement la dclaration de variables de classe et de mthodes permettant dagrger les traitements de prsentation. Dans ce cas, les dlimiteurs <%! et %> sont employs.
Exemples
<% String str = "Boucle "; for (int i=0; i<10; i++) { out.println(str + i + "<br/>"); } %>
Le code prcdent utilise la variable out dfinie par le traducteur de pages JSP en code Java intgral. Il est strictement quivalent :
<% for (int i=0; i<10; i++) { %> Boucle <%= i %><br/> <% } %>
Laffichage dune variable ou dune expression (lvaluation dune mthode par exemple) se fait ainsi indiffremment par <% out.println(nomVariable); %>, <%= nomVariable %> ou en utilisant lopration getProperty approprie lorsquil sagit dun bean. Lquivalent XML est <jsp:expression>nomVariable</jsp:expression>.
Version 1.2
Les informations situes entre les balises de dbut et fin de commentaire ne sont pas intgres lors de la traduction de la page JSP. Par contre, les commentaires HTML apparatront comme tels lors de la gnration de la page HTML. Rappelons que les commentaires en langage HTML scrivent comme suit :
<!-- Dbut de commentaire HTML Fin de commentaire HTML -->
Utilisation avance
Dclaration de mthodes et variables de classe
Lensemble du code Java (et des instructions DHTML) est plac par le traducteur de scripts JSP au sein de la mthode _jspService , similaire la mthode service des servlets. De fait, les variables dfinies dans le script JSP sont dclares comme locales cette mthode par le traducteur ayant gnr lobjet Java correspondant au JSP. Il est toutefois permis de : Dclarer des mthodes utilisables par les scriptlets du JSP. Par exemple, la mthode isEmpty qui vrifie si un paramtre est renseign ou non :
<%! private boolean isEmpty(String str) { return (str == null) || str.equals(""); } %>
De dclarer des variables de classe. Lexemple suivant dclare les variables prives non statiques monEntier et maChaine :
<%! private int monEntier = 0; private String maChaine = null; %>
Note : lintrt de recourir lutilisation de variables de classe semble beaucoup moins vident que la dclaration de mthodes. En effet, les variables dfinies au sein
10
Version 1.2
des scriptlets sont reconnues par lensemble des scriptlets situes en aval dans la page JSP.
Flux de sortie, de type JspWriter (similaire PrintWriter) Valeur retourne par getServletConfig() Remplace le mot-cl this Erreur : Transmise la page derreur (si errorPage est spcifi dans la page ayant gnr lereur). Rcupre par la page derreur (uniquement si la proprit isErrorPage est positionne true ).
Page derreur
Les erreurs non captures par les scriptlets peuvent tre rediriges vers une page derreur via la directive <%@ page errorPage="cheminRelatifPageErreur" %> . Cette page derreur peut tre ou non une page JSP. Seul un chemin relatif est tolr.
11
Version 1.2
Dans le cas o la page derreur est un script JSP, lattribut isErrorPage doit explicitement tre indiqu de sorte que la variable intitule exception (de type Throwable) puisse tre utilise. Il est galement possible de faire appel lattribut javax.servletjsp.jspException de la requte (via lobjet implicite request ). Lorsquune exception est leve, le tampon de sortie est vid avant affichage de la page derreur. Nanmoins, si la page source de lexception non capture autorise lautoFlush, une erreur HTTP 500 (voir codes HTTP en annexe) peut survenir si une rponse partielle a dj t retourne au client.
JavaBeans
Dfinition
Un JavaBean est un composant Java permettant de sparer la logique mtier de la prsentation des donnes. Il sagit dune classe Java (souvent dclare finale ) qui dispose des proprits suivantes : Il doit tre dclar public afin dtre accessible depuis les scripts JSP. Le seul constructeur disponible est public sans argument. Ce dernier doit initialiser les valeurs par dfaut des attributs ( null par exemple). Les mthodes publiques set et get sont dfinies pour chaque attribut, selon le principe des DesignPatterns. Par exemple, un attribut att doit disposer des mthodes public void setAtt(String val) et public String getAtt() .
Dclaration
Les JavaBeans sintgrent au sein des JSP en les dclarant en en-tte laide de la directive useBean :
<jsp:useBean id="beanId" class="beanClass" scope="session" />
La porte du bean ( scope ) peut tre : application : le bean est persistant et partag entre tous les clients (et par consquent les pages JSP) qui en font usage. session : le bean est spcifique chaque client. Il permet de transmettre des informations de session dune page lautre. Il suffit de dclarer le mme bean dans la seconde page pour que celle-ci soit en mesure de rcuprer les informations de session enregistres par le bean. request : le bean est dtruit lorsque le client demande une nouvelle page. Le passage des paramtres de suivi de session doit ainsi tre ralis manuellement (par formulaire avec champs cachs ou par rcriture dURL). page : rarement employe, cette porte est limite la page en cours (aucune information de session ne peut tre rcupre) : ce bean est accessible nimporte quel endroit de la page.
12
Version 1.2
Cette dernire permet de limiter ventuellement les informations que le bean doit mettre jour. Par dfaut, le caractre gnrique * permet de spcifier que le bean rcupre autant dinformations que possible. Cette dclaration de mise jour doit tre indique dans le code JSP : dans le cas contraire, les attributs du bean ne seront pas mis jour. Une restriction cependant : lutilisation des JavaBeans au sein des pages JSP ne permet pas de diffrentier directement (sans recourir lobjet implicite request ) la mthode denvoi HTTP (GET, POST, etc.).
Utilisation
Lorsquun bean est dclar et porte didentifiant beanId , il peut tre utilis directement au sein du code DHTML ou depuis une scriptlet. Chaque bean dispose dun ensemble dattributs (et des mthodes set et get correspondantes). Lors de lappel dun script JSP avec envoi dinformations, les attributs dclars dans les beans sont mis jour (par appel interne des mthodes set ). Lorsquune valeur dattribut nest pas mentionne par la requte, deux cas sont envisageables (la porte page est ici hors de propos) : La porte du bean est application ou session : lancienne valeur de lattribut est conserve (la requte ne mettant pas jour cet attribut). La porte du bean est request : dans ce cas, la valeur de lattribut est rinitialise (souvent null , mais toute autre valeur peut tre spcifie par le constructeur, appel lors de la cration de linstance du bean). Utilisation directe En rgle gnrale, lutilisation directe nest pas conseille car il est souvent ncessaire deffectuer des vrifications (validation) avant affichage. Nanmoins, laffichage de lattribut att du bean beanId utilise la syntaxe suivante :
<jsp:getProperty name="beanId" property="att" />
Lorsque le paramtre att na pas t transmis, le rsultat de cet appel sera null (conversion du pointeur null en chane de 4 caractres valant null ). La mise jour de cet attribut emploie la directive jsp:setProperty :
<jsp:setProperty name="beanId" property="att" value="valeur" />
Le caractre gnrique * peut remplacer att afin dindiquer que lensemble des proprits du bean doivent tre mises jour. Dans ce cas, le dernier paramtre nest pas mentionn :
13
Version 1.2
Le serveur dapplications utilise le principe de rflexion (ou miroir ) Java pour appeler les mthodes de type set dclares par le bean. Consulter le paquetage java.lang.reflect de lAPI Java pour en savoir davantage. Utilisation au sein des scriptlets Il est possible dappeler lensemble des mthodes dclares publiquement par le bean, comme lindique lexemple suivant :
<% String valeur = beanId.getAtt(); if (valeur == null) { // La requte ne mentionne pas le paramtre "att" } else { // La requte mentionne effectivement le paramtre "att" } %>
Construction du bean
Le bean simpleBean est dclar comme faisant partie du paquetage test afin dtre compatible avec le serveur dapplications Tomcat. A lattribut priv name sont associes les mthodes setName et getName . Enfin, le constructeur sans argument est indiqu (mme sil est dans ce cas facultatif). Le code du bean propos est donc conforme la dfinition dun JavaBean.
package test; // La dfinition dun paquetage est ncessaire sous Tomcat public final class SimpleBean { private String name = null;
// Attribut priv
public SimpleBean() { // Il nest pas ncessaire dindiquer le constructeur dans ce cas // car lattribut dj t initialis } public void setName(String value) { this.name = value; } public String getName() { return this.name; } }
14
Version 1.2
15
Version 1.2
</body> </html>
Seconde version
Le paramtre name fourni par lenvoi du formulaire est rcupr par le bean simpleBean . Si une valeur a t transmise, celle-ci est affiche. Dans le cas contraire, le script redirige vers la page derreur error.jsp via la directive JSP <%@ page errorPage="error.jsp" %> :
<%@ page language="java" %> <%@ page errorPage="error.jsp" %> <jsp:useBean id="simpleBean" class="test.SimpleBean" scope="request" /> <jsp:setProperty name="simpleBean" property="*" /> <%! // Ajout de la mthode "isEmpty" pour valider le paramtre "name" private boolean isEmpty(String str) { return (str == null) || str.equals(""); } %> <% String nameStr = simpleBean.getName(); if (isEmpty(nameStr)) { throw new Exception("Veuillez saisir un nom"); } %> <html> <head> </head> <body> Nom saisi: <%= nameStr %> <br/><br/> <form action="display.jsp" method="post"> Nouveau nom : <input type="text" name="name"> <br/><input type="submit" value="Envoyer"> </form> </body> </html>
16
Version 1.2
<html> <head> </head> <body> <h1>Erreur : <span style="color:red;"> <!-- Utilisation de la variable "exception" dclare automatiquement lors de la gnration du code Java correspondant la page JSP --> <%= exception.getMessage() %> </span> </h1> <br/><br/> <form action="display.jsp" method="post"> Nouveau nom : <input type="text" name="name"> <br/><input type="submit" value="Envoyer"> </form> </body> </html>
Seconde version
Cette version propose dafficher la pile des erreurs, notamment en cas de dbogage.
<%@ page language="java" %> <%@ page isErrorPage="true" import= "java.io.*" %> <html> <head> </head> <body> <h1>Erreur : <span style="color:red;"> <!-- Utilisation de la variable "exception" dclare automatiquement lors de la gnration du code Java correspondant la page JSP --> <% StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); excetion.printstackTrace(pw); out.println(sw); %> </span> </h1> <br/><br/> <form action="display.jsp" method="post"> Nouveau nom : <input type="text" name="name"> <br/><input type="submit" value="Envoyer"> </form> </body> </html>
17
Version 1.2
<br/><br/> <form action="display.jsp" method="post"> Nouveau nom : <input type="text" name="name"> <br/><input type="submit" value="Envoyer"> </form>
Ce bloc est commun lensemble des pages JSP. Il serait judicieux de lextraire dans un fichier tiers form.dat (par exemple) qui sera ensuite intgr au sein des pages JSP laide de la commande <%@ include file="form.dat" %> .
Linterface java.sql.Connection permet dtablir la connexion avec la base de donnes ( laide du service java.sql.DriverManager ). Elle indique de plus lensemble des tables qui composent la base ainsi que les proprits de ces tables (description) :
<% String urlBase = "jdbc:TypeBase://Hte/NomBase?user=UtilisateurParDfaut"; Connection c = DriverManager.getConnection(urlBase); %>
La machine hte est souvent rfrence comme tant localhost . La portion dadresse dbutant par ?user= est facultative.
Version 1.2
Lexcution dune requte laide de linterface java.sql.Statement suppose que la connexion avec une base de donnes est tablie. Dans une premire phase, la requte est prpare. Deux alternatives sont proposes : effectuer une prparation statique ou une prparation dynamique. La prparation statique revient spcifier la requte directement sous forme dune chane de caractres :
<% int id = 10; String query = "SELECT * FROM MA_TABLE WHERE `id` = '" + id + "'"; Statement stmt = c.createStatement(); ResultSet rs = stmt.executeQuery(query); %>
De fait, appeler cette mme requte avec un identifiant diffrent ( id ) revient recrer entirement la requte et demander sa r-excution. Pour pallier ce dfaut, le paquetage java.sql fournit la possibilit de crer une requte pour laquelle certaines valeurs sont omises :
<% PreparedStatement ps = c.prepareStatement(query) ; ps.setInt(1, 10); Statement stmt = c.createStatement(); ResultSet rs = stmt.executeQuery(); %>
Note : linstruction executeQuery doit tre remplace par executeUpdate dans le cas o la requte SQL est de type INSERT ou UPDATE .
Conclusion
Les JSP (et les JavaBeans) sont en rgle gnrale plus intuitifs que les servlets, cependant le recours aux servlets peut savrer utile dans certains cas : Besoin de contrle total du code pour viter les lignes de code inutiles gnres par le serveur dapplications lors de la transformation des JSP en code Java. Envoi de donnes qui ne sont pas au format HTML (le contenu dun fichier zip par exemple), c'est--dire dont le type de contenu ( content-type ) nest pas text/html .
Les JSP permettent en outre de sparer au maximum la logique mtier (traitements) de la prsentation. La possibilit dinclure textuellement des fichiers annexes permet de conserver une certaine cohrence dans la prsentation des informations. Enfin, les JSP (et les servlets) ouvrent la conception web lensemble de la technologie Java ct serveur dcrite par J2EE et notamment aux systmes distribus par le biais des EJB ( Enterprise Java Beans ) ou par le recours aux solutions de type CORBA. Nathanal COTTIN http://www.ncottin.net 19
Version 1.2
Mise en route
Tomcat coute le port 8080 ( alternate HTTP ) par dfaut. Les URL permettant de se connecter avec le serveur dapplications sont donc de la forme http://serveur:8080/ . Les tests en mode local sont de fait excuts avec http://localhost:8080/ ou http://127.0.0.1:8080/ . Pour y accder, il faut : Dmarrer Tomcat. Ouvrir un navigateur web et indiquer lURL http://localhost:8080/ .
Ces deux dernires URL ouvrent la page daccueil de Tomcat, partir de laquelle il est possible daccder un ensemble dexemples avec leur code source.
Lorsquune modification dun fichier source demande une compilation manuelle, Tomcat doit tre redmarr pour que celle-ci soit prise en compte. De plus, des problmes lis la mise en uvre de caches par le navigateur web peuvent survenir. Il est conseill de fermer galement toutes les fentres de navigateur ouvertes (afin de vider le cache) avant de procder de nouveaux essais.
20
Version 1.2
Cette phase de compilation demande lintgration des paquetages drivant de javax.servlet . Deux solutions sont envisageables : Installer un JSDK (et non un JDK). Utiliser la librairie servlet-api.jar de Tomcat : dans ce cas, il faut penser lajouter au CLASSPATH avant de demander la compilation de la servlet. Cette mise jour peut tre ralise temporairement au moment de la compilation laide de linstruction (sous environnement Windows) :
set TOMCAT_HOME=C:\Program Files\Apache Software Foundation\Tomcat 5.0 set CLASSPATH=%TOMCAT_HOME%\common\lib\servlet-api.jar;%CLASSPATH%
La compilation nest cependant pas suffisante pour pouvoir excuter la servlet : celleci doit en effet tre dclare dans le fichier web.xml situ sous le dossier WEBINF voqu prcdemment (attention : il ne sagit pas du fichier web.xml du dossier conf de Tomcat !). La commande de compilation suivante ralise ce travail, en supposant que les servlets sont cres dans le dossier webapps/servlets/ :
javac *.java d ./WEB-INF/classes
Voici un exemple de fichier web.xml simplifi dclarant les servlets Test et Hello :
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Servlet examples</display-name> <description>Servlet examples</description> <servlet> <servlet-name>Test</servlet-name> <servlet-class>Test</servlet-class> </servlet> <servlet-mapping> <servlet-name>Test</servlet-name> <url-pattern>/Test</url-pattern> </servlet-mapping> <servlet> <servlet-name>Hello</servlet-name> <servlet-class>Hello</servlet-class> </servlet> <servlet-mapping> <servlet-name>Hello</servlet-name>
21
Version 1.2
22
Version 1.2
Code HTTP 200 201 202 204 400 401 403 404 500
Description La requte a abouti La requte a abouti et a cr une ressource sur le serveur La requte est accepte mais nest pas effectue La requte a abouti mais na produit aucun contenu nouveau La syntaxe de la requte est incorrecte La requte exige une authentification HTTP Le serveur interprte correctement la requte mais ne peut la traiter La ressource demande est inaccessible Une erreur survenue au sein du serveur HTTP a fait chouer lexcution de la requte Le serveur HTTP ne prend pas en charge les fonctionnalits ncessaires au traitement de la requte Le serveur HTTP est surcharg et ne peut traiter la requte
SC_ACCEPTED
SC_NO_CONTENT
SC_BAD_REQUEST SC_UNAUTHORIZED
SC_FORBIDDEN
SC_NOT_FOUND SC_INTERNAL_SERVER_ERROR
501
SC_NOT_IMPLEMENTED
503
SC_SERVICE_UNAVAILABLE
Ces codes derreur peuvent tre retourns au client laide de la mthode sendError de la classe HttpServletResponse . Depuis les servlets ou les pages JSP, lappel est effectu comme suit :
response.sendError(400, "Message")
23
Version 1.2
Requte statique
<%@ page import="java.sql.*" %> <%@ page import="util.sql.*" %> <% DbConnection dbc = new DbConnection( "org.gjt.mm.mysql.Driver", "jdbc:mysql://localhost/simpledb"); String query = "SELECT * FROM `TABLE`"; ResultSet rs = dbc.execute(query); if (dbc.isEmpty(rs)) { throw new Exception("EMPTY TABLE"); } int i = 0; dbc.prepareIteration(rs); while (dbc.canIterate(rs)) { i++; } %> <p>Nombre denregistrements : <%= i %></p>
Requte dynamique
<%@ page import="java.sql.*" %> <%@ page import="util.sql.*" %> <% DbConnection dbc = new DbConnection( "org.gjt.mm.mysql.Driver", "jdbc:mysql://localhost/simpledb"); String query = "SELECT * FROM `TABLE` WHERE `privilege` = '?'";
24
Version 1.2
PreparedStatement ps = dbc.prepareQuery(query); ps.setString(1, "admin"); ResultSet rs = dbc.execute(ps); if (dbc.isEmpty(rs)) { throw new Exception("EMPTY TABLE"); } int i = 0; dbc.prepareIteration(rs); while (dbc.canIterate(rs)) { i++; } %> <p>Nombre denregistrements : <%= i %></p>
25
Version 1.2
Les requtes reues ne rpondant pas aux contraintes spcifies par la servlet (ou la page JSP) sont automatiquement rejetes. Voici un exemple gnrique de description XML des contraints de validation de formulaires HTTP :
<formDesc method="get|post|multipart|any" extraAllowed="true|false" tmpDirPath="..." bufferSize="..."> <forms> /* Restricted to listed forms (if present) */ method="get|post|multipart|any" extraAllowed="true|false" tmpDirPath="..." bufferSize="..."/> ... </forms> <params> <param name="..." mandatory="true|false" isFile="true|false"> <values> ... </values> <minSize>...</minSize> <maxSize>...</maxSize> <maxOccur>...</maxOccur> <regex>...</regex> <validations> ... </validations> </param> </params> <validations> ... </validations> </formDesc> /* Form validation constraints */ <validation id="..."/> /* Parameter validation constraints */ <validation id="..."/> /* If isFile="true" only */ <value>...</value> <form name="..."
26
Version 1.2
27