From c7d6b7edb9e4f3c6273c63faa7bd083f673d640c Mon Sep 17 00:00:00 2001
From: Lorenzo Ferron <20024182@studenti.uniupo.it>
Date: Sun, 6 Dec 2020 01:46:40 +0100
Subject: [PATCH] Rinomina e aggiunta file sorgente.

Rinominata la directory contenente i file sorgente per l'appendice. Aggiunti i
file sorgenti.
---
 .../appendices/orm/assets/code/.gitkeep       |   0
 .../appendices/orm/assets/snippets/DAO.java   |  87 +++++
 .../appendices/orm/assets/snippets/Login.java |  95 +++++
 .../orm/assets/snippets/LoginDAO.java         |  22 ++
 .../orm/assets/snippets/ResultSetMapper.java  | 339 ++++++++++++++++++
 .../orm/assets/snippets/Signup.java           |  82 +++++
 6 files changed, 625 insertions(+)
 delete mode 100644 src/contents/appendices/orm/assets/code/.gitkeep
 create mode 100644 src/contents/appendices/orm/assets/snippets/DAO.java
 create mode 100644 src/contents/appendices/orm/assets/snippets/Login.java
 create mode 100644 src/contents/appendices/orm/assets/snippets/LoginDAO.java
 create mode 100644 src/contents/appendices/orm/assets/snippets/ResultSetMapper.java
 create mode 100644 src/contents/appendices/orm/assets/snippets/Signup.java

diff --git a/src/contents/appendices/orm/assets/code/.gitkeep b/src/contents/appendices/orm/assets/code/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/contents/appendices/orm/assets/snippets/DAO.java b/src/contents/appendices/orm/assets/snippets/DAO.java
new file mode 100644
index 0000000..f94e265
--- /dev/null
+++ b/src/contents/appendices/orm/assets/snippets/DAO.java
@@ -0,0 +1,87 @@
+package com.trimaral.dao;
+
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.trimaral.orm.ResultSetMapper;
+
+import javax.sql.DataSource;
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * @param <T> a DAO class
+ * @author Lorenzo Ferron
+ * @version 2019.09.07
+ */
+public abstract class DAO<T extends Serializable> {
+
+    protected final ResultSetMapper<T> resultSetMapper;
+    protected final DataSource dataSource;
+
+    protected DAO(DataSource dataSource, Class<T> clazz) {
+        this.dataSource = dataSource;
+        this.resultSetMapper = new ResultSetMapper<>(dataSource, clazz);
+    }
+
+    public static String getQuotedString(String s) {
+        return "'" + s + "'";
+    }
+
+    public List<T> findAll() throws SQLException, NumberParseException {
+        return findByCriteria(null);
+    }
+
+    public List<T> findByCriteria(String criteria, Object... params) throws SQLException, NumberParseException {
+        return resultSetMapper.findByCriteria(criteria, params);
+    }
+
+    public T findOneByCriteria(String criteria, Object... params) throws SQLException, NumberParseException {
+        resultSetMapper.getByCriteria(criteria, params);
+        try {
+            return findByCriteria(criteria, params).get(0);
+        } catch (IndexOutOfBoundsException e) {
+            return null;
+        }
+    }
+
+    public T findById(Long id) throws SQLException, NumberParseException {
+        return findOneByCriteria(resultSetMapper.findById(), id);
+    }
+
+    public void save(T item) throws SQLException, NumberParseException {
+        resultSetMapper.save(item, false, false);
+    }
+
+    public void replace(T item) throws SQLException, NumberParseException {
+        resultSetMapper.save(item, true, false);
+    }
+
+    public void update(T item) throws SQLException, NumberParseException {
+        resultSetMapper.update(item);
+    }
+
+    public void updateByCriteria(T item, String criteria, Object... params) throws SQLException, NumberParseException {
+        resultSetMapper.updateByCriteria(item, criteria, params);
+    }
+
+    public void updatesByCriteria(T[] items, String criteria, Object... params) throws SQLException, NumberParseException {
+        for (T item : items)
+            updateByCriteria(item, criteria, params);
+    }
+
+    public void delete(T item) throws SQLException, NumberParseException {
+        resultSetMapper.delete(item);
+    }
+
+    public void deleteByCriteria(String criteria, Object... params) throws SQLException, NumberParseException {
+        resultSetMapper.deleteByCriteria(criteria, params);
+    }
+
+    public void deleteById(Long id) throws SQLException, NumberParseException {
+        resultSetMapper.deleteById(id);
+    }
+
+    public Object customQuery(String query, Object... params) throws SQLException, NumberParseException {
+        return resultSetMapper.customQuery(query, params);
+    }
+}
diff --git a/src/contents/appendices/orm/assets/snippets/Login.java b/src/contents/appendices/orm/assets/snippets/Login.java
new file mode 100644
index 0000000..6217df5
--- /dev/null
+++ b/src/contents/appendices/orm/assets/snippets/Login.java
@@ -0,0 +1,95 @@
+package com.trimaral.entity;
+
+import com.google.common.base.Preconditions;
+import com.trimaral.orm.annotations.Column;
+import com.trimaral.orm.annotations.Table;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * @author Lorenzo Ferron
+ * @version 2019.09.05
+ */
+@Table(name = "login")
+public class Login implements Serializable {
+
+    public static final short PASSWORD_LENGTH = 8;
+
+    @Column(name = "password")
+    private String password;
+
+    @Column(name = "is_valid", hasDefaultValue = true)
+    private Boolean isValid;
+
+    @Column(name = "login_id", isPrimaryKey = true)
+    private Long loginId;
+
+    @Column(name = "email")
+    private String email;
+
+    @Column(name = "enroll_date", hasDefaultValue = true, hold = true)
+    private Timestamp enrollDate;
+
+    private List<EmailConfirmation> confirmations;
+
+    public Login() {
+        confirmations = new ArrayList<>(0);
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    public Boolean getIsValid() {
+        return isValid;
+    }
+
+    public void setIsValid(Boolean isValid) {
+        this.isValid = isValid;
+    }
+
+
+    public Long getLoginId() {
+        return loginId;
+    }
+
+    public void setLoginId(Long loginId) {
+        this.loginId = loginId;
+    }
+
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        Preconditions.checkNotNull(email);
+        Preconditions.checkArgument(Pattern.compile("^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])$", Pattern.CASE_INSENSITIVE).matcher(email).matches());
+        this.email = email.trim();
+    }
+
+    public List<EmailConfirmation> getConfirmations() {
+        return confirmations;
+    }
+
+    public void setConfirmations(List<EmailConfirmation> confirmations) {
+        this.confirmations = confirmations;
+    }
+
+    public Timestamp getEnrollDate() {
+        return enrollDate;
+    }
+
+    public void setEnrollDate(Timestamp enrollDate) {
+        this.enrollDate = enrollDate;
+    }
+}
diff --git a/src/contents/appendices/orm/assets/snippets/LoginDAO.java b/src/contents/appendices/orm/assets/snippets/LoginDAO.java
new file mode 100644
index 0000000..0e40341
--- /dev/null
+++ b/src/contents/appendices/orm/assets/snippets/LoginDAO.java
@@ -0,0 +1,22 @@
+package com.trimaral.dao;
+
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.trimaral.entity.Login;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+
+/**
+ * @author Lorenzo Ferron
+ * @version 2019.09.05
+ */
+public class LoginDAO extends DAO<Login> {
+
+    public LoginDAO(DataSource dataSource) {
+        super(dataSource, Login.class);
+    }
+
+    public Login findByEmail(String email) throws SQLException, NumberParseException {
+        return findOneByCriteria("email = " + getQuotedString(email));
+    }
+}
diff --git a/src/contents/appendices/orm/assets/snippets/ResultSetMapper.java b/src/contents/appendices/orm/assets/snippets/ResultSetMapper.java
new file mode 100644
index 0000000..c0f266d
--- /dev/null
+++ b/src/contents/appendices/orm/assets/snippets/ResultSetMapper.java
@@ -0,0 +1,339 @@
+package com.trimaral.orm;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.collect.ObjectArrays;
+import com.google.common.primitives.Primitives;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.trimaral.orm.annotations.Column;
+import com.trimaral.orm.annotations.Table;
+import com.trimaral.orm.exceptions.AnnotationNotFoundException;
+import com.trimaral.orm.exceptions.DAOException;
+import com.trimaral.orm.util.ClassUtil;
+
+import javax.sql.DataSource;
+import java.lang.reflect.Field;
+import java.sql.*;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * @param <T> An entity class
+ * @author Lorenzo Ferron
+ * @version 2019.09.28
+ */
+public class ResultSetMapper<T> {
+
+    public static final String SET_CLAUSE = " SET ";
+    public static final String WHERE_CLAUSE = " WHERE ";
+    public static final String VALUES_CLAUSE = " VALUES ";
+    public static final String FROM_CLAUSE = " FROM ";
+
+    public static final String SELECT_SQL = "SELECT *" + FROM_CLAUSE;
+    public static final String INSERT_SQL = "INSERT ";
+    public static final String IGNORE_SQL = "IGNORE ";
+    public static final String INTO_SQL = "INTO ";
+    public static final String UPDATE_SQL = "UPDATE ";
+    public static final String DELETE_SQL = "DELETE";
+    public static final String DELIMITER = ", ";
+    private static final String REPLACE_SQL = " ON DUPLICATE KEY UPDATE ";
+
+    private final DataSource dataSource;
+    private final Class<T> clazz;
+    private Table table;
+    private Field[] fields;
+    private List<Field> primaryKeyField = new ArrayList<>(0);
+
+    public ResultSetMapper(DataSource dataSource, Class<T> clazz) {
+        this.dataSource = dataSource;
+        this.clazz = clazz;
+
+        if (clazz != null) {
+            if (!clazz.isAnnotationPresent(Table.class))
+                throw new AnnotationNotFoundException();
+            table = clazz.getAnnotation(Table.class);
+            fields = ClassUtil.getAnnotatedDeclaredFields(clazz, Column.class);
+            for (Field field : fields)
+                if (field.getAnnotation(Column.class).isPrimaryKey())
+                    primaryKeyField.add(field);
+            /*if (primaryKeyField.isEmpty()) {
+                primaryKeyFromParent(clazz.getSuperclass());
+                fields = ObjectArrays.concat(fields, primaryKeyField.toArray(new Field[0]), Field.class);
+            }*/
+        }
+    }
+
+    private static void questionMarksParamsCount(String criteria, Object[] params) {
+        int criteriaCount = criteria == null ? 0 : CharMatcher.is('?').countIn(criteria);
+        int paramsCount = params != null ? params.length : 0;
+        if (criteriaCount != paramsCount)
+            throw new DAOException("?: criteria = " + criteriaCount + "; params = " + paramsCount);
+    }
+
+    /*private void primaryKeyFromParent(Class<? super T> parent) {
+        if (parent.equals(Object.class)) // caso base
+            return;
+        Field[] parentFields = ClassUtil.getAnnotatedDeclaredFields(parent, Column.class);
+        for (Field field : parentFields)
+            if (field.getAnnotation(Column.class).isPrimaryKey())
+                primaryKeyField.add(field);
+        primaryKeyFromParent(parent.getSuperclass());
+    }*/
+
+    @SuppressWarnings({"unchecked"})
+    public <T> List<T> findByCriteria(String criteria, Object... params) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        questionMarksParamsCount(criteria, params);
+
+        String sqlStatement = SELECT_SQL + table.name();
+
+        if (criteria != null)
+            sqlStatement += WHERE_CLAUSE + criteria.trim();
+
+        List<T> result = new ArrayList<>(0);
+
+        try (Connection conn = dataSource.getConnection();
+             PreparedStatement stmt = conn.prepareStatement(sqlStatement)) {
+            if (criteria != null) for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]);
+            System.out.println(stmt.toString()); // For debug purpose
+            try (ResultSet rs = stmt.executeQuery()) {
+                while (rs.next()) {
+                    T item = (T) clazz.newInstance();
+                    for (Field field : fields) {
+                        Object value;
+                        Class<?> type = field.getType();
+                        value = type.equals(YearMonth.class) ? YearMonth.parse(rs.getString(field.getAnnotation(Column.class).name()), DateTimeFormatter.ofPattern("yyyy-MM-00")) : rs.getObject(field.getAnnotation(Column.class).name());
+                        if (type.isPrimitive()) {
+                            Class<?> boxed = Primitives.wrap(type);
+                            value = boxed.cast(value);
+                        }
+                        field.setAccessible(true);
+                        field.set(item, value);
+                        field.setAccessible(false);
+                    }
+                    result.add(item);
+                }
+            }
+        } catch (IllegalAccessException | InstantiationException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    public String findById() {
+        if (primaryKeyField.isEmpty())
+            throw new NullPointerException("Primary Key is not found");
+        else if (primaryKeyField.size() > 1)
+            throw new UnsupportedOperationException("Only one primary key");
+        return primaryKeyField.get(0).getAnnotation(Column.class).name() + " = ?";
+    }
+
+    public void deleteById(Long id) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        if (primaryKeyField.isEmpty())
+            throw new NullPointerException("Primary Key is not found");
+        else if (primaryKeyField.size() > 1)
+            throw new UnsupportedOperationException("Only one primary key");
+        deleteByCriteria(primaryKeyField.get(0).getAnnotation(Column.class).name() + " = ?", id);
+    }
+
+    public void getByCriteria(String criteria, Object... params) {
+        questionMarksParamsCount(criteria, params);
+    }
+
+    public void update(T item) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        if (primaryKeyField.isEmpty())
+            throw new NullPointerException("Primary Key is not found");
+        StringJoiner joiner = new StringJoiner(" AND ");
+        Object[] params = criteriaJoiner(item, joiner);
+        updateByCriteria(item, joiner.toString(), params);
+    }
+
+    public void updateByCriteria(T item, String criteria, Object... params) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        questionMarksParamsCount(criteria, params);
+
+        StringJoiner joiner = new StringJoiner(DELIMITER);
+
+        List<Field> filteredFields = new ArrayList<>(0);
+        Column column;
+        for (Field field : fields) {
+            field.setAccessible(true);
+            column = field.getAnnotation(Column.class);
+            if ((field.getDeclaringClass().equals(clazz) || column.isPrimaryKey()) && !column.hold()) {
+                joiner.add(field.getAnnotation(Column.class).name() + " = ?");
+                filteredFields.add(field);
+            }
+        }
+
+        String sqlStatement = UPDATE_SQL + table.name() + SET_CLAUSE + joiner.toString() +
+                WHERE_CLAUSE + criteria.trim();
+
+        executeStatement(sqlStatement, item, filteredFields, params);
+    }
+
+    private void executeStatement(String sqlStatement, T item, List<Field> filteredFields, Object... params) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        try {
+            boolean keysGeneration = primaryKeyField.stream().allMatch(x -> {
+                try {
+                    return x.get(item) == null;
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                    return false;
+                }
+            });
+            try (Connection conn = dataSource.getConnection();
+                 PreparedStatement stmt = keysGeneration ? conn.prepareStatement(sqlStatement, Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sqlStatement)) {
+                int counter = 1;
+                for (Field field : filteredFields) {
+                    if (field.getType().equals(YearMonth.class)) {
+                        String date = ((YearMonth) field.get(item)).format(DateTimeFormatter.ofPattern("yyyy-MM-00"));
+                        stmt.setString(counter++, field.getAnnotation(Column.class).isNullable() && "".equals(date) ? null : date);
+                    } else
+                        stmt.setObject(counter++, field.getAnnotation(Column.class).isNullable() && "".equals(field.get(item)) ? null : field.get(item));
+                }
+                for (Object param : params)
+                    stmt.setObject(counter++, param);
+
+                System.out.println(stmt.toString()); // For debug purpose
+
+                int affectedRows = stmt.executeUpdate();
+                if (keysGeneration) {
+                    if (affectedRows == 0)
+                        throw new SQLException("No rows affected.");
+                    try (ResultSet generatedKeys = stmt.getGeneratedKeys()) {
+                        if (generatedKeys.next())
+                            Arrays.stream(fields).filter(p -> p.getAnnotation(Column.class).isPrimaryKey()).findFirst().get().set(item, generatedKeys.getLong(1));
+                        else
+                            throw new SQLException("No ID obtained.");
+                    }
+                }
+            }
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } finally {
+            filteredFields.clear();
+            for (Field field : fields)
+                field.setAccessible(false);
+        }
+    }
+
+    public void save(T item, boolean replaceMode, boolean ignore) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        try {
+            StringJoiner joiner = new StringJoiner(DELIMITER, " ( ", " ) ");
+
+            List<Field> filteredFields = new ArrayList<>(0);
+            Column column;
+            for (Field field : fields) {
+                field.setAccessible(true);
+                column = field.getAnnotation(Column.class);
+                if ((field.getDeclaringClass().equals(clazz) || column.isPrimaryKey()) && ((replaceMode || ignore) && column.isPrimaryKey() && field.get(item) != null || !(field.get(item) == null && (column.isPrimaryKey() || column.hasDefaultValue())))) {
+                    joiner.add(field.getAnnotation(Column.class).name());
+                    filteredFields.add(field);
+                }
+            }
+
+            String sqlStatement = INSERT_SQL;
+            if (ignore)
+                sqlStatement += IGNORE_SQL;
+            sqlStatement += INTO_SQL + table.name() + joiner.toString() + VALUES_CLAUSE;
+
+            joiner = new StringJoiner(DELIMITER, " (", ")");
+
+            for (int i = 0; i < filteredFields.size(); i++)
+                joiner.add("?");
+
+            sqlStatement += joiner.toString();
+
+            if (replaceMode) {
+                sqlStatement += REPLACE_SQL;
+                joiner = new StringJoiner(DELIMITER + " ");
+                for (Field field : filteredFields) {
+                    String nameColumn = field.getAnnotation(Column.class).name();
+                    joiner.add(nameColumn + "=" + VALUES_CLAUSE + "(" + nameColumn + ")");
+                }
+                sqlStatement += joiner.toString();
+            }
+            executeStatement(sqlStatement, item, filteredFields);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private Object[] criteriaJoiner(T item, StringJoiner joiner) {
+        Object[] params = new Object[0];
+        for (Field field : primaryKeyField) {
+            joiner.add(field.getAnnotation(Column.class).name() + " = ?");
+            field.setAccessible(true);
+            try {
+                params = ObjectArrays.concat(params, field.get(item));
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } finally {
+                field.setAccessible(false);
+            }
+        }
+        return params;
+    }
+
+    public void delete(T item) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        if (primaryKeyField.isEmpty())
+            throw new NullPointerException("Primary Key is not found");
+        StringJoiner joiner = new StringJoiner(" AND ");
+        Object[] params = criteriaJoiner(item, joiner);
+        deleteByCriteria(joiner.toString(), params);
+    }
+
+    public void deleteByCriteria(String criteria, Object... params) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        questionMarksParamsCount(criteria, params);
+
+        String sqlStatement = DELETE_SQL + FROM_CLAUSE + table.name() + WHERE_CLAUSE + criteria.trim();
+
+        try (Connection conn = dataSource.getConnection();
+             PreparedStatement stmt = conn.prepareStatement(sqlStatement)) {
+            for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]);
+            System.out.println(stmt.toString()); // For debug purpose
+            stmt.executeUpdate();
+        }
+    }
+
+    public Object customQuery(String query, Object... params) throws SQLException, IllegalArgumentException, NullPointerException, NumberParseException {
+        questionMarksParamsCount(query, params);
+
+        try (Connection conn = dataSource.getConnection();
+             PreparedStatement stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
+            for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]);
+            System.out.println(stmt.toString()); // For debug purpose
+            if (stmt.execute()) {
+                try (ResultSet rs = stmt.getResultSet()) {
+                    List<T> items = new ArrayList<>(0);
+                    while (rs.next()) {
+                        T item = clazz.newInstance();
+                        for (Field field : fields) {
+                            Object value;
+                            Class<?> type = field.getType();
+                            value = type.equals(YearMonth.class) ? YearMonth.parse(rs.getString(field.getAnnotation(Column.class).name()), DateTimeFormatter.ofPattern("yyyy-MM-00")) : rs.getObject(field.getAnnotation(Column.class).name());
+                            if (type.isPrimitive()) {
+                                Class<?> boxed = Primitives.wrap(type);
+                                value = boxed.cast(value);
+                            }
+                            field.setAccessible(true);
+                            field.set(item, value);
+                            field.setAccessible(false);
+                        }
+                        items.add(item);
+                    }
+                    return items;
+                }
+            } else {
+                try (ResultSet rs = stmt.getGeneratedKeys()) {
+                    if (rs.next())
+                        return rs.getLong(1);
+                }
+            }
+        } catch (IllegalAccessException | InstantiationException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}
diff --git a/src/contents/appendices/orm/assets/snippets/Signup.java b/src/contents/appendices/orm/assets/snippets/Signup.java
new file mode 100644
index 0000000..faf1077
--- /dev/null
+++ b/src/contents/appendices/orm/assets/snippets/Signup.java
@@ -0,0 +1,82 @@
+package com.trimaral.servlet.customer;
+
+import com.trimaral.Roles;
+import com.trimaral.dao.LoginDAO;
+import com.trimaral.dao.UserRoleDAO;
+import com.trimaral.entity.Login;
+import com.trimaral.entity.UserRole;
+import com.trimaral.utils.PasswordCreator;
+import com.trimaral.utils.SMTPMXLookup;
+import com.trimaral.utils.VerifyRecaptcha;
+
+import javax.annotation.Resource;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.sql.DataSource;
+import java.io.IOException;
+
+@WebServlet(name = "Signup", urlPatterns = {"%2Fsignup"})
+public class Signup extends HttpServlet {
+
+    @Resource(name = "jdbc/TrimarDB")
+    private DataSource dataSource;
+    private LoginDAO loginDAO;
+
+    @Override
+    public void init() throws ServletException {
+        loginDAO = new LoginDAO(dataSource);
+    }
+
+    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        try {
+            String email = request.getParameter("email");
+            Login login = loginDAO.findByEmail(email);
+            if (login != null) {
+                request.getRequestDispatcher("/WEB-INF/pages/customer/signup.jsp?exists=true").forward(request, response);
+                return;
+            }
+            String newPassword = request.getParameter("newPassword");
+            String confirmPassword = request.getParameter("confirmPassword");
+            if (newPassword.length() < Login.PASSWORD_LENGTH)
+                throw new IllegalArgumentException("Password too short");
+            if (!newPassword.equals(confirmPassword))
+                throw new IllegalArgumentException("Passwords don't match");
+            if (request.getParameter("termsCheckbox") == null)
+                throw new IllegalArgumentException("Unchecked Terms & Conditions");
+
+            // get reCAPTCHA request param
+            String gRecaptchaResponse = request.getParameter("g-recaptcha-response");
+            boolean verify = VerifyRecaptcha.verify(gRecaptchaResponse);
+            if (!verify) {
+                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            if (!SMTPMXLookup.isAddressValid(email)) {
+                request.getRequestDispatcher("/WEB-INF/pages/customer/signup.jsp?invalid=true").forward(request, response);
+                return;
+            }
+
+            login = new Login();
+            login.setEmail(email);
+            login.setPassword(PasswordCreator.getHexPassword(newPassword));
+            loginDAO.save(login);
+            UserRole userRole = new UserRole();
+            userRole.setEmail(login.getEmail());
+            userRole.setRoleName(String.valueOf(Roles.PRIVATO));
+            UserRoleDAO userRoleDAO = new UserRoleDAO(dataSource);
+            userRoleDAO.save(userRole);
+            request.getSession().setAttribute("requestEmail", login);
+            response.sendRedirect(request.getContextPath() + "/confirm-email");
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+    }
+
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        request.getRequestDispatcher("/WEB-INF/pages/customer/signup.jsp").forward(request, response);
+    }
+}
-- 
GitLab