SAÉ S4 · Projet groupe · Jakarta EE

EcoDrop — API REST

API REST complète de gestion de collecte de déchets, développée en Java natif avec Jakarta EE (sans Spring). Authentification Bearer, contrôle de rôles, SQL avancé, et 52 tests fonctionnels couvrant tous les endpoints — cas nominaux et cas d'erreur.

Java 17 Jakarta EE PostgreSQL Apache Tomcat 10 JDBC Bruno (tests) Jackson
Collection de tests Bruno — EcoDrop API

SAÉ S4 — Semestre 4, BUT Informatique

Cadre

SAÉ S4 — BUT Informatique, IUT de Lille. Projet évalué, durée environ 1 semestre.

Équipe

Binôme : Victor Bredelle et Aubin Cambier. Partie A réalisée ensemble, Partie C réalisée seul par Aubin.

Objectif

Concevoir et implémenter une API REST complète : modélisation BDD, endpoints CRUD, authentification et rôles, suite de tests fonctionnels.

Domaine

Application fictive EcoDrop : gestion de points de collecte de déchets, dépôts et leaderboard de contributeurs.

Projet en binôme — Victor Bredelle & Aubin Cambier

Partie A — En binôme
Conception & architecture
  • Conception de la base de données (modélisation, schéma, init.sql)
  • Architecture initiale du projet (structure des couches, conventions)
  • Modélisation des entités et des DAO
Réalisée en binôme avec Victor Bredelle
Partie C — Réalisée seul
Implémentation & tests
  • Implémentation de l'ensemble des controllers (Servlets Jakarta)
  • Système d'authentification Bearer token
  • Gestion des rôles via @WebFilter (user / admin)
  • Écriture et correction des 52 tests Bruno (cas nominaux + erreurs)
  • Endpoints avancés : PUT/PATCH users et deposits
  • Endpoint leaderboard (GROUP BY + LEFT JOIN + COALESCE)
  • Endpoint points surchargés (HAVING SQL)

Stack technique

Java 17
Langage unique client + serveur
Jakarta EE (Servlet 6.x)
Sans Spring — routing manuel
PostgreSQL + JDBC
Accès direct, PreparedStatement
Jackson
Sérialisation JSON
@WebFilter (auth)
TokenAuthFilter — Bearer token
Bruno — 52 tests
Tous endpoints, tous codes HTTP
Git (branches)
Branche par développeur, merges
Apache Tomcat 10.1
Serveur d'application

Flux d'une requête HTTP

Couche 0
TokenAuthFilter
  • @WebFilter sur les routes protégées
  • Vérifie le header Authorization
  • Contrôle du rôle (user / admin)
  • Retourne 401 ou 403 si invalide
Couche 1
Controller (Servlet)
  • @WebServlet par domaine
  • Routing sur pathInfo()
  • Parsing JSON / XML (Accept)
  • Codes HTTP (200, 201, 204…)
Couche 2
DAO + DTO
  • Requêtes SQL (PreparedStatement)
  • DTOs pour le transfert de données
  • Isolation complète de la BDD
Couche 3
PostgreSQL
  • 5 tables relationnelles
  • DS.java — connexion centralisée
  • init.sql — schéma + seed

Pas de Spring : chaque route est dispatchée manuellement via pathInfo(). Le PATCH est géré en surchargeant service() car HttpServlet ne le supporte pas nativement.

Principaux endpoints

Méthode Route Description Accès
GET /auth/token HTTP Basic Auth → token UUID Bearer Public
GET /waste Liste des types de déchets (JSON ou XML selon Accept) Public
POST /deposits Créer un dépôt — rejet 409 si point saturé (capacity check) User
PATCH /points/{id}/clear Marque tous les dépôts du point comme collectés (sans suppression) Admin
GET /points/overloaded Points dont l'occupation dépasse 80% — SQL HAVING Admin
GET /leaderboard Top 10 utilisateurs par score — SQL GROUP BY + LEFT JOIN + COALESCE Public
DELETE /waste/{id} Suppression d'un type de déchet Admin
PUT /users/{id} Mise à jour complète d'un utilisateur Admin

Schéma relationnel — 5 tables

WasteType
id (PK)
nom
pointsPerKilo
CollectionPoint
id (PK)
adresse
capaciteMax
accepts
pointid (FK → CollectionPoint)
wastetypeid (FK → WasteType)
CASCADE / RESTRICT
users
id (PK)
login
password
role
Deposit
id (PK)
userid (FK)
pointid (FK)
wastetypeid (FK)
kilos
depositedAt
collecte

Hard skills — illustrés par des situations concrètes

Routing HTTP manuel (sans Spring)

Sans Spring, chaque route est dispatchée en lisant pathInfo() et en branchant sur des if/switch. Le PATCH n'étant pas supporté par HttpServlet, j'ai appris à surcharger service() pour l'intercepter avant le dispatch standard.

Authentification Bearer + contrôle de rôles

Le TokenAuthFilter (@WebFilter) génère un UUID stocké en HttpSession Tomcat. J'ai participé à l'implémentation des vérifications de rôle (user vs admin) en m'assurant que chaque cas (GET public, DELETE admin only, POST user only) était couvert et testé.

SQL avancé — agrégation et sous-requêtes

Le leaderboard utilise un LEFT JOIN + COALESCE + GROUP BY pour inclure les utilisateurs sans dépôt (score = 0). La détection de surcharge repose sur un HAVING SUM(kilos) > capaciteMax * 0.8. La vérification de capacité avant insertion utilise une sous-requête d'agrégation.

Pattern DAO / DTO

Chaque domaine (waste, collectionpoint, deposit, users) dispose de son propre DAO qui encapsule toutes les requêtes JDBC. Les DTOs assurent que la couche Controller ne manipule jamais directement les objets SQL — cette isolation a facilité le débogage et les tests.

Tests fonctionnels avec Bruno

J'ai écrit et corrigé 52 fichiers de tests Bruno couvrant tous les endpoints : 200, 201, 204 pour les cas nominaux, et 400, 401, 403, 404, 409 pour les cas d'erreur. Cette couverture a permis de détecter des bugs subtils (id incorrect en réponse 404, PUT se comportant comme un PATCH).

Git en binôme — branches et merges

Travail sur branches séparées avec Victor. J'ai géré les merges entre la branche de conception commune (Partie A) et ma branche d'implémentation (Partie C), en veillant à ne pas écraser les choix d'architecture initiaux tout en ajoutant mes propres couches.

Soft skills — illustrés par des situations concrètes

Autonomie & résolution de problèmes

Quand les tests Bruno retournaient un code HTTP 404 avec l'id de la donnée au lieu de l'id du paramètre, j'ai isolé le bug, remonté au controller concerné et proposé la correction sans attendre l'encadrement. J'ai appris à lire des stacktraces Tomcat et à croiser les logs avec le comportement attendu.

Adaptabilité — reprendre le code d'un autre

J'ai étendu des fonctionnalités initialement développées par le lead technique (Victor) sans documentation : j'ai lu le code existant pour comprendre les conventions adoptées (nommage des DAO, structure des DTOs) avant d'ajouter les endpoints PUT/PATCH users et deposits en cohérence avec l'architecture en place.

Organisation & découpage du travail

Le travail était découpé par domaine métier, ce qui a permis à chaque membre d'avancer en parallèle sans bloquer les autres. Cette organisation m'a appris à définir des interfaces claires avec l'équipe avant de commencer à coder, pour éviter de devoir refactorer les DAO après coup.

Communication & coordination

Lorsque j'ai détecté des incohérences entre le comportement d'un endpoint et la spécification, j'ai documenté le bug (input, réponse reçue, réponse attendue) et l'ai communiqué clairement à l'équipe, ce qui a accéléré la correction en évitant les allers-retours.

Ce que ce projet m'a apporté

Ce que j'ai appris

HTTP en profondeur — sans filet

Coder une API sans Spring oblige à comprendre HTTP réellement : les codes de statut, les headers Accept/Content-Type, la gestion des méthodes non standard comme PATCH. Je ne me contente plus d'utiliser un framework comme une boîte noire — je sais ce qu'il y a dessous.

Ce que j'ai appris

Les tests révèlent les vraies spécifications

Écrire les 52 tests Bruno m'a obligé à lire la spécification attentivement et à anticiper tous les cas limites (409, 403, 401, capacité exactement à 80%…). Les tests ne sont pas une formalité : ils m'ont aidé à trouver des bugs que la revue de code n'avait pas détectés.

Difficultés rencontrées

Routage manuel verbeux et source d'erreurs

Sans Spring, le routage sur pathInfo() devient vite un enchevêtrement de conditions. Une faute de frappe dans un chemin, un else if mal placé, et la requête tombe dans le mauvais handler. J'ai compris pourquoi les frameworks existent : ils résolvent des problèmes réels, pas du confort.

Difficultés rencontrées

Gestion des rôles — cas limites nombreux

Le TokenAuthFilter a demandé plusieurs itérations : GET public vs GET protégé (ex : /points/overloaded admin only), combinaisons user/admin sur le même endpoint selon la méthode HTTP. Chaque cas manqué était un test Bruno qui échouait avec un 403 inattendu.

Ce que je ferais différemment

Spring Boot et TDD dès le départ

J'utiliserais Spring Boot pour éviter le boilerplate du routing manuel et de la gestion des sessions. Surtout, j'écrirais les tests Bruno en parallèle du développement (TDD) plutôt qu'à la fin : les bugs auraient été détectés plus tôt, au moment où le contexte du développeur est encore frais.

Ce que je ferais différemment

Schéma SQL plus contraint dès init.sql

La version finale du schéma manque de contraintes CHECK (ex : kilos > 0, role IN ('user', 'admin')). Ces contraintes auraient dû être définies dès le départ dans init.sql pour déléguer une partie de la validation à la base de données et alléger les contrôles dans les DAO.