Guia Uso MAVEN
Guia Uso MAVEN
Índice
1 Introducción a Maven...................................................................................................3
1.1 Instalando Maven.....................................................................................................3
1.2 El proceso de build de un proyecto..........................................................................4
1.3 Estructura de un proyecto Maven............................................................................ 5
1.4 POM: Project Object Model.................................................................................... 6
1.5 Repositorios............................................................................................................. 8
1.6 Versiones................................................................................................................10
1.7 Gestión de dependencias........................................................................................11
1.8 El ciclo de vida de Maven......................................................................................12
1.9 Ejecutando tests..................................................................................................... 13
1.10 Plugins y goals..................................................................................................... 14
1.11 Creando proyectos de cero: arquetipos................................................................ 16
1.12 Usando Maven en Eclipse.................................................................................... 19
1.13 Para saber más......................................................................................................22
2 Caso de estudio...........................................................................................................23
2.1 Introducción........................................................................................................... 23
2.2 Ingeniería de Requisitos.........................................................................................23
2.3 Análisis y Diseño OO............................................................................................ 29
3 Implementación.......................................................................................................... 36
3.1 Construcción del proyecto Maven......................................................................... 36
3.2 Ficheros POM........................................................................................................ 41
3.3 Primera iteración....................................................................................................43
3.4 Enumeraciones.......................................................................................................50
3.5 Implementar y completar las clases de entidad......................................................50
3.6 Gestión de las Excepciones....................................................................................51
3.7 Implementación de las Reglas de Negocio............................................................ 52
3.8 Tests....................................................................................................................... 53
3.9 Resumen.................................................................................................................55
2
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
1. Introducción a Maven
3
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Maven es una aplicación Java, y utiliza la variable JAVA_HOME para encontrar el path del
JDK. También es necesario añadir el directorio bin de Maven al PATH del sistema. Se
pueden definir en el fichero de configuración .bashrc de un usuario o en el fichero del
sistema /etc/bashrc para todos los usuarios. En nuestro caso hemos modificado el único
usuario de la MV especialista. El código que hemos añadido ha sido este:
export JAVA_HOME=/opt/jdk1.6.0_27
export MAVEN_HOME=/opt/apache-maven-3.0.3
export PATH=${PATH}:${MAVEN_HOME}/bin
Los que hemos programado en C recordamos los ficheros Makefile en los que se
especificaban las dependencias entre los distintos elementos de un proyecto y la secuencia
de compilación necesaria para generar una librería o un ejecutable. En Java, el desarrollo
de aplicaciones medianamente complejas es más complicado que en C. Estamos
obligados a gestionar un gran número de recursos: código fuente, ficheros de
configuración, librerías externas, librerías desarrolladas en la empresa, etc. Para gestionar
este desarrollo es necesario algo de más nivel que las herramientas que proporciona Java
(javac, jar, rmic, java, etc.)
¿En qué consiste el proceso de compilación y empaquetado en Java?. Básicamente en
construir lo que Maven llama un artefacto (terminología de Maven que significa fichero)
a partir de un proyecto Java definido con una estructura propia de Maven (apartado
siguiente). Los posibles artefactos en los que podemos empaquetar un programa Java son:
• Fichero JAR: librería de clases o aplicación standalone. Contiene clases Java
compiladas (.class) organizadas en paquetes, ficheros de recursos y (opcionalmente)
otros ficheros JAR con librerías usadas por las clases. En las aplicaciones enterprise,
los EJB también se empaquetan en ficheros JAR que se despliegan en servidores de
aplicaciones.
• Fichero WAR: aplicación web lista para desplegarse en un servidor web. Contiene un
conjunto de clases Java, librerías, ficheros de configuración y ficheros de distintos
formatos que maneja el servidor web (HTML, JPG, etc.)
• Fichero EAR: aplicación enterprise que se despliega en un servidor de aplicaciones.
Contiene librerías, componentes EJB y distintas aplicaciones web (ficheros WAR).
Además, el ciclo de desarrollo de un proyecto es más complejo que esta construcción, ya
que es necesario realizar un conjunto de tareas adicionales como gestionar las
dependencias con librerías externas, integrar el código en repositorios de control de
versiones (CVS, subversion o Git), lanzar tests o desplegar la aplicación en algún servidor
de aplicaciones.
Podría pensarse que los entornos de desarrollo (Eclipse o Netbeans) pueden dar una
buena solución a la complejidad del proceso de construcción, pero no es así. Son
imprescindibles para el desarrollo, pero no ayudan demasiado en la construcción del
4
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
5
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Los ficheros de recursos son ficheros leídos desde la aplicación. Tal y como hemos visto
en el módulo JHD, estos recursos deben estar contenidos en la raíz del JAR para poder
acceder a ellos utilizando el método getResourceAsStream de la clase Class:
InputStream in =
getClass().getResourceAsStream("/datos.txt");
Maven se encarga de colocar los recursos en la raíz del JAR al empaquetar el proyecto.
La primera definición project xmlns es común para todos los ficheros pom.xml. En ella
se declara el tipo de esquema XML y la dirección donde se encuentra el fichero de
esquema XML. Se utiliza para que los editores de XML puedan validar correctamente el
fichero. Esta sintaxis depende de la versión de Maven que se esté utilizando.
Después aparece la identificación del proyecto, en la que hay que definir el grupo que
desarrolla el proyecto (groupId), el nombre del artefacto que genera el proyecto
(artifactId), el tipo de empaquetamiento (packaging) y su versión (version). Estos campos
representan las denominadas coordenadas del proyecto (hablaremos de ello más
adelante). En nuestro caso son:
org.especialistajee.proyint:jbib-modelo:jar:2011
Por último, hay que definir el nombre lógico del proyecto (name) y una URL asociada al
mismo url.
6
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
A continuación viene la definición de las dependencias del proyecto: librerías de las que
dependen el proyecto. En nuestro caso:
• JUnit: junit:junit:4.8.1:jar
• Log4j: log4j:log4j:1.2.14:jar
• Commons Logging: commons-logging:commons-logging:1.1.1:jar
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
Por último, definimos algunas características de los procesos de Maven que construyen el
proyecto, definiendo parámetros para los pluging de Maven que se encargan de
ejecutarlos.
En nuestro caso, definimos el nivel de compilación de Java, necesario para que no haya
conflicto al importar el proyecto en Eclipse. Es interesante hacer notar que el plugin se
identifica de una forma similar al proyecto, utilizando el identificador del grupo, el de
proyecto y su número de versión.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
7
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
</plugins>
</build>
</project>
Es posible definir herencia entre los ficheros POM utilizando el identificador parent. Es
útil para definir elementos comunes y evitar repetirlos en todos los POM. Por ejemplo,
podríamos definir un POM en el que se declaren todas las librerías que se usan
habitualmente por nuestros proyectos e incluir este POM en todos los proyectos
utilizando herencia.
<project>
<parent>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
</parent>
...
</project>
Maven define un super POM que por defecto es el padre de todos los POM. Allí se
definen elementos comunes como la localización de los repositorios o la estructura de
directorios por defecto de Maven. Se puede encontrar este super POM en el fichero
llamado pom-4.0.0.xml en el JAR maven-2.2.1-uber.jar en el directorio lib de
Maven.
Maven resuelve todas las relaciones de herencia entre POMs y genera internamente un
POM efectivo (effective POM) en el que combinan todos los POMs que afectan a un
determinado proyecto. Este POM efectivo es el que se utiliza para realizar la construcción
del proyecto. Es posible consultar este POM efectivo con el comando:
mvn help:effective-pom
También puede consultarse en la pestaña correspondiente del editor POM del plugin de
Ecipse.
1.5. Repositorios
Los proyectos software están relacionados. Los proyectos necesitan de clases y librerías
definidas en otros proyectos. Esos proyectos pueden ser otros desarrollados por nosotros
en la empresa o librerías open source bajadas de Internet.
La tarea de mantener las dependencias de un proyecto es complicada, tanto para las
dependencias entre nuestros proyectos como las dependencias con otros proyectos open
source disponibles en Internet. Por ejemplo, si queremos utilizar un framework como
Spring, tendremos que descargarnos no sólo los JAR desarrollados en el proyecto, sino
también un buen número de otras librerías open source que usa. Cada librería es un
fichero JAR. ¿Qué pasa si alguna de esas librerías ya las estamos usando y las tenemos ya
descargadas? O, peor aún, ¿Qué pasa si estamos usando otras versiones de esas librerías
en nuestros proyectos? ¿Podremos detectar los posibles conflictos?. Maven obliga a
declarar explícitamente estas dependencias en el fichero POM del proyecto y se encarga
8
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
El artefacto (fichero JAR) generado por el proyecto tiene como nombre email-1.1.jar
Cuando ejecutamos Maven por primera vez veremos que descarga un número de ficheros
del repositorio remoto de Maven. Estos ficheros corresponden a plugins y librerías que
necesita para construir el proyecto con el que estamos trabajando. Maven los descarga de
un repositorio global a un repositorio local donde están disponibles para su uso. Sólo es
necesario hacer esto la primera vez que se necesita la librería o el plugin. Las siguientes
ocasiones ya está disponible en el repositorio local.
La direcciones en las que se encuentran los repositorios son las siguientes:
• Repositorio central: El repositorio central de Maven se encuentra en
http://repo1.maven.org/maven2. Se puede acceder a la dirección con un navegador y
explorar su estructura.
• Repositorio local: El repositorio local se encuentra en el directorio
9
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
${HOME}/.m2/repository.
1.6. Versiones
El estándar de Maven para los números de versiones es muy importante, porque permite
definir reglas para gestionar correctamente las dependencias en caso de conflicto. El
número de versión de un proyecto se define por un número principal, un número menor y
un número incremental. También es posible definir un calificador, para indicar una
versión alfa o beta. Los números se separan por puntos y el calificador por un guión. Por
ejemplo, el número 1.3.5-alpha-03 define un número de versión principal 1, la versión
menor 3, la versión incremental de 5 y el calificador de "alpha-03".
Maven compara las versiones de una dependencia utilizando este orden. Por ejemplo, la
versión 1.3.4 representa un build más reciente que la 1.0.9. Los clasificadores se
comparan utilizando comparación de cadenas. Hay que tener cuidado, porque "alpha10"
es anterior a "alpha2"; habría que llamar al segundo "alpha02".
Maven permite definir rangos de versiones en las dependencias, utilizando los operadores
de rango exclusivos "(", ")" o inclusivos "[", "]". Así, por ejemplo, si queremos indicar
que nuestro proyecto necesita una versión de JUnit mayor o igual de 3.8, pero menor que
4.0, lo podemos indicar con el siguiente rango:
<version>[3.8,4.0)</version>
Si una dependencia transitiva necesita la versión 3.8.1, esa es la escoge Maven sin crear
ningún conflicto.
Es posible también indicar rangos de mayor que o menor que dejando sin escribir ningún
número de versión antes o después de la coma. Por ejemplo, "[4.0,)" representa cualquier
número mayor o igual que 4.0, "(,2.0)" representa cualquier versión menor que la 2.0 y
"[1.2]" significa sólo la versión 1.2 y ninguna otra.
Cuando dos proyectos necesitan dos versiones distintas de la misma librería, Maven
intenta resolver el conflicto, descargándose la que satisface todos los rangos. Si no
utilizamos los operadores de rango estamos indicando que preferimos esa versión, pero
que podríamos utilizar alguna otra. Por ejemplo, es distinto especificar "3.1" y "[3.1]". En
el primer caso preferimos la versión 3.1, pero si otro proyecto necesitara la 3.2 Maven se
10
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
descargaría esa. En el segundo caso exigimos que la versión descargada sea la 3.1. Si otro
proyecto especifica otra versión obligatoria, por ejemplo "3.2", entonces el proyecto no se
compilará.
11
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
proyecto. También es posible especificar otros ámbitos. Por ejemplo test, indicando que
la librería es necesaria para realizar pruebas del proyecto:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<type>jar</type>
<scope>test</scope>
</dependency>
Otros ámbitos posibles son provided y runtime. Una dependencia se define provided
cuando es necesaria para compilar la aplicación, pero que no se incluirá en el WAR y no
será desplegada. Por ejemplo las APIs de servlets:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
Las dependencias runtime son dependencias que no se necesitan para la compilación, sólo
para la ejecución. Por ejemplo los drivers de JDBC para conectarse a la base de datos:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>3.1.13</version>
<scope>runtime</scope>
</dependency>
Una herramienta muy útil es el informe de dependencia. Este informe se genera cuando se
ejecuta el objetivo site. Maven construye un sitio web con información sobre el proyecto
y coloca el informe en el fichero target/dependencies.html:
$ mvn site
12
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
framework de testing disponible. Estos tests no deben necesitar que el proyecto haya
sido empaquetado o desplegado
• package: empaqueta el código compilado del proyecto en un formato distribuible,
como un JAR
• integration-test: procesa y despliega el paquete en un entorno en donde se pueden
realizar tests de integración
• verify: lanza pruebas que verifican que el paquete es válido y satisface ciertos
criterios de calidad
• install: instala el paquete en el repositorio local, para poder ser usado como librería
en otros proyectos locales
• deploy: realizado en un entorno de integración o de lanzamiento, copia el paquete
final en el repositorio remoto para ser compartido con otros desarrolladores y otros
proyectos.
Todas estas fases se lanzan especificándolas como parámetro en el comando mvn. Si
ejecutamos una fase, Maven se asegura que el proyecto pasa por todas las fases
anteriores. Por ejemplo:
$ mvn install
Esta llamada realiza la compilación, los tests, el empaquetado los tests de integración y la
instalación del paquete resultante en el repositorio local de Maven.
Nota:
Para un listado completo de todas las opciones se puede consultar la página de Apache Maven
Introduction to the Build Lifecycle
Los tests de unidad son una parte importante de cualquier metodología moderna de
desarrollo, y juegan un papel fundamental en el ciclo de vida de desarrollo de Maven. Por
defecto, Maven obliga a pasar los tests antes de empaquetar el proyecto. Maven permite
utilizar los frameworks de prueba JUnit y TestNG. Las clases de prueba deben colocarse
en el directorio src/test.
Para ejecutar los tests se lanza el comando mvn test:
especialista@especialista:~/proy-int/jbib-modelo$ mvn test
[INFO] Scanning for projects...
...
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running org.especialistajee.jbib.model.UsuarioTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.082 sec
Running org.especialistajee.jbib.model.OperacionTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.141 sec
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.018 sec
Running org.especialistajee.jbib.model.AvisoTest
13
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Maven compilará los tests si es necesario. Por defecto, los tests deben colocarse en el
directorio src/test siguiendo una estructura idéntica a la estructura de clases del
proyecto. Maven ejecutará todas las clases que comiencen o terminen con Test o que
terminen con TestCase.
Los resultados detallados de los tests se producen en texto y en XML y se dejan en el
directorio target/surefire-reports. Es posible también generar los resultados en
HTML utilizando el comando:
$ mvn surefire-report:report
Todo el trabajo que realiza Maven es realizado por módulos independientes que son
también descargados del repositorio global. Estos módulos reciben el nombre de plugins.
Cada plugin tiene un conjunto de goals que podemos lanzar desde línea de comando. La
sintaxis de una ejecución de un goal de un plugin es:
$ mvn <plugin>:goal -Dparam1=valor1 -Dparam2=valor2 ...
Las fases del ciclo de vida vistas en el apartado anterior también se procesan mediante el
mecanismo de plugins. Por ejemplo, la llamada a mvn test realmente genera una llamada
al objetivo test del plugin surefire:
$ mvn surefire:test
Otro ejemplo es el plugin Jar que define el objetivo jar para realizar la fase package:
$ mvn jar:jar
Existen múltiples plugins de Maven que pueden utilizarse para automatizar distintas
tareas complementarias necesarias para la construcción del proyecto. Un ejemplo es el
plugin SQL de Codehaus que permite lanzar comandos SQL utilizando su objetivo
execute:
$ mvn sql:execute
14
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
<artifactId>sql-maven-plugin</artifactId>
<version>1.4</version>
<dependencies>
<!-- specify the dependent JDBC driver here -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.8</version>
</dependency>
</dependencies>
<!-- common configuration shared by all executions -->
<configuration>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/</url>
<username>root</username>
<password>especialista</password>
</configuration>
<executions>
<execution>
<id>create-db</id>
<phase>process-test-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<srcFiles>
<srcFile>src/main/sql/biblioteca.sql</srcFile>
</srcFiles>
</configuration>
</execution>
<execution>
<id>create-data</id>
<phase>process-test-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<srcFiles>
<srcFile>src/test/sql/datos.sql</srcFile>
</srcFiles>
</configuration>
</execution>
</executions>
</plugin>
Vemos que en el apartado dependency se definen las dependencias del plugin. En este
caso el JAR mysql-connector-java-5.0.8 necesario para la conexión a la base de
datos. En el apartado executions es donde se definen los ficheros SQL que se ejecutan y
la fase del ciclo de vida en la que se lanza.
Cuando se llame a la fase process-test-resoures de Maven (o a cualquiera posterior)
se ejecutaran los ficheros SQL:
$ mvn process-test-resources
[INFO] Scanning for projects...
...
[INFO] [sql:execute {execution: create-db}]
[INFO] Executing file: /tmp/biblioteca.2081627011sql
15
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
La siguiente figura muestra un resumen de los conceptos vistos hasta ahora de fases de
build y plugins:
Es tedioso crear todo un proyecto Maven desde cero. Implica crear toda una estructura de
directorios y algunos ficheros de configuración y clases Java vacías. Maven puede
hacerlo automáticamente con el plugin Archetype, que permite construir un proyecto
vacío a partir de plantillas estándar. El proyecto construido contiene la estructura estándar
de directorios así como algunos ficheros de ejemplo que ilustran las convenciones y
buenas prácticas de Maven. El modelo de arquetipo por defecto creará un proyecto que
construye una librería JAR. Es posible utilizar otras plantillas, como proyectos web,
proyectos JPA, etc.
Por ejemplo, supongamos que somos una empresa de desarrollo de software llamada
PrensaSoft, especializada en dar soporte a grupos editoriales y que estamos desarrollando
un módulo de recomendaciones de noticias que llamamos RecomendBackend. Todas las
clases del módulo queremos que se creen en el paquete
16
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Otra posible forma de crear el proyecto es ejecutar el comando sin especificar los
parámetros. En este caso Maven los va pidiendo de forma interactiva y sugiriendo
opciones.
Por último, una tercera forma de crear un proyecto Maven es utilizando el plugin de
Eclipse. Incluye un asistente que permite introducir todas las opciones utilizando
ventanas, menús y diálogos.
El proyecto se creará en el subdirectorio con el mismo nombre que el del artefacto
(recomend-backend). El groupId y el artifactId se utilizarán para colocar la librería JAR
generada en el repositorio de Maven. El nombre de paquete es el paquete raíz en el que se
definen todas las clases del proyecto. Se crearán los directorios correspondentes bajo
src/main/java (en nuestro caso es/prensasoft/backend/recomend).
17
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>es.prensasoft</groupId>
<artifactId>recomend-backend</artifactId>
<packaging>jar</packaging>
<version>2011</version>
<name>recomend-backend</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Existen muchos arquetipos posibles que pueden utilizarse para crear distintos tipos de
proyectos. Por ejemplo, para crear una aplicación web debemos utilizar el arquetipo
maven-archetype-webapp que creará un proyecto WAR vacío.
Esto creará una aplicación web ejecutable y con un fichero POM completo. Se intentará
conectar la base de datos local MySQL utilizando el usuario "root" y sin password.
Podemos ejecutarla utilizando el plugin de Jetty (un servidor web, similar a Tomcat).
Descargará todas las librerías necesarias (cuidado, serán muchas), compilará el proyecto,
pasará los tests y desplegará la aplicación en el servidor jetty:
$ cd prueba-jsf
$ mvn jetty:run
...
mvn jetty:run-war
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'jetty'.
[INFO]
----------------------------------------------------------------------------
[INFO] Building AppFuse JSF Application
[INFO] task-segment: [jetty:run-war]
[INFO]
----------------------------------------------------------------------------
...
18
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
19
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Desde esa pestaña es posible editar las dependencias del POM y añadir nuevas. Una
ventana de diálogo permite explorar todos los artefactos del repositorio de Maven.
20
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Es posible activar una vista especial que permite hojear los repositorios. Se activa en la
opción Window > Show View > Other... y buscando Maven y seleccionando Maven
Repositories.
21
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Las siguientes referencias son de mucho interés para aprender más sobre Maven o como
referencia para consultar dudas:
• Maven in 5 minutes: Introducción rápida a Maven en la que se muestra cómo
construir, empaquetar y ejecutar una sencilla aplicación.
• Maven by Example: Libro de introducción a Maven realizado por Sonatype, la
compañía creada por Jason Van Zyl el desarrollador principal de Maven. Disponible
también en PDF en esta dirección.
• Maven, the complete reference: Otro libro muy recomendable de Sonatype.
Disponible también en PDF en esta dirección.
• Java Power Tools: Excelente libro sobre 30 herramientas que ayudan en el desarrollo
de software Java. El capítulo 1 está dedicado a Ant y el 2 a Maven.
• Maven: Página principal del proyecto Maven
22
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
2. Caso de estudio
2.1. Introducción
23
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
• Devolver un libro
• Para realizar un préstamo tanto los profesores como los alumnos deberán personarse
en la biblioteca, coger el libro y solicitar su préstamo al bibliotecario. El bibliotecario
utilizará la aplicación para asignar el préstamo del libro al usuario. El número de
libros que puede tener en préstamo un usuario dependerá de si es profesor o alumno.
• Los usuarios profesores pueden solicitar préstamos en depósito. En esta modalidad, el
profesor puede quedarse el libro hasta que otro usuario (profesor o alumno) solicite
una reserva.
• Los usuarios pueden reservar libros con la aplicación:
• Si el libro está disponible en sala, no podrá ser prestado a ningún otro usuario
durante un número determinado de días.
• Si el libro está prestado, se pondrá en estado de 'prereserva': cuando el usuario
prestatario devuelva el libro, el bibliotecario lo dejará en la sala para consulta,
pero quedará reservado durante un número de días (dependiendo del tipo de
usuario que ha hecho la reserva). En el momento de la devolución la aplicación
enviará un aviso al usuario que ha hecho la reserva y el libro pasará a estado
'reservado'. Si el usuario prestatario es un profesor que tiene el libro EN depósito,
se le enviará un aviso para solicitar su devolución.
• El estado por defecto de un usuario es activo. Cuando el usuario se retrasa en la
devolución de un préstamo pasa a estado moroso. En ese estado no puede pedir
prestado ni reservar ningún otro libro. Cuando devuelve el libro se le crea una multa y
pasa a estado multado.
• De la multa nos interesa saber la fecha de inicio y de finalización de la misma. La
finalización de la multa dependerá del tipo de usuario. Nos han comunicado que
quieren mantener un histórico de las multas que ha tenido un usuario. Cuando pasa la
fecha de finalización, el estado del usuario vuelve a activo.
Estas funcionalidades las vamos a ir implementando a lo largo del curso, conforme vaya
avanzando el proyecto de integración.
24
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
En un principio, el número de casos de uso es muy limitado. A lo largo del proyecto nos
vamos a centrar en el desarrollo de los siguientes casos de uso:
25
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Un poco de UML...
Recordar que las relaciones entre casos de uso con estereotipo ‹include› representan que el caso
de uso incluido se realiza siempre que se realiza el caso base. En cambio, el estereotipo ‹extend›
representa que el caso de uso extendido puede realizarse cuando se realiza el caso de uso padre (o
puede que no).
2.2.2.1. Bibliotecario
26
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
En cuanto a un usuario cuyo tipo sea alumno o profesor, y por tanto, sea un usuario
registrado en el sistema, las operaciones que puede realizar son las siguientes:
27
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Respecto a las restricciones que se aplicarán tanto a los casos de uso como a los requisitos
de información, y que concretan las reglas de negocio, hemos averiguado:
• Diferencias a la hora de realizar una operación tanto por parte de un profesor como de
un alumno:
Número Máximo Días de Reserva Días de Prestamo
de Libros
Alumno 3 5 7
28
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Profesor 10 10 30
El equipo directivo del "jUA" nos ha confirmado que para la primera versión de la
aplicación se van a cumplir las siguientes premisas:
• No se permite más de una existencia de un libro. Es decir, sólo hay un ejemplar de
cada libro, y la aplicación no permite libros repetidos.
• Un libro sólo tiene un autor.
A partir de esta captura de requisitos inicial, vamos a plantear los elementos que van a
formar parte de la aplicación, comenzado por el modelo de clases.
A partir de los requisitos y tras unas sesiones de modelado, hemos llegado al siguiente
modelo de clases conceptual representado mediante el siguiente diagrama UML:
29
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Diagrama de Clases
El elemento central de nuestro sistema va a ser una clase Operacion, el cual,
dependiendo de un atributo enumerado, nos indicará si el objeto es un préstamo, un
depósito o una reserva. Además, cada operación se compone de un Libro y de un
Usuario. La clase tendrá dos subclases, Activa y Historica, para reflejar la distinta
cardinalidad de la relación con Libro. La relación entre Libro y Activa es uno-a-uno (un
libro sólo puede estar una operación activa), mientras que entre Libro y Historica es
uno-a-muchos.
Alternativa
Otra forma de modelar este problema sería mediante una relación de herencia entre una clase
abstracta Operacion, del cual tuviésemos las clases extendidas de Reserva y Prestamo,
pero realmente para la lógica de nuestra aplicación, no nos interesa esta separación.
La decisión de desnormalizar esta jerarquía se debe a que realmente estas dos entidades no tienen
ningún atributo diferenciador, ni se prevé que vayamos a necesitar almacenar algún dato
específico de alguna entidad que lo haga diferenciarse una de la otra.
Definimos también una clase Usuario con tres subclases: Bibliotecario, Profesor y
Alumno.
Todas las entidades tienen un campo identificador que permitirá indentificarlas de forma
única. No usaremos claves primarias naturales (por ejemplo, el isbn de un libro o el login
30
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
En el caso de un libro, su estado determina las operaciones que se pueden realizar con él.
31
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Para saber el estado actual de un libro, tendremos que consultar en la base de datos las
relaciones que tiene con operaciones y con pre-reservas:
• Si no tiene ninguna operación activa ni está pre-reservado: disponible
• Si tiene una pre-reserva: pre-reservado
• Si tiene una operación activa de tipo reserva: reservado en sala
• Si tiene una operación activa de tipo préstamo o depósito: prestado
Las operaciones que hacen que un libro cambie de un estado a otro son las siguientes:
Del mismo modo, los estados de un usuario dependen de si el usuario realiza sus
operaciones dentro de las reglas de negocio permitidas. Si un usuario devuelve un libro
con fecha caducada, se le multará y pasará a ser moroso. Destacar que si un usuario posee
un libro con fecha de devolución caducada, hasta que el usuario no devuelve el libro, no
se le considera moroso, ya que no podemos calcular la cuantía de la multa.
32
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
33
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Tanto las operaciones de reserva como de préstamo de un libro conllevan una serie de
comprobaciones que hemos definido en el siguiente diagrama:
34
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
35
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
3. Implementación
Vamos a crear el módulo inicial del proyecto de integración. Todos los módulos los
crearemos en un espacio de trabajo eclipse llamado proy-int dentro de
/home/especialista. Para ello, seleccionamos un espacio de trabajo con ese nombre. Si
no existe el directorio, Eclipse lo crea.
36
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
37
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
pequeña "M" que indica que se trata de un proyecto Maven y que el plugin Maven de
Eclipse lo ha reconocido como tal.
Proyecto padre
Podemos abrir el fichero POM para comprobar que el XML se ha creado correctamente:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.especialistajee</groupId>
<artifactId>pperez-proyint-jbib</artifactId>
<version>2011</version>
<packaging>pom</packaging>
<name>pperez-proyint-jbib</name>
<description>Proyecto padre de los módulos del proyecto de
integración Biblioteca Java</description>
</project>
Procedemos a continuación a crear el módulo con el que vamos a trabajar en esta sesión:
jbib-modelo (precedido de tu login). Lo hacemos también utilizando el asistente de
Eclipse. Seleccionamos con el botón derecho la opción New > Project... > Maven >
Maven Module:
38
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
39
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Por último, para comprobar que la estructura de directorios es correcta, podemos abrir el
explorador de archivos y asegurarnos de que se ha creado un directorio padre con el
proyecto padre y un subdirectorio hijo con el módulo jbib-modelo:
40
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Hay que añadir algunos elemenots a los ficheros POM para configurar correctamente los
proyectos
En primer lugar, podemos ver que el plug-in de Maven interpreta incorrectamente la
versión de la máquina virtual Java del proyecto. En el proyecto aparece la J2SE-1.5, en
lugar de la que está configurada por defecto en Eclipse, la 1.6. Para solucionar el aviso,
indicamos en el POM del proyecto padre explícitamente que el proyecto va a trabajar con
la versión 1.6 del compilador Java.
También indicamos que la codificación de los ficheros de código fuente que componen el
proyecto es UTF-8 (la versión de Eclipse de la máquina virtual está configurado de esa
forma, se puede comprobar en Windows > Preferences > General > Workspace > Text
file encoding).
Para ello añadimos el siguiente código al fichero POM del proyecto padre
(login-proyint-jbib/pom.xml). Para que el plugin de Maven actualice los proyectos,
hay que pulsar con el botón derecho la opción Maven > Update project configuration.
Hazlo en el proyecto hijo jbib-modelo y verás que se actualiza la versión de Java y
desaparece el aviso.
<project>
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
41
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
</configuration>
</plugin>
</plugins>
</build>
</project>
Por último, añadimos en el proyecto padre las dependencias de algunas librerías que
vamos a usar en todos los módulos del proyecto:
• Librería de pruebas junit: junit-4.8.1
• Librerías de gestión de logs commons-logging y log4j: commons-logging-1.1.1 y
log4j-1.2.12
...
</properties>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
...
42
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Nota:
También es posible añadir dependencias utilizando el asistente de Eclipse, que permite ademas
buscar por nombre y versión.
Las clases de dominio representan las entidades que van a relacionarse en la aplicación.
En las siguientes sesiones, cuando veamos JPA, veremos cómo se podrán definir la capa
de persistencia de la aplicación directamente a partir de estas clases. Serán también
objetos que podremos utilizar como Transfer Objects (TOs) dentro del sistema,
realizando funciones de meros contenedores y viajando por las capas de la aplicación.
Para asegurarnos que todos nuestros objetos de dominio tienen una estructura común,
definimos una clase abstracta, que será la clase padre de todas las clases de dominio. La
hacemos serializable para asegurar que los objetos pueden transmitirse entre capas físicas
de la aplicación (por ejemplo, entre la capa de presentación y la capa de lógica de
negocio) y definimos los métodos equals() y hashCode() para obligar a que las clases
hijas implementen la redefinan la igualdad. Estos métodos son muy útiles en el caso en el
que los objetos se guarden y queramos buscarlos en colecciones.
package org.especialistajee.jbib.model;
43
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
import java.io.Serializable;
/**
* Padre de todos los objetos de dominio del modelo. Los objetos del
dominio
* están relacionados entre si y se hacen persistentes utilizando
entidades JPA.
* Pulsando F4 en Eclipse podemos ver la jerarquía. En la clase definimos
los
* métodos abstractos que deben implementar todas las clases del dominio.
Los
* objetos de dominio participan entre ellos en relaciones uno-a-uno,
* uno-a-muchos y muchos-a-uno y estan relacionados mediante
referencias. Tendrán
* una clave única que permite identificarlos en la BD. Una Direccion no
es un
* objeto de dominio.
*/
public abstract class DomainObject implements Serializable {
private static final long serialVersionUID = 1L;
public abstract boolean equals(Object object);
public abstract int hashCode();
}
44
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
45
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Es interesante hacer notar la forma en que están implementados los métodos hashCode y
equals. Son métodos muy importantes porque son los que se utilizan para buscar en las
colecciones. Dos importante ideas relacionadas con estos métodos (ver el capítulo 3 de
Effective Java de Joshua Bloch):
• Si dos objetos son equals deben tener el mismo hashCode, pero no tiene por qué ser
al revés. En nuestro caso, el método hashCode se basa en el campo isbn. Si dos libros
46
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
son equals puede ser porque: a) tengan la misma referencia o b) tengan el mismo
identificador. En ambos casos van a tener el mismo isbn. Puede ser que al revés no.
Supongamos dos libros que tienen el mismo isbn pero distinto identificador (dos
ejemplares de un mismo libro). Tendrían el mismo hashCode pero no serían equals.
Esto es posible, ya que el hashCode define la posición en una tabla hash, pero en una
posición puede haber más de un elemento.
• El método hashCode debe devolver un valor estable, que no cambie en el tiempo que
un objeto está en una colección. En nuestro caso también es correcto, ya que basamos
el hashCode en un campo que no se modifica, el isbn del libro (una clave natural).
Definimos las clases vacías necesarias para eliminar los errores de la clase Libro:
• Clase abstracta OperacionDomain y sus clases hijas ActivaDomain y
HistoricasDomain.
• Clase PrereservaDomain
Fichero src/main/resources/log4j.properties:
# Coloca el nivel root del logger en DEBUG (muestra mensajes de DEBUG
hacia arriba)
# Añade appender A1
log4j.rootLogger=DEBUG, A1
# A1 se redirige a la consola
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.Threshold=INFO
# A1 utiliza PatternLayout
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{dd/MM/yyyy HH:mm:ss} - %p -
%m %n
47
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
package org.especialistajee.jbib.model;
import static org.junit.Assert.*;
import org.junit.Test;
public class LibroDomainTest {
@Test
public void testEquals() {
LibroDomain lib1 = new LibroDomain("123456789");
LibroDomain lib2 = new LibroDomain("123456789");
LibroDomain lib3 = lib1;
assertFalse(lib1.equals(lib2));
assertTrue(lib1.equals(lib3));
lib1.setId(1L);
lib2.setId(1L);
assertTrue(lib1.equals(lib2));
lib2.setId(2L);
assertFalse(lib1.equals(lib2));
}
}
Ejectuamos el test con el botón derecho sobre la clase o el paquete: Run As > JUnit Test y
debe aparecer en verde:
Abrimos un terminal y ejecutamos los comandos Maven para construir el JAR que
contiene el proyecto:
$ cd /home/especialista/proyint/pperez-proyint-jbib
$ mvn clean
$ mvn install
Todos los comandos Maven los ejecutamos en el directorio del proyecto principal, que
contiene el subproyecto jbib-modelo. Cuando añadamos más subproyectos los
empaquetaremos de la misma forma.
El comando Maven clean borra todos los ficheros .class que puedan haber sido creados
anteriormente. El comando install realiza secuencialmente las siguientes acciones:
• Compila todos los módulos del proyecto principal
• Ejecuta todos los tests
• Si los tests son correctos, empaqueta el subproyecto pperez-jbib-modelo en el
fichero JAR pperez-jbib-modelo-2011.jar
• Copia el fichero JAR en el repositorio local de Maven (directorio .m2) para dejarlo
como una librería disponible para otros proyectos.
48
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Todos los ficheros .class y JAR se guardan en el directorio target del proyecto. Puedes
explorar el sistema de archivos para comprobarlo.
Esto lo podemos hacer también desde el asistente de Eclipse situándonos en el proyecto
principal y pulsando con el botón derecho la opción Run As > Maven instal. Puede ser
que la salida de la consola sea ligeramente distinta, porque Eclipse usa una versión de
Maven instalada en el propio entorno.
Para usar la misma versión que cuando la lanzamos desde línea de comandos, podemos
seleccionar la opción Window > Preferences > Maven > Installations y añadir la ruta en
la que se encuentra Maven en el sistema operativo: /opt/apache-maven-3.0.3.
49
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Aviso:
Los directorios target no deben subirse al repositorio SVN, ya que sólo debemos colocar allí
los ficheros fuentes. Podemos configurar Eclipse de forma que haga que Subversion los ignore.
Debemos entrar en la opción Window > Preferences > Team > Ignored Resources y añadir el
patrón target. De esta forma el cliente de Subversion no subirá ni el directorio ni sus
contenidos.
Pulsamos con el botón derecho sobre el proyecto padre la opción Team > Share project
y creamos la conexión SVN con la URL
svn+ssh://server.jtech.ua.es/home/svn/pperez/proyint. Después escojemos la
opción Advanced Mode para espeficicar la localización del proyecto. La localización que
debe aparecer en la parte inferior del asistente es:
svn+ssh://server.jtech.ua.es/home/svn/pperez/proyint/trunk/pperez-proyint-jbib
3.4. Enumeraciones
En cuanto a las enumeraciones, Java (desde su versión 5.0) permite su creación mediante
la clase java.lang.Enum. En nuestro caso, por ejemplo, la enumeración de
EstadoUsuario quedaría del siguiente modo:
package org.especialistajee.jbib.model;
public enum EstadoUsuario {
ACTIVO, MOROSO, MULTADO
}
Para implementar nuestras clases, tendremos que codificar todas las clases y relaciones
expuestas en el Modelo de Clases Conceptual.
Hay que tener especial cuidad con las relaciones de herencia. Tenemos dos: la clase
UsuarioDomain y las clases hijas BibliotecarioDomain, ProfesorDomain y
50
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Nota:
No olvides implementar un método getEstado en la clase Libro. El estado del libro se puede
calcular a partir de sus relaciones.
Todas las aplicaciones empresariales definen una política de gestión de las excepciones
de aplicación.
En nuestro caso, conforme crezca el proyecto, iremos creando nuevas excepciones. Como
punto de partida, y como buena norma de programación, vamos a definir una excepción
genérica de tipo unchecked (BibliotecaException), que será la excepción padre de
todas las excepciones de aplicación de la biblioteca. El hecho de que la excepción se
unchecked remarca el carácter de que estamos definiendo excepciones relacionadas con el
mal uso del API. En general, un método debe realizar su funcionalidad y terminar
correctamente cuando todo ha funcionado bien. Se lanzará una excepción si algo falla.
Por ejemplo, cuando definamos un método prestar(libro,usuario) lanzaremos
excepciones cuando no se cumplan las condiciones que hacen que el libro pueda ser
prestado al usuario. Al lanzar excepciones no chequeadas permitimos que el programador
chequee las condiciones antes de llamar al método y no tenga que obligatoriamente
capturar una excepción que sabemos que no se va a producir.
Definimos las excepciones en el paquete global org.especialistajee.jbib
package org.especialistajee.jbib;
51
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Es común agrupar las reglas de negocio de una aplicación en una o más clases
(dependiendo de los diferentes subsistemas de la aplicación), para evitar que estén
dispersas por la aplicación y acopladas a un gran número de clases.
En nuestro caso, vamos a crear un Singleton, al que llamaremos BibliotecaBR (BR =
Business Rules). En principio, los valores estarán escritos directamente sobre la clase,
pero en un futuro podríamos querer leer los valores de las reglas de negocio de un fichero
de configuración).
El esqueleto de nuestras reglas de negocio será el siguiente:
package org.especialistajee.jbib;
// faltan los imports
/**
* Reglas de Negocio de la Biblioteca
* BR = Business Rules
*
* Lo implementamos como un singleton por si algun dia queremos leer
* las constantes desde un fichero de configuración, lo podemos
* hacer desde el constructor del singleton
*/
public class BibliotecaBR {
private static BibliotecaBR me = new BibliotecaBR();
private BibliotecaBR() {
}
public static BibliotecaBR getInstance() {
return me;
}
/**
* Calcula el número de dias de plazo que tienen un usuario para
52
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
Podemos observar que vamos a tener un método por cada regla de negocio, y que estos
métodos lanzarán excepciones de aplicación en el caso de un comportamiento anómalo.
3.8. Tests
Vamos a definir dos tipos de tests, unos relacionados con las clases del modelo y otros
con las reglas de negocio.
Los tests se deben colocar dentro de la carpeta test, en el mismo paquete que la clase a
probar.
Tests de reglas de negocio:
package org.especialistajee.jbib;
import static org.junit.Assert.*;
import org.especialistajee.jbib.model.TipoUsuario;
import org.junit.Test;
/**
* Pruebas jUnit sobre las reglas de negocio de la biblioteca
*/
public class BibliotecaBRTest {
53
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
@Test
public void testCalculaNumDiasReservaProfesor() {
int diasProfesor = BibliotecaBR.getInstance()
.calculaNumDiasReserva(TipoUsuario.PROFESOR);
assertEquals(diasProfesor, 10);
}
@Test
public void testCalculaNumDiasReservaSocio() {
int diasAlumno = BibliotecaBR.getInstance()
.calculaNumDiasReserva(TipoUsuario.ALUMNO);
assertEquals(diasAlumno, 5);
}
@Test
public void testCalculaNumDiasPrestamoProfesor() {
int diasProfesor = BibliotecaBR.getInstance()
.calculaNumDiasPrestamo(TipoUsuario.PROFESOR);
assertEquals(diasProfesor, 30);
}
@Test
public void testCalculaNumDiasPrestamoSocio() {
int diasAlumno = BibliotecaBR.getInstance()
.calculaNumDiasPrestamo(TipoUsuario.ALUMNO);
assertEquals(diasAlumno, 7);
}
@Test
public void testCompruebaCupoOperacionesProfesorCorrecto() {
try {
BibliotecaBR.getInstance()
.compruebaCupoOperaciones(
TipoUsuario.PROFESOR, 4);
BibliotecaBR.getInstance()
.compruebaCupoOperaciones(
TipoUsuario.PROFESOR, 0);
} catch (BibliotecaException e) {
fail("No debería fallar - el cupo de operaciones del PROFESOR es
correcto");
}
}
@Test(expected = BibliotecaException.class)
public void testCompruebaCupoOperacionesProfesorIncorrecto()
throws BibliotecaException {
BibliotecaBR.getInstance().compruebaCupoOperaciones(
TipoUsuario.PROFESOR, 11);
}
@Test
public void testCompruebaCupoOperacionesAlumnoCorrecto() {
try {
BibliotecaBR.getInstance()
.compruebaCupoOperaciones(
TipoUsuario.ALUMNO, 2);
BibliotecaBR.getInstance()
.compruebaCupoOperaciones(
TipoUsuario.ALUMNO, 0);
} catch (BibliotecaException e) {
fail("No debería fallar - el cupo de operaciones del ALUMNO es
correcto");
}
}
54
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
@Test(expected = BibliotecaException.class)
public void testCompruebaCupoOperacionesAlumnoIncorrecto()
throws BibliotecaException {
BibliotecaBR.getInstance().compruebaCupoOperaciones(
TipoUsuario.ALUMNO, 4);
}
}
3.9. Resumen
En esta sesión vamos a preparar la base para el resto de sesiones. Por ellos, debemos crear
un proyecto padre Maven, al que llamaremos login-proyint-jbib, que contendrá al
módulo login-jbib-modelo con el modelo de dominio de la aplicación:
1. modelo de objetos dentro del paquete org.especialistajee.jbib.model,
implementando cada entidad con los atributos representados en el diagrama UML, sus
relaciones, y teniendo en cuenta la relación de herencia con la clase DomainObject y
los constructores necesarios y los métodos de acceso.
2. clase BibliotecaBR con las reglas de negocio, implementada como un singleton, la
cual debe pasar las pruebas JUnit aportadas. Para implementar esta clase, es necesaria
la clase BibliotecaException.
3. tests de las clases de dominio y de las reglas de negocio que realicen algunas
comprobaciones de los métodos equals y de las actualizaciones de las relaciones.
Las siguientes imágenes muestran todos los archivos que debe contener el proyecto
Eclipse:
55
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
56
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
57
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.
Caso de estudio
58
Copyright © 2011-2012 Dept. Ciencia de la Computación e IA All rights reserved.