Architettura tecnica

Stack, layering, packaging, configurazione Spring e sicurezza

Stack

LayerTecnologiaVersioneNote
Application ServerApache Tomcat7.0.106Tomcat 7 = Servlet 3.0 / JSP 2.2 / EL 2.2. Richiede Java 8 max.
Framework MVCSpring MVC4.1.xAnnotation-driven (@Controller, @RequestMapping). Zero XML config per i controller.
SecuritySpring Security3.2.xForm-login, ShaPasswordEncoder, JDBC user service.
ORMHibernate4.3.xAnnotation JPA. Fetch LAZY di default, JOIN esplicito nei DAO.
DatabaseMySQL5.7Charset latin1 (eredità applicativa). Da convertire a utf8mb4 in migrazione.
ViewJSP + JSTL + Bootstrap 3Form Spring (<form:form>). Datepicker Bootstrap, dropdown AJAX dipendenti via jQuery.
Stampa ExcelApache POI3.xEndpoint /semiorto-report/report in WAR separato.
Stampa DOCXdocx4jTemplate .docx con placeholder $$_NOME_$$.
BuildWAR explodedNessun Maven/Gradle nel deploy: classi compilate + .java sorgenti lasciati a fianco.

Deployment topology

┌─ Client browser ───────────────────────────────────────────────┐
│  Bootstrap 3 + jQuery (vanilla, nessun framework SPA)          │
└──────────────────────┬─────────────────────────────────────────┘
                       │  HTTP(S), form POST + AJAX GET JSON
                       ▼
┌─ nginx (front door) ───────────────────────────────────────────┐
│  location ^~ /germinabilita-old/ → proxy_pass 127.0.0.1:8085   │
│  sub_filter riscrive path assoluti /assets/**, /j_spring_**    │
│  proxy_redirect http://127.0.0.1:8085/ → /germinabilita-old/   │
└──────────────────────┬─────────────────────────────────────────┘
                       │
                       ▼
┌─ Tomcat 7.0.106 (container Docker) ────────────────────────────┐
│  webapps/ROOT/                                                 │
│    WEB-INF/                                                    │
│      web.xml  spring-security.xml  mvc-dispatcher-servlet.xml  │
│      classes/it/dss/                                           │
│        controller/ · service/ · dao/ · model/vo/               │
│        view/BO/ · view/validator/                              │
│      pages/ (JSP)  lib/ (49 JAR)                               │
│      TEMPLATES_DIRECTORY/ (docx template — vuoto nel nostro)   │
└──────────────────────┬─────────────────────────────────────────┘
                       │  JDBC (Hibernate)
                       ▼
┌─ MySQL 5.7 (container Docker, bind 127.0.0.1:3307) ────────────┐
│  database: semiorto (charset latin1, 25 tabelle)               │
│  User app: semiorto/iron666M  · User root: root/iron666M        │
└────────────────────────────────────────────────────────────────┘

Layering applicativo

Layer 1 — Controller (it.dss.controller)

Ogni controller gestisce un dominio (Analisi, Lotto, Produttore, Specie, Varietà, Utente, Amministrazione). Configurazione solo via @RequestMapping, nessuna XML. Il controller:

  • Riceve richieste HTTP e preleva parametri
  • Popola il ModelMap con liste statiche (tipologieProva, tipologieVigore, ecc.) iniettate da Spring
  • Valida l'input via @Valid + MyValidator
  • Delega al service
  • Restituisce il nome della JSP o un redirect:
Layer 2 — Service (it.dss.service[.impl])

Interfaccia + implementazione. Annotazioni @Transactional(readOnly=…). Il service:

  • Orchestra la logica di business
  • Converte VO (entità Hibernate) in BO (view objects di Spring)
  • Esegue calcoli (germinabilità media, generazione FT, flusso "Da Analizzare")
  • Richiama uno o più DAO
Layer 3 — DAO (it.dss.dao[.impl])

Wrapper su Hibernate SessionFactory. Due stili:

  • Hibernate Criteria API (createCriteria + Restrictions) per le ricerche dinamiche
  • SQL grezzo (createSQLQuery) per le query complesse come "Da Analizzare" che fanno join su 5 tabelle + subquery aggregata
Layer 4 — View Objects

VO = entità JPA (it.dss.model.vo): mappate 1:1 con tabelle DB.
BO = view objects (it.dss.view): servono per bind con form Spring, hanno campi aggiuntivi come dataArrivoS (stringa formattata) accanto a dataArrivo (Date).

La conversione VO↔BO avviene nei service (createBO / createVO).

Configurazione Spring

mvc-dispatcher-servlet.xml

Definisce le liste statiche iniettate via setter injection nei controller. Questo permette di modificare i valori senza ricompilare.

<bean class="it.dss.controller.AnalisiController">
  <property name="tipologieProva">
    <list>
      <value>PETRI</value>
      <value>TORBA</value>
      <value>CONTENITORI ALVEOLATI</value>
    </list>
  </property>
  <property name="tipologieVigore">
    <list><value>SCARSO</value><value>MEDIO</value><value>OTTIMO</value></list>
  </property>
  ...
</bean>

In TopSeed questo diventerà una tabella config_liste o ENUM MySQL, editabile dall'UI admin.

spring-security.xml

Un solo <http> block con 3 intercept-url:

<intercept-url pattern="/assets/**" access="permitAll" />
<intercept-url pattern="/accesso/**" access="!isAuthenticated()" />
<intercept-url pattern="/**" access="isAuthenticated()" />

Login form-based con redirect always a /. Logout URL /logout.

⚠ Criticità security

Nessun hasRole(). La filtrazione per ruolo NON è sugli URL ma solo sui menu visualizzati. Significa che un utente Magazzino che conosca l'URL /analisis/analisi/new può accedervi, anche se non vede il link nel menu. Da fissare nella riscrittura TopSeed.

Authentication provider (JDBC)

users-by-username-query:
  SELECT username, password, attivo
  FROM utente WHERE USERNAME=? AND attivo=1

authorities-by-username-query:
  SELECT u.username, p.permesso
  FROM utente u, permessi ps, permesso p
  WHERE ps.id_utente = u.id
    AND p.id = ps.id_permesso
    AND u.username = ?

password-encoder: sha (Spring ShaPasswordEncoder, SHA-1)

Struttura packaging completa

ROOT/
├── META-INF/
│   └── MANIFEST.MF
├── WEB-INF/
│   ├── web.xml                       — servlet + filter bootstrap
│   ├── spring-security.xml           — auth + authz
│   ├── spring-beans.xml              — datasource + sessionFactory Hibernate
│   ├── mvc-dispatcher-servlet.xml    — controller beans + liste statiche
│   ├── classes/
│   │   ├── hibernate.cfg.xml         — mappings VO
│   │   └── it/dss/
│   │       ├── controller/           — 8 controller + DocxManipulator + StampaSchedaControllo
│   │       ├── service/              — 7 service interface
│   │       │   └── impl/             — 7 service implementation
│   │       ├── dao/                  — 6 DAO interface
│   │       │   └── impl/             — 6 DAO implementation
│   │       ├── model/
│   │       │   ├── vo/               — 16 entità JPA
│   │       │   ├── dao/              — UtenteDao (eccezione)
│   │       │   └── vo_generated/     — VO generati automaticamente
│   │       ├── view/                 — 20+ BO (view objects)
│   │       │   └── validator/        — MyValidator, regole validazione
│   │       └── exception/            — NotFoundException, DuplicateException, DeleteException
│   ├── pages/                        — 23 JSP
│   │   ├── accesso/                  — login, recupera password
│   │   ├── analisi/                  — cerca, lista, nuova, update
│   │   ├── lotto/                    — lista, update
│   │   ├── produttore/               — lista, update
│   │   ├── specie/                   — lista, update (con matrice mesi×tipologie)
│   │   ├── varieta/                  — lista, update
│   │   ├── utente/                   — list, update, mieiDati, cambioPassword
│   │   ├── template/                 — head.jsp + menu.jsp (incluse ovunque)
│   │   ├── assets/                   — CSS, JS, img, less, fonts (static)
│   │   ├── errore.jsp · vuota.jsp · index.jsp
│   ├── messages/                     — i18n (mai usato in produzione)
│   ├── lib/                          — 49 JAR (Spring, Hibernate, POI, docx4j, …)
│   └── TEMPLATES_DIRECTORY/          — docx template stampa (VUOTO nel nostro dump)

Flusso request tipico

  1. Browser invia GET /analisis/analisi/{id}
  2. nginx proxy → Tomcat 8085
  3. Spring Security intercept → controlla cookie JSESSIONID. Se non auth → redirect /accesso/login
  4. DispatcherServlet route a AnalisiController.singola(id, model)
  5. Controller chiama analisiService.analisi(id) che restituisce AnalisiBO (con conte, lotto, tecnico eagerly loaded via Criteria JOIN)
  6. Controller popola ModelMap con bo, tipologieProva, tipologieCategoria, …
  7. Restituisce nome JSP analisi/update
  8. JSP update.jsp renderizza il form (739 righe HTML con Spring form tag)
  9. Response HTML al browser

Build & deploy originali di Diego

Dal docker-compose.yml ricevuto:

services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: iron666M
      MYSQL_DATABASE: semiorto
      MYSQL_USER: semiorto
      MYSQL_PASSWORD: iron666M
    volumes:
      - ./db:/docker-entrypoint-initdb.d

  web:
    image: tomcat:7.0.106
    environment:
      JDBC_URL: jdbc:mysql://db:3306/semiorto?connectTimeout=0&socketTimeout=0&autoReconnect=true
      JDBC_USER: semiorto
      JDBC_PASS: iron666M
    ports:
      - "80:8080"
    volumes:
      - ./tomcat/webapps:/usr/local/tomcat/webapps

Questa config è stata riprodotta sul server TopSeed (porta 8085 invece di 80 per evitare conflitti).