WebService amb wsgen/wsimport i Maven

WebServices amb Maven

1 Crear un webservice a partir d’una classe java

1.1 Preparació

Aquest és un post que tenia ganes d’escriure de feia temps: Com fer un webservice amb java, i com invocar-lo.

El entorn que faré servir per a desenvolupar el webservice és:

Comencem. És una bona pràctica encetar el desenvolupament a partir d’un archetype Maven. Trio l’archetype d’aplicació webapp-javaee6.

mvn archetype:generate \ 
   -DgroupId=cat.apuntstecnologia.proves \
   -DartifactId=jeews \
   -DarchetypeGroupId=org.codehaus.mojo.archetypes \
   -DarchetypeArtifactId=webapp-javaee6

Que genera la següent estructura:

project
|-- pom.xml
`-- src
    `-- main
        |-- java
        `-- webapp
            |-- WEB-INF
            |   `-- web.xml
            `-- index.jsp

Tanmateix també hauria estat una bona tria començar amb l’archetype de webapp. Mancaria afegir la carpeta java i modificar el pom.xml.

mvn archetype:generate \
    -DgroupId=cat.apuntstecnologia.proves \
    -DartifactId=warws \
    -DarchetypeArtifactId=maven-archetype-webapp

Un projecte de webapp simple genera l’estructura següent:

project
|-- pom.xml
`-- src
    `-- main
        `-- webapp
            |-- WEB-INF
            |   `-- web.xml
            `-- index.jsp

O fins i tot un simple projecte estàndard de java. Aleshores hauria d’afegir encara més modificacions:

mvn archetype:generate -DgroupId=cat.apuntstecnologia.proves \
                       -DartifactId=jarws \
                       -DarchetypeArtifactId=maven-archetype-quickstart

que és equivalent l’archetype per defecte i és el mateix que:

mvn archetype:generate -DgroupId=cat.apuntstecnologia.proves \
                       -DartifactId=jarws \

Si ho prefereixo, puc treballar amb Eclipse. El projecte Maven es pot convertir a Eclipse amb:

mvn eclipse:eclipse

I, a continuació, puc importar el projecte creat al workspace de l’Eclipse.

1.2 La classe java del servei

Ja sigui amb Eclipse o amb un altre editor, creo la classe del servei web:

package cat.apuntstecnologia.proves;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public class ServeiProva {
    public ServeiProva() {
    }
    
    @WebMethod
    public int suma(int a, int b) {
    	return a + b;
    } 
    
    @WebMethod
    public String concatena(String a, String b) {
    	return a + b;
    }
    
    @WebMethod
    public String quinDiaEsAvui() {
    	SimpleDateFormat sdf = new SimpleDateFormat("DD/MM/YYYY");
    	return sdf.format(new Date());
    }
}

Un cop tinc el font, compilo la classe:

mvn compile

1.3 wsgen per a generar els objectes del servei

Ara faig servir wsgen sobre la classe per a generar els objectes del web service. wsgen és una eina estàndard que es proporciona amb el JDK.

Primer creo la carpeta src/main/resources. un cop he creat la carpeta em situo a l’arrel del projecte i faig:

wsgen -cp target/classes cat.apuntstecnologia.proves.ServeiProva \ 
      -s src/main/java/ \
      -r src/main/resources/ \
      -d target/classes \
      -wsdl

Amb l’instrucció anterior, ha generat la carpeta

{workspace}/jeews/src/main/java/cat/apuntstecnologia/proves/jaxws

Amb el següent contingut

Concatena.java
ConcatenaResponse.java
QuinDiaEsAvui.java
QuinDiaEsAvuiResponse.java
Suma.java
SumaResponse.java

i també el package amb els .class corresponent a la carpeta target/classes

A més, ha generat el wsdl i l’schema xsd a la carpeta src/main/resources

1.4 Generar el war

En aquest punt ja només queda preparar el paquet war per al seu desplegament com a webservice

Per això a la carpeta src/main/webapp/ creo la subcarpeta WEB-INF i el fitxer web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, 
Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>
    <display-name>ServeiProva</display-name>
</web-app>

i genero el war

mvn package

Que obté, finalment, el war:

/home/albert/workspace/java/jeews/target/jeews-1.0-SNAPSHOT.war

1.5 Deploy al WildFly 9

El renombro a jeews.war i, finalment, faig el desplegament. En el meu cas estic utilitzant el servidor WildFly 9. Per a desplegar en aquest servidor nomes he de deixar el jeews.war a la carpeta

/home/albert/wildfly-9.0.2/standalone/deployments

i fa el deploy automàticament:

17:19:48,943 INFO  [org.jboss.ws.cxf.metadata] (MSC service thread 1-3)
 JBWS024061: Adding service endpoint metadata: 
 id=cat.apuntstecnologia.proves.ServeiProva
 address=http://localhost:8080/jeews/ServeiProva
 implementor=cat.apuntstecnologia.proves.ServeiProva
 serviceName={http://proves.apuntstecnologia.cat/}ServeiProvaService
 portName={http://proves.apuntstecnologia.cat/}ServeiProvaPort
 annotationWsdlLocation=null
 wsdlLocationOverride=null
 mtomEnabled=false

17:19:53,876 INFO  
[org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean] 
(MSC service thread 1-3) 
Creating Service {http://proves.apuntstecnologia.cat/}ServeiProvaService 
from class cat.apuntstecnologia.proves.ServeiProva

17:19:56,321 INFO  [org.apache.cxf.endpoint.ServerImpl] 
(MSC service thread 1-3) 
Setting the server's publish address to be 
http://localhost:8080/jeews/ServeiProva

17:19:56,912 INFO  [org.jboss.ws.cxf.deployment] (MSC service thread 1-3)
 JBWS024074: WSDL published to:
file:/wildfly-9.0.2/standalone/data/wsdl/jeews.war/ServeiProvaService.wsdl

I ara provo el meu web service. Obro un navegador i accedeixo a la URL http://localhost:8080/jeews/ServeiProva?wsdl

i obtinc el wsdl.

1.6 Provar el servei amb SoapUI

Si faig servir aquesta URL amb una eina com SoapUI puc provar els diferents mètodes que exposa el servei.

A la imatge adjunta, veig com s’ha executat l’operació de suma.

 

Figure 1: Prova del webservice amb SoapUI

web-service-soap-ui

2 Crear un client de webservice a partir d’un WSDL

2.1 Preparació

Al punt anterior he fet la prova del servei amb l’aplicació Soap UI. Soap UI és capaç de generar un client del servei web a partir de l’URI del WSDL. El que faré a continuació és això mateix: generar un client del servei web a partir del WSDL.

Com abans, el primer pas és crear un projecte de webapp. Faig servir el mateix archetype que al punt 1.1, el webapp-javaee6.

mvn archetype:generate \ 
   -DgroupId=cat.apuntstecnologia.proves \
   -DartifactId=jeeclientws \
   -DarchetypeGroupId=org.codehaus.mojo.archetypes \
   -DarchetypeArtifactId=webapp-javaee6

2.2 wsimport per a generar les classes del client.

Ara he de generar les classes del client. Faig servir wsimport. Igual que wsgen, wsimport és una eina estàndard del jdk.

wsimport -s src/main/java/ \
         -d target/classes \
         -p cat.apuntstecnologia.proves.jeeclientws \
         http://localhost:8080/jeews/ServeiProva?wsdl

La instrucció anterior ha creat a la carpeta src/main/java el package cat.apuntstecnologia.proves.jeeclientws on ha desat les fonts de les classes java de la implementació del client:

Concatena.java
ConcatenaResponse.java
ObjectFactory.java
package-info.java
QuinDiaEsAvui.java
QuinDiaEsAvuiResponse.java
ServeiProva.java
ServeiProvaService.java
Suma.java
SumaResponse.java

A la carpeta target/classes, per la seva banda, hi són els fitxers .class i l’estructura de carpetes corresponents al package.

En aquest punt ja tinc la implementació de les classes del client. Ara preparo una pàgina jsp per invocar els diferents mètodes del servei, i una pàgina de resposta.

Es tracta dels tres mètodes

int suma(int a, int b);
String concatena(String a, String b);
String quinDiaEsAvui();

Vet aquí el formulari (index.jsp)

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>jeeclientws</title>
	
        function sendRequest(arg) {
            f1.methodInvoked.value = arg;    
            f1.submit();
        }
        
    </head>
    <body>
        <h1>WebService ServeiProva Client</h1>
	<form name="f1" method="post" action="response.jsp">
	    <input type="hidden" name="methodInvoked" />
	    <table>
	        <thead>
		    <tr><td>method</td><td>arg1</td><td>arg2</td><td>&nbsp</td></tr>
		</thead>
		<tbody>
		    <tr>
		        <td>Suma</td>
			<td><input type="text" name="sumArg1" /></td>
			<td><input type="text" name="sumArg2" /></td>
			<td><input type="button" value="send"
			           onclick="javascript:sendRequest('suma');">
		        </td>
		    </tr>
		    <tr>
		        <td>Concatena</td>  
			<td><input type="text" name="concatArg1" /></td>
			<td><input type="text" name="concatArg2" /></td>
			<td><input type="button" value="send"
			           onclick="javascript:sendRequest('concatena');">
		        </td>
		    </tr>
		    <tr>
		        <td colspan="3">Quin dia és avui?</td>
			<td><input type="button" value="send"
			           onclick="javascript:sendRequest('quindia');">			
		    </tr>
		</tbody>
		<tfoot>
		    <tr>
		        <td colspan="4"><input type="reset" value="reset" />
		    </tr>
		</tfoot>
            </table>
	</form>
    </body>
</html>

i el més interessant response.jsp, que és on es fa la invocació propiament dita al webservice

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="cat.apuntstecnologia.proves.jeeclientws.*"%>
<%
String methodInvoked = request.getParameter("methodInvoked");
String sumArg1 = request.getParameter("sumArg1");
String sumArg2 = request.getParameter("sumArg2");
String concatArg1 = request.getParameter("concatArg1");
String concatArg2 = request.getParameter("concatArg2");
String result = "";

ServeiProvaService serveiProvaService = new ServeiProvaService();
ServeiProva serveiProva = serveiProvaService.getServeiProvaPort();

if ("suma".equals(methodInvoked))  {
    result = "" + serveiProva.suma(Integer.parseInt(sumArg1), 
                                   Integer.parseInt(sumArg2));      
}

if ("concatena".equals(methodInvoked))  {
    result = serveiProva.concatena(concatArg1, concatArg2);      
}

if ("quindia".equals(methodInvoked))  {
    result = serveiProva.quinDiaEsAvui();      
}
%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>jeeclientws response</title>
    </head>
    <body>
        <h1>WebService ServeiProva Client - Response</h1>
	<table>
	    <thead>
	        <tr><td>method invoked</td><td>response</td></tr>
	    </thead>
	    <tbody>
                <tr><td><%= methodInvoked%></td><td><%= result%></td></tr>
	    </tbody>
            <tfoot>
                <tr><td colspan="2"><a href="index.jsp">back</a></td></tr>
            </tfoot>
        </table>
    </body>
</html>

I l’execució:

request

Figure 2: Prova del mètode ‘concatena’

response

Figure 3: Resposta del webservice

3 Conclusió. Repositoris GitHub

He creat un web service, i el corresponent client, a partir d’una classe java fent servir les eines wsgen i wsimport que proporciona el jdk; i archetytpes de Maven per a generar les aplicacions.

La generació del codi java del webservice i del client amb les eines ha estat directa i em permet centrar-me en el desenvolupament de les funcionalitats del webservice, més que no pas en la “lampisteria” per posar el webservice en funcionament.

El codi del webservice i del client es pot trobar al meu github, a les adreces:

Webservice (projecte jeews): https://github.com/abaranguer/jeews.git

Client del Webservice (projecte jeeclientws): https://github.com/abaranguer/jeeclientws.git

Aquest article es pot descarregar en format PDF des de http://abaranguer.eresmas.com/webservices.pdf

Author: Albert Baranguer Codina

Created: 2016-01-31 dg 20:54

Emacs 24.5.1 (Org mode 8.2.10)

Validate

Entorn de desenvolupament LifeRay – MySQL sobre Lubuntu

LifeRay
Aquest mes de juny m’ha portat una interessant novetat: sembla que professionalment m’hauré de dedicar al manteniment d’un grup de portals muntats amb LifeRay, un sistema de gestió de continguts basat en tecnologia java.

Sempre que és possible m’agrada disposar d’un entorn de proves així que, dit i fet, he muntat el meu propi entorn al sobretaula Linux, per a poder fer  proves i experiments amb LifeRay sense riscos. Aprofitant l’avinentesa, escric aquest article que també em serveix per “documentar” el procés.

Programari emprat
He muntat l’entorn LifeRay sobre una distribució Lubuntu 12.10. Per proporcionar la base de dades he fet servir una MySQL 5.0, però no la versió dels repositoris d’Ubuntu, si no la que ve incorporada al XAMPP, d’Apache Friends, un paquet que incorpora un stack complet de serviddor MySQL, Servidor Web Apache i PHP. Per què he triat aquesta versió? La resposta és prosaica: essencialment, perquè ja el tenia instal·lat 😉

El primer de tot ha estat descarregar de la web de LifeRay el programari. A la feina farem servir la versió Enterprise de Liferay, amb llicència comercial, però també es disposa d’una versió LifeRay Community. La versió community pot ser útil precisament per a desenvolupadors o per a desplegaments de petits llocs.

En aquest enllaç podeu trobar les diferències entre les versións enterprise i community de LifeRay.

Val a dir que es disposa de força documentació al lloc de LifeRay. Hi ha disponible documentació prou interessant i útil servida, és d’agraïr, sota llicència Creative Commons. Per exemple,la User Guide.

La versió community de LifeRay va amb una llicència lliure. Descarrego, doncs , la versió community que va empaquetada amb Tomcat. També es poden trobar a la web altres  paquets de LIferay amb JBoss, Geronimo, Glassfish… però la de Tomcat té un avantage principal:El LifeRay IDE per a desenvolupar portlets i components per al portal és un Eclipse al que s’afegeix un plugin. El plugin incorpora el control del servidor Tomcat on es munta LifeRay. Per a d’altres tipus de servidors cal descarregar-se un plugin addicional. Trio la versió Tomcat per simplificar la instal·lació.

El LifeRay IDE també em demanará disposar del paquet de documentació javadoc, del codi font -útil per a depuració- i del plugins SDK, és dir, del framework java necessari per a desenvolupar portlets, templates o themes per a LifeRay.

En el meu cas he descarregat, doncs, aquests quatre paquets:

El plugins SDK: liferay-plugins-sdk-6.2-ce-ga2-20140319114139101.zip
El Javadoc: liferay-portal-doc-6.2-ce-ga2-20140321115737138.zip
El codi font: liferay-portal-src-6.2-ce-ga2-20140319114139101.zip
El paquet LifeRay-Tomcat: liferay-portal-tomcat-6.2-ce-ga2-20140319114139101.zip

Els passos a seguir són directes. No m’invento res, és el que està descrit a aquest enllaç de la Wiki de LifeRay.

1. Descomprimeixo el paquet de LifeRay-Tomcat i obtinc un servidor Apache Tomcat 7.0 amb el .war del CMS desplegat.

2. Engego el servidor anant a la carpeta bin i executant startup.sh

3. Per a poder controlar l’arrencada, a un terminal faig

tail -f catalina.out 

de la carpeta logs del Tomcat.

Base de dades MySQL
L’arrencada inicial resulta força lenta, si més no a la màquina que estic fent servir.

4. Aprofito per engegar el servidor de BD, en el meu cas, que faig servir XAMPP, ho faig amb un

sudo /opt/lampp/lampp startmysql

5. Creo la base de dades que farà servir LifeRay. lportal és el nom que fa servir per defecte. Suposant que tinc al PATH la ruta /opt/lampp/bin, aleshores faig:

mysql --user=root

i un cop dins,

create database lportal;

Finalment, quan el servidor ha arrencat, s’obre el Firefox apuntant a la pàgina http://localhost:8080

6. En aquesta pàgina hi configuro el compte inicial

2014-06-08-171247_1024x744_scrot

7. En aquest punt cal indicar que vull fer servir la base de dades MySQL. Faig click a Change i obtinc

2014-06-08-171302_1024x744_scrot

Indico que és una BD de tipus MySQL. El user per defecte de la BD de XAMPP és root, i sense password.
Finalment, procedeix a la instal·lació.

Al portatil de la feina la instal·lació ha portat el seu temps, però s’ha completat. Un cop els objectes de la BD han estat creats i la instal·lació completada el FireFox m’ha mostrat la pàgina d’instal·lació completada amb èxit. A continuació m’ha mostrat el formulari de d’acceptació de les condicions d’us, m’ha demanat que actualitzés el password i m’ha demanat la pregunta de seguretat per si se m’oblida el password.

En canvi, a casa, el procés m’ha donat alguns problemes de time out. Em temo que em caldrà actualitzar l’equip. La tàctica que m’ha funcionat ha estat, simplement, no carregar les dades de prova, ignorar el time out i esperar a que segons el log tomcat (tail -f catalina.out) s’hagués completat el procés.

Pantalla de benvinguda
Quan ha estat llest he obert manualment http://localhost:8080 i m’he trobat amb la pàgina de login. Ara la tinc així: he deixat només el portlet de sign-in

2014-06-08-204530_1024x744_scrot

Coses ràpides que es poden fer. Afegir-hi portlets. Per exemple, li he afegit el portlet del generador de contrasenyes. Click a afegir. Triar el portlet i col·locar-lo. És similar a com s’editen pàgines amb el SharePoint de MS. D’aquests components encastables a MS en diuen WebParts, aquí es fa servir el nom (més clàssic i més java-ish) de portlet.

2014-06-08-205223_1024x744_scrot

Això ens dona una idea de quina mena de tasques podem esperar trobar:

Desenvolupament, podrem crear i modificar llocs, pàgines, plantilles de pàgina, temes, estils i, per descomptat, portlets. Algunes d’aquestes activitats es desenvoluparan des de la mateixa interfície web de LifeRay, i d’altres, des del LifeRay IDE. Es podria arribar a donar el cas, extraordinari, d’intervenir directament sobre la base de dades.

Administració. Trobarem tasques com donar d’alta i baixa usuaris i grups, modificar-ne els permisos d’accés a pàgines i llocs.

Interfície amb altres sistemes. Segurament on es troba la major dificultat. Les grans corporacions tenen sistemes molt diversos i necessitats d’interconnexió variades. No és gens estrany que l’autenticació i autorització dels usuaris i els grups a diversos sistemes i entorns estigui gestionada des de, sovint, més d’un sistema. El single-sign-on és un dels més grans maldecaps amb que es pot trobar el departament TIC. No es tant una tasca de LifeRay com una tasca genèrica, a mig camí entre el desenvolupament, l’administració i la gestió.

Continguts. Queda en mans dels usuaris. Els usuaris, tanmateix, poden demanar suport i assistència a l’hora de pujar contingut al CMS.

Son les mateixes tasques que hom es troba amb altres CMS. Varia la tecnologia, però les tasques son similars.

LifeRay IDE
Per a poder fer els desenvolupaments descrits es pot fer servir el LifeRay IDE. Com he dit abans, es tracta d’un plugin per a Eclipse que permet engegar i aturar el servidor de LifeRay, fer-ne desplegament i desenvolupar portlets, templates, themes, hooks…

La versió mínima d’Eclipse és la 3.7 (Indigo). Des de la web de LifeRay es pot descarregar versions d’Eclipse amb el plugin pre-instal·lat. La darrera versió que es pot descarregar del lloc LifeRay és la Kepler.

A la pàgina de descàrrega es pot obtenir tant l’URL per a instal·lació del plugin des de l’eclipse, com el paquet zip amb plugin propiament dit, o la versió del Kepler amb el plugin pre-instal.lat:

2014-06-08-222821_1024x744_scrot

En engegar el Liferay IDE puc observar que es tracta d’un Eclipse adaptat.

Java EE - Eclipse _002

A destacar aquests tres botons:

Selecció_001

Els botons anteriors permeten accedir als wizards dels diferents projectes i desenvolupaments que és poden fer amb el LifeRay IDE.

Selecció_003

Selecció_007

I també configurar l’IDE per accedir a servidors LifeRay.

Selecció_008

Faré això últim:

Configurar LifeRay IDE

És molt senzill:

Al següent vídeo mostro com establir el Plugin SDK i, a continuació, com configurar un nou servidor de LifeRay. A tenir en compte: he deixat els zips de docs, src i liferay-tomcat bundle dins de la carpeta home de la emva instal·lació de Liferay, i també dins d’aquesta carpeta he descomprimit el zip de plugins. Això és necessari per a configurar, primer de tot, el plugins SDK, i a continuació, el nou server.

Des d’aquest moment ja puc engegar o aturar el meu servidor de LieRay des de l’Eclipse:

2014-06-09-010051_1024x744_scrot

Per a un proper post, la creació d’un porlet que pugui posar a les pàgines.

Java 8. Com simular closures amb classes internes i expressions lambda

El passat 18 de març es va publicar la versió 8 de Java SE/ME. El JDK 8. Es tracta, doncs,  d’una versió que encara manté l’escalforeta del forn.

La nova versió de Java inclou característiques que, segons Oracle converteixen aquesta release en “revolucionària”: “Java 8 is a revolutionary release of the world’s #1 development platform. It includes a huge upgrade to the Java programming model and a coordinated evolution of the JVM, Java language, and libraries. Java 8 includes features for productivity, ease of use, improved polyglot programming, security and improved performance.”

En aquest article exploren els trets més característics de la release Java 8. En resum, serien aquests:

–  Expressions lambda.  Es tracta d’una estructura que prové de la programació funcional. Es tracta d’una reclamació antiga de la comunitat de programadors Java que va patir una gran decepció quan  es va saber que Java 7 no incorporava elements de la programació funcional. Els llenguatges funcionals ofereixen una estructura, les clausures (closures) molt potent. Les  closures fa temps que “estan de moda”. De fet, molts dels llenguatges per a la JVM -com Clojure, Scala i Groovy- fa temps que la suporten.

Doncs bé, Java 8 NO incorpora closures. Però la funcionalitat de les closures es pot aproximar amb inner classes i expressions lambda. El motiu de fons és que al Java els objectes són els elements de primer ordre, mentre que a un llenguatge funcional, ho són les funcions. Java treballa amb noms, i els llenguatges funcionals amb verbs.

Si el paràgraf anterior sembla críptic és perquè, efectivament, ho és.

Nashorn. Nou engine Javascript nadiu de la JVM . Nashorn reemplaça al veterà Rhino i proporciona accés a la immensitat de les llibreries de Java des d’un llenguatge més lleuger i interpretat com Javascript. Nashorn permetrà una major integració entre Java i Javascript.

Java ME i Compact Profiles. La plataforma Java es presenta en tres edicions: Standard, Enterprise i Micro . L’actual release 8 avança les edicions estàndard i micro.

L’edició micro és la que es fa servir a hardware divers com set-top boxes, electrònica de consum, telèfons mòbils… Aquesta edició sembla que està cridada a augmentar la seva importància amb les smart cities i la “Internet of things”.

Java ME defineix perfils de hardware (memòria disponible, sensors, connectivitat…) i la versió Java 8 ME afegeix nous perfils als ja existents.

– Nova API per a Temps i Dates.

– Integració de Java FX. On Java FX és la resposta Java a Flash (Flex) d’Adobe o SilverLight de Microsoft.

– Major participació de la comunitat Java:  major pes dels JUGs (Java Users Groups) en la definició del llenguatge i programa “Adopt a JSR (Java Specification Request)”; Open JDK;  Millora del JCP (Java Community Process).

– Millores a la JVM que afecten de forma destacable al GC (Garbage collector). Millora general del rendiment.

Aquests són els grans trets.

 

Closures amb inner class i expressions lambda

Com de costum, el millor sempre és provar amb les pròpies mans.

Una bona idea és descarregar-se el JDK 8. Es pot fer des de la pàgina de descàrreges de la web d’Oracle. Jo m’he descarregat la versió empaquetada amb NetBeans 8.0.

Punt de partida: Feia temps que volia tocar les closures. Tenia preparat aquest exemple amb Javascript:

<!DOCTYPE html>
<html>
    <head>
    <script>
    f = function(x) {
        var z = x;
 
        g = function(x) {
            z++;
            return x + z;
        };
 
        return g;
 }
 </script> 
 </head>
 <body>
     <script language>
     f2 = f(1);
     f3 = f(2);
     f4 = f(3);
     document.writeln("Value f2(3): " + f2(3) + "</br>");
     document.writeln("Value f3(3): " + f3(3) + "</br>");
     document.writeln("Value f4(3): " + f4(3) + "</br>");
     document.writeln("Value f2(3): " + f2(3) + "</br>");
     document.writeln("Value f3(3): " + f3(3) + "</br>");
     document.writeln("Value f4(3): " + f4(3) + "</br>");
     document.writeln("Value f2(3): " + f2(3) + "</br>");
     document.writeln("Value f3(3): " + f3(3) + "</br>");
     document.writeln("Value f4(3): " + f4(3) + "</br>");
 </script>
 </body>
</html>
 
Presenta:
 
Value f2(3): 5
Value f3(3): 6
Value f4(3): 7
Value f2(3): 6
Value f3(3): 7
Value f4(3): 8
Value f2(3): 7
Value f3(3): 8
Value f4(3): 9

Jo veig les clausures com funcions amb estat. Estem acostumats a veure factories d’objectes amb java, doncs bé, el codi anterior no deixa de ser una factoria de funcions.

 

Següent pas: Expressions lambda a Java 8.

Per a definir una expressió lambda amb Java 8 cal seguir els següents passos:

1. Cal definir la interface funcional de l’expressió lambda. Una interface amb un únic mètode (la funció):

interface InterfaceFuncional(Tipus1 var1, Tipus2 var2,..) {
  TipusRetorn UnUnicMètode(Tipus1 var1, Tipus2 var2,..);
}

2. A continuació, l’expressió lambda pròpiament dita.

InterfaceFuncional varFuncional = (Tipus var1, Tipus var2,...) -> {
  instruccions;
  instruccions;
  ...
}

3. Finalment, la invocació de l’expressió lambda:

varFuncional.UnUnicMetode(var1, var3, ...);

 

Això no va

Com podria fer amb Java 8 i expressions lambda el mateix que a l’exemple amb JavaScript?

Doncs bé, realment no es pot fer de forma directa. El següent codi dona un error de compilació:

package cat.apuntsdetecnologia;

/**
 *
 * @author albert
 */
public class ProvaClosures {
  /**
  * @param args the command line arguments
  */
   
  public static void main(String[] args) {
    new ProvaClosures();
  }
 
  interface g {
    int operation(int x);
  } 
 
  interface f {
    g init(int x); 
  }

  public ProvaClosures() {
    f f1 = (x) -> {
      int z_f = x; 
      
      g g1 = (y) -> {
        z_f++;
        return y + z_f;
      };
    
      return g1;
    };

    g g1 = f1.init(1);
    g g2 = f1.init(2);
    g g3 = f1.init(3);
 
    System.out.println("Value g1.operation(3): " + g1.operation(3));
    System.out.println("Value g2.operation(3): " + g2.operation(3));
    System.out.println("Value g3.operation(3): " + g3.operation(3));
    System.out.println("---------------------------------------");
    System.out.println("Value g1.operation(3): " + g1.operation(3));
    System.out.println("Value g2.operation(3): " + g2.operation(3));
    System.out.println("Value g3.operation(3): " + g3.operation(3));
    System.out.println("---------------------------------------");
    System.out.println("Value g1.operation(3): " + g1.operation(3));
    System.out.println("Value g2.operation(3): " + g2.operation(3));
    System.out.println("Value g3.operation(3): " + g3.operation(3)); 
  }
}

Què és el que falla? Que dins l’expressió lambda la variable z_f ha de ser final. Però un mètode java NO pot tenir un estat intern. L’estat el manté la instància de la classe. En particular, una expressió lambda de java tampoc pot mantenir un estat intern. Si vull simular la closure hauré de fer servir classes.

 

Però això sí

El següent codi SÍ que compila. Com simular una closure amb una classe interna i una expressió lambda amb Java 8?

 

package cat.apuntsdetecnologia;

/**
 *
 * @author albert
 */
public class ProvaClosures {
  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    new ProvaClosures();
  }

  interface g {
    int operation(int x);
  } 

  public ProvaClosures() {
    class f {
      int z_f;
      
      g init(int x) {
        z_f = x;
        
        g g1 = (y) -> {
          this.z_f++;
          return y + this.z_f;
        };
 
        return g1;
      }
    }

    g g1 = (new f()).init(1);
    g g2 = (new f()).init(2);
    g g3 = (new f()).init(3);

    System.out.println("Value g1.operation(3): " + g1.operation(3));
    System.out.println("Value g2.operation(3): " + g2.operation(3));
    System.out.println("Value g3.operation(3): " + g3.operation(3));
    System.out.println("---------------------------------------");

    System.out.println("Value g1.operation(3): " + g1.operation(3));
    System.out.println("Value g2.operation(3): " + g2.operation(3));
    System.out.println("Value g3.operation(3): " + g3.operation(3));
    System.out.println("---------------------------------------");

    System.out.println("Value g1.operation(3): " + g1.operation(3));
    System.out.println("Value g2.operation(3): " + g2.operation(3));
    System.out.println("Value g3.operation(3): " + g3.operation(3)); 
 }
}

Què he fet?

– He transformat la closure que era  f en una classe interna f;

– L’expressió lambda associada a f en el mètode init de la classe interna;

– La variable z_f ha esdevingut una propietat d’f. D’aquesta forma  l’expressió lambda g ha pogut referir-se a la variable z_f que quedava definida implícitament com final.

– La invocació a f.init(var) es transforma en (new f()).init(var). La resta, tot igual.

 

El resultat és l’esperat:

run:
Value g1.operation(3): 5
Value g2.operation(3): 6
Value g3.operation(3): 7
---------------------------------------
Value g1.operation(3): 6
Value g2.operation(3): 7
Value g3.operation(3): 8
---------------------------------------
Value g1.operation(3): 7
Value g2.operation(3): 8
Value g3.operation(3): 9
BUILD SUCCESSFUL (total time: 0 seconds)

En el post d’avui he comentat algunes de les caraterístiques principals de Java 8 i he parat atenció a les expressions lambda. Amb les expressions lambda Java 8 fa un pas per apropar-se a les estructures de la programació funcional. A l’exemple he presentat una tècnica per simular closures amb classes internes i expressions lambda.

 

Podeu descarregar la classe del repositori GitHub. https://github.com/abaranguer/java8lambdaexp

Com fer servir jProgressBar i SwingWorker

Introducció
Quan a una aplicació gràfica hi ha una tasca de llarga duració i que fa que l’usuari hagi d’esperar a que es completi cal informar a l’usuari d’aquesta circumstància, i una forma de fer-ho és mitjançant barres de progrés.

A Java Swing es fa servir el component jProgressBar. A l’apunt d’avui explicaré com fer servir jProgressBar per a informar sobre l’evolució d’una tasca que triga un temps a completar-se.

Plantejament de la solució
La construcció d’una barra de progrés que informi dels avenços d’una tasca requereix la col·laboració de diversos objectes. No és tan senzill com actualitzar l’estat de la barra. En general, si es realitza una tasca pesada al fil d’execució principal, no hi ha temps per a executar altres tasques de menor prioritat, com per exemple el repintat de finestres. La solució a aquest problema consisteix en crear fils d’execució encarregats d’aquestes actualitzacions. Es tracta d’un problema comú als entorns gràfics i la tècnica que acabo de mencionar és la forma comú de resoldre’l.

Amb Java, una aproximació genèrica faria servir la classe Thread, però a Swing hi ha disponible la classe SwingWorker que simplifica l’esquema.

Documentació
A la documentació oficial d’Oracle es pot trobar aquest tutorial “How to use a progress bar“.

Com a referència, l’enllaç al javadoc de la classe SwingWorker.

Com es fa amb java
L’esquema d’una progress bar és el següent:

1. La classe amb el fil principal d’execució ha d’implementar la interface PropertyChangeListener. Això vol dir que ha de tenir un mètode void propertyChange(PropertyChangeEvent evt). Aquest mètode s’activarà cada cop que es produeixi un canvi a les propietats progress o state de la classe que implementi la tasca en progrés. El mètode propertyChange serà l’encarregat d’actualitzar l’estat de la barra de progrés.

2. La tasca s’executarà dins d’una classe que derivarà de extends SwingWorker<Void, Void>. Això vol dir que, com a mínim, caldrà escriure doInBackground() i, si és necessari, també es pot sobreescriure done(). Al mètode doInBackground és on executarem la tasca pesada. Dins d’aquest mètode, també cal fer que el progrés de la tasca es reflexi en la propietat progress de SwingWorker mitjançant el mètode setProgress().
La propietat progress de SwingWorker només accepta valors enters entre 0 i 100. Provar d’establir altres valors amb setProgress() tornarà error.

3. Els canvis en la propietat de progrés de la classe derivada de SwingWorker seran els que caldrà capturar al mètode propertyChange de la classe de fil principal. Per aconseguir això, des de la classe principal es llençarà l’execució de la tasca.
3.1. Es crearà una instància de la tasca.
3.2. La instància de classe amb el fil de control principal es registrarà com a PropertyChangeListener de la instància de classe de la tasca.
3.3. S’executarà la tasca. El progrés de la tasca es reflexarà en la propietat progress. Els canvis en les propietats dispararan els events PropertyChange que activaran el mètode listener propertyChange dins del qual, si és el cas, s’actualitzarà la progress bar. En aquest mètode recollirem el valor de progress amb el mètode getNewValue() de l’objecte event rebut com a paràmetre i el farem servir per actualitzar la progressbar.

Un exemple
Tenint en compta l’esquema acabat de descriure, a continuació en presento un exemple: Es tracta d’una aplicació Java que presenta un formulari amb un botó. En fer click al botó es crea un fitxer de 1.000.000 de línies. Al llençar la creació del fitxer s’obre un formulari amb una progress bar que s’actualitza a mida que es creen les línies del fitxer.

El codi es pot descarregar del meu github (https://github.com/abaranguer/progressbar). El codi pujat correspon al projecte NetBeans.

El projecte té la següent estructura:

projecte

És dir, una classe per al formulari “principal”,FrameMainProvaProgressBar; una per al formulari amb la progress barFrameProgressBar; més una classe inicial ProvaProgressBar; un Controller i una classe Task.
He fet que l’aplicació seguís el patró MVC i he separat les funcionalitats seguint aquest criteri.

La classe amb el main executa el controlador.

package prova.progressbar;

import prova.progressbar.controller.Controller;

public class ProvaProgressBar {
    static Controller controller;
    
    public static void main(String[] args) {
        controller = new Controller();
        controller.initGUI();
    }
}

El constructor mostra el formulari principal i amaga el del progress bar.
El controlador té un mètode per respondre al click al botó. Quan es fa click al botó instancia una nova Task. Registra el Controller com a listener dels canvis de les propietats; mostra el formulari de la progress bar i canvia el cursor gràfic pel de “en espera”; Finalment, llença l’execució tasca en un nou fil.

El controlador implementa PropertyChangeListener. Per tant, cal sobreescriure public void propertyChange(PropertyChangeEvent evt). En aquest mètode intercepto els events corresponents als canvis de progress, en prenc el valor i actualitzo la progress bar.

El mètode done s’activa quan la tasca s’ha completat. Restaura el cursor i oculta el formulari de la progress bar

package prova.progressbar.controller;

import java.awt.Cursor;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import prova.progressbar.gui.*;

public class Controller implements PropertyChangeListener {
    FrameMainProvaProgressBar frameMainProvaProgressBar = null;
    FrameProgressBar frameProgressBar = null;

    public void initGUI() {
        frameMainProvaProgressBar = new FrameMainProvaProgressBar(this);
        frameMainProvaProgressBar.setVisible(true);
        frameProgressBar = new FrameProgressBar();
        frameProgressBar.setVisible(false);
    }
            
    public void loadProva() {
        frameProgressBar.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        frameProgressBar.setVisible(true);
        Task task = new Task(this);
        task.addPropertyChangeListener(this);
        task.execute();
    }

    public void propertyChange(PropertyChangeEvent evt) {
        System.out.println("Evt: " + evt.getPropertyName() + "; value: " + evt.getNewValue());
        if ("progress" == evt.getPropertyName()) {         
            int progress = (Integer) evt.getNewValue();
            frameProgressBar.setProgresBarValue(progress);
        } 
    }   
    
    public void done() {
        frameProgressBar.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        frameProgressBar.setVisible(false);
        System.out.println("Task done");
    }
}

La tasca java, que estén SwingWorker és on es fa tota la tasca de crear el fitxer.
Task ha de sobreescriure (@override) els mètodes doInBackground i done
La propietat progress de SwingWorker s’actualitza amb setProgress a mida que es va generant el fitxer. Cal recordar que progress només pren valors entre 0 i 100. Per això es divideix el nombre de fila (variable i) per 10000.

Quan la tasca es completa, s’activa el mètode done, que al seu temps invoca el done del Controller.

package prova.progressbar.controller;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import javax.swing.SwingWorker;

public class Task extends SwingWorker {
    Controller controller;
        
    public Task(Controller controller) {
        this.controller = controller;
    }
    
    @Override
    protected Void  doInBackground() throws Exception {    
        try {
            System.out.println("Entra");
            File file = new File("/home/albert/NetBeansProjects/prova-progressbar/file/test.txt");
            
            FileWriter fw = new FileWriter(file.getAbsoluteFile());
	    BufferedWriter bw = new BufferedWriter(fw);
            
            for(int i=0; i<1000000; i++) {
                setProgress(i/10000);
                bw.write("id: " + i +"; name: nom_" + i + "; value: valor_" + i+ "\n");
            }
	    
            bw.close();
            System.out.println("Surt");
	} catch (Exception e) {
	    e.printStackTrace();
	}

        return null;
    }
    
    @Override
    protected void done() {
        controller.done();
    }    
}

Les classes dels formularis no tenen molta cosa a comentar. Només dir que a les propietats del component progressBar cal establir que el valor màxim és 100 i el mínim 0. El motiu és que estic fent servir directament progress i, per tant, imposa aquesta límit.

En aquest vídeo, podeu veure l’execució de l’aplicació des de NetBeans.

url jdbc d’oracle per SID o per Service Name

Apunt ràpid de java i Oracle. La forma de fer servir bases de dades relacionals a Java és amb JDBC. LA connexió es basa en drivers que, depenent del protocol d’accés a la base de dades, són de diferents tipus.

Per accedir a bases de dades Oracle es recomana fer servir els drivers JDBC de tipus IV, que són drivers java pur.

Ara mateix estic involucrat amb el manteniment d’una aplicació que fa servir una BD java i en la migració d’aquesta aplicació d’un servidor Weblogic 8.1 a un JBoss 4.0 (sí, ja se, aquesta tecnologia és antiga!).

Java ja complert la seva promesa de “write once run anywhere” i la migració ha estat directa: el war s’ha desplegat sense canvis. Lògicament ha calgut modificar alguns fitxers de configuració per apuntar a nous paths.

1. Ha calgut posar el jar amb el driver d’oracle a la carpeta server/default/lib .
2. Els datasources a JBoss es despleguen copiant un fitxer xml amb sufix -ds.xml a la carpeta de deploy.

Les connexions JDBC a base de dades prenen la forma d’URL. I vet aquí el que cal tenir en compte: A Oracle la URL JDBC pren formes diferents si s’accedeix per SID, o si s’accedeix per Service Name. Haurem de saber de quina forma cal accedir. És una informació que ens haurà de proporcionar el DBA. En general , el SID és el nom únic (l’identificador) de la base de dades; i el Service name, ve a ser el nom públic amb el que la Base de dades està registrada al listener. SID i Service Name poden coincidir, o no.

La forma de la URL per sid és: jdbc:oracle:thin:@host:port:SID
Un exemple de fitxer de datasource de JBoss amb URL JDBC d’Oracle accedint per SID

<?xml version="1.0" encoding="ISO-8859-1"?>
<datasources>
   <local-tx-datasource>
      <!-- jndi name -->
      <jndi-name>ElMeuDataSource</jndi-name>
	  
	  <!-- url -->
      <connection-url>jdbc:oracle:thin:@host:port:SID</connection-url>

      <!-- driver class -->
      <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

      <!-- login i password -->
      <user-name>usuari</user-name>
      <password>password</password>
   </local-tx-datasource>
</datasources>

La forma de la URL per Service Name és: jdbc:oracle:thin:@//host:port/SERVICE_NAME

La diferència es troba a ‘//’ entre @ i al host; i el separador entre port i SID (‘:’), o entre port i Service Name (‘/’).
Un exemple de fitxer de datasource de JBoss amb URL JDBC d’Oracle accedint per Service Name

<?xml version="1.0" encoding="ISO-8859-1"?>
<datasources>
   <local-tx-datasource>
      <!-- jndi name -->
      <jndi-name>ElMeuDataSource</jndi-name>
	  
	  <!-- url -->
      <connection-url>jdbc:oracle:thin:@//host:port/SERVICE_NAME</connection-url>

      <!-- driver class -->
      <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

      <!-- login i password -->
      <user-name>usuari</user-name>
      <password>password</password>
   </local-tx-datasource>
</datasources>

Un últim detall, a Weblogic feia servir el datasource directament per nom ‘ElMeuDataSource’; a JBoss ha estat necessari afegir el prefix java: al nom del datasource per a utilitzar-lo. És dir: ‘java:ElMeuDataSource’

Octave – java – MySQL

Des de fa uns temporada estic involucrat en el manteniment d’una aplicació que combina una aplicació web d’anàlisi financera feta amb Java, una base de dades Oracle i un motor de càlcul suportat per Matlab. El motor de càlcul Matlab agafa i emmagatzema directament de la base de dades Oracle.

Una cosa que se’m va passar immediatament pel cap va ser com es podria implementar aquesta aplicació fent servir, exclusivament, programari lliure. Per als puristes, dir que incloc al Java en aquesta categoria de Programari Lliure, i també incloc, si més no, de moment, la base de dades MySQL. PostgreSQL també és una candidata interessant a base de dades. El PL/pgSQL de PostgreSQL és un factor a tenir molt en compte.

El tercer element de l’arquitectura, el motor de càlcul, tenia uns quants candidats. Octave és compatible a un 95% amb el llenguatge “.m” que s’escriu per a Matlab. FreeMat i Scilab també incorporen un llenguatge molt compatible amb el de matlab. Últimament, a més, també caldria tenir en compte també el llenguatge R. R, però, no és compatible amb Matlab de la forma gairebé directa que ho són Octave, Freemat o Scilab.

Val a dir també que Octave, Freemat i Scilab presenten diferències entre ells. Octave, per exemple, es presenta en consola de text, quan Scilab i Freemat ho fan en una GUI. Els llenguatges i propòsits d’Octave, Freemat i Scilab tampoc són exactament iguals entre ells ni amb Matlab. Octave és, des del meu punt de vista, el més proper a Matlab; Scilab sembla més orientat a les simulacions i es diria, per les toolboxes disponibles, que és més “d’enginyeria” que “matemàtic; Per la seva banda, Freemat té l’aspecte d’estar més orientat a la formació

Vet aquí també que una de les diferències entre Matlab, Octave, FreeMat i Scilab es dona justament en el punt que tm’havia cridat l’atenció: la interfície amb bases de dades.

A Scilab la solució general per estendre l’entorn i fer coses que inicialment no hi estan previstes, com atacar una base de dades, és desenvolupar scripts en llenguatge Tcl/Tk. Tcl/Tk forma part del core de Scilab. Així, per exemple, per atacar una base de dades Oracle caldria afegir el paquet Oratcl i fer un script tcl que ataqués la base de dades Oracle. Scilab proporciona l’entorn d’execució per a l’script tcl, i la interfície per passar objectes i dades entre l’script tcl i l’entorn Scilab.

Pel cas de MySQL la cosa es simplifica perquè hi ha un mòdul específic scilab-scimysql que en instal·lar-lo i activar-lo a un Scilab ens proporciona comandes que es poden fer servir directament dins els scripts .sce (l’equivalent als .m de matlab) per accedir a bases de dades MySql. És molt semblant a la combinació de Matlab + Oracle del sistema que estem mantenint.

El mòdul scilab-scimysql està disponible per instal·lar des del Synaptic (faig servir una Debian 7.0). Cal activar el MySQL Toolbox al menú d’eines:

scimysql

I també, un cop configurat l’entorn i l’accés a la base de dades, es poden executar les demos (menú d’ajuda)

Les demos es poden trobar a /usr/share/scilab/contrib/scimysql (alternativament, es pot descarregar de la pàgina web anterior el scimysql-0.1.1.tar.gz

FreeMat, per la seva banda només ofereix la possibilitat de carregar o escriure fitxers plans de text. Es disposa de la “Freemat External Interface” (FEI)) que permet extendre Freemat desenvolupant mòduls amb C. Sempre que tinguem una llibreria amb C per accedir a una base de dades, es podrà fer servir la FEI per a desenvolupar el mòdul que permeti fer la interfície amb els nostres scripts de Freemat. No és una opció per al desenvolupament ràpid.

Finalment, Octave disposa, per una banda, del package “database” amb el que se’ns ofereix connectivitat directa amb bases de dades de tipus PostgreSQL. Per altra banda també disposa del package octave-java que permet fer servir Java com a llenguatge per a estendre la funcionalitat de l’aplicació (el mateix que fa Scilab amb Tcl/Tk).

Disposar de Java a l’Octave és una opció molt poderosa. Mitjançant Java es pot accedir a totes les bases de dades per a les que existeixi un driver Jdbc, és dir, a la pràctica totalitat. No només a bases de dades: a qualsevol servei per al que es disposi de driver Java.

El package octave-java incorpora un reduït nombre de funcions que permeten instanciar objectes java i executar-ne els mètodes; obtenir els mètodes i atributs d’una classe; afegir classes i llibreries al classpath… Els tipus dels valors retornats pels mètodes executats des d’Octave de les instàncies dels objectes java es tradueixen als tipus propis d’Octave per a tipus simples, i es poden fer servir directament des les funcions pròpies d’Octave. Potser aquest és el punt més fluix d’aquesta llibreria: Es troba a faltar un mapeig directe entre algun tipus matricial de Java a les matrius d’Octave que són, en definitiva, el tipus bàsic d’aquesta aplicació.

Tenint en compte els criteris anteriors, la meva tria és Octave + Java + MySql. La tria de MySQL respon a un criteri banal: ja la tinc instal·lada amb el XAMPP. La tria d’Octave respon a le fet de poder fer servir Java, que és un llenguatge amb el que em trobo còmode.

No cal dir que les combinacions Octave + PostgreSQL (amb package octave-database), o Scilab + MySQL (amb Scilab MySQL Toolbox) ofereixen implementacions més directes al permetre accedir a les bases de dades des dels mateixos scripts “.m” d’Octave o “.sce” de Scilab, sense haver de passar per un script o aplicació externa en Java o Tcl/Tk.

Res millor que un exemple per veure com funciona.

Primer de tot, la descripció del programari involucrat:

GNU Linux Debian Versió 7.0 (wheezy) de 32 bits
GNU Octave, versió 3.6.2
MySQL 5.5.27 (el que va amb la versió de Xampp que tinc instal·lada).
Java JDK 1.7.0
Driver JDBC de MySQL mysql-connector-java-5.1.22-bin.jar
phpMyAdmin per a fer l’administració de la base de dades MySQL.
Eclipse Juno (IDE de Java)

El primer pas ha estat assegurar que el paquet octave-java està disponible. Si no ho està, es pot instal·lar des del Synaptic:

synaptic

Cal establir correctament la variable d’entorn JAVA_HOME.

El que vull és, des d’Octave,
1. crear una taula ‘prova’, amb tres camps (id int, clau varchar(50), valor varchar(50))
2. afegir-hi dades a la taula (insert)
3. modificar-ne alguns valors (update)
4. recuperar-los (select)

Com es pot aconseguir?

Em cal una classe Java d’utilitat. Només tres mètodes: executarSql(sql) per a Create, Insert, Update i Delete; i obrirCursor(sql) i tancarCursor() per a obtenir un ResultSet.

La classe java és aquesta:

OctaveMysql.java

package cat.stsoftlliure.octave.mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class OctaveMysql {

	String url = "jdbc:mysql://localhost/test";
	Connection conn;
	Statement stmt;
	ResultSet rs;

	public OctaveMysql() throws ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
	}

	public void executeSql(String sql) throws ClassNotFoundException,
			SQLException {
		conn = DriverManager.getConnection(url);
		stmt = conn.createStatement();
		stmt.execute(sql);
		stmt.close();
		conn.close();
	}

	public ResultSet openCursor(String sql) throws SQLException {
		conn = DriverManager.getConnection(url);
		stmt = conn.createStatement();
		return rs = stmt.executeQuery(sql);
	}

	public void closeCursor() throws SQLException {
		stmt.close();
		conn.close();
	}

	public static void main(String args[]) {
		String sql = "CREATE TABLE IF NOT EXISTS `prova` ("
				+ "`id` int(11) NOT NULL," + "`clau` varchar(50) NOT NULL,"
				+ "`valor` varchar(50) NOT NULL," + "PRIMARY KEY (`id`)" + ")";
		try {
			OctaveMysql om = new OctaveMysql();
			om.executeSql(sql);
		} catch (Exception e) {
			System.out.println("error: " + e.getMessage());
		}
	}

}

I llavors, puc fer servir aquesta classe als meus scripts “.m”

prova_mysql.m

# afegeix al classpath la classe de prova i el driver de mysql 
javaaddpath("/home/albert/Workspace/wk-java/octave-mysql/jar/octave-mysql.jar");
javaaddpath("/home/albert/drivers-jdbc/mysql-connector-java-5.1.22/mysql-connector-java-5.1.22-bin.jar");

# instancia l'objecte java 
mysql = java_new("cat.stsoftlliure.octave.mysql.OctaveMysql")

# itera sobre la taula
rs = mysql.openCursor("select clau, valor from prova")

while rs.next()
  printf("%s - %s\n", rs.getString(1), rs.getString(2))
endwhile

# tanca objectes de connexió
mysql.closeCursor()

L’execució de l’script anterior em torna següent:

albert@athena:~$ octave
GNU Octave, version 3.6.2
Copyright (C) 2012 John W. Eaton and others.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type `warranty'.

Octave was configured for "i486-pc-linux-gnu".

Additional information about Octave is available at http://www.octave.org.

Please contribute if you find this software useful.
For more information, visit http://www.octave.org/help-wanted.html

Read http://www.octave.org/bugs.html to learn how to submit bug reports.

For information about changes from previous versions, type `news'.

octave:1> clc

octave:2> prova_mysql
mysql =



rs =



nom1 - valor1
nom3 - valor2
nom3 - valor3
nom4 - valor4
nom5 - valor5
nom6 - valor6
ans = [](0x0)
octave:3> 

He amagat tota la lampisteria que cal per accedir a la base de dades a la classe Java, que actua com una capa tècnica que exposa mètodes de servei a l’Octave . Aquests mètodes són el que invoco des d’Octave. Tanmateix Octave té la capacitat d’utilitzar directament les classes java: El mètode que obre un “cursor” retorna un ResultSet, i els mètodes del ResultSet (a l’exemple, getString) són invocats des d’Octave. És dir, també hauria pogut posar la lampisteria java a l’Octave (però no hauria estat una bona idea).

En resum, un exemple molt senzill de com ampliar la funcionalitat d’Octave amb Java, en concret per accedir a la base de dades MySql, però que es pot aplicar a qualsevol àmbit per al que es disposi de classes i llibreries Java.

I ara que ja se com, ja puc crear una replica del sistema Matlab + Oracle fent servir només eines lliures. Cost de les llicències, zero. 😉

Tornar a programar amb Java

Crisi és canvi. Els canvis requereixen una ment oberta. Les activitats professionals canvien i, potser, ens cal recuperar i actualitzar coneixements.

Posem un cas, i d’això va aquest post:  imaginem que fa pocs anys érem capaços de desenvolupar aplicacions java de servidor; vam passar a dirigir un equip de desenvolupament i vam abandonar les tasques més tècniques, o potser vam fer un canvi de tecnologia. Avui, però, les circumstàncies ens porten a haver d’afrontar de nou desenvolupaments amb Java, o tornar a aquesta tecnologia o, senzillament, volem deixar-nos aquesta porta oberta. Ens hem de posar al dia en Java. Suposem que vam deixar Java quan els temps del JDK 1.4.2

 

Llenguatge Java

Partim , doncs, d’un coneixement previ del llenguatge, potser una mica rovellat, però res que no s’arregli amb unes poques hores de revisar codi. Cal dir que la versió actual de JDK és la 1.7, o Java 7. El Java 7 té algunes construccions que no tenia el JDK 1.4.2. Potser les més cridaneres són les anotacions, les templates, alguna sintaxi nova pels bucles. Amb el futur Java 8 (estava previst per al Java 7, però va caure) s’espera l’arribada de les “closures” i característiques de programació funcional. L’evolució del llenguatge la podeu trobar a http://en.wikipedia.org/wiki/Java_version_history.

Per a un repàs del JDK, pot anar bé aquest enllaç: http://docs.oracle.com/javase/tutorial/java/index.html

Aquestes característiques s’utlitzen als frameworks més actuals. Ara bé, juntament amb aquestes característiques noves també estan els POJO, els Plain Old Java Object, els objectes java “de tota la vida”, i els frameworks més moderns procuren que una de els seves característiques sigui, justament, que es poden fer servir amb els POJOs.

Refrescar la sintaxi és el primer pas.

 

Java SE, EE, ME i Android

En aquest punt podem triar camins, hi han tres vies principals mantingudes per Oracle i una quarta per Google. cada via té el seu SDK respectiu, Els SDK son paquets amb eines i llibreries java que implementen unes especificacions estandaritzades de tecnologies relacionades amb un determinat tipus d’aplicacions:

  • Les aplicacions java d’escriptori, el Java SE;  La Standard Edition. El JDK de tota la vida.
  • Les aplicacions java de servidor, el Java EE; la Enterprise Edition. Servlets, EJB, Servidors d’aplicacions…
  • Les aplicacions de mobilitat i per a sistemes encastats, el Java ME; la Micro Edition, java als telefons mòbils no Android, a dispositius i sensors “smart” de tota mena, targetes intel·ligents,  televisors…
  • Les aplicacions Android. Els dispositius Android  defineixen un entorn de desenvolupament i un runtime d’execució en que el principal llenguatge de desenvolupament, no l’únic, és java.

L’ambient en que es desenvolupen les aplicacions de servidor és la Java EE. Realment, EE i SE no estan separades i Java EE és, més aviat, un superconjunt de java SE que l’amplia amb llibreries i packages orientats al desenvolupament enterprise.

Java EE es troba actualment en la versió 6, la Java EE 6.

Una bona referència és The Java EE 6 Tutorial (PDF). Aquest tutorial és força més dens que l’anterior.<

 

Patrons de disseny i Frameworks

El desenvolupament d’aplicacions Java EE amb entitat demana, a més de l’SDK, llibreries i frameworks de suport. La tria del framework i arquitectura adequats és, segurament, el factor d’èxit més important en el desenvolupament d’aplicacions EE. En aquest sentit, és molt bo repassar la bibliografia disponible sobre patrons de disseny. Un repàs ràpid a http://www.oodesign.com/.  El llibre clàssic sobre patrons de disseny és el “Design Patterns: elements of reusable object-oriented software“.Però el més important són els frameworks. Res no impedeix fer una aplicació només amb jsp i servlets fets a ma al més pur old-style, però no és una bona idea. Serà mollt més productiu fer servir un framework que, ell tot sol, ja doni una bona part de la feina feta.

 

Spring

El framework més utilitzat per a desenvolupar aplicacions web l’Spring Framework, que és part del projecte Spring. Spring Framework fa funcionar juntes tot un conjunt de mòduls i llibreries: des deLog4j, fins a Hibernate, passant per Struts. Per dir-ne només algunes. Cadascuna de les caixetes que surten a la imatge de sota gairebé és un món. És recomanable fer-lis un cop d’ull. No dic de conèixer-les en detall, però sí saber “de què van” per a que, si arribat el moment, saber per on agafar-ho.

spring-overview

Aquest tutorial explica com fer pas a pas una aplicació Spring basada en el patró MVC: http://www.davidmarco.es/tutoriales/spring-mvc-sbs/

Els manuals de referència de la darrera versió (a dia d’avui) de l’Spring Framework en HTML / PDF

Spring encara pot fer les coses més fàcils. Amb Spring ROO si volem crear una aplicació nova, en comptes de crear l’estructura des de zero, disposem una eina per a crear de forma automàtica l’esquelet (i alguns organs vitals) de l’aplicació. La generació automàtica de codi ara rep el nom d’scaffolding. Un anglicisme més.

Per cert, vinculat a Spring, però fent una vida apart, hi ha Groovy,  (i Grails, Groovy on Rails) que és  essencialment un java interpretat, és dir, sense declaració de tipus. Groovy permet desenvolupar scripts, interactuar amb objectes java… Ja n’he parlat en posts anteriors. Cal esmentar-lo.

 

Eines

A més dels frameworks, les eines són importants. No cal que presenti Eclipse. Degut a que és un IDE open-source es fa servir com a base per a crear IDES específics. En concret hi ha un Eclipse IDE for Java EE developers , o un Spring Tool Suite que no és més que un Eclipse “tunejat” per Spring.

A l’Eclipse hi podem afegir plugins que resolen qüestions com el control de fonts i el treball en grup. El plugin Subclipse, per exemple,  permet atacar repositoris de fonts Subversion. El que està de moda ara és Git.

A més dels IDE ens calen servidors locals de desenvolupament. N’hi han un munt, els servidors de servlets i JSP Tomcat o Jetty, passant pels servidors d’aplicacions amb contenidor EJB, com GeronimoGlassFish o JBoss. Tots ells poden ser, a més, servidors de producció. L’administració de servidors és un món en ella mateixa.

 

L’equip de desenvolupament java

Això em porta a un altre punt que crec que cal considerar: què vol dir “fer java” o “ser un javero”. Java és un llenguatge que permet nivells molt alts d’abstracció. A més, quan afegim frameworks, els nivells d’abstracció encara poden ser més alts. Què vol dir això? que, en realitat, el “programador java estricte” no existeix. Com a mínim estem parlant de perfils d’analista programador. Senzillament perquè el llenguatge permet centrar-se més en la solució, que no haver de pensar en la tecnologia. Un analista-programador en java ha de ser capaç de programar, i ha de tenir un coneixement suficient de les arquitectures i frameworks sobre els que treballa.

En un equip de desenvolupament java, a mida que es vagi fent gran, caldrà anar introduint perfils més especialitzats i tècnics. Administradors de Respositori de Fonts, Administradors dels Servidors d’Aplicacions, amb capacitat per instal·lar i fer el manteniment d’aquests servidors. Especialistes en els frameworks, per prestar suport, instal·lar els frameworks i posar en marxa les arquitectures, o extendre els frameworks amb noves capacitats. Són rols que hauria d’assumir algú de l’equip de desenvolupament amb formació o experiència específica.

Finalment, la gestió del projecte: de l’equip, de l’usuari, de l’agenda i del cost. En definitiva, la metodologia de treball (metodologies àgils? o pesades com RUPquè triar?). Si en comptes de projectes, parlem de manteniments, aleshores potser ens cal recordar ITIL i com implementar-lo; i els diferents programaris que el poden suportar, per exemple, de gestió d’incidències, com BMC Remedy o, a un altre nivell, Mantis BT.

 

On es fa servir Java? té futur això?

I, per acabar, cap a on crec que aniran les coses:

A dia d’avui, els perfils java estan buscats. La demanda ve impulsada per la telefonia mòbil, i per la irrupció del mercat “smart”. La propera revolució, si no m’equivoco, serà la de les Smart Cities. Les ciutats intel·ligents, verdes, segures, netes i eficients, potser més democràtiques i tot. Les Smart cities funcionaran sobre una gran xarxa de telecomunicacions que interconnectaran dispositius i sensors de tota mena i fluxos d’energia en “smart” grids de forma que estarem parlant de la “Internet de les coses”. En tot aquest maremàgnum de servidors, Data Ware Houses, Temps Real, sensors i disposotius smart (evidentment amb els smartphones com a dispositius estrella) el Java està molt ben posicionat per a ser el llenguatge dominant.

 

Pot ser una bona idea posar-se al dia en Java.

Un exercici amb el Framework Seam

Des de fa un temps que estic involucrat en el manteniment d’una aplicació desenvolupada sobre el framework Seam. Fins dates recents el manteniment s’ha limitat a modificacions menors, però fa poc ens ha entrat un canvi en profunditat.Ha estat l’oportunitat que estava esperant per a poder aprendre una mica més sobre aquest potent framework.

Primer de tot, què és Seam? La Viquipèdia ens dona una definició breu, senzilla i entenedora: “Seam és un Framework per a aplicacions Web desenvolupat per JBoss, una divisió de Red Hat.”

Hi han molts frameworks per a aplicacions web.  Un framework és  la sistematització de l’experiència acumulada en el desenvolupament de moltes aplicacions web. Aquesta sistematització pren la forma d’arquitectures, patrons, llibreries, bones pràctiques i eines de diferents tipus. En adoptar un framework per a desenvolupar una aplicació web el que estem fent, doncs, es manifestar la voluntat de “fer bé les coses”, repetint models coneguts que han funcionat.  De forma pràctica, l’adopció d’un framework es tradueix en l’ús de certes llibreries, metodologies i eines de desenvolupament

Spring ve a ser l’estàndar de facto de la indústria o, si més no, el framework java per a aplicacions web més popular. Si coneixem Spring, doncs, ja sabem que podem esperar de Seam.  Tanmateix, Seam té les seves pròpies característiques. En aquest enllaç podeu trobar una anàlisi comparativa entre els frameworks Seam i Spring.

Per aproximar-me a Seam provaré de fer “alguna cosa” amb el framework. Per exemple un gestor de biblioteca molt senzill.

El que vull fer és una aplicació web que em permeti donar d’alta i de baixa  llibres, classificar-los segons autor, temàtica i tipus de suport físic, i que em permeti saber a quin lloc de la casa paren.

Doncs bé, resulta que això puc fer-ho sense escriure una línia de codi java. Seam es capaç de generar una aplicació web de manteniment sense més que proporcionar-li un esquema de dades. La veritat és que és força espectacular. No cal dir que aquests superpoders no són pas els únics dels que disposa Seam, però per a una primera aproximació -res més pretenc en aquest post- és més que suficient.

Comencem: aplicacions. El framework Seam es pot fer servir amb qualsevol Servidor d’Aplicacions que suporti EJB3.0, ara bé, Seam és un producte de JBoss i, com es pot esperar s’integra fàcilment amb aquest servidor. El primer que faré, doncs, és descarregar les darreres versions de  JBoss AS i del Seam framework. Proveu els següents enllaços:

Seam 3.1.0 http://sourceforge.net/projects/jboss/files/Seam/3/3.1.0.Final/

jBoss AS 7.1.1 Final http://download.jboss.org/jbossas/7.1/jboss-as-7.1.1.Final/jboss-as-7.1.1.Final.zip

Ens caldrà una versió de JDK igual o superior a la 1.6 (feu un cop d’ull a http://www.oracle.com/technetwork/java/javase/downloads/index.html).

Si per alguna raó no poguéssim disposar almenys de JDK1.6, aleshores hauríem de fer servir versions anteriors de JBoss AS i de SEAM. Podria ser aquest el cas d’haver d’ocupar-nos del manteniment d’una aplicació legacy.

La següent configuració funciona amb JDK 1.5 (evidentment, funciona amb JDK superiors):

Seam 2.3.0 GA http://sourceforge.net/projects/jboss/files/JBoss%20Seam/2.3.0.Final/

JBoss AS 5.1.0.GA http://sourceforge.net/projects/jboss/files/JBoss/JBoss-5.1.0.GA

La instal·lació del servidor JBoss AS 1.7 i del Seam Framework 3.1.0 no presenta cap dificultat, es tracta, simplement de descomprimir el paquet zip corresponent. he creat una carpeta seam-jboss i els he descomprimit allà.

Si tinc versions diverses de JDK, puc modificar l’script d’arrencada de JBoss bin/standalone.sh per a indicar-hi explícitament quin JDK vull fer servir, només em cal donar un valor inicial a JAVA_HOME per a que apunti a la carpeta del JDK triat, per exemple:

JAVA_HOME=/home/albert/jdk1.6.0_30

El següent pas és crear les taules i relacions de la base de dades. Faré servir el servidor MySQL que em ve amb el XAMPP que tinc instal·lat de posts anteriors, i el phpMyAdmin com interfase de la base de dades.

Només vull fer una petita demostració, o sigui que no entraré en detalls: la idea és crear una petita bd de biblioteca. Tindré
– una taula per guardar els llibres (id, llibre, isbn, id_ubicació, id_format);
– una taula d’autors (id, autor);
– una taula de relació m-n entre llibres i autors (un autor pot haver escrit molts llibres, un llibre pot ser escrit per molts autors. Per tant: id, id_autor, id_llibre);
– una taula de relació m-n entre llibres i temàtiques (un llibre pot tractar diversos temes; un tema pot ser tractat per molts llibres. Per tant, id, id_llibre, id_tematica);
– lògicament, doncs, una taula de temàtiques (id, tematica).
– una taula d’ubicacions (id, lloc) ja que puc tenir llibres per uns quants llocs de la casa, a prestatges, o a DVDs (suposo que tinc els DVDs etiquetats, cosa que algun dia hauré de fer, però, en fi, això només pretén ser un exemple 😉
– una taula de formats: “paper”, “pdf”, “epub”, “cbr”… (id, format).

Dit i fet. Primer de tot creo la base de dades “prova_seam”, i les taules “llibre”, “autor”, “llibre_autor”, “llibre_tematica”, “ubicacio” i “format”.

A continuació, creo els índexos sobre totes les columnes que participaran en les relacions i, finalment, creo les relacions. Cal esmerçar-se en crear correctament els índexos i les relacions. És necessari per a que la màgia de Seam funcioni. Es tracta, simplement, de permetre que les eines de mapeig de model relacional a model d’objectes tinguin tota la informació necessària per a crear una capa de classes java que encapsulin l’accés a les dades.

Després d’una sessió de phpMyAdmin, obtinc un model de dades que m’agrada. L’esquema és el següent:


-- phpMyAdmin SQL Dump
-- version 3.4.5
-- http://www.phpmyadmin.net
--
-- Servidor: localhost
-- Tiempo de generación: 21-11-2012 a las 21:05:25
-- Versión del servidor: 5.5.16
-- Versión de PHP: 5.3.8

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- Base de datos: `prova_seam`
--

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `autor`
--

CREATE TABLE IF NOT EXISTS `autor` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `autor` varchar(100) COLLATE latin1_spanish_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=2 ;


-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `format`
--

CREATE TABLE IF NOT EXISTS `format` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `format` varchar(100) COLLATE latin1_spanish_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=2 ;


-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `llibre`
--

CREATE TABLE IF NOT EXISTS `llibre` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `llibre` varchar(200) COLLATE latin1_spanish_ci NOT NULL,
  `isbn` varchar(13) COLLATE latin1_spanish_ci NOT NULL,
  `id_ubicacio` int(11) NOT NULL,
  `id_format` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `id_ubicacio` (`id_ubicacio`),
  KEY `id_format` (`id_format`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `llibre_autor`
--

CREATE TABLE IF NOT EXISTS `llibre_autor` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `id_llibre` int(11) NOT NULL,
  `id_autor` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `id_llibre` (`id_llibre`),
  KEY `id_autor` (`id_autor`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `llibre_tematica`
--

CREATE TABLE IF NOT EXISTS `llibre_tematica` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `id_autor` int(11) NOT NULL,
  `id_tematica` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `id_autor` (`id_autor`),
  KEY `id_tematica` (`id_tematica`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `tematica`
--

CREATE TABLE IF NOT EXISTS `tematica` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tematica` varchar(100) COLLATE latin1_spanish_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `ubicacio`
--

CREATE TABLE IF NOT EXISTS `ubicacio` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ubicacio` varchar(100) COLLATE latin1_spanish_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci AUTO_INCREMENT=2 ;

--
-- Volcado de datos para la tabla `ubicacio`
--

INSERT INTO `ubicacio` (`id`, `ubicacio`) VALUES
(1, 'llibreria de l''escriptori');

--
-- Restricciones para tablas volcadas
--

--
-- Filtros para la tabla `llibre`
--
ALTER TABLE `llibre`
  ADD CONSTRAINT `llibre_ibfk_1` FOREIGN KEY (`id_ubicacio`) REFERENCES `ubicacio` (`id`),
  ADD CONSTRAINT `llibre_ibfk_2` FOREIGN KEY (`id_format`) REFERENCES `format` (`id`);

--
-- Filtros para la tabla `llibre_autor`
--
ALTER TABLE `llibre_autor`
  ADD CONSTRAINT `llibre_autor_ibfk_1` FOREIGN KEY (`id_llibre`) REFERENCES `llibre` (`id`),
  ADD CONSTRAINT `llibre_autor_ibfk_2` FOREIGN KEY (`id_autor`) REFERENCES `autor` (`id`);

--
-- Filtros para la tabla `llibre_tematica`
--
ALTER TABLE `llibre_tematica`
  ADD CONSTRAINT `llibre_tematica_ibfk_1` FOREIGN KEY (`id_autor`) REFERENCES `autor` (`id`),
  ADD CONSTRAINT `llibre_tematica_ibfk_2` FOREIGN KEY (`id_tematica`) REFERENCES `tematica` (`id`);

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;


En aquest punt, deixo el phpMyAdmin i me’n vaig a la carpeta on tinc el seam.

Estic fent servir la versió de Seam 3.2.0. Amb aquesta versió hi han les següents eines disponibles per al desenvolupament d’aplicacions web:
– el JBoss Developer Estudio. que només requereix registre per a poder-lo descarregar lliurement.
– Les JBoss Tools. Que són plugins per Eclipse. Les JBoss Tools permeten crear, directament, aplicacions per al Framework Seam 2.3.0 i, indirectament, a través del plugin de Seam Forge, per al Framework Seam 3.1.0.
Seam Forge. Es tracta d’una shell des de la que es poden crear aplicacions, deployar, crear objectes nous… Aquesta shell s’integra com a plugin a les JBoss Tools o al JBoss Developer Studio. El Seam Forge vindria a ser l’evolució de l’script seam-gen de la versió Seam Framework 2.3.0. A diferència de seam-gen, que formava part del paquet de distribució del Framework, el Seam Forge ha de descarregar-se com una aplicació separada.

Seam Forge es pot extendre amb plugins. A la pàgina de plugins en trobarem alguns que ens caldran per a poder fer l’enginyeria inversa de la base de dades i per automatitzar les tasques d’administració dels desplegaments al JBoss AS. Però n’hi han d’altres. Serà cosa de provar a veure què fan.

Com que el repte és “crear una aplicació sense escriure una línia de codi”, i tenint en compte les explicacions anteriors, descarrego el Seam Forge. És un paquet zip que desplego a la mateixa carpeta de seam-jboss on tinc les carpetes del JBoss AS i del Seam Framework. Em cal indicar-li el JDK que penso fer servir i quina és la seva carpeta home. Per a fer-ho vaig a la carpeta bin del Seam Forge i allà modifico l’script forge per a establir les variables JAVA_HOME i FORGE_HOME.

Seam Forge em permetrà generar (scaffold) una aplicació o, si més no, l’esquelet i alguns òrgans vitals, d’una aplicació web. Tot plegat d’una forma que recorda els Archetypes de Maven. pel que estic veient, Maven és una eina que es fa força imprescindible a l’hora de treballar amb projectes desenvolupats a partir de Forge. En general Maven és una eina molt potent que caldria tenir en compte (almenys considerar-ne l’ús) de forma habitual.

A més, em caldrà tenir a ma el driver jdbc del MySQL per a poder desplegar-lo al JBoss.

És el primer que faig. Per a desplegar el driver jdbc n’hi ha prou amb col·locar-lo a la carpeta JBOSS_HOME/standalone/deployments. Si tenim una finestra oberta amb els logs del servidor veurem com, efectivament, el desplega. A més, a la consola d’administració del JBoss també veurem el nou desplegament:

UN cop tinc el driver disponible al JBoss, passo a la construcció de l’aplicació amb el Forge.

Escric forge al terminal i accedeixo a la shell. Una advertència, no tanqueu la shell del Forge fins completar tots els passos del desplegament de l’aplicació:

Per a poder fer l’enginyeria inversa del model de dades al MySQL i generar les classes java per hibernate em cal, primer de tot, carregar els hibernate-tools. Aquesta part del post està basada en aquest link (http://www.mastertheboss.com/forge/reverse-engineer-your-db-schema-using-jboss-forge).


albert@athena:~/seam-jboss/forge-distribution-1.1.2.Final/bin$ forge
    _____                    
   |  ___|__  _ __ __ _  ___ 
   | |_ / _ \| `__/ _` |/ _ \  \\
   |  _| (_) | | | (_| |  __/  //
   |_|  \___/|_|  \__, |\___| 
                   |___/      

JBoss Forge, version [ 1.1.2.Final ] - JBoss, by Red Hat, Inc. [ http://jboss.org/forge ]
[no project] bin $
[no project] bin $ forge install-plugin hibernate-tools
***INFO*** Preparing to install plugin: hibernate-tools
***INFO*** Checking out plugin source files to [/tmp/forgetemp2102291278366764408/repo] via 'git'
***INFO*** Switching to branch/tag [refs/heads/master]
***INFO*** Invoking build with underlying build system.
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building hibernate-tools-plugin 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ hibernate-tools-plugin ---
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ hibernate-tools-plugin ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ hibernate-tools-plugin ---
[INFO] Compiling 9 source files to /tmp/forgetemp2102291278366764408/repo/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ hibernate-tools-plugin ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ hibernate-tools-plugin ---
[INFO] Not compiling test sources
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ hibernate-tools-plugin ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ hibernate-tools-plugin ---
[INFO] Building jar: /tmp/forgetemp2102291278366764408/repo/target/hibernate-tools-plugin-1.0.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 22.651s
[INFO] Finished at: Sun Nov 18 14:29:57 CET 2012
[INFO] Final Memory: 33M/79M
[INFO] ------------------------------------------------------------------------
***INFO*** Installing plugin artifact.
***SUCCESS*** Installed from [https://github.com/forge/plugin-hibernate-tools.git] successfully.
Wrote /home/albert/.forge/httpsrawgithubcomforgepluginrepositorymasterrepositoryyaml.yaml
Wrote /home/albert/.forge/plugins/org/jboss/hibernate/forge/hibernate-tools-plugin/1.0.0-SNAPSHOT-311059c3-c505-46c1-915e-4a3af362ea9a
...

després d’escriure un munt de jars a la carpeta de plugins, finalment, tinc les hibernate tools disponibles al Forge:

Wrote /home/albert/.forge/plugins/org/jboss/hibernate/forge/hibernate-tools-plugin/1.0.0-SNAPSHOT-311059c3-c505-46c1-915e-4a3af362ea9a/slf4j-log4j12-1.5.10.jar
Wrote /home/albert/.forge/plugins/org/jboss/hibernate/forge/hibernate-tools-plugin/1.0.0-SNAPSHOT-311059c3-c505-46c1-915e-4a3af362ea9a/hibernate-tools-plugin.jar
Wrote /home/albert/.forge/plugins/org/jboss/hibernate/forge/hibernate-tools-plugin/1.0.0-SNAPSHOT-311059c3-c505-46c1-915e-4a3af362ea9a/module.xml
    _____                    
   |  ___|__  _ __ __ _  ___ 
   | |_ / _ \| `__/ _` |/ _ \  \\
   |  _| (_) | | | (_| |  __/  //
   |_|  \___/|_|  \__, |\___| 
                   |___/      

JBoss Forge, version [ 1.1.2.Final ] - JBoss, by Red Hat, Inc. [ http://jboss.org/forge ]

Ara creo el nou projecte:

[no project] bin $ new-project
 ? [named=The name of the new project (of type java.lang.String)]: provaseam
 ? Use [/home/albert/seam-jboss/forge-distribution-1.1.2.Final/bin/provaseam] as project directory? [Y/n] n

 ? Where would you like to create the project? [Press ENTER to use the current directory: bin] [ .../bin] /home/albert/workspace/wk-java/provaseam
***SUCCESS*** Created project [provaseam] in new working directory [/home/albert/workspace/wk-java/provaseam]
Wrote /home/albert/workspace/wk-java/provaseam
Wrote /home/albert/workspace/wk-java/provaseam/pom.xml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java
Wrote /home/albert/workspace/wk-java/provaseam/src/test/java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/resources
Wrote /home/albert/workspace/wk-java/provaseam/src/test/resources
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam
[provaseam] provaseam $

new-project ha creat un fitxer pom.xml (per a compilar i deployar amb maven) i una estructura de carpetes.

Ara cal omplir aquesta estructura de carpetes de contingut. A continuació, configuro paràmetres incials de persistència:

[provaseam] provaseam $ persistence setup
 ? [provider=ARG (of type org.jboss.forge.spec.javaee.jpa.api.JPAProvider)]: org.jboss.forge.spec.javaee.jpa.api.JPAProvider
[provider=ARG (of type org.jboss.forge.spec.javaee.jpa.api.JPAProvider)]: 

  1 - [HIBERNATE]
  2 - [OPENJPA]
  3 - [ECLIPSELINK]
  4 - [INFINISPAN]

 ? Choose an option by typing the number of the selection: 1
 ? [container=ARG (of type org.jboss.forge.spec.javaee.jpa.api.JPAContainer)]: org.jboss.forge.spec.javaee.jpa.api.JPAContainer
[container=ARG (of type org.jboss.forge.spec.javaee.jpa.api.JPAContainer)]: 

  1 - [JBOSS_AS6]
  2 - [JBOSS_AS7]
  3 - [JBOSS_EAP6]
  4 - [GLASSFISH_3]
  5 - [CUSTOM_JDBC]
  6 - [CUSTOM_JTA]
  7 - [CUSTOM_NON_JTA]

L’anterior es podia haver fet en una sola línia així si hagués afegit les opcions provider i container a persistence setup:

persistence setup --provider HIBERNATE --container JBOSS_AS7

Segueix:

 ? Choose an option by typing the number of the selection: 2
Use which version of 'jboss-javaee-6.0' ?

  1 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta4]
  2 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta5]
  3 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta6]
  4 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta7]
  5 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.CR1]
  6 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Final]
  7 - [org.jboss.spec:jboss-javaee-6.0:pom::2.0.0.Beta1]
  8 - [org.jboss.spec:jboss-javaee-6.0:pom::2.0.0.CR1]
  9 - [org.jboss.spec:jboss-javaee-6.0:pom::2.0.0.Final]
  10 - [org.jboss.spec:jboss-javaee-6.0:pom::2.1.0.Beta1]
  11 - [org.jboss.spec:jboss-javaee-6.0:pom::3.0.0.Beta1]
  12 - [org.jboss.spec:jboss-javaee-6.0:pom::3.0.0.Final]
  13 - [org.jboss.spec:jboss-javaee-6.0:pom::3.0.1.Final]*

 ? Choose an option by typing the number of the selection [*-default]  [0] 
***SUCCESS*** Installed [forge.spec.jpa] successfully.
***INFO*** Setting transaction-type="JTA"
***INFO*** Using example data source [java:jboss/datasources/ExampleDS]
 ? Do you want to install a JPA 2 metamodel generator? [y/N] 
 ? The JPA provider [HIBERNATE], also supplies extended APIs. Install these as well? [y/N] 
***SUCCESS*** Persistence (JPA) is installed.
Wrote /home/albert/workspace/wk-java/provaseam/src/main/resources/META-INF/persistence.xml
Wrote /home/albert/workspace/wk-java/provaseam/pom.xml
[provaseam] provaseam $

Fixem-nos en aquesta línia:

***INFO*** Using example data source [java:jboss/datasources/ExampleDS]

S’ha creat un datasource de prova. El podem trobar a la carpeta /home/albert/workspace/wk-java/provaseam/src/main/resources/META-INF/

El persistence.xml generat:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="forge-default" transaction-type="JTA">
    <description>Forge Persistence Unit</description>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hibernate.transaction.flush_before_completion" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

El que faré serà canviar el persistence.xml que fa servir el datasource d’exemple per un persistence.xml que apunti al datasource que crearé ara.

Creo el datasource. Per això copio un fitxer datasource.xml amb el següent contingut a la carpeta de deployments (JBOSS_HOME/standalone/deployments).

<datasources xmlns="http://www.jboss.org/ironjacamar/schema">
<datasource jndi-name="java:jboss/datasources/mysqldev" pool-name="poolmysqldev">
    <connection-url>jdbc:mysql://localhost/prova_seam</connection-url>
    <driver>mysql-connector-java-5.1.22-bin.jar</driver>
    <pool>
        <max-pool-size>30</max-pool-size>
    </pool>
    <security>
        <user-name>root</user-name>
        <password></password>
    </security>
</datasource>
</datasources>

Com abans amb el driver, si tenim una consola oberta amb els logs de funcionament del JBoss veurem com fa el deploy del datasource. A la consola d’administració podrem vereu el datasource que tot just hem creat i en podrem provar la connexió amb la base de dades.

I ara canvio el persistence.xml per un persistence.xml que apunti al meu datasource. Fixem-nos que les properties canvien. Pot ser interessant consultar la següent taula de Hibernate Dialects. En general, no està de més fer-li un cop d’ull a la documentació d’Hibernate.

Vet aquí el persistence.xml que poso a /home/albert/workspace/wk-java/provaseam/src/main/resources/META-INF/

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="test">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/datasources/mysqldev</jta-data-source>
        <properties>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value=""/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
        </properties>
    </persistence-unit>  
</persistence>

I ara ja puc generar les classes amb les hibernate tools:

[provaseam] provaseam $ generate-entities
 ? Specify the URL for the JDBC connection. [jdbc:h2:tcp://localhost/sakila] jdbc:mysql://localhost/prova_seam
 ? Enter the user name for JDBC connection. [null] root
 ? Enter the password for JDBC connection. 
 ? Enter the dialect to use for the datasource. [org.hibernate.dialect.H2Dialect] org.hibernate.dialect.MySQLDialect
 ? Specify the class name for the JDBC driver for the datasource. [org.h2.Driver] com.mysql.jdbc.Driver
 ? Enter the path in the local file system to the jar file containing the JDBC driver. [null] /home/albert/jdbc-drivers/mysql.jar
 ? In which package you'd like to generate the entities, or enter for default: [com.example.provaseam.model] com.stsoftlliure.provaseam.model
Found 7 tables in datasource
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/LlibreAutor.java
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/LlibreTematica.java
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Tematica.java
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Autor.java
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Llibre.java
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Ubicacio.java
Generated java at /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Format.java
Generated 7 java files.
[provaseam] provaseam $

Ara genero l’esquelet de l’aplicació web:

[provaseam] provaseam $ scaffold setup
 ? No scaffold type was selected, use default [JavaServer Faces]? [Y/n] 
 ? Scaffold provider [faces] is not installed. Install it? [Y/n] 
 ? Facet [forge.maven.WebResourceFacet] requires packaging type(s) [war], but is currently [jar]. Update packaging? (Note: this could deactivate other plugins in your project.) [Y/n] 
***SUCCESS*** Installed [forge.maven.WebResourceFacet] successfully.
***SUCCESS*** Installed [forge.spec.ejb] successfully.
***SUCCESS*** Installed [forge.spec.cdi] successfully.
***SUCCESS*** Installed [forge.spec.servlet] successfully.
***SUCCESS*** Installed [forge.spec.jsf.api] successfully.
***SUCCESS*** Installed [faces] successfully.
 ? Create scaffold in which sub-directory of web-root? (e.g. http://localhost:8080/provaseam/DIR) [/] 
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp
Wrote /home/albert/workspace/wk-java/provaseam/pom.xml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/WEB-INF/beans.xml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/WEB-INF/faces-config.xml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/favicon.ico
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/scaffold/paginator.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/scaffold/pageTemplate.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/index.html
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/index.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/error.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/add.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/bootstrap.css
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/false.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/favicon.ico
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/forge-logo.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/forge-style.css
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/remove.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/search.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/true.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/jboss-community.png
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/WEB-INF/web.xml
[provaseam] provaseam $

Genero les classes de l’aplicació bàsica, a partir del model. L’aplicació bàsica és del tipus CRUD (Create, Read, Update i Delete), és dir, les quatre opcions mínimes de tot manteniment de taules.

[provaseam] provaseam $ scaffold from-entity com.stsoftlliure.provaseam.model.* --overwrite
***INFO*** Using currently installed scaffold [faces]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.Autor]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.Format]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.Llibre]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.LlibreAutor]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.LlibreTematica]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.Tematica]
***SUCCESS*** Generated UI for [com.stsoftlliure.provaseam.model.Ubicacio]
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/AutorBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/autor/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/autor/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/autor/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/resources/scaffold/pageTemplate.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/ViewUtils.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/WEB-INF/classes/META-INF/forge.taglib.xml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Autor.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/FormatBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/format/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/format/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/format/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Format.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/LlibreBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibre/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibre/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibre/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Llibre.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/LlibreAutorBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibreAutor/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibreAutor/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibreAutor/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/LlibreAutor.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/LlibreTematicaBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibreTematica/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibreTematica/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/llibreTematica/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/LlibreTematica.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/TematicaBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/tematica/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/tematica/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/tematica/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Tematica.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/example/provaseam/view/UbicacioBean.java
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/ubicacio/create.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/ubicacio/view.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/webapp/ubicacio/search.xhtml
Wrote /home/albert/workspace/wk-java/provaseam/src/main/java/com/stsoftlliure/provaseam/model/Ubicacio.java
[provaseam] provaseam $

Anem molt bé. En principi, la aplicació ja està llesta per compilar i desplegar. Però en realitat no. Si compilem i despleguem ara, obtindrem l’aplicació, però en provar a introduir valors a les taules obtindré errors. (si no us ho creieu simplement salteu-vos aquest pas i compileu i desplegueu ara). Com anuncia el link http://www.mastertheboss.com/forge/reverse-engineer-your-db-schema-using-jboss-forge alguna cosa no ha acabat d’anar a l’hora. Els errors que obtindreu si decidiu provar amb l’aplicació tal comestà deixen aquesta traça:

22:51:02,392 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/provaseam].[FacesServlet]] (http--127.0.0.1-8080-2) Servlet.service() for servlet FacesServlet threw exception: org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.stsoftlliure.provaseam.model.Format. Expected: class java.lang.Integer, got class java.lang.Long

Els ID que ha generat el plugin de les hibernate-tools per a les entitats ha estat del tipus Integer; El codi al que em refereixo és aquest:

A la carpeta

src/main/java/com/stsoftlliure/provaseam/model/
    LlibreAutor.java
    LlibreTematica.java
    Tematica.java
    Autor.java
    Llibre.java
    Ubicacio.java
    Format.java

Però resulta que el codi que han generat el pas d’scaffolding (la línia “scaffold from-entity com.stsoftlliure.provaseam.model.* –overwrite”), que ha generat el següent codi java:

src/main/java/com/example/provaseam/view/
    ViewUtils.java
    AutorBean.java
    FormatBean.java
    LlibreBean.java
    LlibreAutorBean.java
    LlibreTematicaBean.java
    TematicaBean.java 
    UbicacioBean.java

Tenim que els ID que referencien els objectes són del tipus Long.

Per exemple, mirem les classes Autor i AutorBean

Autor

package com.stsoftlliure.provaseam.model;

// Generated 19/11/2012 23:16:54 by Hibernate Tools 3.4.0.CR1

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * Autor generated by hbm2java
 */
@Entity
@Table(name = "autor", catalog = "prova_seam")
public class Autor implements java.io.Serializable
{

   private Integer id;
   private String autor;
   private Set llibreAutors = new HashSet(0);
   private Set llibreTematicas = new HashSet(0);

   public Autor()
   {
   }

   public Autor(String autor)
   {
      this.autor = autor;
   }

   public Autor(String autor, Set llibreAutors, Set llibreTematicas)
   {
      this.autor = autor;
      this.llibreAutors = llibreAutors;
      this.llibreTematicas = llibreTematicas;
   }

   @Id
   @GeneratedValue(strategy = IDENTITY)
   @Column(name = "id", unique = true, nullable = false)
   public Integer getId()
   {
      return this.id;
   }

   public void setId(Integer id)
   {
      this.id = id;
   }

...

AutorBean

package com.example.provaseam.view;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.ejb.Stateful;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import com.stsoftlliure.provaseam.model.Autor;

/**
 * Backing bean for Autor entities.
 *
* This class provides CRUD functionality for all Autor entities. It focuses * purely on Java EE 6 standards (e.g. @ConversationScoped for * state management, PersistenceContext for persistence, * CriteriaBuilder for searches) rather than introducing a CRUD framework or * custom base class. */ @Named @Stateful @ConversationScoped public class AutorBean implements Serializable { private static final long serialVersionUID = 1L; /* * Support creating and retrieving Autor entities */ private Long id; public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } ...

En principi, doncs, i atenent al missatge d’error, tenim dues possibilitats: o corregir les classes generades amb les hibernate tools (canviant integer per long), o corregir les generades amb l’scaffolding (canviant Long per Integer), ara bé, resulta que a les classes generadese amb l’scaffolding, les NomClasseBean, es fa servir l’Id del tipus Long en altres mètodes d’us intern, com el FindById. Per tant, és millor canviar les classes generades amb Hibernate-tools, on l’Id només apareix un cop.

Per tant, canvio Integer per Long a Autor. Així:


(- Ep! Un moment! havies dit que no caldria escriure ni una línia de codi!
– Bé, estrictament no estem escrivint cap línia de codi, només estic modificant-ne alguna…Sí, d’acord… és una mica lleig)

package com.stsoftlliure.provaseam.model;

// Generated 19/11/2012 23:16:54 by Hibernate Tools 3.4.0.CR1

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * Autor generated by hbm2java
 */
@Entity
@Table(name = "autor", catalog = "prova_seam")
public class Autor implements java.io.Serializable
{

   private Long id;
   private String autor;
   private Set llibreAutors = new HashSet(0);
   private Set llibreTematicas = new HashSet(0);

   public Autor()
   {
   }

   public Autor(String autor)
   {
      this.autor = autor;
   }

   public Autor(String autor, Set llibreAutors, Set llibreTematicas)
   {
      this.autor = autor;
      this.llibreAutors = llibreAutors;
      this.llibreTematicas = llibreTematicas;
   }

   @Id
   @GeneratedValue(strategy = IDENTITY)
   @Column(name = "id", unique = true, nullable = false)
   public Long getId()
   {
      return this.id;
   }

   public void setId(Long id)
   {
      this.id = id;
   }

...

Un cop he fet els canvis a les classes d’entitat (Autor.java, Format.java, Llibre.java, Tematica.java, Ubicacio.java, LlibreAutor.java i LlibreTematica.java), puc compilar i deployar.

Per a fer-ho, primer instal·laré al forge el plugin de control del JBoss AS 7.1:

[provaseam] provaseam $ forge install-plugin jboss-as-7
Connecting to remote repository [https://raw.github.com/forge/plugin-repository/master/repository.yaml]... connected!
***INFO*** Preparing to install plugin: jboss-as-7
***INFO*** Checking out plugin source files to [/tmp/forgetemp3143468914443124072/repo] via 'git'
***INFO*** Switching to branch/tag [refs/heads/1.0.5.Final]
***INFO*** Invoking build with underlying build system.
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building plugin-jboss-as7 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ plugin-jboss-as7 ---
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ plugin-jboss-as7 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ plugin-jboss-as7 ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 23 source files to /tmp/forgetemp3143468914443124072/repo/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ plugin-jboss-as7 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/forgetemp3143468914443124072/repo/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ plugin-jboss-as7 ---
[INFO] Not compiling test sources
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ plugin-jboss-as7 ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ plugin-jboss-as7 ---
[INFO] Building jar: /tmp/forgetemp3143468914443124072/repo/target/plugin-jboss-as7.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.310s
[INFO] Finished at: Sun Nov 18 16:25:25 CET 2012
[INFO] Final Memory: 47M/113M
[INFO] ------------------------------------------------------------------------
***INFO*** Installing plugin artifact.
***SUCCESS*** Installed from [https://github.com/forge/plugin-jboss-as.git] successfully.
Wrote /home/albert/.forge/httpsrawgithubcomforgepluginrepositorymasterrepositoryyaml.yaml

...

Wrote /home/albert/.forge/plugins/org/jboss/as/plugin-jboss-as7/dependencies/1.0.0-SNAPSHOT-b32f7bdd-fc1e-4d44-91ab-18119749258b/module.xml
    _____                    
   |  ___|__  _ __ __ _  ___ 
   | |_ / _ \| `__/ _` |/ _ \  \\
   |  _| (_) | | | (_| |  __/  //
   |_|  \___/|_|  \__, |\___| 
                   |___/      

JBoss Forge, version [ 1.1.2.Final ] - JBoss, by Red Hat, Inc. [ http://jboss.org/forge ]
The following plugins have been activated: [as7]
[provaseam] provaseam $

Compilo…

[provaseam] provaseam $ build
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building provaseam 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-war-plugin/2.1.1/maven-war-plugin-2.1.1.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-war-plugin/2.1.1/maven-war-plugin-2.1.1.pom (7 KB at 38.6 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-war-plugin/2.1.1/maven-war-plugin-2.1.1.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-war-plugin/2.1.1/maven-war-plugin-2.1.1.jar (76 KB at 657.7 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.3.1/maven-install-plugin-2.3.1.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.3.1/maven-install-plugin-2.3.1.pom (5 KB at 83.7 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.3.1/maven-install-plugin-2.3.1.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.3.1/maven-install-plugin-2.3.1.jar (23 KB at 352.8 KB/sec)
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ provaseam ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ provaseam ---
[INFO] Compiling 15 source files to /home/albert/workspace/wk-java/provaseam/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ provaseam ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ provaseam ---
[INFO] Not compiling test sources
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ provaseam ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ provaseam ---
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.2/plexus-archiver-1.2.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.2/plexus-archiver-1.2.pom (2 KB at 32.2 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-io/1.0.1/plexus-io-1.0.1.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-io/1.0.1/plexus-io-1.0.1.pom (2 KB at 20.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.pom
Downloaded: http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.pom (12 KB at 166.6 KB/sec)
Downloading: http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream-parent/1.3.1/xstream-parent-1.3.1.pom
Downloaded: http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream-parent/1.3.1/xstream-parent-1.3.1.pom (14 KB at 210.2 KB/sec)
Downloading: http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.pom
Downloaded: http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.pom (2 KB at 33.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-filtering/1.0-beta-2/maven-filtering-1.0-beta-2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-filtering/1.0-beta-2/maven-filtering-1.0-beta-2.pom (4 KB at 64.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-shared-components/10/maven-shared-components-10.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-shared-components/10/maven-shared-components-10.pom (9 KB at 84.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-parent/9/maven-parent-9.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/maven-parent/9/maven-parent-9.pom (33 KB at 433.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/1.5.6/plexus-utils-1.5.6.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/1.5.6/plexus-utils-1.5.6.pom (6 KB at 101.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus/1.0.12/plexus-1.0.12.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus/1.0.12/plexus-1.0.12.pom (10 KB at 106.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-interpolation/1.6/plexus-interpolation-1.6.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-interpolation/1.6/plexus-interpolation-1.6.pom (3 KB at 55.7 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.2/plexus-archiver-1.2.jar
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-io/1.0.1/plexus-io-1.0.1.jar
Downloading: http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.jar
Downloading: http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar
Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-filtering/1.0-beta-2/maven-filtering-1.0-beta-2.jar
Downloaded: http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar (25 KB at 135.4 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-io/1.0.1/plexus-io-1.0.1.jar (50 KB at 248.4 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-filtering/1.0-beta-2/maven-filtering-1.0-beta-2.jar (33 KB at 150.7 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.2/plexus-archiver-1.2.jar (178 KB at 595.9 KB/sec)
Downloaded: http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.jar (422 KB at 934.1 KB/sec)
[INFO] Packaging webapp
[INFO] Assembling webapp [provaseam] in [/home/albert/workspace/wk-java/provaseam/target/provaseam]
[INFO] Processing war project
[INFO] Copying webapp resources [/home/albert/workspace/wk-java/provaseam/src/main/webapp]
[INFO] Webapp assembled in [248 msecs]
[INFO] Building war: /home/albert/workspace/wk-java/provaseam/target/provaseam.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] 
[INFO] --- maven-install-plugin:2.3.1:install (default-install) @ provaseam ---
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.pom (2 KB at 19.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-components/1.1.7/plexus-components-1.1.7.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-components/1.1.7/plexus-components-1.1.7.pom (5 KB at 71.4 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus/1.0.8/plexus-1.0.8.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus/1.0.8/plexus-1.0.8.pom (8 KB at 117.7 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8.pom (8 KB at 150.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.jar
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.jar (12 KB at 160.8 KB/sec)
[INFO] Installing /home/albert/workspace/wk-java/provaseam/target/provaseam.war to /home/albert/.m2/repository/com/example/provaseam/provaseam/1.0.0-SNAPSHOT/provaseam-1.0.0-SNAPSHOT.war
[INFO] Installing /home/albert/workspace/wk-java/provaseam/pom.xml to /home/albert/.m2/repository/com/example/provaseam/provaseam/1.0.0-SNAPSHOT/provaseam-1.0.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.090s
[INFO] Finished at: Sun Nov 18 16:28:22 CET 2012
[INFO] Final Memory: 65M/155M
[INFO] ------------------------------------------------------------------------
[provaseam] provaseam $

Configuro el plugin per a fer-lo servir amb el meu JBoss local

[provaseam] provaseam $ as7 setup
 ? The Java Home '/home/albert/jdk1.6.0_30' is already set, would you like to override it? [y/N] 
 ? A default version of 7.1.1.Final is already set, would you like to override it? [y/N] 
 ? Enter path for JBoss AS or leave blank to download: /home/albert/jboss-as-7.1.1.Final
***SUCCESS*** Installed [AS7ServerFacet] successfully.
[provaseam] provaseam $

i, finalment, faig el deploy:

[provaseam] provaseam $ as7 deploy
The deployment operation (FORCE_DEPLOY) was successful.
[provaseam] provaseam $

I ja només em queda provar l’aplicació que acabo de deplegar. Per això apunto el navegador a http://localhost:8080/provaseam i, efectivament obtinc una senzilla aplicació de biblioteca. Basada en un manteniment de taules. Com he dit abans, el resultat és força espectacular. L’esforç ha estat en definir i crear correctament les taules, els índexos i les relacions entre les taules (i arreglar alguns petits desajustos que Seam Forge ha deixat).

A la consola d’administració del JBoss podem veure els desplegaments realitzats:

Vet aquí la pantalla inicial de l’aplicació:

Per cert, com a extra, des de la pantalla inicial es pot enviar un tuit (feu click al link Get Excited!) per anunciar al món que ja hem fet la nostra primera aplicació amb Seam 3 i Forge! No m’hi estic pas!

Les relacions entre taules es resolen amb camps desplegables. Entro alguns formats i ubicacions i creo un llibre:

format:

ubicacio:

vaig a crear un llibre:

El llibre creat:

Cal reconèixer que és força lletja la representació dels formats i les ubicacions. Caldria corregir les visualitzacions. En tot cas, podem seguir l’enllaç, per exemple, de les ubicacions per obtenir tots els llibres que estan al prestatge de l’estudi:

En resum, una generació força automatitzada d’una aplicació de manteniment de taules (un CRUD) feta amb l’eina Seam Forge, amb la versió de Framework Seam 3.1. desplegada sobre un JBoss 7.1.

Val a dir que després de provar això mateix amb el seam-gen del framework Seam 2.3.0 un es queda una mica decebut. Fa l’efecte com si s’hagués anat enrere. En realitat, sembla que la qüestió és que s’ha iniciat un camí nou amb el Forge. Encara més, a la mateixa definició de Seam3 que fan a la web www.seamframeworkorg/seam3 han fet desaparèixer la paraula framework per parlar de conjunt de mòduls i eines: “Seam3 is a collection of modules and developer tooling tailored for Java EE 6 application development, with CDI as the central piece.”i encara una mica després, referint-se a Forge el defineixen com “JBoss Forge is an incremental project enhancement API and shell.” De fet, la forma de treballar amb Forge té més a veure amb definir des del forge les entitats i les accions, que no pas fer una enginyeria inversa d’un model existent. O sigui que hem fet servir el Forge no de la forma com “estava prevista” (en canvio seam-gen de Seam2, sí que estava més pensat per a això).

La definició anterior de Seam, a més, feia aparèixer la sigla màgica CDI. CDI, Context and Dependency Injection. CDI i Java EE 6 (HTML | PDF). No és ara el moment per parlar-ne. Però caldrà fer-ho.

Al proper post faré de nou l’aplicació de Biblioteca amb el Seam 2 i el seam-gem. Tot i que es tracta de la versió anterior manté tot l’interès ja que, en cap cas, es pot considerar que Seam 2 sigui obsolet. De fet, com he comentat abans, per una qüestió de compatibilitat amb versions de JDK i Java EE podem ben bé trobar-nos amb que no es pugui fer servir la versió Seam 3, que demana últimes versions.

HTML5. WebSockets amb Jetty

Continuant amb l’exploració de l’HTML5, avui parlo dels WebSockets. I per a il·lustrar-lo, faré un petit exercici amb websockets fent servir el servidor java Jetty.

La idea de l’exemple l’he tret del projecte http://code.google.com/p/phpwebsocket/. El client html és pràcticament idèntic. La part del servidor, en el meu exemple, l’he implementada amb Java; estant en PHP en l’original.

Els WebSockets permeten realitzar la comunicació bidireccional i full-duplex sobre un canal TCP, entre la pàgina web i el servidor de forma molt més eficient que amb l’esquema de petició-resposta de l’HTML, o que la comunicació amb AJAX o, més explí­citament, mitjançant Comet.

El protocol WebSocket es descriu a la RFC6455.

A dia d’avui, aquest protocol es troba implementat per les darreres versions dels principals navegadors:  Chrome, Internet Explorer, Firefox i Safari.

A més, el servidor ha de soportar també els WebSockets de servidor.

En si mateix el protocol recorda una crida HTTP, però no ho és:

Exemple de petició des del client:

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

Exemple de resposta del servidor:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Des del punt de vista del desenvolupament parlem, doncs, de clients i servidors de WebSockets.
HTML5 ofereix una API (normalitzada pel W3C) per a desenvolupar clients d’aplicacions basades en WebSocket.

Per la banda del servidor, els llenguatges més utlitzats, com Java o php aporten classes, mètodes i funcions per a crear i utilitzar websockets de servidor.

Sobre servidors Java: jWebSocket, Apache Tomcat i Jetty també tenen implementacions de sockets de servidor.
Sobre servidors PHP: phpWebSockets

Podem trobar implementacions de servidor WebSocket amb altres llenguatges. Per exemple amb Python una cerca ens proporciona: pywebsocket, o WebSocket for Python (ws4py).

Per a fer un experiment senzill utilitzaré el servidor de java servlets Jetty (versió 8) que proporciona una implementació java de WebSockets de servidor. Podeu revisar aquest article: Jetty WebSocket Server, i aquest altre: Jetty/Feature/WebSockets.
Per descomptat, el javadoc: http://download.eclipse.org/jetty/stable-8/apidocs/

Per a provar els WebSockets he creat un petit projecte amb Eclipse, amb la següent estructura de carpetes:

A la carpeta src hi creo el package java amb la següent classe: com.stsoftlliure.proves.html5.websockets.ProvaWebSocket

Fitxer ProvaWebSocket.java:

package com.stsoftlliure.proves.html5.websockets;

import java.io.IOException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.eclipse.jetty.util.log.Log;
public class ProvaWebSocket extends WebSocketServlet  {

   private static final long serialVersionUID = 8442112605629206192L;


    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
    throws ServletException ,IOException  {
      response.getOutputStream().println("aquesta és la resposta");
      Log.info("[doGet] No fa res");
    }

    public WebSocket doWebSocketConnect(HttpServletRequest request,
                                        String protocol) {
      Log.info("[doWebSocketConnect] entra a doWebSocketConnect");
      return new ProvaServerWebSocket();
    }

    // inner class que implementa la interface WebSocket
    class ProvaServerWebSocket implements WebSocket.OnTextMessage {
      // la connexió
      Connection wsConn;
      Date dataInici;

      public ProvaServerWebSocket() {
         dataInici = new Date();
      }

      @Override
      public void onClose(int arg0, String arg1) {
         // TODO Auto-generated method stub
         Log.info("[onClose] arg0:" + arg0 + "; arg1: " + arg1);
         wsConn.close(arg0, arg1);
      }

      @Override
      public void onOpen(Connection wsConn) {
         this.wsConn = wsConn;
         try {
            Log.info("[onOpen] wsConn:" + wsConn);
            wsConn.sendMessage("Estic connectat!");
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }

      @Override
      public void onMessage(String sMessage) {
         // TODO Auto-generated method stub
         Log.info("[onMessage] Missatge rebut: " + sMessage);
            // fa el tractament:
         // to do
         // help, hola, nom, edat, data, adeu
 
         try {
            if (sMessage.trim().equalsIgnoreCase("help")) {
               wsConn.sendMessage("ordres acceptades: help, hola, nom, edat, data, adeu");
            }

            if (sMessage.trim().equalsIgnoreCase("hola")) {
               wsConn.sendMessage("Hola! benvingut al servidor WebSocket!");
            }

            if (sMessage.trim().equalsIgnoreCase("nom")) {
               wsConn.sendMessage("Jo em dic ProvaWebSocket.class");
            }

            if (sMessage.trim().equalsIgnoreCase("edat")) {
               wsConn.sendMessage("En funcionament des de: " + dataInici);
            }

            if (sMessage.trim().equalsIgnoreCase("data")) {
               wsConn.sendMessage("ara són les: " + (new Date()));
            }

            if (sMessage.trim().equalsIgnoreCase("adeu")) {
               wsConn.sendMessage("Adéu siau!");
               wsConn.close();
            }
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }
   }
}

Algunes remarques a la classe anterior:

– La classe és un servlet del tipus nou WebSocketServlet.
– Els WebSocketServlet implementen el doGet. El puc invocar directament i obtinc una resposta HTML. Però la funcionalitat apareix amb el nou mètode doWebSocketConnect. A doWebSocketConnect és on treballa el socket de servidor.
-Quan es rep una nova connexió, el mètode doWebSocketConnect el que fa és instanciar la inner class ProvaServerWebSocket, que implementa la interface WebSocket.OnTextMessage.
– La interface WebSocket té altres subinterfaces, a més de la OnTextMessage, per a tractar altres tipus de connexions. Les subinterfaces de WebSocket són: WebSocket.OnBinaryMessage, WebSocket.OnControl, WebSocket.OnFrame, WebSocket.OnTextMessage.
– en la connexió onOpen, obtinc l’objecte Connection wsConn. Que serà propiament el canal full duplex entre servidor i client.
– El tractament d’un missatge rebut el faig en implementar el mètode public void onMessage(String sMessage). sMessage és el missatge rebut des del client. Les respostes al client s’envien mitjançant l’objecte Connection wsConn obtingut en l’event onOpen. En aquest exemple, simplement analitzo quin missatge rebo i, depenent del missatge, envio una o altre resposta al client.
– A la sortida onClose, tanco la connexió

Senzill i net, oi?

L’altre part és la pàgina del client. Una pàgina HTML5 que fa servir websockets ha d’implementar els mètodes que responen als esdeveniments de connexió, alliberament, recepció i enviament de missatges al servidor. Vet aquí la pàgina HTML que dialoga amb el servidor:

Fitxer prova.html

<html>
<head>
<title>WebSocket</title>

<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
</style>

<script>
var socket;

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

function quit(){
  log("Adéu!");
  socket.close();
  socket=null;
}

// Utilities
function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="<br>"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>

</head>
<body onload="init()">
 <h3>WebSocket</h3>
 <div id="log"></div>
 <input id="msg" type="textbox" onkeypress="onkey(event)"/>
 <button onclick="send()">Send</button>
 <button onclick="quit()">Quit</button>
 <div>Commands: help, hola, nom, edat, data, adeu</div>
</body>
</html>

Alguns comentaris: com es veu, la pàgina és senzilla, la clau del funcionament està al mètode init

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

El qual crea l’objecte socket, el connecta al host, adonem-nos del protocol “ws”; i enllaça els esdeveniments del socket: onopen, onmessage, onclose amb les funcions que els tracten.

L’enviament de missatges al servidor es realitza amb la funció send, que invoca al mètode send de l’objecte socket.

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

Amb l’anterior ja tinc tot el codi. Poso la pàgina prova.html a la carpeta webcontent del projecte Eclipse. A més, en la carpeta webcontent també hi posaré la carpeta WEB-INF, amb una subcarpeta classes buida, la carpeta lib amb les llibreries que em calen i el web.xml.

En resum, a webcontent hi trobo:

webcontent/
    prova.html
    WEB-INF/
        classes/
        lib/
            jetty-util-8.1.2.v20120308.jar
            jetty-websocket-8.1.2.v20120308.jar
        web.xml

Les llibreries a la carpeta lib corresponen a la versió 8 del Jetty i es poden trobar a la seva carpeta lib. He vist que cal incloure-les al war. Em pensava que es referenciarien automàticament al fer deploy al Jetty, però no ha estat així.

El web.xml és força estàndar:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<display-name>tailor</display-name>
	<servlet>
		<servlet-name>WebSocket</servlet-name>
		<servlet-class>com.stsoftlliure.proves.html5.websockets.ProvaWebSocket</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>WebSocket</servlet-name>
		<url-pattern>/servlet/*</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>prova.html</welcome-file>
	</welcome-file-list>
</web-app>

Si de cas, remarcar el mapeig de /servlet/* a WebSocket. Aquesta és l’explicació de
var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;
a la funció init.

Amb Jetty, a més, m’ha calgut establir el “context.” Al projecte, el fitxer de context l’he guardat a la carpeta homònima. És el següent fitxer.

websockets2.xml:

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/websockets2</Set>
  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/websockets2.war</Set>
</Configure>

Compte amb els salts de línia. El fitxer XML de context potser no queda “bonic” tal com està, però us podeu trobar amb problemes estranys si afegiu salts de línia.

Amb el fitxer anterior li estem dient quin és el war que ha de desplegar i quin nom tindrà el context. Amb aquest fitxer de context acabem de completar l’explicació del

var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;

Ara és evident:

ws:// – Protocol websocket. Amb websockets segurs, serà wss://
localhost:8080 – Host i port del servidor websocket
websockets2 – El context, o l’aplicació web.
servlet/WebSocket – el Servlet servidor de WebSocket

Finalment, per automatitzar el muntatge i ddesplegament al servidor jetty local de l’apliació, faig servir el següent build.xml d’ant:

<project name="websocket" default="deploy" basedir=".">
  <description>
  proves amb websockets 2
  </description>

  <!-- estableix propietats globals -->
  <property name="src" location="src"/>
  <property name="bin" location="bin"/>
  <property name="war" location="war"/>
  <property name="webcontent" location="webcontent" />
  <property name="context" location="context" />
  <property name="build" location="build"/>
  <property name="lib" location="/home/albert/jetty/lib" />
  <property name="deploy" location="/home/albert/jetty/webapps" />
  <property name="deploy-context" location="/home/albert/jetty/contexts" />


  <target name="init">
    <!-- time stap -->
    <tstamp/>
    <!-- Crea el directory bin utilitzat en la compilació -->
    <mkdir dir="${bin}"/>
  </target>


  <target name="compile" depends="init" description="compila els fitxers font" >
    <!-- Compila les fonts java de ${src} en ${build} -->
    <javac srcdir="${src}" destdir="${bin}">
      <classpath>
       <pathelement location="${lib}/jetty-websocket-8.1.2.v20120308.jar"/>
       <pathelement location="${lib}/servlet-api-3.0.jar"/>
       <pathelement location="${lib}/jetty-util-8.1.2.v20120308.jar"/>
      </classpath>
   </javac>
  </target>

  <target name="build" depends="compile" description="genera el build">
    <!-- Crea el directori de distribució -->
    <delete dir="${build}" />
    <mkdir dir="${build}"/>

    <!--  copia webcontent a build -->
    <copy todir="${build}" >
        <fileset dir="${webcontent}" />
    </copy>
    
    <!--  copia bin a classes -->
    <copy todir="${build}/WEB-INF/classes">
        <fileset dir="${bin}" />
    </copy>
  </target>
     
  <target name="war" depends="build" description="genera el war">   
    <delete file="${war}/websockets2.war" />
    <jar jarfile="${war}/websockets2.war" basedir="${build}"> 
      <include name="**/*"/>
    </jar>
  </target>
  	
  <target name="deploy" depends="war" description="deploy del war i del context">
    <delete file="${deploy}/websockets2.war" />
    <delete file="${deploy-context}/websockets2.xml" />
  	<copy file="${war}/websockets2.war" todir="${deploy}" />
  	<copy file="${context}/websockets2.xml" todir="${deploy-context}" /> 
  </target>

</project>

Aleshores, ses d’Eclipse puc executar el build.xml que compila la classe, construeix el war i el copia a la carpeta webapps del meu Jetty local.

Per provar l’aplicació només en cal engegar el Jetty i obrir el navegador amb suport de websockets (en el meu cas, el Chrome) i apuntar-lo a l’adreça http://localhost:8080/websockets2

I el resultat és el següent.

I, efectivament, puc mantindre el diàleg amb el servidor: