Mòdul 7  
Pràctica 10: Ultims serveis i darrers detalls
Tornar presentació tema
    Prąctica 1 Pràctica 2 Prąctica 3 Pràctica 4 Pràctica 5 Pràctica 6 Pràctica 7 Pràctica 8 Pràctica 9 Prąctica 10
     
 

Completaràs el mòdul afegint els darrers serveis al Portal. Amb això hauràs tancat el cicle de creació d'una aplicació web senzilla. Has instal·lat i configurat un servidor d'aplicacions Java, has aprés els fonaments de les pàgines jsp, el seu sistema d'etiquetatge, has instal·lat un motor de dades i l'has connectat al servidor de pàgines web, has aprés com fer consultes a la base de dades des de Java i rudiments del llenguatge SQL. Amb aquests coneixements consolidats podràs escriure aplicacions web convencionals, sòlides i fiables.

Per a què el portal de notícies ACME sigui mínimament funcional et resten només les següents tasques:

1) Elaborar una llista dels articles més votats ordenats per número de vots.

2) Fer una pàgina que serveixi per a redactar articles.

 

 
     
  La llista d'articles més votats  
 

 

Has de crear una llista d'articles, que presentaràs sota la llista de darreres publicacions, ordenats pel número de vots que han obtingut. Al costat del títol ha d'aparéixer imprès el número de vots recollits per l'article.

Com al POJO Article.java no existeix cap camp que reculli el número de vots, l'has de crear.

Modifica, en primer lloc, la classe acme.Article afegint el camp privat mumeroVots i els seus mètodes getter i setter corresponents:

 

 
private int numeroVots;

public int getNumeroVots() {
    return numeroVots;
}

public void setNumeroVots(int numeroVots) {
    this.numeroVots=numeroVots;
}
 
 


La consulta

Ara has d'escriure la consulta que retornarà la matriu d'articles més votats. Has de crear el mètode getArticlesMesVotats() a la classe acme.util.Consultes:

 
public Article[] getArticlesMesVotats(int numarticles) throws SQLException {
    Article[] articles = null;
    Article article = null;
    Vector v = new Vector();
    Connection connexio = DAOUtils.getDBConnection();
    PreparedStatement stmt = connexio.prepareStatement("SELECT         A.ID,A.TITOL,A.AUTOR,A.CONTINGUT,COUNT(*) AS NUMERO FROM         articles a, votacions v WHERE a.id=v.article_id GROUP BY         A.ID,A.TITOL,A.AUTOR,A.CONTINGUT
        ORDER BY NUMERO DESC LIMIT ?");
    stmt.setInt(1,numarticles);
    ResultSet rs = stmt.executeQuery();
    while (rs.next()) {
        article = new Article();
        article.setId(rs.getInt("ID"));
        SimpleDateFormat sdf =
            new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
        try {
            article.setData(sdf.parse(rs.getString("DATA")));
        }catch(Exception e) {
            article.setData(new Date());
        }
        article.setAutor(rs.getString("AUTOR"));
        article.setTitol(rs.getString("TITOL"));
        article.setContingut(rs.getString("CONTINGUT"));
        article.setNumeroVots(rs.getInt("NUMERO"));
        v.add(article);
    }
    rs.close();
    stmt.close();
    connexio.close();

    articles = new Article[v.size()];
    for (int n=0;n<articles.length;n++) {
        articles[n]=(Article)v.get(n);
    }

    return articles;
}

 
 


Aquest mètode només té una novetat destacable respecte al que has après a les pràctiques anteriors: la consulta SQL, que fa tota la feina requerida d'agrupació i recompte. Creuant la taula d'articles i de votacions retorna les dades de l'article i el número de votacions que té. Només presenta, però, el subgrup d'articles que han estat més votats. Un bon exemple de la potència de l'SQL:

"SELECT A.ID,A.TITOL,A.AUTOR,A.CONTINGUT,COUNT(*) AS NUMERO
FROM ARTICLES A, VOTACIONS V
WHERE A.ID=V.ARTICLE_ID
GROUP BY A.ID,A.TITOL,A.AUTOR,A.CONTINGUT
ORDER BY NUMERO DESC

LIMIT ?"

La clau de la consulta està en la combinació entre la sentència GROUP BY i la funció COUNT(*). Si construim la consulta en dues parts potser entendràs millor el seu contingut.

Si escriues al Query Browser la mateixa consulta, però sense fer l'agrupament GROUP BY

"SELECT A.ID,A.TITOL,A.AUTOR,A.CONTINGUT FROM ARTICLES A, VOTACIONS V WHERE A.ID=V.ARTICLE_ID ORDER BY ID "

aconseguiràs que el MySQL faci una creuament complet de les dues taules, articles i votacions. Això vol dir que el servidor retornarà tots els registres de la taula articles i tots els registres de la taula de votacions. D'aquesta forma:

A la taula de votacions hi ha dos registres (o vots) per l'article 'Cabo Trafalgar'. El MySQL retorna dues files amb les dades d'aquest article. L'article 'La fragata Surprise' , en canvi, té 17 registres a la taula de votacions i el motor retorna 17 vegades les dades d'aquest article.

Afegint la sentència d'agrupació GROUP BY fas que el motor agrupi totes les incidències de cada article en un sol registre:

"SELECT A.ID,A.TITOL,A.AUTOR,A.CONTINGUT,COUNT(*) AS NUMERO FROM ARTICLES A, VOTACIONS V WHERE A.ID=V.ARTICLE_ID GROUP BY A.ID,A.TITOL,A.AUTOR,A.CONTINGUT ORDER BY ID "

El MySQL només retornarà una fila per a cada article diferent i afegeix un darrer camp amb el recompte de registres que ha agrupat en cada cas:

 

 
 



Modificacions a la pàgina index.jsp

 

Ara prepararàs l'impressió de la llista d'articles a la pàgina index.jsp de la carpeta arrel del projecte web. Obre aquesta pàgina amb l'editor de pàgines web. Modifica-la afegint el codi font assenyalat en negreta:

 
<html>
<head>
<title>Agència de Notícies ACME</title>
</head>
<body>
<%@ include file="top.jsp"%>
<%
Article[] darrersarticles = consultes.getDarrersArticles(10);
Article[] mesvotats = consultes.getArticlesMesVotats(10);
%>
<h2 align="center">Portal de l'Agència de Notícies ACME. </h2>
<table width="100%" border="0" cellpadding="1">
<tr>
<td>&nbsp;</td>
<td><p><strong>Darrers Articles</strong><br>
<br>
<% if (darrersarticles!=null) for (int n=0;n<darrersarticles.length;n++) {%>
<a href="article.jsp?id=<%=darrersarticles[n].getId()%>" target="_blank">
<%=darrersarticles[n].getTitol()%><br>
</a> <% } %>

</p>
<p><strong>Articles m&eacute;s votats</strong><br>
<br>
<% if (mesvotats!=null) for (int n=0;n<mesvotats.length;n++) {%>
<a href="article.jsp?id=<%=mesvotats[n].getId()%>" target="_blank">
<%=mesvotats[n].getTitol()%></a>. <%=mesvotats[n].getNumeroVots()%> vots<br>
<% } %>
</p>
</td>
</tr>
</table>
<p>&nbsp;</p>
<%@ include file="peu.jsp"%>
</body>
</html>

 
 


El que has fet ha estat

crear una nova matriu que recull els articles més votats

Article[] mesvotats = consultes.getArticlesMesVotats(10);

i escriure un bucle d'impressió de la matriu

<% if (mesvotats!=null) for (int n=0;n<mesvotats.length;n++) {%>
    <a href="article.jsp?id=<%=mesvotats[n].getId()%>"         target="_blank">
    <%=mesvotats[n].getTitol()%></a>.         <%=mesvotats[n].getNumeroVots()%> vots<br>
<% } %>

Si obres el servidor web i crides a http://localhost t'ha de contestar així:

 
 
 
     
  Escriure articles  
 


Ara prepararàs un dels instruments més importants del portal, la pàgina de redacció d'articles. La crearàs seguint una metodologia molt simple, que hauries de millorar si estessis escrivint un gestor d'articles de veritat:

Un enllaç a la pàgina index.jsp obrirà un formulari web de redacció. Quan l'usuari piqui sobre el botó "Enregistrar", l'aplicació obrirà una pàgina jsp oculta, sense etiquetes html visals, que farà la feina de gravació i posteriorment farà forward a la pàgina index.jsp:


 

És un mecanisme molt simple que més endavant pots intentar millorar.

Necessitaràs, doncs modificar la pàgina index.jsp, fer una pàgina web amb el formulari d'entrada de l'article, fer la pàgina jsp oculta que fa la gravació i el forward i, finalment, ampliar la classe de consultes amb un mètode de gravació d'articles.

Comencem per aquesta consulta

La consulta per afegir articles

Obre el BlueJ i afegeix el següent mètode a la classe acme.util.Consultes:

 
     
public void addArticle(Article article) throws SQLException {
    Connection connexio=DAOUtils.getDBConnection();
    PreparedStatement stmt = connexio.prepareStatement("INSERT INTO         ARTICLES (AUTOR,TITOL,CONTINGUT) VALUES (?,?,?)");
    stmt.setString(1,article.getAutor());
    stmt.setString(2,article.getTitol());
    stmt.setString(3,article.getContingut());
    stmt.execute();
    stmt.close();
    connexio.close();
}

 
 

Passant al mètode un objecte Article, aquest l'enregistra a la base de dades. No té cap dificultat especial.

Ara escriuràs el formulari de redacció

La pàgina redacta.jsp

Obre l'editor de pàgines web i escriu la pàgina redacta.jsp a la carpeta arrel del projecte web:

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>ACME - Redacci&oacute;</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<h3>Entrada d'Articles :</h3>
<form action="/enregistra.jsp" method="post" name="form1">
<table width="100%" border="0" cellpadding="1">
<tr>
<td width="2%"><div align="right"></div></td>
<td width="98%" valign="top"><strong>Usuari:</strong><br>
<input name="autor" type="text" size="50" maxlength="50">
<br>
<strong>T&iacute;tol:</strong><br>
<input name="titol" type="text" size="80" maxlength="250">
<br>
<strong>Article:</strong><br> <textarea name="contingut" cols="80" rows="15"></textarea></td>
</tr>
</table>

<p align="left">
<input type="submit" name="Submit" value="Enregistra">
</p>
<p>&nbsp;</p>
</form>
<%@ include file="peu.jsp"%>
</body>
</html>

 

 
 


El llenguatge html utilitza els formularis per a enviar que els navegador clients envïin informació als servidors web. Un formulari és qualsevol conjunt de text i instruccions que es trobi entre les etiquetes <form> i </form>.

En el teu cas, declares el formulari següent:

<form action="/enregistra.jsp" method="post" name="form1">

que es pot traduir, poc més o menys, a "crea un formulari que es digui form1, quan l'usuari trameti la informació continguda en el formulari, la rebrà la pàgina enregistra.jsp en el servidor".

Per a sistematitzar la informació que gestiona un formulari, utilitzem els objectes de formulari: les caixes amb entrada de text, els desplegables, els botons de selecció múltiple, els botos d'acció...

Cadascun d'aquests objectes és passat a la pàgina web receptora del formulari com un paràmetre. Així, el teu objecte

<input name="autor" type="text" size="50" maxlength="50">

que és una caixa de text de 50 caràcters, recollirà el que l'usuari escrigui i ho enviarà a la pàgina enregistra.jsp com un paràmetre amb el nom "autor".

Si segueixes el codi, veuràs que el teu formulari envia tres paràmetres a la pàgina receptora:

<input name="autor" type="text" size="50" maxlength="50">
<input name="titol" type="text" size="80" maxlength="250">
<textarea name="contingut" cols="80" rows="15"></textarea>

dues caixes d'una línia "autor" i "títol" i una caixa d'àrea de text "contingut". Corresponen al autor de l'article, el títol i el cos de l'article. L'usuari d'Internet veurà la pàgina d'aquesta forma:

Escriu ara la pàgina receptora del formulari, la pàgina enregistra.jsp.

La pàgina oculta enregistra.jsp


Aquesta pàgina no conté codi visible a l'usuari, fa una feina i obre una altra pàgina, seguint el model que ja has utilitzat per a votar els articles.

Obre l'editor de pàgines web i escriu una pàgina amb el nom enregistra.jsp a l'arrel del projecte web amb aquest contingut:


 

<%@ page language="java" import ="acme.util.*,acme.*"%>
<%
    String autor=request.getParameter("autor");
    String titol=request.getParameter("titol");
    String contingut=request.getParameter("contingut");

    Article article = new Article();
    article.setAutor(autor);
    article.setTitol(titol);
    article.setContingut(contingut);

    Consultes consultes = new Consultes();
    consultes.addArticle(article);
%>
<jsp:forward page="/index.jsp">
</jsp:forward>

 
 

 

La pàgina fa aquestes feines:

Recull els paràmetres que li enviem a través del formulari de redacta.jsp.

    String autor=request.getParameter("autor");
    String titol=request.getParameter("titol");
    String contingut=request.getParameter("contingut");

Els paràmetres entren sempre en forma de String. Si necessites moure altres tipus de dades hauràs de fer operacions de conversió de String al tipus desitjat.

Crea un nou objecte article i li carrega la informació dels paràmetres:

    Article article = new Article();
    article.setAutor(autor);
    article.setTitol(titol);
    article.setContingut(contingut);

Afegeix l'article a la base de dades:

    Consultes consultes = new Consultes();
    consultes.addArticle(article);

I, finalment, fa forward a la pàgina index.jsp.:

    <jsp:forward page="/index.jsp">
    </jsp:forward>

 
 


Modificar index.jsp

Només et queda modificar la pàgina index.jsp per afegir-li un enllaç a la pàgina de redacció d'articles.

Obre la pàgina amb l'editor de pàgines web i modifica-la afegint el codi marcat en negreta:

 
<html>
<head>
<title>Agència de Notícies ACME</title>
</head>
<body>
<%@ include file="top.jsp"%>
<%
    Article[] darrersarticles = consultes.getDarrersArticles(10);
    Article[] mesvotats = consultes.getArticlesMesVotats(10);
%>
<h2 align="center">Portal de l'Agència de Notícies ACME. </h2>
<table width="100%" border="0" cellpadding="1">
<tr>
<td width="32%" valign="top"> <a href="/redacta.jsp">Redactar un Article</a> . </td>
<td width="68%"><p><strong>Darrers Articles</strong><br>
<br>
<% if (darrersarticles!=null) for (int n=0;n<darrersarticles.length;n++) {%>
<a href="article.jsp?id=<%=darrersarticles[n].getId()%>" target="_blank">
<%=darrersarticles[n].getTitol()%><br>
</a> <% } %>

</p>
<p><strong>Articles m&eacute;s votats</strong><br>
<br>
<% if (mesvotats!=null) for (int n=0;n<mesvotats.length;n++) {%>
<a href="article.jsp?id=<%=mesvotats[n].getId()%>" target="_blank">
<%=mesvotats[n].getTitol()%></a>. <%=mesvotats[n].getNumeroVots()%> vots<br>
<% } %>
</p></td>
</tr>
</table>
<p>&nbsp;</p>
<%@ include file="peu.jsp"%>
</body>
</html>

 
 

 

Obre el servidor web Resin i afegeix uns quants articles.

Observaràs que l'entrada d'articles està molt desprotegida: quan piques sobre el botó enregistrar es desa l'article sense que el seu autor el pugui recuperar o esborrar.

Et proposem que pensis quina estructura de control i quines pàgines jsp farien falta per a donar un acabat professional a la redacció d'articles.

Si has anat practicant amb el programa i has donat d'alta uns quants articles, t'hauràs fixat en què, quan llegeixes un article, el programa 'es menja' tots els punts i a part, quedant tot el text de l'article en un sol paràgraf. Com en aquest exemple:

 

 
 
 
 

 

Això és degut a què el salt de línia incorporat en un String de Java no té el mateix format que un salt de línia a la pàgina web.

Concluiràs la pràctica i el mòdul solucionant aquest inconvenient: crearàs un mètode de 'neteja' que convertirà els salts de línia d'una cadena de Java en etiquetes <br>, que representen salts de línia en una pàgina web.

 

 
  Transformar salts de pàgina en etiquetes <br>  
 

 

Crea una nova classe al paquet util de l'aplicació amb el nom HTMLUtil. Escriu el següent codi:

 
package acme.util;

/**
*
* @author Angel Solans
* @version 15-12-2004
*/
public class HTMLUtil {

static public String toHtmlString(String source) {
    return replaceInString(source, "\n", "<br>");
}

static public String replaceInString(String source, String   old_pattern, String new_pattern) {
    if (source == null) return "";
    if (old_pattern == null) return source;
    if (new_pattern == null) new_pattern = "";
    String res = "";
    String togo = new String(source);
    while (togo.indexOf(old_pattern) >= 0) {
        res = new_pattern +           togo.substring(togo.lastIndexOf(old_pattern) +
          old_pattern.length()) + res;
        togo = togo.substring(0, togo.lastIndexOf(old_pattern));
    }
    res = togo + res;
    return res;
 }
}

 
 

 

La classe incorpora dos mètodes:

toHtmlString() que fa la conversió dels salts de línia de cadena (el caràcter amb codi d'escapament \n) en etiquetes <br>.

Crida al mètode auxiliar replaceInString() qui donat una cadena d'entrada, un patró de recerca i un patró de substitució en fa la correcció.

Modifica ara la pàgina article.jsp. En lloc de fer una sortida directa a article.getContingut(), aplica el filtre del mètode toHtmlString():

 
<%@ page language="java" import ="acme.util.*,acme.*"%>
<%
int articleid = 0;
try {
articleid = Integer.parseInt(request.getParameter("id"));
}catch(Exception e) {
}
Consultes consultes = new Consultes();
Article article = consultes.getArticle(articleid);
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>ACME - <%=article.getTitol()%></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<h3><%=article.getTitol()%></h3>
Per <%=article.getAutorComplet()%>
<hr>
<%=HTMLUtil.toHtmlString(article.getContingut())%>
<hr>
<p><a href="vota.jsp?id=<%=article.getId()%>">Vota per aquest article</a>. (<%=consultes.getVotacionsArticle(articleid)%>) votacions obtingudes </p>
<%@ include file="peu.jsp"%>
</body>
</html>

 
 

 

Ara els salts de línia ja s'imprimeixen correctament a la pàgina web:

 
 
 
     
Tasques pendents  
 

 

Si poses una mica d'imaginació pots continuar treballant sobre el projecte i completar-lo. Faria falta una eina d'edició d'articles ja enregistrats, algun sistema de recerca