Commit daf6bd79 by Antonio Rueda

Implementación de bloqueos para evitar reservas simultáneas de la misma

habitación.
parent f63846d1
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0" version="24.7.16"> <mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0" version="24.8.0">
<diagram name="Página-1" id="IpY-njWp8TfkcAYWz6vd"> <diagram name="Página-1" id="IpY-njWp8TfkcAYWz6vd">
<mxGraphModel dx="3589" dy="1303" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="2336" math="0" shadow="0"> <mxGraphModel dx="2950" dy="820" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="2336" math="0" shadow="0">
<root> <root>
<mxCell id="0" /> <mxCell id="0" />
<mxCell id="1" parent="0" /> <mxCell id="1" parent="0" />
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
<mxCell id="a_C5hD_1tt0fRA46Nat0-17" value="reserva" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxCell id="a_C5hD_1tt0fRA46Nat0-17" value="reserva" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="1410" y="204" width="60" height="30" as="geometry" /> <mxGeometry x="1410" y="204" width="60" height="30" as="geometry" />
</mxCell> </mxCell>
<mxCell id="a_C5hD_1tt0fRA46Nat0-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="a_C5hD_1tt0fRA46Nat0-10" target="a_C5hD_1tt0fRA46Nat0-1" edge="1"> <mxCell id="a_C5hD_1tt0fRA46Nat0-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;startArrow=open;startFill=0;" parent="1" source="a_C5hD_1tt0fRA46Nat0-10" target="a_C5hD_1tt0fRA46Nat0-1" edge="1">
<mxGeometry relative="1" as="geometry" /> <mxGeometry relative="1" as="geometry" />
</mxCell> </mxCell>
<mxCell id="a_C5hD_1tt0fRA46Nat0-22" value="cliente" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxCell id="a_C5hD_1tt0fRA46Nat0-22" value="cliente" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
<mxCell id="a_C5hD_1tt0fRA46Nat0-35" value="cliente" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxCell id="a_C5hD_1tt0fRA46Nat0-35" value="cliente" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="790" y="499" width="60" height="30" as="geometry" /> <mxGeometry x="790" y="499" width="60" height="30" as="geometry" />
</mxCell> </mxCell>
<mxCell id="nwQueBcBUwvZKA8jkqrU-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;startArrow=diamond;startFill=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" target="nwQueBcBUwvZKA8jkqrU-5"> <mxCell id="nwQueBcBUwvZKA8jkqrU-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;startArrow=diamond;startFill=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" target="nwQueBcBUwvZKA8jkqrU-5" edge="1">
<mxGeometry relative="1" as="geometry"> <mxGeometry relative="1" as="geometry">
<mxPoint x="130" y="344" as="sourcePoint" /> <mxPoint x="130" y="344" as="sourcePoint" />
<mxPoint x="130" y="510" as="targetPoint" /> <mxPoint x="130" y="510" as="targetPoint" />
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
</Array> </Array>
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="nwQueBcBUwvZKA8jkqrU-5" value="&lt;p style=&quot;margin:0px;margin-top:4px;text-align:center;text-decoration:underline;&quot;&gt;&lt;b&gt;direccion:Usuario&lt;/b&gt;&lt;/p&gt;&lt;hr size=&quot;1&quot; style=&quot;border-style:solid;&quot;&gt;&lt;p style=&quot;margin:0px;margin-left:8px;&quot;&gt;nombre = &quot;direccion&quot;&lt;br&gt;email = &quot;direccion@hotelxyz.es&quot;&lt;br&gt;clave =&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;&quot;SeCrEtO&quot;&quot;&lt;/span&gt;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;html=1;whiteSpace=wrap;" vertex="1" parent="1"> <mxCell id="nwQueBcBUwvZKA8jkqrU-5" value="&lt;p style=&quot;margin:0px;margin-top:4px;text-align:center;text-decoration:underline;&quot;&gt;&lt;b&gt;direccion:Usuario&lt;/b&gt;&lt;/p&gt;&lt;hr size=&quot;1&quot; style=&quot;border-style:solid;&quot;&gt;&lt;p style=&quot;margin:0px;margin-left:8px;&quot;&gt;nombre = &quot;direccion&quot;&lt;br&gt;email = &quot;direccion@hotelxyz.es&quot;&lt;br&gt;clave =&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;&quot;SeCrEtO&quot;&quot;&lt;/span&gt;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="50" y="520" width="190" height="80" as="geometry" /> <mxGeometry x="50" y="520" width="190" height="80" as="geometry" />
</mxCell> </mxCell>
</root> </root>
......
...@@ -4,16 +4,15 @@ package es.ujaen.dae.reservahoteles.entidades; ...@@ -4,16 +4,15 @@ package es.ujaen.dae.reservahoteles.entidades;
import es.ujaen.dae.reservahoteles.excepciones.ReservaNoValida; import es.ujaen.dae.reservahoteles.excepciones.ReservaNoValida;
import es.ujaen.dae.reservahoteles.excepciones.NoDisponibilidadReserva; import es.ujaen.dae.reservahoteles.excepciones.NoDisponibilidadReserva;
import static es.ujaen.dae.reservahoteles.util.UtilString.normalizar; import static es.ujaen.dae.reservahoteles.util.UtilString.normalizar;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.Version;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero; import jakarta.validation.constraints.PositiveOrZero;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.LinkedList; import java.util.LinkedList;
...@@ -59,7 +58,11 @@ public class Hotel { ...@@ -59,7 +58,11 @@ public class Hotel {
@OneToMany @OneToMany
@JoinColumn(name = "hotel_id") @JoinColumn(name = "hotel_id")
List<Reserva> reservas; List<Reserva> reservas;
// Para habilitar bloqueo optimista
@Version
int version;
public Hotel() { public Hotel() {
} }
......
...@@ -4,9 +4,10 @@ import es.ujaen.dae.reservahoteles.entidades.Hotel; ...@@ -4,9 +4,10 @@ import es.ujaen.dae.reservahoteles.entidades.Hotel;
import es.ujaen.dae.reservahoteles.entidades.Reserva; import es.ujaen.dae.reservahoteles.entidades.Reserva;
import static es.ujaen.dae.reservahoteles.util.UtilString.normalizar; import static es.ujaen.dae.reservahoteles.util.UtilString.normalizar;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.LockModeType;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -21,6 +22,16 @@ public class RepositorioHoteles { ...@@ -21,6 +22,16 @@ public class RepositorioHoteles {
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Optional<Hotel> buscarPorId(int id) {
return Optional.ofNullable(em.find(Hotel.class, id));
}
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Optional<Hotel> buscarPorIdBloqueando(int id) {
return Optional.ofNullable(em.find(Hotel.class, id, LockModeType.PESSIMISTIC_WRITE));
}
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public List<Hotel> buscarPorNombreLocalidad(String nombre, String localidad) { public List<Hotel> buscarPorNombreLocalidad(String nombre, String localidad) {
return em.createQuery("select h from Hotel h where " + return em.createQuery("select h from Hotel h where " +
...@@ -52,6 +63,10 @@ public class RepositorioHoteles { ...@@ -52,6 +63,10 @@ public class RepositorioHoteles {
return em.merge(hotel); return em.merge(hotel);
} }
public void comprobarErrores() {
em.flush();
}
public void guardarReserva(Reserva reserva) { public void guardarReserva(Reserva reserva) {
em.persist(reserva); em.persist(reserva);
} }
......
...@@ -6,6 +6,7 @@ import es.ujaen.dae.reservahoteles.entidades.Usuario; ...@@ -6,6 +6,7 @@ import es.ujaen.dae.reservahoteles.entidades.Usuario;
import es.ujaen.dae.reservahoteles.entidades.Hotel; import es.ujaen.dae.reservahoteles.entidades.Hotel;
import es.ujaen.dae.reservahoteles.entidades.Reserva; import es.ujaen.dae.reservahoteles.entidades.Reserva;
import es.ujaen.dae.reservahoteles.excepciones.ClienteYaRegistrado; import es.ujaen.dae.reservahoteles.excepciones.ClienteYaRegistrado;
import es.ujaen.dae.reservahoteles.excepciones.NoDisponibilidadReserva;
import es.ujaen.dae.reservahoteles.repositorios.RepositorioHoteles; import es.ujaen.dae.reservahoteles.repositorios.RepositorioHoteles;
import es.ujaen.dae.reservahoteles.repositorios.RepositorioUsuarios; import es.ujaen.dae.reservahoteles.repositorios.RepositorioUsuarios;
import es.ujaen.dae.reservahoteles.util.UtilString; import es.ujaen.dae.reservahoteles.util.UtilString;
...@@ -17,11 +18,11 @@ import jakarta.validation.constraints.NotBlank; ...@@ -17,11 +18,11 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.PositiveOrZero; import jakarta.validation.constraints.PositiveOrZero;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.TreeMap; import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -40,27 +41,19 @@ public class ServicioReservas { ...@@ -40,27 +41,19 @@ public class ServicioReservas {
@Autowired @Autowired
RepositorioHoteles repositorioHoteles; RepositorioHoteles repositorioHoteles;
Map<Integer, Hotel> hoteles;
Map<String, Usuario> clientes;
@Value("${meses-historico}") @Value("${meses-historico}")
int mesesHistorico; int mesesHistorico;
// Cliente especial de dirección // Cliente especial de dirección
private static final Usuario direccion = new Usuario("direccion", "-", "670343332", "direccion@hotelxyz.es", "SeCrEtO"); private static final Usuario direccion = new Usuario("direccion", "-", "670343332", "direccion@hotelxyz.es", "SeCrEtO");
private static int nReserva = 1;
public ServicioReservas() { public ServicioReservas() {
hoteles = new TreeMap<>();
clientes = new TreeMap<>();
} }
public void nuevoHotel(Usuario direccion, @Valid Hotel hotel) { public void nuevoHotel(Usuario direccion, @Valid Hotel hotel) {
if (!direccion.nombre().equals("direccion")) if (!direccion.nombre().equals("direccion"))
throw new OperacionDeDireccion(); throw new OperacionDeDireccion();
//hoteles.put(hotel.id(), hotel);
repositorioHoteles.guardar(hotel); repositorioHoteles.guardar(hotel);
} }
...@@ -68,25 +61,15 @@ public class ServicioReservas { ...@@ -68,25 +61,15 @@ public class ServicioReservas {
// Evitar que se cree un usuario con la cuenta de direccion // Evitar que se cree un usuario con la cuenta de direccion
if (cliente.email().equals(direccion.email())) if (cliente.email().equals(direccion.email()))
throw new ClienteYaRegistrado(); throw new ClienteYaRegistrado();
/*
if (clientes.containsKey(cliente.email()))
throw new ClienteYaRegistrado();
clientes.put(cliente.email(), cliente);
*/
repositorioClientes.guardar(cliente); repositorioClientes.guardar(cliente);
} }
public Optional<Usuario> login(@Email String email, String clave) { public Optional<Usuario> login(@Email String email, String clave) {
// Equivalente al código de abajo pero más seguro y compacto
// return Optional.ofNullable(clientes.get(email))
// .filter(cliente -> cliente.clave().equals(clave));
// Caso especial de login de la direccion // Caso especial de login de la direccion
if (direccion.email().equals(email) && direccion.clave().equals(clave)) if (direccion.email().equals(email) && direccion.clave().equals(clave))
return Optional.of(direccion); return Optional.of(direccion);
// Usuario cliente = clientes.get(email);
return repositorioClientes.buscar(email).filter(cliente -> cliente.clave().equals(clave)); return repositorioClientes.buscar(email).filter(cliente -> cliente.clave().equals(clave));
} }
...@@ -97,7 +80,7 @@ public class ServicioReservas { ...@@ -97,7 +80,7 @@ public class ServicioReservas {
* @param fechaFin fecha de final de la estancia * @param fechaFin fecha de final de la estancia
* @param numHabSimple número de habitaciones simples solicitadas * @param numHabSimple número de habitaciones simples solicitadas
* @param numHabDoble número de habitaciones dobles solicitadas * @param numHabDoble número de habitaciones dobles solicitadas
* @return la lista de hoteles candidatos * @return la lista de hoteles candidatos (sin lista de reservas)
*/ */
@Transactional @Transactional
public List<Hotel> buscarHotelesDisponiblesPorLocalidad(@NotBlank String localidad, public List<Hotel> buscarHotelesDisponiblesPorLocalidad(@NotBlank String localidad,
...@@ -105,16 +88,11 @@ public class ServicioReservas { ...@@ -105,16 +88,11 @@ public class ServicioReservas {
@PositiveOrZero int numHabSimple, @PositiveOrZero int numHabDoble) { @PositiveOrZero int numHabSimple, @PositiveOrZero int numHabDoble) {
var localidadNorm = UtilString.normalizar(localidad); var localidadNorm = UtilString.normalizar(localidad);
// return hoteles.values().stream().filter(h ->
// UtilString.normalizar(h.localidad()).contains(localidadNorm) &&
// h.disponible(fechaInicio, fechaFin, numHabSimple, numHabDoble)
// ).toList();
List<Hotel> hotelesLocalidad = repositorioHoteles.buscarPorLocalidad(localidadNorm); List<Hotel> hotelesLocalidad = repositorioHoteles.buscarPorLocalidad(localidadNorm);
return hotelesLocalidad.stream().filter(h -> return hotelesLocalidad.stream().filter(h ->
UtilString.normalizar(h.localidad()).contains(localidadNorm) && UtilString.normalizar(h.localidad()).contains(localidadNorm) &&
h.disponible(fechaInicio, fechaFin, numHabSimple, numHabDoble) h.disponible(fechaInicio, fechaFin, numHabSimple, numHabDoble)
).toList(); ).map(h -> repositorioHoteles.buscarPorId(h.id()).get()).toList();
} }
/** /**
...@@ -122,23 +100,24 @@ public class ServicioReservas { ...@@ -122,23 +100,24 @@ public class ServicioReservas {
* disponibilidad) * disponibilidad)
* @param nombre el nombre total o parcial del hotel * @param nombre el nombre total o parcial del hotel
* @param localidad el nombre total o parcial de la localidad * @param localidad el nombre total o parcial de la localidad
* @return la lista de hoteles candidatos * @return la lista de hoteles candidatos (sin lista de reservas)
*/ */
@Transactional @Transactional
public List<Hotel> buscarHotel(@NotBlank String nombre, @NotBlank String localidad) { public List<Hotel> buscarHotel(@NotBlank String nombre, @NotBlank String localidad) {
// var nombreNorm = UtilString.normalizar(nombre); return repositorioHoteles.buscarPorNombreLocalidad(nombre, localidad);
// var localidadNorm = UtilString.normalizar(localidad); }
// return hoteles.values().stream().filter(h -> /**
// UtilString.normalizar(h.localidad()).contains(localidadNorm) && * Carga las reservas de un hotel
// UtilString.normalizar(h.nombre()).contains(nombreNorm)) * @param hotel el hotel cuyas lista de reservas se va a cargar
// .toList(); * @return el hotel con las reservas
*/
List<Hotel> hoteles = repositorioHoteles.buscarPorNombreLocalidad(nombre, localidad); @Transactional
for (var hotel: hoteles) { public Hotel hotelConReservas(Hotel hotel) {
hotel.reservasEntre(LocalDate.MIN, LocalDate.MAX); hotel = repositorioHoteles.actualizar(hotel);
} // Usar cualquier operación que acceda a las reservas para que se carguen
return hoteles; hotel.disponible(LocalDate.now(), 1, 1);
return hotel;
} }
/** /**
...@@ -150,11 +129,14 @@ public class ServicioReservas { ...@@ -150,11 +129,14 @@ public class ServicioReservas {
* @param numHabDoble número de habitaciones dobles solicitadas * @param numHabDoble número de habitaciones dobles solicitadas
* @return true si hay disponibilidad, false en caso contrario * @return true si hay disponibilidad, false en caso contrario
*/ */
@Transactional
public boolean disponible(Hotel hotel, public boolean disponible(Hotel hotel,
@FutureOrPresent LocalDate fechaInicio, @FutureOrPresent LocalDate fechaInicio,
@FutureOrPresent LocalDate fechaFin, @FutureOrPresent LocalDate fechaFin,
@PositiveOrZero int numHabSimple, @PositiveOrZero int numHabSimple,
@PositiveOrZero int numHabDoble) { @PositiveOrZero int numHabDoble) {
hotel = repositorioHoteles.actualizar(hotel);
return hotel.disponible(fechaInicio, fechaFin, numHabSimple, numHabDoble); return hotel.disponible(fechaInicio, fechaFin, numHabSimple, numHabDoble);
} }
/** /**
...@@ -167,17 +149,38 @@ public class ServicioReservas { ...@@ -167,17 +149,38 @@ public class ServicioReservas {
* @param numHabSimple número de habitaciones simples solicitadas * @param numHabSimple número de habitaciones simples solicitadas
* @param numHabDoble número de habitaciones dobles solicitadas * @param numHabDoble número de habitaciones dobles solicitadas
* @return la reserva recien creada en caso de éxito * @return la reserva recien creada en caso de éxito
*/ */
@Transactional
public Reserva reserva(Usuario cliente, Hotel hotel, public Reserva reserva(Usuario cliente, Hotel hotel,
LocalDate fechaInicio, LocalDate fechaFin, LocalDate fechaInicio, LocalDate fechaFin,
@PositiveOrZero int numHabSimple, @PositiveOrZero int numHabDoble) { @PositiveOrZero int numHabSimple, @PositiveOrZero int numHabDoble) {
// Opción con bloqueo pesimista (no requiere atributo version en Hotel)
//
// hotel = repositorioHoteles.buscarPorIdBloqueando(hotel.id()).get();
// var reserva = new Reserva(cliente, fechaInicio, fechaFin, numHabSimple, numHabDoble);
//
// hotel.nuevaReserva(reserva);
// repositorioHoteles.guardarReserva(reserva);
// Opción con bloqueo optimista (requiere el atributo version en Hotel)
var reserva = new Reserva(cliente, fechaInicio, fechaFin, numHabSimple, numHabDoble); var reserva = new Reserva(cliente, fechaInicio, fechaFin, numHabSimple, numHabDoble);
repositorioHoteles.guardarReserva(reserva);
hotel.nuevaReserva(reserva);
repositorioHoteles.actualizar(hotel);
boolean reservado = false;
while (!reservado) {
try {
hotel = repositorioHoteles.buscarPorId(hotel.id()).get();
hotel.nuevaReserva(reserva);
repositorioHoteles.guardarReserva(reserva);
repositorioHoteles.comprobarErrores();
reservado = true;
}
catch(OptimisticLockingFailureException e) {
}
}
// No hace falta guardar explícitamente el hotel porque está conectado con la transacción
return reserva; return reserva;
} }
...@@ -191,9 +194,5 @@ public class ServicioReservas { ...@@ -191,9 +194,5 @@ public class ServicioReservas {
idHoteles.stream() idHoteles.stream()
.map(id -> repositorioHoteles.buscarPorId(id).get()) .map(id -> repositorioHoteles.buscarPorId(id).get())
.forEach(hotel -> hotel.eliminarReservasAnteriores(fechaLimite)); .forEach(hotel -> hotel.eliminarReservasAnteriores(fechaLimite));
//for (var hotel: hoteles.values()) {
// hotel.eliminarReservasAnteriores(fechaLimite);
//}
} }
} }
...@@ -8,6 +8,7 @@ import es.ujaen.dae.reservahoteles.excepciones.NoDisponibilidadReserva; ...@@ -8,6 +8,7 @@ import es.ujaen.dae.reservahoteles.excepciones.NoDisponibilidadReserva;
import jakarta.validation.ConstraintViolationException; import jakarta.validation.ConstraintViolationException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
...@@ -128,5 +129,39 @@ public class TestServicioReservas { ...@@ -128,5 +129,39 @@ public class TestServicioReservas {
// Reservar una simple y otra doble: no hay disponibilidad para fechas solapadas con la anterior reserva // Reservar una simple y otra doble: no hay disponibilidad para fechas solapadas con la anterior reserva
assertThatThrownBy(()-> servicio.reserva(cliente, hotel, LocalDate.now().plusDays(5), LocalDate.now().plusDays(8), 1, 2)) assertThatThrownBy(()-> servicio.reserva(cliente, hotel, LocalDate.now().plusDays(5), LocalDate.now().plusDays(8), 1, 2))
.isInstanceOf(NoDisponibilidadReserva.class); .isInstanceOf(NoDisponibilidadReserva.class);
}
@Test
@DirtiesContext
void testReservaHotelesConcurrente() {
var direccion = servicio.login("direccion@hotelxyz.es", "SeCrEtO").get();
servicio.nuevoHotel(direccion, new Hotel("Bed and Breakfast Almería", "Almería", "Almería", "04001", 2, 2, 60, 100));
servicio.nuevoCliente(new Usuario("Pedro", "Jaén Jaén", "611203025", "pjaen@gmail.com", "miClAvE"));
servicio.nuevoCliente(new Usuario("Juan", "Granada Granada", "611213126", "jgranada@gmail.com", "miClAvE"));
var hotel = servicio.buscarHotel("bed and breakfast", "almeria").get(0);
var cliente1 = servicio.login("pjaen@gmail.com", "miClAvE").get();
var cliente2 = servicio.login("jgranada@gmail.com", "miClAvE").get();
// Reservar 2 habitaciones dobles
new Thread(()-> {
try {
servicio.reserva(cliente1, hotel, LocalDate.now().plusDays(7), LocalDate.now().plusDays(10), 0, 2);
}
catch(NoDisponibilidadReserva e) {
Logger.getLogger(servicio.getClass().getName()).warning("Reserva de cliente 1 sin disponibilidad");
}
}).start();
try {
servicio.reserva(cliente2, hotel, LocalDate.now().plusDays(5), LocalDate.now().plusDays(12), 0, 2);
}
catch(NoDisponibilidadReserva e) {
Logger.getLogger(servicio.getClass().getName()).warning("Reserva de cliente 2 sin disponibilidad");
}
var hotelConReservas = servicio.hotelConReservas(servicio.buscarHotel("bed and breakfast", "almeria").get(0));
assertThat(hotelConReservas.reservasEntre(LocalDate.MIN, LocalDate.MAX)).singleElement();
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment