Commit d2648847 authored by Iria19's avatar Iria19

DAAExample con Mascotas

DAAExample con Mascotas
parent aa9a77e2
# General
/bak
# Eclipse
.project
.classpath
.settings
WebContent
# Maven
/bin
/target
/assembly
# Testing
/servers
C:\\nppdf32Log\\debuglog.txt
# Angular
src/main/angular
This diff is collapsed.
DROP DATABASE IF EXISTS `daaexample`;
CREATE DATABASE `daaexample`;
CREATE TABLE `daaexample`.`people` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`surname` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `daaexample`.`users` (
`login` varchar(100) NOT NULL,
`password` varchar(64) NOT NULL,
`role` varchar(10) NOT NULL,
PRIMARY KEY (`login`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `daaexample`.`pets` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`food` varchar(100) NOT NULL,
`id_person` int NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `id_person` FOREIGN KEY (`id`) REFERENCES `daaexample`.`people`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE USER IF NOT EXISTS 'daa'@'localhost' IDENTIFIED WITH mysql_native_password BY 'daa';
GRANT ALL ON `daaexample`.* TO 'daa'@'localhost';
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Antón','Pérez');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Manuel','Martínez');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Laura','Reboredo');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Perico','Palotes');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Ana','María');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'María','Nuevo');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Alba','Fernández');
INSERT INTO `daaexample`.`people` (`id`,`name`,`surname`) VALUES (0,'Asunción','Jiménez');
INSERT INTO `daaexample`.`pets` (`id`,`name`,`food`,`id_person`) VALUES (0,'Ali','pienso',0);
INSERT INTO `daaexample`.`pets` (`id`,`name`,`food`,`id_person`) VALUES (0,'Pepe','atún',0);
-- The password for each user is its login suffixed with "pass". For example, user "admin" has the password "adminpass".
INSERT INTO `daaexample`.`users` (`login`,`password`,`role`)
VALUES ('admin', '713bfda78870bf9d1b261f565286f85e97ee614efe5f0faf7c34e7ca4f65baca','ADMIN');
INSERT INTO `daaexample`.`users` (`login`,`password`,`role`)
VALUES ('normal', '7bf24d6ca2242430343ab7e3efb89559a47784eea1123be989c1b2fb2ef66e83','USER');
DROP DATABASE IF EXISTS `daaexample`;
CREATE DATABASE `daaexample`;
CREATE TABLE `daaexample`.`people` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`surname` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `daaexample`.`users` (
`login` varchar(100) NOT NULL,
`password` varchar(64) NOT NULL,
`role` varchar(10) NOT NULL,
PRIMARY KEY (`login`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `daaexample`.`pets` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`food` varchar(100) NOT NULL,
`id_person` int NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `id_person` FOREIGN KEY (`id`) REFERENCES `daaexample`.`people`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE USER IF NOT EXISTS 'daa'@'localhost' IDENTIFIED WITH mysql_native_password BY 'daa';
GRANT ALL ON `daaexample`.* TO 'daa'@'localhost';
This diff is collapsed.
package es.uvigo.esei.daa;
import static java.util.stream.Collectors.toSet;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import es.uvigo.esei.daa.rest.PeopleResource;
import es.uvigo.esei.daa.rest.UsersResource;
import es.uvigo.esei.daa.rest.PetsResource;
/**
* Configuration of the REST application. This class includes the resources and
* configuration parameter used in the REST API of the application.
*
* @author Miguel Reboiro Jato
*
*///aplicaciones con varios recursos asociados. internamente se sirve aplìcation
@ApplicationPath("/rest/*")
public class DAAExampleApplication extends Application {//todos recursos llevan /rest/
@Override
public Set<Class<?>> getClasses() {//se sobrescribe conjunto java con clases que quiero servir
return Stream.of(
PeopleResource.class,
UsersResource.class,
PetsResource.class
).collect(toSet());
}
@Override
public Map<String, Object> getProperties() {
// Activates JSON automatic conversion in JAX-RS
return Collections.singletonMap(
"com.sun.jersey.api.json.POJOMappingFeature", true
);//cuando respuesta es java la pasa a json
}
}
package es.uvigo.esei.daa.dao;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
//permite pedir cosas a tomcat
/**
* Simple base class for DAO (Data Access Object) classes. This super-class is
* responsible for providing a {@link java.sql.Connection} to its sub-classes.
*
* @author Miguel Reboiro Jato
*
*/
public abstract class DAO {
private final static Logger LOG = Logger.getLogger(DAO.class.getName());
private final static String JNDI_NAME = "java:/comp/env/jdbc/daaexample";
private DataSource dataSource;
/**
* Constructs a new instance of {@link DAO}.
*/
public DAO() {
try {
this.dataSource = (DataSource) new InitialContext().lookup(JNDI_NAME);//obtiene fuente datos(DataSource)
} catch (NamingException e) {
LOG.log(Level.SEVERE, "Error initializing DAO", e);
throw new RuntimeException(e);
}
}
/**
* Returns an open {@link java.sql.Connection}.
*
* @return an open {@link java.sql.Connection}.
* @throws SQLException if an error happens while establishing the
* connection with the database.
*/
protected Connection getConnection() throws SQLException {
return this.dataSource.getConnection();
}
}
package es.uvigo.esei.daa.dao;
/**
* A general exception class for the DAO layer.
*
* @author Miguel Reboiro Jato
*/
public class DAOException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs a new instance of {@link DAOException} with {@code null} as
* its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
*/
public DAOException() {
}
/**
* Constructs a new instance of {@link DAOException} with the specified
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for later
* retrieval by the {@link #getMessage()} method.
*/
public DAOException(String message) {
super(message);
}
/**
* Constructs a new instance of {@link DAOException} with the specified
* cause and a detail message of
* {@code (cause==null ? null : cause.toString())} (which typically contains
* the class and detail message of {@code cause}). This constructor is
* useful for exceptions that are little more than wrappers for other
* throwables (for example, {@link java.security.PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is permitted, and
* indicates that the cause is nonexistent or unknown.)
*/
public DAOException(Throwable cause) {
super(cause);
}
/**
* Constructs a new instance of {@link DAOException} with the specified
* detail message and cause.
*
* <p>Note that the detail message associated with {@code cause} is
* <i>not</i> automatically incorporated in this exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). A {@code null} value is permitted, and
* indicates that the cause is nonexistent or unknown.
*/
public DAOException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new instance of {@link DAOException} with the specified
* detail message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. A {@code null} value is permitted, and indicates
* that the cause is nonexistent or unknown.
* @param enableSuppression whether or not suppression is enabled or
* disabled.
* @param writableStackTrace whether or not the stack trace should be
* writable.
*/
public DAOException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package es.uvigo.esei.daa.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import es.uvigo.esei.daa.entities.Person;
/**
* DAO class for the {@link Person} entities.
*
* @author Miguel Reboiro Jato
*
*/
public class PeopleDAO extends DAO {
private final static Logger LOG = Logger.getLogger(PeopleDAO.class.getName());
/**
* Returns a person stored persisted in the system.
*
* @param id identifier of the person.
* @return a person with the provided identifier.
* @throws DAOException if an error happens while retrieving the person.
* @throws IllegalArgumentException if the provided id does not corresponds
* with any persisted person.
*/
//JBBC
public Person get(int id)
throws DAOException, IllegalArgumentException {//Igual que en users
try (final Connection conn = this.getConnection()) {//consegimos coneción bd
final String query = "SELECT * FROM people WHERE id=?";//creamos consulta
try (final PreparedStatement statement = conn.prepareStatement(query)) {//conseguimos statement
statement.setInt(1, id);//doy valor a ?- ls 1º valor 1
//ejecuto consulta que devuelve result set- y conjunto resultado nos da acceso a resultados(iterando)
try (final ResultSet result = statement.executeQuery()) {
if (result.next()) {
return rowToEntity(result);
} else {
throw new IllegalArgumentException("Invalid id");
}
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error getting a person", e);
throw new DAOException(e);
}
}
/**
* Returns a list with all the people persisted in the system.
*
* @return a list with all the people persisted in the system.
* @throws DAOException if an error happens while retrieving the people.
*/
public List<Person> list() throws DAOException {//todas las personas
try (final Connection conn = this.getConnection()) {
final String query = "SELECT * FROM people";
try (final PreparedStatement statement = conn.prepareStatement(query)) {
try (final ResultSet result = statement.executeQuery()) {//manejo ResultSet
final List<Person> people = new LinkedList<>();
while (result.next()) {//mientras haya resultados se añadiran
people.add(rowToEntity(result));//dame valor de ese campo, creando persona
}
return people;
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error listing people", e);
throw new DAOException(e);
}
}
/**
* Persists a new person in the system. An identifier will be assigned
* automatically to the new person.
*
* @param name name of the new person. Can't be {@code null}.
* @param surname surname of the new person. Can't be {@code null}.
* @return a {@link Person} entity representing the persisted person.
* @throws DAOException if an error happens while persisting the new person.
* @throws IllegalArgumentException if the name or surname are {@code null}.
*/
public Person add(String name, String surname)
throws DAOException, IllegalArgumentException {//añadir persona
if (name == null || surname == null) {
throw new IllegalArgumentException("name and surname can't be null"); //no se puede insertar vacio
}
try (Connection conn = this.getConnection()) {//si se puede conectar con BD
final String query = "INSERT INTO people VALUES(null, ?, ?)"; //se realiza insercion
try (PreparedStatement statement = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, name);
statement.setString(2, surname);
if (statement.executeUpdate() == 1) {
try (ResultSet resultKeys = statement.getGeneratedKeys()) {
if (resultKeys.next()) {
return new Person(resultKeys.getInt(1), name, surname);
} else {
LOG.log(Level.SEVERE, "Error retrieving inserted id");
throw new SQLException("Error retrieving inserted id");
}
}
} else {
LOG.log(Level.SEVERE, "Error inserting value");
throw new SQLException("Error inserting value");
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error adding a person", e);
throw new DAOException(e);
}
}
/**
* Modifies a person previously persisted in the system. The person will be
* retrieved by the provided id and its current name and surname will be
* replaced with the provided.
*
* @param person a {@link Person} entity with the new data.
* @throws DAOException if an error happens while modifying the new person.
* @throws IllegalArgumentException if the person is {@code null}.
*/
public void modify(Person person)
throws DAOException, IllegalArgumentException {
if (person == null) {
throw new IllegalArgumentException("person can't be null");
}
try (Connection conn = this.getConnection()) {
final String query = "UPDATE people SET name=?, surname=? WHERE id=?";
try (PreparedStatement statement = conn.prepareStatement(query)) {
statement.setString(1, person.getName());
statement.setString(2, person.getSurname());
statement.setInt(3, person.getId());
if (statement.executeUpdate() != 1) {
throw new IllegalArgumentException("name and surname can't be null");
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error modifying a person", e);
throw new DAOException();
}
}
/**
* Removes a persisted person from the system.
*
* @param id identifier of the person to be deleted.
* @throws DAOException if an error happens while deleting the person.
* @throws IllegalArgumentException if the provided id does not corresponds
* with any persisted person.
*/
public void delete(int id)
throws DAOException, IllegalArgumentException {
try (final Connection conn = this.getConnection()) {
final String query = "DELETE FROM people WHERE id=?";
try (final PreparedStatement statement = conn.prepareStatement(query)) {
statement.setInt(1, id);
if (statement.executeUpdate() != 1) {
throw new IllegalArgumentException("Invalid id");
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error deleting a person", e);
throw new DAOException(e);
}
}
private Person rowToEntity(ResultSet row) throws SQLException {
return new Person(
row.getInt("id"),
row.getString("name"),
row.getString("surname")
);
}
}
package es.uvigo.esei.daa.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import es.uvigo.esei.daa.entities.Pet;
/**
* DAO class for the {@link Pet} entities.
*
* @author Iria Martínez Álvarez
*
*/
public class PetsDAO extends DAO {
private final static Logger LOG = Logger.getLogger(PetsDAO.class.getName());
/**
* Returns a pet stored persisted in the system.
*
* @param id identifier of the pet.
* @param id_person identifier of the pet.
* @return a pet with the provided identifier.
* @throws DAOException if an error happens while retrieving the pet.
* @throws IllegalArgumentException if the provided id does not corresponds
* with any persisted pet.
*/
public Pet get(int id)
throws DAOException, IllegalArgumentException {//Igual que en users
try (final Connection conn = this.getConnection()) {
final String query = "SELECT * FROM pets WHERE id=?";
try (final PreparedStatement statement = conn.prepareStatement(query)) {
statement.setInt(1, id);
try (final ResultSet result = statement.executeQuery()) {
if (result.next()) {
return rowToEntity(result);
} else {
throw new IllegalArgumentException("Invalid id");
}
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error getting a pet", e);
throw new DAOException(e);
}
}
/**
* Returns a list with all the pets persisted in the system.
*
* @return a list with all the pets persisted in the system.
* @throws DAOException if an error happens while retrieving the pets.
*/
public List<Pet> listPets(int id_person) throws DAOException {// todas las pets
try (final Connection conn = this.getConnection()) {
final String query = "SELECT * FROM pets WHERE id_person=?";
try (final PreparedStatement statement = conn.prepareStatement(query)) {
statement.setInt(1, id_person);
try (final ResultSet result = statement.executeQuery()) {
final List<Pet> pets = new LinkedList<>();
while (result.next()) {
pets.add(rowToEntity(result));
}
return pets;
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error listing pets", e);
throw new DAOException(e);
}
}
/**
* Persists a new pet in the system. An identifier will be assigned
* automatically to the new pet.
*
* @param name name of the new pet. Can't be {@code null}.
* @param food food of the new pet. Can't be {@code null}.
* @param id_person id_person of the new pet. Can't be {@code null}.
* @return a {@link Pet} entity representing the persisted pet.
* @throws DAOException if an error happens while persisting the new pet.
* @throws IllegalArgumentException if the name or food are {@code null}.
*/
public Pet add(String name, String food, int id_person)
throws DAOException, IllegalArgumentException {// añadir pet
if (name == null || food == null) {
throw new IllegalArgumentException("name and food can't be null"); // no se puede insertar vacio
}
try (Connection conn = this.getConnection()) {// si se puede conectar con BD
System.err.println(name + " - " + food + " - " + id_person);
final String query = "INSERT INTO pets (name, food, id_person) VALUES (?, ?, ?)"; // se realiza
// insercion
try (PreparedStatement statement = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, name);
statement.setString(2, food);
statement.setInt(3, id_person);
if (statement.executeUpdate() == 1) {
try (ResultSet resultKeys = statement.getGeneratedKeys()) {
if (resultKeys.next()) {
return new Pet(resultKeys.getInt(1), name, food, id_person);
} else {
LOG.log(Level.SEVERE, "Error retrieving inserted id");
throw new SQLException("Error retrieving inserted id");
}
}
} else {
LOG.log(Level.SEVERE, "Error inserting value");
throw new SQLException("Error inserting value");
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error adding a pet", e);
throw new DAOException(e);
}
}
/**
* Modifies a pet previously persisted in the system. The pet will be
* retrieved by the provided id and its current name,id_person and food will be
* replaced with the provided.
*
* @param pet a {@link Pet} entity with the new data.
* @throws DAOException if an error happens while modifying the new pet.
* @throws IllegalArgumentException if the pet is {@code null}.
*/
public void modify(Pet pet)
throws DAOException, IllegalArgumentException {
if (pet == null) {
throw new IllegalArgumentException("pet can't be null");
}
try (Connection conn = this.getConnection()) {
final String query = "UPDATE pets SET name=?, food=?, id_person=? WHERE id=?";
try (PreparedStatement statement = conn.prepareStatement(query)) {
statement.setString(1, pet.getName());
statement.setString(2, pet.getFood());
statement.setInt(3, pet.getId_person());
statement.setInt(4, pet.getId());
if (statement.executeUpdate() != 1) {
throw new IllegalArgumentException("name,id_person and food can't be null");
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error modifying a pet", e);
throw new DAOException();
}
}
/**
* Removes a persisted pet from the system.
*
* @param id identifier of the pet to be deleted.
* @throws DAOException if an error happens while deleting the pet.
* @throws IllegalArgumentException if the provided id does not corresponds
* with any persisted pet.
*/
public void delete(int id)
throws DAOException, IllegalArgumentException {
try (final Connection conn = this.getConnection()) {
final String query = "DELETE FROM pets WHERE id=?";
try (final PreparedStatement statement = conn.prepareStatement(query)) {
statement.setInt(1, id);
if (statement.executeUpdate() != 1) {
throw new IllegalArgumentException("Invalid id");
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error deleting a pet", e);
throw new DAOException(e);
}
}
private Pet rowToEntity(ResultSet row) throws SQLException {
return new Pet(
row.getInt("id"),
row.getString("name"),
row.getString("food"),
row.getInt("id_person")
);
}
}
package es.uvigo.esei.daa.dao;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import es.uvigo.esei.daa.entities.User;
/**
* DAO class for managing the users of the system.
*
* @author Miguel Reboiro Jato
*/
public class UsersDAO extends DAO {
private final static Logger LOG = Logger.getLogger(UsersDAO.class.getName());
/**
* Returns a user stored persisted in the system.
*
* @param login the login of the user to be retrieved.
* @return a user with the provided login.
* @throws DAOException if an error happens while retrieving the user.
* @throws IllegalArgumentException if the provided login does not
* corresponds with any persisted user.
*/
public User get(String login) throws DAOException { //obtiene usuario
try (final Connection conn = this.getConnection()) {//prueba a establecer conexión con BD
//se establece conexión
final String query = "SELECT * FROM users WHERE login=?";//busco que coincida el login
try (final PreparedStatement statement = conn.prepareStatement(query)) {//secuencias que se puede ejecutar varias veces pero con distintos parametros
statement.setString(1, login);
try (final ResultSet result = statement.executeQuery()) {
if (result.next()) {
return rowToEntity(result);
} else {
throw new IllegalArgumentException("Invalid id");
}
}
}
} catch (SQLException e) {
LOG.log(Level.SEVERE, "Error checking login", e);
throw new DAOException(e);
}
}
/**
* Checks if the provided credentials (login and password) correspond with a
* valid user registered in the system.
*
* <p>The password is stored in the system "salted" and encoded with the
* SHA-256 algorithm.</p>
*
* @param login the login of the user.
* @param password the password of the user.
* @return {@code true} if the credentials are valid. {@code false}
* otherwise.
* @throws DAOException if an error happens while checking the credentials.
*/
public boolean checkLogin(String login, String password) throws DAOException {//comprueba credenciales
try {
final User user = this.get(login);//obtenemos login
final String dbPassword = user.getPassword();//obtenemos contraseña
final String shaPassword = encodeSha256(password);//descodificamos
return shaPassword.equals(dbPassword);//miramos que coincida
} catch (IllegalArgumentException iae) {
return false;
}
}
private final static String encodeSha256(String text) {//descodificamos
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] digested = digest.digest(text.getBytes());
return hexToString(digested);
} catch (NoSuchAlgorithmException e) {
LOG.log(Level.SEVERE, "SHA-256 not supported", e);
throw new RuntimeException(e);
}
}
private final static String hexToString(byte[] hex) {//parte descodificación
final StringBuilder sb = new StringBuilder();
for (byte b : hex) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
private User rowToEntity(ResultSet result) throws SQLException {//devuelve usuario
return new User(
result.getString("login"),
result.getString("password"),
result.getString("role")
);
}
}
package es.uvigo.esei.daa.entities;
import static java.util.Objects.requireNonNull;
/**
* An entity that represents a person.
*
* @author Miguel Reboiro Jato
*/
public class Person {
private int id;
private String name;
private String surname;
// Constructor needed for the JSON conversion
Person() {}
/**
* Constructs a new instance of {@link Person}.
*
* @param id identifier of the person.
* @param name name of the person.
* @param surname surname of the person.
*/
public Person(int id, String name, String surname) {
this.id = id;
this.setName(name);
this.setSurname(surname);
}
/**
* Returns the identifier of the person.
*
* @return the identifier of the person.
*/
public int getId() {
return id;
}
/**
* Returns the name of the person.
*
* @return the name of the person.
*/
public String getName() {
return name;
}
/**
* Set the name of this person.
*
* @param name the new name of the person.
* @throws NullPointerException if the {@code name} is {@code null}.
*/
public void setName(String name) {
this.name = requireNonNull(name, "Name can't be null");
}
/**
* Returns the surname of the person.
*
* @return the surname of the person.
*/
public String getSurname() {
return surname;
}
/**
* Set the surname of this person.
*
* @param surname the new surname of the person.
* @throws NullPointerException if the {@code surname} is {@code null}.
*/
public void setSurname(String surname) {
this.surname = requireNonNull(surname, "Surname can't be null");
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Person))
return false;
Person other = (Person) obj;
if (id != other.id)
return false;
return true;
}
}
package es.uvigo.esei.daa.entities;
import static java.util.Objects.requireNonNull;
/**
* An entity that represents a pet.
*
* @author Iria Martínez Álvarez
*/
public class Pet {
private int id;
private String name;
private String food;
private int id_person;
// Constructor needed for the JSON conversion
Pet() {}
/**
* Constructs a new instance of {@link Pet}.
*
* @param id identifier of the pet.
* @param name name of the pet.
* @param food food of the pet.
* @param id_person id_person of the pet.
*/
public Pet(int id, String name, String food, int id_person) {
this.id = id;
this.setName(name);
this.setFood(food);
this.setId_person(id_person);
}
/**
* Returns the identifier of the pet.
*
* @return the identifier of the pet.
*/
public int getId() {
return id;
}
/**
* Returns the name of the pet.
*
* @return the name of the pet.
*/
public String getName() {
return name;
}
/**
* Set the name of this pet.
*
* @param name the new name of the pet.
* @throws NullPointerException if the {@code name} is {@code null}.
*/
public void setName(String name) {
this.name = requireNonNull(name, "Name can't be null");
}
/**
* Returns the food of the pet.
*
* @return the food of the pet.
*/
public String getFood() {
return food;
}
/**
* Set the food of this pet.
*
* @param food the new food of the pet.
* @throws NullPointerException if the {@code food} is {@code null}.
*/
public void setFood(String food) {
this.food = requireNonNull(food, "Food can't be null");
}
/**
* Returns the id_person of the pet.
*
* @return the id_person of the pet.
*/
public int getId_person() {
return id_person;
}
/**
* Set the id_person of this pet.
*
* @param id_person the new id_person of the pet.
* @throws NullPointerException if the {@code id_person} is {@code id_person}.
*/
public void setId_person(int id_person) {
this.id_person = requireNonNull(id_person, "Id_person can't be null");
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Pet))
return false;
Pet other = (Pet) obj;
if (id != other.id)
return false;
return true;
}
}
package es.uvigo.esei.daa.entities;
import static java.util.Objects.requireNonNull;
/**
* An entity that represents a user.
*
* @author Miguel Reboiro Jato
*/
public class User {
private String login;
private String password;
private String role;
// Constructor needed for the JSON conversion
User() {}
/**
* Constructs a new instance of {@link User}.
*
* @param login login that identifies the user in the system.
* @param password password of the user encoded using SHA-256 and with the
* "salt" prefix added.
*/
public User(String login, String password, String role) {
this.setLogin(login);
this.setPassword(password);
this.setRole(role);
}
/**
* Returns the login of the user.
*
* @return the login of the user.
*/
public String getLogin() {
return login;
}
/**
* Sets the login of the user.
*
* @param login the login that identifies the user in the system.
*/
public void setLogin(String login) {
this.login = requireNonNull(login, "Login can't be null");
}
/**
* Returns the password of the user.
*
* @return the password of the user.
*/
public String getPassword() {
return password;
}
/**
* Sets the users password.
* @param password the password of the user encoded using SHA-256 and with
* the "salt" prefix added.
*/
public void setPassword(String password) {
requireNonNull(password, "Password can't be null");
if (!password.matches("[a-zA-Z0-9]{64}"))
throw new IllegalArgumentException("Password must be a valid SHA-256");
this.password = password;
}
/**
* Returns the role of the user.
*
* @return the role of the user.
*/
public String getRole() {
return role;
}
/**
* Sets the role of the user.
*
* @param role the role of the user
*/
public void setRole(String role) {
this.role = requireNonNull(role, "Role can't be null");
}
}
package es.uvigo.esei.daa.rest;
//Especificación de java para trabajar con rest
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import es.uvigo.esei.daa.dao.DAOException;
import es.uvigo.esei.daa.dao.PeopleDAO;
import es.uvigo.esei.daa.entities.Person;
/**
* REST resource for managing people.
*
* @author Miguel Reboiro Jato.
*/
@Path("/people")
@Produces(MediaType.APPLICATION_JSON)
public class PeopleResource {
private final static Logger LOG = Logger.getLogger(PeopleResource.class.getName());
private final PeopleDAO dao;
/**
* Constructs a new instance of {@link PeopleResource}.
*/
public PeopleResource() {
this(new PeopleDAO());
}
// Needed for testing purposes
PeopleResource(PeopleDAO dao) {
this.dao = dao;
}
/**
* Returns a person with the provided identifier.
*
* @param id the identifier of the person to retrieve.
* @return a 200 OK response with a person that has the provided identifier.
* If the identifier does not corresponds with any user, a 400 Bad Request
* response with an error message will be returned. If an error happens
* while retrieving the list, a 500 Internal Server Error response with an
* error message will be returned.
*/
@GET
@Path("/{id}")
public Response get(
@PathParam("id") int id
) {//response genera respuestas http de forma sencilla
try {
final Person person = this.dao.get(id);//entidades
return Response.ok(person).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid person id in get method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error getting a person", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Returns the complete list of people stored in the system.
*
* @return a 200 OK response with the complete list of people stored in the
* system. If an error happens while retrieving the list, a 500 Internal
* Server Error response with an error message will be returned.
*/
@GET
public Response list() {
try {
return Response.ok(this.dao.list()).build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error listing people", e);
return Response.serverError().entity(e.getMessage()).build();
}
}
/**
* Creates a new person in the system.
*
* @param name the name of the new person.
* @param surname the surname of the new person.
* @return a 200 OK response with a person that has been created. If the
* name or the surname are not provided, a 400 Bad Request response with an
* error message will be returned. If an error happens while retrieving the
* list, a 500 Internal Server Error response with an error message will be
* returned.
*/
@POST
public Response add(
@FormParam("name") String name,
@FormParam("surname") String surname
) {
try {
final Person newPerson = this.dao.add(name, surname);
return Response.ok(newPerson).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid person id in add method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error adding a person", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Modifies the data of a person.
*
* @param id identifier of the person to modify.
* @param name the new name of the person.
* @param surname the new surname of the person.
* @return a 200 OK response with a person that has been modified. If the
* identifier does not corresponds with any user or the name or surname are
* not provided, a 400 Bad Request response with an error message will be
* returned. If an error happens while retrieving the list, a 500 Internal
* Server Error response with an error message will be returned.
*/
@PUT
@Path("/{id}")
public Response modify(
@PathParam("id") int id,
@FormParam("name") String name,
@FormParam("surname") String surname
) {
try {
final Person modifiedPerson = new Person(id, name, surname);
this.dao.modify(modifiedPerson);
return Response.ok(modifiedPerson).build();
} catch (NullPointerException npe) {
final String message = String.format("Invalid data for person (name: %s, surname: %s)", name, surname);
LOG.log(Level.FINE, message);
return Response.status(Response.Status.BAD_REQUEST)
.entity(message)
.build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid person id in modify method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error modifying a person", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Deletes a person from the system.
*
* @param id the identifier of the person to be deleted.
* @return a 200 OK response with the identifier of the person that has
* been deleted. If the identifier does not corresponds with any user, a 400
* Bad Request response with an error message will be returned. If an error
* happens while retrieving the list, a 500 Internal Server Error response
* with an error message will be returned.
*/
@DELETE
@Path("/{id}")
public Response delete(
@PathParam("id") int id
) {
try {
this.dao.delete(id);
return Response.ok(id).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid person id in delete method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error deleting a person", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
}
package es.uvigo.esei.daa.rest;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import es.uvigo.esei.daa.dao.DAOException;
import es.uvigo.esei.daa.dao.PetsDAO;
import es.uvigo.esei.daa.entities.Pet;
/**
* REST resource for managing pet.
*
* @author Iria Martínez Álvarez.
*/
@Path("/pets")
@Produces(MediaType.APPLICATION_JSON)
public class PetsResource {
private final static Logger LOG = Logger.getLogger(PetsResource.class.getName());
private final PetsDAO dao;
/**
* Constructs a new instance of {@link PetsResource}.
*/
public PetsResource() {
this(new PetsDAO());
}
// Needed for testing purposes
PetsResource(PetsDAO dao) {
this.dao = dao;
}
/**
* Returns a pet with the provided identifier.
*
* @param id the identifier of the pet to retrieve.
* @return a 200 OK response with a pet that has the provided identifier.
* If the identifier does not corresponds with any user, a 400 Bad Request
* response with an error message will be returned. If an error happens
* while retrieving the list, a 500 Internal Server Error response with an
* error message will be returned.
*/
@GET
@Path("/{id}")
public Response get(
@PathParam("id") int id
) {
try {
final Pet pet = this.dao.get(id);
return Response.ok(pet).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid pet id in get method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error getting a pet", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Returns the complete list of pets stored in the system.
*
* @return a 200 OK response with the complete list of pets stored in the
* system. If an error happens while retrieving the list, a 500 Internal
* Server Error response with an error message will be returned.
*/
@GET
public Response listPets(
@QueryParam("id_person") int id_person
) {
try {
return Response.ok(this.dao.listPets(id_person)).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid id_person", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error listing pets", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Creates a new pet in the system.
*
* @param name the name of the new pet.
* @param food the food of the new pet.
* @param id_person the id_person of the new pet.
* @return a 200 OK response with a pet that has been created. If the
* name or the food are not provided, a 400 Bad Request response with an
* error message will be returned. If an error happens while retrieving the
* list, a 500 Internal Server Error response with an error message will be
* returned.
*/
@POST
public Response add(
@FormParam("name") String name,
@FormParam("food") String food,
@FormParam("id_person") int id_person
) {
try {
final Pet newPet = this.dao.add(name, food, id_person);
return Response.ok(newPet).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid pet id in add method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error adding a pet", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Modifies the data of a pet.
*
* @param id identifier of the pet to modify.
* @param name the new name of the pet.
* @param food the new food of the pet.
* @param id_person the new id_person of the pet.
* @return a 200 OK response with a pet that has been modified. If the
* identifier does not corresponds with any user or the name,id_person or food are
* not provided, a 400 Bad Request response with an error message will be
* returned. If an error happens while retrieving the list, a 500 Internal
* Server Error response with an error message will be returned.
*/
@PUT
@Path("/{id}")
public Response modify(
@PathParam("id") int id,
@FormParam("name") String name,
@FormParam("food") String food,
@FormParam("id_person") int id_person
) {
try {
final Pet modifiedPet = new Pet(id, name, food, id_person);
this.dao.modify(modifiedPet);
return Response.ok(modifiedPet).build();
} catch (NullPointerException npe) {
final String message = String.format("Invalid data for peet (name: %s, food: %s, id_person: %d)", name, food);
LOG.log(Level.FINE, message);
return Response.status(Response.Status.BAD_REQUEST)
.entity(message)
.build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid pet id in modify method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error modifying a pet", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
/**
* Deletes a pet from the system.
*
* @param id the identifier of the pet to be deleted.
* @return a 200 OK response with the identifier of the pet that has
* been deleted. If the identifier does not corresponds with any user, a 400
* Bad Request response with an error message will be returned. If an error
* happens while retrieving the list, a 500 Internal Server Error response
* with an error message will be returned.
*/
@DELETE
@Path("/{id}")
public Response delete(
@PathParam("id") int id
) {
try {
this.dao.delete(id);
return Response.ok(id).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid pet id in delete method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error deleting a pet", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
}
}
package es.uvigo.esei.daa.rest;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import es.uvigo.esei.daa.dao.DAOException;
import es.uvigo.esei.daa.dao.UsersDAO;
/**
* REST resource for managing users.
*
* @author Miguel Reboiro Jato.
*/
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UsersResource {
private final static Logger LOG = Logger.getLogger(UsersResource.class.getName());
private final UsersDAO dao;
private @Context SecurityContext security;
/**
* Constructs a new instance of {@link UsersResource}.
*/
public UsersResource() {
this(new UsersDAO());
}
// Needed for testing purposes
UsersResource(UsersDAO dao) {
this(dao, null);
}
// Needed for testing purposes
UsersResource(UsersDAO dao, SecurityContext security) {
this.dao = dao;
this.security = security;
}
/**
* Returns a user with the provided login.
*
* @param login the identifier of the user to retrieve.
* @return a 200 OK response with an user that has the provided login.
* If the request is done without providing the login credentials or using
* invalid credentials a 401 Unauthorized response will be returned. If the
* credentials are provided and a regular user (i.e. non admin user) tries
* to access the data of other user, a 403 Forbidden response will be
* returned. If the credentials are OK, but the login does not corresponds
* with any user, a 400 Bad Request response with an error message will be
* returned. If an error happens while retrieving the list, a 500 Internal
* Server Error response with an error message will be returned.
*/
@GET
@Path("/{login}")
public Response get(
@PathParam("login") String login
) {
final String loggedUser = getLogin();
// Each user can only access his or her own data. Only the admin user
// can access the data of any user.
if (loggedUser.equals(login) || this.isAdmin()) {
try {
return Response.ok(dao.get(login)).build();
} catch (IllegalArgumentException iae) {
LOG.log(Level.FINE, "Invalid user login in get method", iae);
return Response.status(Response.Status.BAD_REQUEST)
.entity(iae.getMessage())
.build();
} catch (DAOException e) {
LOG.log(Level.SEVERE, "Error getting an user", e);
return Response.serverError()
.entity(e.getMessage())
.build();
}
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
private String getLogin() {
return this.security.getUserPrincipal().getName();
}
private boolean isAdmin() {
return this.security.isUserInRole("ADMIN");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>DAAExample</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Authorization</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET, POST, DELETE, PUT</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/rest/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
<role-name>USER</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/rest/people/*</url-pattern>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/rest/pets/*</url-pattern>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
</auth-constraint>
</security-constraint>
<!-- Security roles referenced by this web application -->
<security-role>
<role-name>ADMIN</role-name>
</security-role>
<security-role>
<role-name>USER</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>DAAExample</realm-name>
</login-config>
</web-app>
\ No newline at end of file
html, body {
height: 100%;
}
body {
display: -ms-flexbox;
display: -webkit-box;
display: flex;
-ms-flex-align: center;
-ms-flex-pack: center;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
padding-top: 40px;
padding-bottom: 40px;
}
#form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
#form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
}
#form-signin .form-control:focus {
z-index: 2;
}
#login {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
#password {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
\ No newline at end of file
button,input{
margin-top: 10px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>DAA Example - Login</title>
<link rel="stylesheet" href="css/login.css">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body class="text-center">
<form id="form-singin">
<h1 class="h1 mb-3 font-weight-normal">DAA Example</h1>
<label for="login" class="sr-only">Usuario</label> <input id="login"
name="login" type="text" class="form-control" placeholder="Usuario"
required autofocus /> <label for="password" class="sr-only">Contraseña</label>
<input id="password" name="password" type="password"
class="form-control" placeholder="Contraseña" required />
<button type="submit" class="btn btn-lg btn-primary btn-block mt-3">Entrar</button>
</form>
<script type="text/javascript"
src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
<script
src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script>
<script type="text/javascript" src="js/login.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#form-singin').submit(function(event) {
event.preventDefault();
var login = $('#login').val();
var password = $('#password').val();
doLogin(login, password);
});
});
</script>
</body>
</html>
\ No newline at end of file
var PeopleDAO = (function() {
var resourcePath = "rest/people/";
var requestByAjax = function(data, done, fail, always) {
done = typeof done !== 'undefined' ? done : function() {};
fail = typeof fail !== 'undefined' ? fail : function() {};
always = typeof always !== 'undefined' ? always : function() {};
let authToken = localStorage.getItem('authorization-token');
if (authToken !== null) {
data.beforeSend = function(xhr) {
xhr.setRequestHeader('Authorization', 'Basic ' + authToken);
};
}
$.ajax(data).done(done).fail(fail).always(always);
};
function PeopleDAO() {
this.listPeople = function(done, fail, always) {
requestByAjax({
url : resourcePath,
type : 'GET'
}, done, fail, always);
};
this.addPerson = function(person, done, fail, always) {
requestByAjax({
url : resourcePath,
type : 'POST',
data : person
}, done, fail, always);
};
this.modifyPerson = function(person, done, fail, always) {
requestByAjax({
url : resourcePath + person.id,
type : 'PUT',
data : person
}, done, fail, always);
};
this.deletePerson = function(id, done, fail, always) {
requestByAjax({
url : resourcePath + id,
type : 'DELETE',
}, done, fail, always);
};
}
return PeopleDAO;
})();
\ No newline at end of file
var PetsDAO = (function() {
var resourcePath = "rest/pets/";
var requestByAjax = function(data, done, fail, always) {
done = typeof done !== 'undefined' ? done : function() {};
fail = typeof fail !== 'undefined' ? fail : function() {};
always = typeof always !== 'undefined' ? always : function() {};
let authToken = localStorage.getItem('authorization-token');
if (authToken !== null) {
data.beforeSend = function(xhr) {
xhr.setRequestHeader('Authorization', 'Basic ' + authToken);
};
}
$.ajax(data).done(done).fail(fail).always(always);
};
function PetsDAO() {
this.listPets = function(id_person,done, fail, always) {
requestByAjax({
url : resourcePath + '?id_person=' + id_person,
type : 'GET'
}, done, fail, always);
};
this.addPet = function(pet, done, fail, always) {
requestByAjax({
url : resourcePath ,
type : 'POST',
data : pet
}, done, fail, always);
};
this.modifyPet = function(pet, done, fail, always) {
requestByAjax({
url : resourcePath + pet.id,
type : 'PUT',
data : pet
}, done, fail, always);
};
this.deletePet = function(id, done, fail, always) {
requestByAjax({
url : resourcePath + id,
type : 'DELETE',
}, done, fail, always);
};
}
return PetsDAO;
})();
\ No newline at end of file
function doLogin(login, password) {
$.ajax({
url: 'rest/users/' + login,
type: 'GET',
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(login + ":" + password));
}
})
.done(function() {
localStorage.setItem('authorization-token', btoa(login + ":" + password));
window.location = 'main.html';
})
.fail(function() {
alert('Invalid login and/or password.');
});
}
function doLogout() {
localStorage.removeItem('authorization-token');
window.location = 'index.html';
}
\ No newline at end of file
var PeopleView = (function() {
var dao;
// Referencia a this que permite acceder a las funciones públicas desde las funciones de jQuery.
var self;
var viewPets;
var formId = 'people-form';
var listId = 'people-list';
var formQuery = '#' + formId;
var listQuery = '#' + listId;
function PeopleView(peopleDao, formContainerId, listContainerId, viewP) {
dao = peopleDao;
self = this;
viewPets=viewP
this.init = function() {
insertPeopleForm($('#' + formContainerId));
insertPeopleList($('#' + listContainerId));
dao.listPeople(function(people) {
$.each(people, function(key, person) {
appendToTable(person);
});
},
function() {//falla
alert('No has sido posible acceder al listado de personas.');
});
// La acción por defecto de enviar formulario (submit) se sobreescribe
// para que el envío sea a través de AJAX
$(formQuery).submit(function(event) {
var person = self.getPersonInForm();
if (self.isEditing()) {
dao.modifyPerson(person,
function(person) {
$('#person-' + person.id + ' td.name').text(person.name);
$('#person-' + person.id + ' td.surname').text(person.surname);
self.resetForm();
},
showErrorMessage,
self.enableForm
);
} else {
dao.addPerson(person,
function(person) {
appendToTable(person);
self.resetForm();
},
showErrorMessage,
self.enableForm
);
}
return false;
});
$('#btnClear').click(this.resetForm);
};
this.getPersonInForm = function() {
var form = $(formQuery);
return {
'id': form.find('input[name="id"]').val(),
'name': form.find('input[name="name"]').val(),
'surname': form.find('input[name="surname"]').val()
};
};
this.getPersonInRow = function(id) {
var row = $('#person-' + id);
if (row !== undefined) {
return {
'id': id,
'name': row.find('td.name').text(),
'surname': row.find('td.surname').text()
};
} else {
return undefined;
}
};
this.editPerson = function(id) {
var row = $('#person-' + id);
if (row !== undefined) {
var form = $(formQuery);
form.find('input[name="id"]').val(id);
form.find('input[name="name"]').val(row.find('td.name').text());
form.find('input[name="surname"]').val(row.find('td.surname').text());
$('input#btnSubmit').val('Modificar');
}
};
this.deletePerson = function(id) {
if (confirm('Está a punto de eliminar a una persona. ¿Está seguro de que desea continuar?')) {
dao.deletePerson(id,
function() {
$('tr#person-' + id).remove();
},
showErrorMessage
);
}
};
this.petPerson = function(id) {
$('#' + formContainerId).empty();
$('#' + listContainerId).empty();
viewPets.init(self,id);
};
this.isEditing = function() {
return $(formQuery + ' input[name="id"]').val() != "";
};
this.disableForm = function() {
$(formQuery + ' input').prop('disabled', true);
};
this.enableForm = function() {
$(formQuery + ' input').prop('disabled', false);
};
this.resetForm = function() {
$(formQuery)[0].reset();
$(formQuery + ' input[name="id"]').val('');
$('#btnSubmit').val('Crear');
};
};
var insertPeopleList = function(parent) {
parent.append(
'<table id="' + listId + '" class="table">\
<thead>\
<tr class="row">\
<th class="col-sm-3">Nombre</th>\
<th class="col-sm-4">Apellido</th>\
<th class="col-sm-5">&nbsp;</th>\
</tr>\
</thead>\
<tbody>\
</tbody>\
</table>'
);
};
var insertPeopleForm = function(parent) {
parent.append(
'<form id="' + formId + '" class="mb-5 mb-10">\
<input name="id" type="hidden" value=""/>\
<div class="row">\
<div class="col-sm-4">\
<input name="name" type="text" value="" placeholder="Nombre" class="form-control" required/>\
</div>\
<div class="col-sm-4">\
<input name="surname" type="text" value="" placeholder="Apellido" class="form-control" required/>\
</div>\
<div class="col-sm-4">\
<input id="btnSubmit" type="submit" value="Crear" class="btn btn-primary" />\
<input id="btnClear" type="reset" value="Limpiar" class="btn" />\
</div>\
</div>\
</form>'
);
};
var createPersonRow = function(person) {
return '<tr id="person-'+ person.id +'" class="row">\
<td class="name col-sm-3">' + person.name + '</td>\
<td class="surname col-sm-4">' + person.surname + '</td>\
<td class="col-sm-5">\
<a class="edit btn btn-primary" href="#">Editar</a>\
<a class="delete btn btn-warning" href="#">Eliminar</a>\
<a class="pet btn btn-primary" href="#">Mascotas</a>\
</td>\
</tr>';
};
var showErrorMessage = function(jqxhr, textStatus, error) {
alert(textStatus + ": " + error);
};
var addRowListeners = function(person) {
$('#person-' + person.id + ' a.edit').click(function() {
self.editPerson(person.id);
});
$('#person-' + person.id + ' a.delete').click(function() {
self.deletePerson(person.id);
});
$('#person-' + person.id + ' a.pet').click(function() {
self.petPerson(person.id);
});
};
var appendToTable = function(person) {
$(listQuery + ' > tbody:last')
.append(createPersonRow(person));
addRowListeners(person);
};
return PeopleView;
})();
var PetsView = (function() {//creo clase
var dao;
// Referencia a this que permite acceder a las funciones públicas desde las funciones de jQuery.
//estas variables son atributos privados clase
var self;
var peopleView;
var formId = 'pets-form';
var listId = 'pets-list';
var formQuery = '#' + formId;
var listQuery = '#' + listId;
function PetsView(petsDao, formContainerId, listContainerId) {//constructor
dao = petsDao;//se guarda internamente
self = this;
//metodos internas=publicas
this.init = function(peopleView,id) {//peopleView se pasa para el volver
peopleView=peopleView;
self.peopleView=peopleView;
id_person=id;
insertPetsForm($('#' + formContainerId));//se guarda form de pets- busco elem con ese id. Se añade html
insertPetsList($('#' + listContainerId));//se guarda lista de pets- se añade html
dao.listPets(id_person,function(pets) {//se pide al dao que pida al backend lista de pets
$.each(pets, function(key, pet) {//para cada pet
appendToTable(pet);//se añade tabla
});
},
function() {
alert('No has sido posible acceder al listado de mascotas.');
});
// La acción por defecto de enviar formulario (submit) se sobreescribe
// para que el envío sea a través de AJAX
$(formQuery).submit(function(event) {//se coge formulario en evento submit y se sustituye por peticion js
var pet = self.getPetInForm();
if (self.isEditing()) {//si se esta editando
dao.modifyPet(pet,
function(pet) {
$('#pet-' + pet.id + ' td.name').text(pet.name);
$('#pet-' + pet.id + ' td.food').text(pet.food);
self.resetForm();
},
showErrorMessage,
self.enableForm
);
} else {
dao.addPet(pet,
function(pet) {//si se esta añadiendo
appendToTable(pet,peopleView);//añade tabla
self.resetForm();
},
showErrorMessage,
self.enableForm
);
}
return false;
});
$('#btnClear').click(this.resetForm);
$('#btnReturn').click(this.returnPeople);
};
this.getPetInForm = function() {
var form = $(formQuery);
return {
'id': form.find('input[name="id"]').val(),
'name': form.find('input[name="name"]').val(),
'food': form.find('input[name="food"]').val(),
'id_person': form.find('input[name="id_person"]').val()
};
};
this.getPetInRow = function(id) {
var row = $('#pet-' + id);
if (row !== undefined) {
return {
'id': id,
'name': row.find('td.name').text(),
'food': row.find('td.food').text(),
'id_person': row.find('td.id_person').text()
};
} else {
return undefined;
}
};
this.editPet = function(id) {
var row = $('#pet-' + id);
if (row !== undefined) {
var form = $(formQuery);
form.find('input[name="id"]').val(id);
form.find('input[name="name"]').val(row.find('td.name').text());
form.find('input[name="food"]').val(row.find('td.food').text());
form.find('input[name="id_person"]').val(id_person);
$('input#btnSubmit').val('Modificar');
}
};
this.deletePet = function(id) {
if (confirm('Está a punto de eliminar a una mascota. ¿Está seguro de que desea continuar?')) {
dao.deletePet(id,
function() {
$('tr#pet-' + id).remove();
},
showErrorMessage
);
}
};
this.isEditing = function() {
return $(formQuery + ' input[name="id"]').val() != "";
};
this.disableForm = function() {
$(formQuery + ' input').prop('disabled', true);
};
this.enableForm = function() {
$(formQuery + ' input').prop('disabled', false);
};
this.resetForm = function() {
$(formQuery)[0].reset();
$(formQuery + ' input[name="id"]').val('');
$('#btnSubmit').val('Crear');
};
this.returnPeople = function() {
window.history.back();
};
};
var insertPetsList = function(parent) {
parent.append(
'<table id="' + listId + '" class="table">\
<thead>\
<tr class="row">\
<th class="col-sm-4">Nombre</th>\
<th class="col-sm-5">Comida</th>\
<th class="col-sm-3">&nbsp;</th>\
</tr>\
</thead>\
<tbody>\
</tbody>\
</table>'
);
};
//metodos privadas
var insertPetsForm = function(parent) {
parent.append(
'<form id="' + formId + '" class="mb-5 mb-10">\
<h1>Mascotas</h1>\
<input name="id" type="hidden" value=""/>\
<div class="col-sm-3">\
<input id="btnReturn" type="button" value="Volver" class="btn btn-primary my-2" />\
</div>\
<div class="row">\
<div class="col-sm-3">\
<input name="name" type="text" value="" input-sm placeholder="Nombre" class="form-control" required/>\
</div>\
<div class="col-sm-3">\
<input name="food" type="text" value="" input-sm placeholder="Comida" class="form-control" required/>\
</div>\
<div class="col-sm-3">\
<input name="id_person" type="text" input-sm value="' + id_person + '" placeholder="Propietario" class="form-control" readonly />\
</div>\
<div class="col-sm-3">\
<input id="btnSubmit" type="submit" value="Crear" class="btn btn-primary" />\
<input id="btnClear" type="reset" value="Limpiar" class="btn" />\
</div>\
</div>\
</form>'
);
};
var createPetsRow = function(pet) {
return '<tr id="pet-'+ pet.id +'" class="row">\
<td class="name col-sm-3">' + pet.name + '</td>\
<td class="name col-sm-3">' + pet.id_person + '</td>\
<td class="food col-sm-3">' + pet.food + '</td>\
<td class="col-sm-3">\
<a class="edit btn btn-primary" href="#">Editar</a>\
<a class="delete btn btn-warning" href="#">Eliminar</a>\
</td>\
</tr>';
};
var showErrorMessage = function(jqxhr, textStatus, error) {
alert(textStatus + ": " + error);
};
var addRowListeners = function(pet) {
$('#pet-' + pet.id + ' a.edit').click(function() {
self.editPet(pet.id);
});
$('#pet-' + pet.id + ' a.delete').click(function() {
self.deletePet(pet.id);
});
};
var appendToTable = function(pet,peopleView) {
$(listQuery + ' > tbody:last')//coge la tabla, coge ultimo elem
.append(createPetsRow(pet));//se añade elem html
addRowListeners(pet);//añade listeners(botones)
};
return PetsView;
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DAA Example</title>
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
<header>
<div class="navbar navbar-dark bg-dark box-shadow">
<div class="container d-flex justify-content-between">
<a href="#" class="navbar-brand d-flex align-items-center">
<strong>DAA Example</strong>
</a>
<button id="logout" class="btn btn-dark">Cerrar sesión</button>
</div>
</div>
</header>
<div class="container">
<div id="people-container">
<h1 class="display-5 mt-3 mb-3">Personas</h1>
</div>
</div>
<script type="text/javascript"
src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="js/dao/people.js"></script>
<script type="text/javascript" src="js/dao/pets.js"></script>
<script type="text/javascript" src="js/view/pets.js"></script>
<script type="text/javascript" src="js/view/people.js"></script>
<script type="text/javascript" src="js/login.js"></script>
<script type="text/javascript">
$(document).ready(//cuando documento sea cargado se ejecuta
function() {
$('#logout').click(function(event) {//codigo al boton de logout
event.preventDefault();
doLogout();
});
var viewP = new PetsView(new PetsDAO(),
'people-container', 'people-container'
);
var view = new PeopleView(new PeopleDAO(), //crea clase javascript de vista a la que le pasa la de dao y les dice donde meter tabla y formulario
'people-container', 'people-container',viewP//formulario,tabla
);
view.init();//al div añade formulario y tabla
});
</script>
</body>
</html>
\ No newline at end of file
package es.uvigo.esei.daa;
import static java.util.Collections.unmodifiableSet;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import es.uvigo.esei.daa.filters.AuthorizationFilter;
@ApplicationPath("/rest/*")
public class DAAExampleTestApplication extends DAAExampleApplication {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>(super.getClasses());
classes.add(AuthorizationFilter.class);
return unmodifiableSet(classes);
}
}
package es.uvigo.esei.daa.dataset;
import static java.util.Arrays.binarySearch;
import static java.util.Arrays.stream;
import java.util.Arrays;
import java.util.function.Predicate;
import es.uvigo.esei.daa.entities.Person;
public final class PeopleDataset {
private PeopleDataset() {}
public static Person[] people() {
return new Person[] {
new Person(1, "Antón", "Álvarez"),
new Person(2, "Ana", "Amargo"),
new Person(3, "Manuel", "Martínez"),
new Person(4, "María", "Márquez"),
new Person(5, "Lorenzo", "López"),
new Person(6, "Laura", "Laredo"),
new Person(7, "Perico", "Palotes"),
new Person(8, "Patricia", "Pérez"),
new Person(9, "Julia", "Justa"),
new Person(10, "Juan", "Jiménez")
};
}
public static Person[] peopleWithout(int ... ids) {
Arrays.sort(ids);
final Predicate<Person> hasValidId = person ->
binarySearch(ids, person.getId()) < 0;
return stream(people())
.filter(hasValidId)
.toArray(Person[]::new);
}
public static Person person(int id) {
return stream(people())
.filter(person -> person.getId() == id)
.findAny()
.orElseThrow(IllegalArgumentException::new);
}
public static int existentId() {
return 5;
}
public static int nonExistentId() {
return 1234;
}
public static Person existentPerson() {
return person(existentId());
}
public static Person nonExistentPerson() {
return new Person(nonExistentId(), "Jane", "Smith");
}
public static String newName() {
return "John";
}
public static String newSurname() {
return "Doe";
}
public static Person newPerson() {
return new Person(people().length + 1, newName(), newSurname());
}
}
package es.uvigo.esei.daa.dataset;
import java.util.Arrays;
import java.util.Base64;
import es.uvigo.esei.daa.entities.User;
public final class UsersDataset {
private UsersDataset() {}
public static User[] users() {
return new User[] {
new User(adminLogin(), "713bfda78870bf9d1b261f565286f85e97ee614efe5f0faf7c34e7ca4f65baca", "ADMIN"),
new User(normalLogin(), "7bf24d6ca2242430343ab7e3efb89559a47784eea1123be989c1b2fb2ef66e83", "USER")
};
}
public static User user(String login) {
return Arrays.stream(users())
.filter(user -> user.getLogin().equals(login))
.findAny()
.orElseThrow(IllegalArgumentException::new);
}
public static String adminLogin() {
return "admin";
}
public static String normalLogin() {
return "normal";
}
public static String userToken(String login) {
final String chain = login + ":" + login + "pass";
return Base64.getEncoder().encodeToString(chain.getBytes());
}
}
package es.uvigo.esei.daa.entities;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import nl.jqno.equalsverifier.EqualsVerifier;
import nl.jqno.equalsverifier.Warning;
public class PersonUnitTest {
@Test
public void testPersonIntStringString() {
final int id = 1;
final String name = "John";
final String surname = "Doe";
final Person person = new Person(id, name, surname);
assertThat(person.getId(), is(equalTo(id)));
assertThat(person.getName(), is(equalTo(name)));
assertThat(person.getSurname(), is(equalTo(surname)));
}
@Test(expected = NullPointerException.class)
public void testPersonIntStringStringNullName() {
new Person(1, null, "Doe");
}
@Test(expected = NullPointerException.class)
public void testPersonIntStringStringNullSurname() {
new Person(1, "John", null);
}
@Test
public void testSetName() {
final int id = 1;
final String surname = "Doe";
final Person person = new Person(id, "John", surname);
person.setName("Juan");
assertThat(person.getId(), is(equalTo(id)));
assertThat(person.getName(), is(equalTo("Juan")));
assertThat(person.getSurname(), is(equalTo(surname)));
}
@Test(expected = NullPointerException.class)
public void testSetNullName() {
final Person person = new Person(1, "John", "Doe");
person.setName(null);
}
@Test
public void testSetSurname() {
final int id = 1;
final String name = "John";
final Person person = new Person(id, name, "Doe");
person.setSurname("Dolores");
assertThat(person.getId(), is(equalTo(id)));
assertThat(person.getName(), is(equalTo(name)));
assertThat(person.getSurname(), is(equalTo("Dolores")));
}
@Test(expected = NullPointerException.class)
public void testSetNullSurname() {
final Person person = new Person(1, "John", "Doe");
person.setSurname(null);
}
@Test
public void testEqualsObject() {
final Person personA = new Person(1, "Name A", "Surname A");
final Person personB = new Person(1, "Name B", "Surname B");
assertTrue(personA.equals(personB));
}
@Test
public void testEqualsHashcode() {
EqualsVerifier.forClass(Person.class)
.withIgnoredFields("name", "surname")
.suppress(Warning.STRICT_INHERITANCE)
.suppress(Warning.NONFINAL_FIELDS)
.verify();
}
}
package es.uvigo.esei.daa.filters;
import java.io.IOException;
import java.security.Principal;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Provider;
import es.uvigo.esei.daa.dao.DAOException;
import es.uvigo.esei.daa.dao.UsersDAO;
import es.uvigo.esei.daa.entities.User;
/**
* This performs the Basic HTTP authentication following (almost) the same
* rules as the defined in the web.xml file.
*
* @author Miguel Reboiro Jato
*/
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthorizationFilter implements ContainerRequestFilter {
// Add here the list of REST paths that an administrator can access.
private final static List<String> ADMIN_PATHS = Arrays.asList("people");
private final UsersDAO dao;
public AuthorizationFilter() {
this.dao = new UsersDAO();
}
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Get the authentication passed in HTTP headers parameters
final String auth = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (auth == null) {
requestContext.abortWith(createResponse());
} else {
final byte[] decodedToken = Base64.getDecoder()
.decode(auth.substring(6));
final String userColonPass = new String(decodedToken);
final String[] userPass = userColonPass.split(":", 2);
if (userPass.length == 2) {
try {
if (this.dao.checkLogin(userPass[0], userPass[1])) {
final User user = this.dao.get(userPass[0]);
if (isAdminPath(requestContext) && !user.getRole().equals("ADMIN")) {
requestContext.abortWith(createResponse());
} else {
requestContext.setSecurityContext(new UserSecurityContext(user));
}
} else {
requestContext.abortWith(createResponse());
}
} catch (DAOException e) {
requestContext.abortWith(createResponse());
}
} else {
requestContext.abortWith(createResponse());
}
}
}
private static boolean isAdminPath(ContainerRequestContext context) {
final List<PathSegment> pathSegments = context.getUriInfo().getPathSegments();
if (pathSegments.isEmpty()) {
return false;
} else {
final String path = pathSegments.get(0).getPath();
return ADMIN_PATHS.contains(path);
}
}
private static Response createResponse() {
return Response.status(Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"DAAExample\"")
.entity("Page requires login.")
.build();
}
private static final class UserSecurityContext implements SecurityContext {
private final User user;
private UserSecurityContext(User user) {
this.user = user;
}
@Override
public boolean isUserInRole(String role) {
return user.getRole().equals(role);
}
@Override
public boolean isSecure() {
return false;
}
@Override
public Principal getUserPrincipal() {
return new Principal() {
@Override
public String getName() {
return user.getLogin();
}
};
}
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
}
}
package es.uvigo.esei.daa.listeners;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ApplicationContextBindings.class)
public @interface ApplicationContextBinding {
public String jndiUrl();
public String name() default "";
public Class<?> type() default None.class;
public final static class None {}
}
package es.uvigo.esei.daa.listeners;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContextBindings {
public ApplicationContextBinding[] value();
}
package es.uvigo.esei.daa.listeners;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import es.uvigo.esei.daa.listeners.ApplicationContextBinding.None;
public class ApplicationContextJndiBindingTestExecutionListener extends AbstractTestExecutionListener {
private SimpleNamingContextBuilder contextBuilder;
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
final Class<?> testClass = testContext.getTestClass();
final ApplicationContextBinding[] bindings = testClass.getAnnotationsByType(ApplicationContextBinding.class);
this.contextBuilder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
for (ApplicationContextBinding binding : bindings) {
final String bindingName = binding.name();
final Class<?> bindingType = binding.type();
Object bean;
if (bindingName.isEmpty() && bindingType.equals(None.class)) {
throw new IllegalArgumentException("name or type attributes must be configured in ApplicationContextBinding");
} else if (bindingName.isEmpty()) {
bean = testContext.getApplicationContext().getBean(bindingType);
} else if (bindingType.equals(None.class)) {
bean = testContext.getApplicationContext().getBean(bindingName);
} else {
bean = testContext.getApplicationContext().getBean(bindingName, bindingType);
}
this.contextBuilder.bind(binding.jndiUrl(), bean);
}
}
@Override
public void afterTestClass(TestContext testContext) throws Exception {
this.contextBuilder.clear();
this.contextBuilder = null;
}
}
package es.uvigo.esei.daa.listeners;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbManagement {
public String[] create() default "";
public String[] drop() default "";
public DbManagementAction action() default DbManagementAction.CREATE_DROP;
}
package es.uvigo.esei.daa.listeners;
public enum DbManagementAction {
DROP_CREATE_DROP, CREATE_DROP, ONLY_CREATE, ONLY_DROP;
}
package es.uvigo.esei.daa.listeners;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class DbManagementTestExecutionListener extends AbstractTestExecutionListener {
private DbManagement configuration;
private DataSource datasource;
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
final Class<?> testClass = testContext.getTestClass();
this.configuration = testClass.getAnnotation(DbManagement.class);
if (this.configuration == null)
throw new IllegalStateException(String.format(
"Missing %s annotation in %s class",
DbManagement.class.getSimpleName(), testClass.getName()
));
this.datasource = testContext.getApplicationContext().getBean(DataSource.class);
switch (this.configuration.action()) {
case DROP_CREATE_DROP:
executeDrop();
case CREATE_DROP:
case ONLY_CREATE:
executeCreate();
break;
default:
}
}
@Override
public void afterTestClass(TestContext testContext) throws Exception {
try {
switch (this.configuration.action()) {
case DROP_CREATE_DROP:
case CREATE_DROP:
case ONLY_DROP:
executeDrop();
break;
default:
}
} finally {
this.configuration = null;
this.datasource = null;
}
}
private void executeCreate() throws SQLException, IOException {
this.executeQueries(configuration.create());
}
private void executeDrop() throws SQLException, IOException {
this.executeQueries(configuration.drop());
}
private void executeQueries(String ... queriesPaths)
throws SQLException, IOException {
try (Connection connection = this.datasource.getConnection()) {
try (Statement statement = connection.createStatement()) {
for (String queryPath : queriesPaths) {
final String queries = readFile(queryPath);
for (String query : queries.split(";")) {
query = query.trim();
if (!query.trim().isEmpty()) {
statement.addBatch(query);
}
}
}
statement.executeBatch();
}
}
}
private static String readFile(String path) throws IOException {
final String classpathPrefix = "classpath:";
if (path.startsWith(classpathPrefix)) {
path = path.substring(classpathPrefix.length());
final ClassLoader classLoader =
DbManagementTestExecutionListener.class.getClassLoader();
final InputStream fileIS = classLoader.getResourceAsStream(path);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(fileIS))) {
final StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
} else {
return new String(Files.readAllBytes(Paths.get(path)));
}
}
}
/*
* #%L
* PanDrugsDB Backend
* %%
* Copyright (C) 2015 Fátima Al-Shahrour, Elena Piñeiro, Daniel Glez-Peña and Miguel Reboiro-Jato
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
package es.uvigo.esei.daa.matchers;
import static java.util.Objects.requireNonNull;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.Response.StatusType;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class HasHttpStatus extends TypeSafeMatcher<Response> {
private final StatusType expectedStatus;
public HasHttpStatus(StatusType expectedStatus) {
this.expectedStatus = requireNonNull(expectedStatus);
}
public HasHttpStatus(int expectedStatus) {
this(Status.fromStatusCode(expectedStatus));
}
@Override
public void describeTo(Description description) {
description.appendValue(this.expectedStatus);
}
@Override
protected void describeMismatchSafely(Response item, Description mismatchDescription) {
mismatchDescription.appendText("was ").appendValue(item.getStatusInfo());
}
@Override
protected boolean matchesSafely(Response item) {
return item != null && expectedStatus.getStatusCode() == item.getStatusInfo().getStatusCode();
}
@Factory
public static Matcher<Response> hasHttpStatus(StatusType expectedStatus) {
return new HasHttpStatus(expectedStatus);
}
@Factory
public static Matcher<Response> hasHttpStatus(int expectedStatus) {
return new HasHttpStatus(expectedStatus);
}
@Factory
public static Matcher<Response> hasOkStatus() {
return new HasHttpStatus(Response.Status.OK);
}
@Factory
public static Matcher<Response> hasBadRequestStatus() {
return new HasHttpStatus(Response.Status.BAD_REQUEST);
}
@Factory
public static Matcher<Response> hasInternalServerErrorStatus() {
return new HasHttpStatus(Response.Status.INTERNAL_SERVER_ERROR);
}
@Factory
public static Matcher<Response> hasUnauthorized() {
return new HasHttpStatus(Response.Status.UNAUTHORIZED);
}
@Factory
public static Matcher<Response> hasForbidden() {
return new HasHttpStatus(Response.Status.FORBIDDEN);
}
}
This diff is collapsed.
package es.uvigo.esei.daa.matchers;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import es.uvigo.esei.daa.entities.Person;
public class IsEqualToPerson extends IsEqualToEntity<Person> {
public IsEqualToPerson(Person entity) {
super(entity);
}
@Override
protected boolean matchesSafely(Person actual) {
this.clearDescribeTo();
if (actual == null) {
this.addTemplatedDescription("actual", expected.toString());
return false;
} else {
return checkAttribute("id", Person::getId, actual)
&& checkAttribute("name", Person::getName, actual)
&& checkAttribute("surname", Person::getSurname, actual);
}
}
/**
* Factory method that creates a new {@link IsEqualToEntity} matcher with
* the provided {@link Person} as the expected value.
*
* @param person the expected person.
* @return a new {@link IsEqualToEntity} matcher with the provided
* {@link Person} as the expected value.
*/
@Factory
public static IsEqualToPerson equalsToPerson(Person person) {
return new IsEqualToPerson(person);
}
/**
* Factory method that returns a new {@link Matcher} that includes several
* {@link IsEqualToPerson} matchers, each one using an {@link Person} of the
* provided ones as the expected value.
*
* @param persons the persons to be used as the expected values.
* @return a new {@link Matcher} that includes several
* {@link IsEqualToPerson} matchers, each one using an {@link Person} of the
* provided ones as the expected value.
* @see IsEqualToEntity#containsEntityInAnyOrder(java.util.function.Function, Object...)
*/
@Factory
public static Matcher<Iterable<? extends Person>> containsPeopleInAnyOrder(Person ... persons) {
return containsEntityInAnyOrder(IsEqualToPerson::equalsToPerson, persons);
}
}
package es.uvigo.esei.daa.matchers;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import es.uvigo.esei.daa.entities.Person;
import es.uvigo.esei.daa.entities.User;
public class IsEqualToUser extends IsEqualToEntity<User> {
public IsEqualToUser(User entity) {
super(entity);
}
@Override
protected boolean matchesSafely(User actual) {
this.clearDescribeTo();
if (actual == null) {
this.addTemplatedDescription("actual", expected.toString());
return false;
} else {
return checkAttribute("login", User::getLogin, actual)
&& checkAttribute("password", User::getPassword, actual);
}
}
/**
* Factory method that creates a new {@link IsEqualToEntity} matcher with
* the provided {@link Person} as the expected value.
*
* @param user the expected person.
* @return a new {@link IsEqualToEntity} matcher with the provided
* {@link Person} as the expected value.
*/
@Factory
public static IsEqualToUser equalsToUser(User user) {
return new IsEqualToUser(user);
}
/**
* Factory method that returns a new {@link Matcher} that includes several
* {@link IsEqualToUser} matchers, each one using an {@link Person} of the
* provided ones as the expected value.
*
* @param users the persons to be used as the expected values.
* @return a new {@link Matcher} that includes several
* {@link IsEqualToUser} matchers, each one using an {@link Person} of the
* provided ones as the expected value.
* @see IsEqualToEntity#containsEntityInAnyOrder(java.util.function.Function, Object...)
*/
@Factory
public static Matcher<Iterable<? extends User>> containsPeopleInAnyOrder(User ... users) {
return containsEntityInAnyOrder(IsEqualToUser::equalsToUser, users);
}
}
This diff is collapsed.
This diff is collapsed.
package es.uvigo.esei.daa.suites;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import es.uvigo.esei.daa.rest.PeopleResourceTest;
import es.uvigo.esei.daa.rest.UsersResourceTest;
@SuiteClasses({
PeopleResourceTest.class,
UsersResourceTest.class
})
@RunWith(Suite.class)
public class IntegrationTestSuite {
}
package es.uvigo.esei.daa.suites;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import es.uvigo.esei.daa.entities.PersonUnitTest;
@SuiteClasses({
PersonUnitTest.class
})
@RunWith(Suite.class)
public class UnitTestSuite {
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT dataset (people*, users*)>
<!ELEMENT people EMPTY>
<!ELEMENT users EMPTY>
<!ATTLIST people
id CDATA #IMPLIED
name CDATA #IMPLIED
surname CDATA #IMPLIED
>
<!ATTLIST users
login CDATA #IMPLIED
password CDATA #IMPLIED
role CDATA #IMPLIED
>
This diff is collapsed.
DROP TABLE People IF EXISTS;
DROP TABLE Users IF EXISTS;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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 to comment