@mostajs/orm 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1233 @@
1
+ # Plan de dev : SessionMostaORMFactory + Multi-Bridge + Auto-Start
2
+
3
+ > Auteur : Dr Hamid MADANI drmdh@msn.com
4
+ > Date : 2026-03-08
5
+ > Projet : @mostajs/orm (mosta-orm)
6
+
7
+ ---
8
+
9
+ ## Sommaire
10
+
11
+ 1. [Analyse de l'existant](#1-analyse-de-lexistant)
12
+ 2. [Schemas de connexion et comportement](#2-schemas-de-connexion-et-comportement)
13
+ 3. [Renommage AbstractSqlDialect → SessionMostaORMFactory](#3-renommage)
14
+ 4. [Multi-Bridge : plusieurs instances JDBC simultanees](#4-multi-bridge)
15
+ 5. [Auto-Start controlable par .env](#5-auto-start)
16
+ 6. [Protection contre boucles et processus orphelins](#6-protections)
17
+ 7. [Variables .env](#7-variables-env)
18
+ 8. [Plan d'implementation](#8-plan-dimplementation)
19
+ 9. [Matrice de risques](#9-matrice-de-risques)
20
+
21
+ ---
22
+
23
+ ## 1. Analyse de l'existant
24
+
25
+ ### 1.1 Architecture actuelle
26
+
27
+ ```
28
+ factory.ts (getDialect/createConnection)
29
+ │ singleton — UNE seule instance dialect a la fois
30
+
31
+ AbstractSqlDialect (abstract-sql.dialect.ts)
32
+ │ - connect() avec interception JDBC bridge
33
+ │ - executeQuery() / executeRun() routage bridge ou natif
34
+ │ - UN seul JdbcNormalizer (1 bridge, 1 port)
35
+
36
+ Dialect concret (oracle.dialect.ts, hsqldb.dialect.ts...)
37
+ │ - doConnect() / doExecuteQuery() / doExecuteRun()
38
+ │ - SQL pur (types, quotes, placeholders)
39
+
40
+ MostaJdbcBridge.java
41
+ │ - UN seul processus Java sur port 8765
42
+
43
+ SGBD cible
44
+ ```
45
+
46
+ ### 1.2 Problemes identifies
47
+
48
+ | # | Probleme | Detail |
49
+ |---|----------|--------|
50
+ | 1 | **Nom** | `AbstractSqlDialect` ne reflete pas son role de SessionFactory |
51
+ | 2 | **Mono-bridge** | Un seul bridge JDBC a la fois — impossible de connecter HSQLDB + Oracle simultanement |
52
+ | 3 | **Port fixe** | Port 8765 en dur — conflit si 2 bridges |
53
+ | 4 | **Auto-start non controlable** | Le bridge demarre toujours si JAR present — pas de choix |
54
+ | 5 | **Processus orphelins** | Si l'app crash, le process Java reste en memoire |
55
+ | 6 | **Boucle de demarrage** | Si le bridge echoue, il pourrait etre relance en boucle |
56
+
57
+ ### 1.3 Mapping Hibernate
58
+
59
+ | Hibernate | @mostajs/orm actuel | @mostajs/orm propose |
60
+ |-----------|--------------------|--------------------|
61
+ | `Dialect` (SQL pur) | `AbstractSqlDialect` (SQL + connexion + bridge) | Dialect concret (SQL pur) |
62
+ | `SessionFactory` | `factory.ts` (getDialect) + `AbstractSqlDialect` (connect) | `SessionMostaORMFactory` |
63
+ | `ConnectionProvider` | Dans chaque dialect (doConnect) | Dans `SessionMostaORMFactory` (interception) |
64
+ | JDBC `DataSource` | `JdbcNormalizer` | `BridgeManager` (multi-instance) |
65
+
66
+ ---
67
+
68
+ ## 2. Schemas de connexion et comportement
69
+
70
+ ### 2.1 Schema global — Vue d'ensemble de l'architecture
71
+
72
+ ```
73
+ ┌─────────────────────────────────────────────────────────────────────────────────────────┐
74
+ │ APPLICATION (SecuAccessPro) │
75
+ │ │
76
+ │ .env.local : │
77
+ │ DB_DIALECT=hsqldb │
78
+ │ SGBD_URI=hsqldb:hsql://localhost:9001/mydb │
79
+ │ MOSTA_BRIDGE_AUTOSTART=true │
80
+ │ MOSTA_BRIDGE_PORT_INCREMENT=true │
81
+ └──────────────────────────────────┬──────────────────────────────────────────────────────┘
82
+
83
+
84
+ ┌─────────────────────────────────────────────────────────────────────────────────────────┐
85
+ │ factory.ts — createConnection() │
86
+ │ │
87
+ │ 1. Lit DB_DIALECT + SGBD_URI depuis .env │
88
+ │ 2. Charge le dialect : import('hsqldb.dialect.js') │
89
+ │ 3. Appelle dialect.connect(config) │
90
+ │ │
91
+ │ ┌──────────────────────────────────────────────────────────────────────────────────┐ │
92
+ │ │ SessionMostaORMFactory.connect(config) │ │
93
+ │ │ │ │
94
+ │ │ ┌─ JAR detecte dans jar_files/ ? │ │
95
+ │ │ │ │ │
96
+ │ │ │ OUI ──┐ MOSTA_BRIDGE_AUTOSTART ? │ │
97
+ │ │ │ │ ├── true → BridgeManager.getOrCreate() │ │
98
+ │ │ │ │ ├── detect → Health check port → reutilise ou lance │ │
99
+ │ │ │ │ └── false → Erreur "Start bridge manually" │ │
100
+ │ │ │ │ │ │
101
+ │ │ │ NON ──┘──── doConnect() du dialect (driver npm) │ │
102
+ │ │ │ ex: import('oracledb') → createPool() │ │
103
+ │ │ │ │ │
104
+ │ │ │ executeQuery(sql, params) / executeRun(sql, params) │ │
105
+ │ │ │ ├── Bridge actif ? → HTTP POST vers BridgeManager │ │
106
+ │ │ │ └── Bridge inactif ? → doExecuteQuery() du dialect │ │
107
+ │ │ └──────────────────────────────────────────────────────────────────────────────┘ │
108
+ └──────────────────────────────────┬──────────────────────────────────────────────────────┘
109
+
110
+
111
+ ┌─────────────────────────────────────────────────────────────────────────────────────────┐
112
+ │ BridgeManager (singleton global) │
113
+ │ │
114
+ │ bridges: Map<cle, BridgeInstance> │
115
+ │ ┌──────────────────────────────────────────────────────────────────────────────────┐ │
116
+ │ │ │ │
117
+ │ │ Cle Port PID JDBC URL │ │
118
+ │ │ ───────────────────────────── ───── ───── ─────────────────────────── │ │
119
+ │ │ hsqldb:localhost:9001/mydb 8765 14201 jdbc:hsqldb:hsql://... │ │
120
+ │ │ oracle:db.prod:1521/ORCLPDB1 8766 14305 jdbc:oracle:thin:@//... │ │
121
+ │ │ db2:srv:50000/MYDB 8767 14410 jdbc:db2://... │ │
122
+ │ │ │ │
123
+ │ │ nextPort: 8768 │ │
124
+ │ │ basePort: 8765 (MOSTA_BRIDGE_PORT_BASE) │ │
125
+ │ │ │ │
126
+ │ └──────────────────────────────────────────────────────────────────────────────────┘ │
127
+ │ │
128
+ │ Protections : │
129
+ │ ├── Anti-boucle : max 3 tentatives / 60s par cle │
130
+ │ ├── PID files : jar_files/.bridge-{port}.pid │
131
+ │ ├── Cleanup orphelins au demarrage │
132
+ │ └── process.on('exit') → stopAll() │
133
+ └───────────┬─────────────────────┬──────────────────────┬───────────────────────────────┘
134
+ │ │ │
135
+ ▼ ▼ ▼
136
+ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
137
+ │ MostaJdbcBridge │ │ MostaJdbcBridge │ │ MostaJdbcBridge │
138
+ │ Java :8765 │ │ Java :8766 │ │ Java :8767 │
139
+ │ │ │ │ │ │
140
+ │ POST /query │ │ POST /query │ │ POST /query │
141
+ │ GET /health │ │ GET /health │ │ GET /health │
142
+ │ │ │ │ │ │
143
+ │ -cp hsqldb.jar │ │ -cp ojdbc11.jar │ │ -cp db2jcc4.jar │
144
+ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
145
+ │ JDBC │ JDBC │ JDBC
146
+ ▼ ▼ ▼
147
+ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
148
+ │ HSQLDB │ │ Oracle DB │ │ IBM DB2 │
149
+ │ :9001 │ │ :1521 │ │ :50000 │
150
+ └──────────────────┘ └──────────────────┘ └──────────────────┘
151
+ ```
152
+
153
+ ### 2.2 Schema — Flux de connexion (decision tree)
154
+
155
+ ```
156
+ connect(config) appele
157
+
158
+
159
+ ┌─────────────────────┐
160
+ │ Dialect dans │
161
+ │ JDBC_REGISTRY ? │
162
+ │ (hsqldb/oracle/ │
163
+ │ db2/sybase/hana) │
164
+ └──────┬──────────────┘
165
+
166
+ NON ─────────┤────────── OUI
167
+ │ │
168
+ ▼ ▼
169
+ ┌──────────────────┐ ┌──────────────────────┐
170
+ │ doConnect() │ │ JAR present dans │
171
+ │ du dialect │ │ jar_files/ ? │
172
+ │ │ └──────┬───────────────┘
173
+ │ import('pg') │ │
174
+ │ import('mysql2') │ NON ─────┤────────── OUI
175
+ │ import('mssql') │ │ │
176
+ │ etc. │ ▼ ▼
177
+ │ │ ┌──────────┐ ┌───────────────────────┐
178
+ │ npm driver natif │ │ doConnect│ │ MOSTA_BRIDGE_AUTOSTART│
179
+ └──────────────────┘ │ du │ │ = ? │
180
+ │ dialect │ └──────┬────────────────┘
181
+ │ (npm) │ │
182
+ │ │ ┌────┼────────┐
183
+ │ Si echec │ │ │ │
184
+ │ → erreur │ true detect false
185
+ │ "npm │ │ │ │
186
+ │ install" │ ▼ ▼ ▼
187
+ └──────────┘ ┌─────────┐ ┌──────────────────┐
188
+ │ Bridge │ │ doConnect() │
189
+ │ Manager │ │ du dialect (npm) │
190
+ │ .get │ │ │
191
+ │ Or │ │ Si echec : │
192
+ │ Create()│ │ "Start bridge │
193
+ └────┬────┘ │ manually or set │
194
+ │ │ AUTOSTART=true" │
195
+ ▼ └──────────────────┘
196
+ ┌──────────────────────┐
197
+ │ Bridge deja actif │
198
+ │ pour cette cle ? │
199
+ └──────┬───────────────┘
200
+
201
+ OUI ─────────┤────────── NON
202
+ │ │
203
+ ▼ ▼
204
+ ┌──────────────────┐ ┌──────────────────────┐
205
+ │ Reutiliser │ │ Port libre ? │
206
+ │ le bridge │ └──────┬───────────────┘
207
+ │ existant │ │
208
+ │ │ NON ─────┤────────── OUI
209
+ │ Pas de nouveau │ │ │
210
+ │ process Java │ ▼ ▼
211
+ └──────────────────┘ ┌──────────┐ ┌──────────────────┐
212
+ │ INCRE- │ │ Lancer │
213
+ │ MENT ? │ │ MostaJdbcBridge │
214
+ │ │ │ .java │
215
+ │ true: │ │ │
216
+ │ port++ │ │ java --source 11 │
217
+ │ │ │ -cp JAR bridge │
218
+ │ false: │ │ --port PORT │
219
+ │ ERREUR │ │ --jdbc-url URL │
220
+ │ "Port │ │ │
221
+ │ occupe" │ │ Ecrire PID file │
222
+ └──────────┘ │ Attendre health │
223
+ └────────┬─────────┘
224
+
225
+
226
+ ┌──────────────────────┐
227
+ │ Health check OK ? │
228
+ └──────┬───────────────┘
229
+
230
+ OUI ────────┤──────── NON
231
+ │ │
232
+ ▼ ▼
233
+ ┌──────────────────┐ ┌───────────────────┐
234
+ │ CONNECTE │ │ Tentative < 3 ? │
235
+ │ │ │ │
236
+ │ bridgeActive │ │ OUI → retry │
237
+ │ = true │ │ NON → ERREUR │
238
+ │ bridgeUrl │ │ "Bridge failed │
239
+ │ = http:// │ │ 3 times. Check │
240
+ │ localhost:PORT │ │ Java/JAR/SGBD" │
241
+ └──────────────────┘ └───────────────────┘
242
+ ```
243
+
244
+ ### 2.3 Schema — Execution des requetes (routage)
245
+
246
+ ```
247
+ Application : dialect.find({ name: 'test' })
248
+
249
+
250
+ SessionMostaORMFactory
251
+ ├── translateFilter() → WHERE "name" = ?
252
+ ├── buildSelectColumns() → SELECT "id", "name"...
253
+ ├── buildOrderBy() → ORDER BY ...
254
+ └── buildLimitOffset() → LIMIT ... OFFSET ...
255
+
256
+ ▼ SQL genere par le dialect
257
+ ┌───────────────┐
258
+ │ executeQuery() │
259
+ └───────┬───────┘
260
+
261
+ ┌──────────┴──────────┐
262
+ │ jdbcBridgeActive ? │
263
+ └──────────┬──────────┘
264
+
265
+ true ────────────┤──────────── false
266
+ │ │
267
+ ▼ ▼
268
+ ┌─────────────────────┐ ┌─────────────────────┐
269
+ │ bridgeExecuteQuery()│ │ doExecuteQuery() │
270
+ │ │ │ du dialect concret │
271
+ │ HTTP POST │ │ │
272
+ │ http://localhost: │ │ PostgreSQL: │
273
+ │ PORT/query │ │ pool.query(sql) │
274
+ │ │ │ │
275
+ │ Body: │ │ MySQL: │
276
+ │ { │ │ pool.execute(sql) │
277
+ │ "sql": "SELECT.." │ │ │
278
+ │ "params": [...] │ │ Oracle: │
279
+ │ } │ │ conn.execute(sql) │
280
+ └─────────┬───────────┘ └──────────┬──────────┘
281
+ │ │
282
+ ▼ ▼
283
+ ┌─────────────────────┐ ┌─────────────────────┐
284
+ │ MostaJdbcBridge │ │ Driver npm natif │
285
+ │ (Java) │ │ (pg/mysql2/mssql │
286
+ │ │ │ oracledb/ibm_db │
287
+ │ PreparedStatement │ │ @sap/hana-client) │
288
+ │ JDBC execute │ │ │
289
+ │ → JSON response │ │ Protocol natif │
290
+ └─────────┬───────────┘ └──────────┬──────────┘
291
+ │ │
292
+ ▼ ▼
293
+ ┌─────────────────────┐ ┌─────────────────────┐
294
+ │ SGBD via JDBC │ │ SGBD via protocol │
295
+ │ │ │ natif │
296
+ │ HSQLDB (HSQL) │ │ PostgreSQL (TCP) │
297
+ │ Oracle (TNS) │ │ MySQL (TCP) │
298
+ │ DB2 (DRDA) │ │ MongoDB (BSON) │
299
+ │ Sybase (TDS) │ │ SQLite (file) │
300
+ │ HANA (TCP) │ │ Spanner (gRPC) │
301
+ └─────────────────────┘ └─────────────────────┘
302
+ ```
303
+
304
+ ### 2.4 Schema — Cycle de vie des bridges
305
+
306
+ ```
307
+ Demarrage Application
308
+
309
+
310
+ ┌─────────────────────────────────────────────────────────┐
311
+ │ BridgeManager.getInstance() │
312
+ │ │
313
+ │ 1. Lire fichiers PID : jar_files/.bridge-*.pid │
314
+ │ 2. Pour chaque PID : │
315
+ │ ├── Process vivant ? → Tuer (orphelin) │
316
+ │ └── Process mort ? → Supprimer fichier PID │
317
+ │ 3. Enregistrer handlers : │
318
+ │ ├── process.on('exit') → stopAll() │
319
+ │ ├── process.on('SIGINT') → stopAll() + exit │
320
+ │ └── process.on('SIGTERM') → stopAll() + exit │
321
+ └──────────────────────────┬──────────────────────────────┘
322
+
323
+
324
+ ┌──────────────── Vie de l'application ────────────────────┐
325
+ │ │
326
+ │ connect(hsqldb) ──┐ │
327
+ │ ▼ │
328
+ │ BridgeManager.getOrCreate('hsqldb:...') │
329
+ │ ├── Nouveau bridge → port 8765 │
330
+ │ └── Ecrire .bridge-8765.pid │
331
+ │ │
332
+ │ connect(oracle) ──┐ │
333
+ │ ▼ │
334
+ │ BridgeManager.getOrCreate('oracle:...') │
335
+ │ ├── Nouveau bridge → port 8766 │
336
+ │ └── Ecrire .bridge-8766.pid │
337
+ │ │
338
+ │ connect(hsqldb) ──┐ (meme cle que le 1er) │
339
+ │ ▼ │
340
+ │ BridgeManager.getOrCreate('hsqldb:...') │
341
+ │ └── Bridge existe deja → REUTILISER port 8765 │
342
+ │ (pas de nouveau process Java) │
343
+ │ │
344
+ │ disconnect(hsqldb) ──┐ │
345
+ │ ▼ │
346
+ │ Le bridge N'EST PAS arrete │
347
+ │ (d'autres connexions peuvent l'utiliser) │
348
+ │ bridgeActive = false sur CE dialect │
349
+ │ │
350
+ └──────────────────────────────────────────────────────────┘
351
+
352
+
353
+ ┌─────────────────────────────────────────────────────────┐
354
+ │ Arret Application (exit / SIGINT / SIGTERM / crash) │
355
+ │ │
356
+ │ BridgeManager.stopAll() │
357
+ │ ├── bridge hsqldb:8765 → kill(PID, SIGTERM) │
358
+ │ │ └── Supprimer .bridge-8765.pid │
359
+ │ ├── bridge oracle:8766 → kill(PID, SIGTERM) │
360
+ │ │ └── Supprimer .bridge-8766.pid │
361
+ │ └── bridges.clear() │
362
+ │ │
363
+ │ Si crash sans cleanup : │
364
+ │ ├── Fichiers .bridge-*.pid restent │
365
+ │ └── Au prochain demarrage → cleanupOrphans() les tue │
366
+ └─────────────────────────────────────────────────────────┘
367
+ ```
368
+
369
+ ### 2.5 Schema — Messages d'erreur et diagnostics
370
+
371
+ ```
372
+ ┌──────────────────────────────────────────────────────────────────────────┐
373
+ │ ARBRE DES MESSAGES D'ERREUR │
374
+ └──────────────────────────────────────────────────────────────────────────┘
375
+
376
+ connect() appele
377
+
378
+
379
+ JAR present ?
380
+
381
+ NON ─┤
382
+ │ Driver npm disponible ?
383
+ │ │
384
+ │ OUI → doConnect() → Succes ✓
385
+ │ │
386
+ │ NON → ╔════════════════════════════════════════════════════╗
387
+ │ ║ ERREUR: No driver found for {dialect}. ║
388
+ │ ║ Option 1: npm install {driver} ║
389
+ │ ║ Option 2: Place {jar}*.jar in jar_files/ ║
390
+ │ ║ and set MOSTA_BRIDGE_AUTOSTART=true ║
391
+ │ ╚════════════════════════════════════════════════════╝
392
+
393
+ OUI ─┤
394
+
395
+ MOSTA_BRIDGE_AUTOSTART ?
396
+
397
+ false ┤── Driver npm disponible ?
398
+ │ │
399
+ │ OUI → doConnect() (npm) → Succes ✓
400
+ │ │
401
+ │ NON → ╔════════════════════════════════════════════════════╗
402
+ │ ║ ERREUR: JDBC bridge disabled. ║
403
+ │ ║ Start manually: ║
404
+ │ ║ java --source 11 -cp {jar} ║
405
+ │ ║ MostaJdbcBridge.java ║
406
+ │ ║ --jdbc-url {jdbcUrl} ║
407
+ │ ║ --port {port} ║
408
+ │ ║ Or set MOSTA_BRIDGE_AUTOSTART=true ║
409
+ │ ╚════════════════════════════════════════════════════╝
410
+
411
+ true/detect
412
+
413
+
414
+ Java installe ?
415
+
416
+ NON ─┤── ╔════════════════════════════════════════════════════════╗
417
+ │ ║ ERREUR: Java 11+ required for JDBC bridge. ║
418
+ │ ║ Install: sudo apt install openjdk-11-jre ║
419
+ │ ║ Or: sudo apt install default-jre ║
420
+ │ ║ Verify: java --version ║
421
+ │ ╚════════════════════════════════════════════════════════╝
422
+
423
+ OUI ─┤
424
+
425
+ Port libre ?
426
+
427
+ NON ─┤── INCREMENT active ?
428
+ │ │
429
+ │ OUI → port++ → retry
430
+ │ │
431
+ │ NON → ╔════════════════════════════════════════════════════╗
432
+ │ ║ ERREUR: Port {port} already in use. ║
433
+ │ ║ Set MOSTA_BRIDGE_PORT_INCREMENT=true ║
434
+ │ ║ Or change MOSTA_BRIDGE_PORT_BASE ║
435
+ │ ║ Or stop process using port {port}: ║
436
+ │ ║ lsof -i :{port} ║
437
+ │ ╚════════════════════════════════════════════════════╝
438
+
439
+ OUI ─┤
440
+
441
+ Lancement bridge...
442
+
443
+ Health check OK ?
444
+
445
+ NON ─┤── Tentatives < MAX_RETRIES ?
446
+ │ │
447
+ │ OUI → retry (compteur++)
448
+ │ │
449
+ │ NON → ╔════════════════════════════════════════════════════╗
450
+ │ ║ ERREUR: JDBC bridge failed {n} times in 60s. ║
451
+ │ ║ Diagnostic: ║
452
+ │ ║ 1. Java installed? → java --version ║
453
+ │ ║ 2. JAR valid? → ls jar_files/{jar}*.jar ║
454
+ │ ║ 3. SGBD running? → check port {sgbdPort} ║
455
+ │ ║ 4. Firewall? → check port {bridgePort} ║
456
+ │ ║ 5. Bridge log: → stderr output above ║
457
+ │ ╚════════════════════════════════════════════════════╝
458
+
459
+ OUI ─┤
460
+
461
+ ╔═══════════════════════════════════════════════════════════════╗
462
+ ║ SUCCES: Connected to {dialect} via JDBC bridge ║
463
+ ║ Bridge: http://localhost:{port} ║
464
+ ║ JDBC: {jdbcUrl} ║
465
+ ║ PID: {pid} ║
466
+ ╚═══════════════════════════════════════════════════════════════╝
467
+ ```
468
+
469
+ ### 2.6 Schema — Comparaison driver npm vs JDBC bridge
470
+
471
+ ```
472
+ ┌─────────────────────────────────────────────────────────────────────┐
473
+ │ CHEMIN A : Driver npm natif │
474
+ │ (postgres, mysql, mariadb, mssql, cockroachdb) │
475
+ │ │
476
+ │ SecuAccessPro │
477
+ │ │ │
478
+ │ ▼ │
479
+ │ SessionMostaORMFactory │
480
+ │ │ connect() │
481
+ │ ▼ │
482
+ │ doConnect() │
483
+ │ │ import('pg') │
484
+ │ ▼ │
485
+ │ ┌──────────┐ Protocol natif (TCP) ┌──────────────┐ │
486
+ │ │ npm │ ──────────────────────────── │ PostgreSQL │ │
487
+ │ │ driver │ │ Server │ │
488
+ │ │ (pg) │ Latence : ~0.1ms │ :5432 │ │
489
+ │ └──────────┘ └──────────────┘ │
490
+ │ │
491
+ │ Avantages : Direct, rapide, zero overhead │
492
+ │ Inconvenients : Depend de npm, parfois binaires C++ │
493
+ └─────────────────────────────────────────────────────────────────────┘
494
+
495
+ ┌─────────────────────────────────────────────────────────────────────┐
496
+ │ CHEMIN B : JDBC Bridge │
497
+ │ (hsqldb, oracle, db2, sybase, hana) │
498
+ │ │
499
+ │ SecuAccessPro │
500
+ │ │ │
501
+ │ ▼ │
502
+ │ SessionMostaORMFactory │
503
+ │ │ connect() │
504
+ │ ▼ │
505
+ │ BridgeManager.getOrCreate() │
506
+ │ │ │
507
+ │ ▼ │
508
+ │ ┌──────────┐ HTTP POST ┌──────────────┐ JDBC ┌─────────┐ │
509
+ │ │ Node.js │ ──────────── │ MostaJdbc │ ─────── │ Oracle │ │
510
+ │ │ fetch() │ localhost │ Bridge.java │ TCP │ Server │ │
511
+ │ │ │ :8765 │ │ :1521 │ │ │
512
+ │ └──────────┘ └──────────────┘ └─────────┘ │
513
+ │ │
514
+ │ Latence : ~1-2ms (HTTP localhost) │
515
+ │ Avantages : Zero npm binaire, driver officiel editeur (JDBC) │
516
+ │ Inconvenients : Process Java supplementaire │
517
+ └─────────────────────────────────────────────────────────────────────┘
518
+
519
+ ┌─────────────────────────────────────────────────────────────────────┐
520
+ │ CHEMIN C : Pas de driver disponible │
521
+ │ (npm absent + JAR absent) │
522
+ │ │
523
+ │ SecuAccessPro │
524
+ │ │ │
525
+ │ ▼ │
526
+ │ SessionMostaORMFactory │
527
+ │ │ connect() │
528
+ │ ▼ │
529
+ │ ╔═══════════════════════════════════════════════════════════╗ │
530
+ │ ║ ERREUR ║ │
531
+ │ ║ ║ │
532
+ │ ║ No driver found for Oracle Database. ║ │
533
+ │ ║ ║ │
534
+ │ ║ Option 1 (npm): ║ │
535
+ │ ║ npm install oracledb ║ │
536
+ │ ║ ║ │
537
+ │ ║ Option 2 (JDBC bridge): ║ │
538
+ │ ║ 1. Download ojdbc11.jar from oracle.com ║ │
539
+ │ ║ 2. Place it in jar_files/ ║ │
540
+ │ ║ 3. Set MOSTA_BRIDGE_AUTOSTART=true in .env ║ │
541
+ │ ╚═══════════════════════════════════════════════════════════╝ │
542
+ └─────────────────────────────────────────────────────────────────────┘
543
+ ```
544
+
545
+ ### 2.7 Schema — Multi-bridge en action (exemple concret)
546
+
547
+ ```
548
+ Scenario : Application qui migre des donnees HSQLDB → Oracle
549
+
550
+ .env.local :
551
+ MOSTA_BRIDGE_AUTOSTART=true
552
+ MOSTA_BRIDGE_PORT_BASE=8765
553
+ MOSTA_BRIDGE_PORT_INCREMENT=true
554
+
555
+ jar_files/ :
556
+ hsqldb-2.7.2.jar
557
+ ojdbc11.jar
558
+
559
+ ┌───────────────────────────────────────────────────────────────────┐
560
+ │ Code application : │
561
+ │ │
562
+ │ // Connexion source (HSQLDB) │
563
+ │ const source = await createConnection({ │
564
+ │ dialect: 'hsqldb', │
565
+ │ uri: 'hsqldb:hsql://localhost:9001/legacy', │
566
+ │ }); │
567
+ │ → BridgeManager lance bridge sur port 8765 │
568
+ │ → PID 14201, hsqldb-2.7.2.jar │
569
+ │ │
570
+ │ // Connexion destination (Oracle) │
571
+ │ const dest = await createConnection({ │
572
+ │ dialect: 'oracle', │
573
+ │ uri: 'oracle://system:pwd@db.prod:1521/ORCLPDB1', │
574
+ │ }); │
575
+ │ → BridgeManager lance bridge sur port 8766 │
576
+ │ → PID 14305, ojdbc11.jar │
577
+ │ │
578
+ │ // Migration │
579
+ │ const clients = await source.find(ClientSchema, {}); │
580
+ │ // → HTTP POST localhost:8765/query │
581
+ │ // → SELECT * FROM clients │
582
+ │ │
583
+ │ for (const client of clients) { │
584
+ │ await dest.create(ClientSchema, client); │
585
+ │ // → HTTP POST localhost:8766/query │
586
+ │ // → INSERT INTO "clients" (...) VALUES (:1, :2, ...) │
587
+ │ } │
588
+ │ │
589
+ │ // Deconnexion │
590
+ │ await source.disconnect(); // bridge 8765 reste actif │
591
+ │ await dest.disconnect(); // bridge 8766 reste actif │
592
+ │ │
593
+ │ // A la sortie de l'app → BridgeManager.stopAll() │
594
+ │ // → kill 14201, kill 14305 │
595
+ │ // → supprimer .bridge-8765.pid, .bridge-8766.pid │
596
+ └───────────────────────────────────────────────────────────────────┘
597
+
598
+ Etat des ports pendant l'execution :
599
+
600
+ Port Process JDBC URL PID file
601
+ ───── ────────────────── ──────────────────────────────── ──────────────────
602
+ 8765 MostaJdbcBridge jdbc:hsqldb:hsql://localhost/ .bridge-8765.pid
603
+ 8766 MostaJdbcBridge jdbc:oracle:thin:@//db.prod/ORC .bridge-8766.pid
604
+ 9001 HSQLDB Server (natif) —
605
+ 1521 Oracle Server (natif) —
606
+ ```
607
+
608
+ ---
609
+
610
+ ## 3. Renommage AbstractSqlDialect → SessionMostaORMFactory
611
+
612
+ ### 3.1 Justification
613
+
614
+ Dans Hibernate :
615
+ - `Dialect` = definition SQL pure (types, quotes, fonctions)
616
+ - `SessionFactory` = fabrique de sessions, gere le cycle de vie connexion
617
+
618
+ Notre `AbstractSqlDialect` fait les deux : SQL pur + connexion + bridge JDBC.
619
+ Le renommage reflete ce double role et aligne avec Hibernate.
620
+
621
+ ### 2.2 Fichiers impactes
622
+
623
+ | Fichier | Modification |
624
+ |---------|-------------|
625
+ | `abstract-sql.dialect.ts` | Renommer la classe `AbstractSqlDialect` → `SessionMostaORMFactory` |
626
+ | | Renommer le fichier → `session-factory.ts` |
627
+ | `postgres.dialect.ts` | `extends SessionMostaORMFactory` |
628
+ | `mysql.dialect.ts` | `extends SessionMostaORMFactory` |
629
+ | `oracle.dialect.ts` | `extends SessionMostaORMFactory` |
630
+ | `mssql.dialect.ts` | `extends SessionMostaORMFactory` |
631
+ | `db2.dialect.ts` | `extends SessionMostaORMFactory` |
632
+ | `hana.dialect.ts` | `extends SessionMostaORMFactory` |
633
+ | `hsqldb.dialect.ts` | `extends SessionMostaORMFactory` |
634
+ | `spanner.dialect.ts` | `extends SessionMostaORMFactory` |
635
+ | `mariadb.dialect.ts` | Herite de MySQLDialect (pas de changement) |
636
+ | `cockroachdb.dialect.ts` | Herite de PostgresDialect (pas de changement) |
637
+ | `sybase.dialect.ts` | Herite de MSSQLDialect (pas de changement) |
638
+ | `JdbcNormalizer.ts` | Mettre a jour les commentaires |
639
+ | `index.ts` | Exporter `SessionMostaORMFactory` |
640
+
641
+ ### 2.3 Retrocompatibilite
642
+
643
+ ```typescript
644
+ // Dans session-factory.ts — alias pour retrocompatibilite
645
+ export { SessionMostaORMFactory as AbstractSqlDialect };
646
+ ```
647
+
648
+ ---
649
+
650
+ ## 3. Multi-Bridge : plusieurs instances JDBC simultanees
651
+
652
+ ### 3.1 Besoin
653
+
654
+ Pouvoir connecter simultanement :
655
+ - HSQLDB sur port 8765
656
+ - Oracle sur port 8766
657
+ - DB2 sur port 8767
658
+
659
+ Sans devoir arreter l'un pour demarrer l'autre.
660
+
661
+ ### 3.2 Architecture proposee : BridgeManager (singleton global)
662
+
663
+ ```
664
+ BridgeManager (singleton)
665
+ ┌──────────────────────────────────────┐
666
+ │ bridges: Map<string, BridgeInstance> │
667
+ │ │
668
+ │ hsqldb:8765 → { process, url, pid } │
669
+ │ oracle:8766 → { process, url, pid } │
670
+ │ db2:8767 → { process, url, pid } │
671
+ │ │
672
+ │ nextPort: 8768 │
673
+ │ basePort: 8765 │
674
+ └──────────┬───────────────────────────┘
675
+
676
+ ┌───────────────────────┼───────────────────────┐
677
+ ▼ ▼ ▼
678
+ MostaJdbcBridge:8765 MostaJdbcBridge:8766 MostaJdbcBridge:8767
679
+ │ │ │
680
+ ▼ ▼ ▼
681
+ HSQLDB :9001 Oracle :1521 DB2 :50000
682
+ ```
683
+
684
+ ### 3.3 Cle d'identification d'un bridge
685
+
686
+ Chaque bridge est identifie par une cle unique :
687
+
688
+ ```
689
+ cle = `${dialect}:${host}:${port}/${database}`
690
+ ```
691
+
692
+ Exemples :
693
+ - `hsqldb:localhost:9001/` → bridge sur port 8765
694
+ - `oracle:db.prod:1521/ORCLPDB1` → bridge sur port 8766
695
+ - `hsqldb:localhost:9001/testdb` → bridge sur port 8767 (autre base)
696
+
697
+ Cela permet :
698
+ - Deux connexions HSQLDB vers des bases differentes
699
+ - Reutiliser un bridge existant si meme cle (pas de duplication)
700
+
701
+ ### 3.4 Incrementation des ports
702
+
703
+ ```
704
+ Mode 1 : MOSTA_BRIDGE_PORT_INCREMENT=true (defaut)
705
+ ─────────────────────────────────────────────────────
706
+ 1er bridge → port 8765 (MOSTA_BRIDGE_PORT_BASE)
707
+ 2eme bridge → port 8766
708
+ 3eme bridge → port 8767
709
+ ...
710
+
711
+ Mode 2 : MOSTA_BRIDGE_PORT_INCREMENT=false
712
+ ─────────────────────────────────────────────────────
713
+ Tous les bridges sur le meme port → ERREUR si 2eme bridge demande
714
+ Utile si un seul SGBD JDBC a la fois
715
+ ```
716
+
717
+ ### 3.5 BridgeManager API
718
+
719
+ ```typescript
720
+ class BridgeManager {
721
+ // Singleton
722
+ private static instance: BridgeManager;
723
+ static getInstance(): BridgeManager;
724
+
725
+ // Etat
726
+ private bridges: Map<string, BridgeInstance>;
727
+ private nextPort: number;
728
+
729
+ // Obtenir ou creer un bridge pour un dialect/URI
730
+ // Si le bridge existe deja (meme cle) → le reutilise
731
+ // Si nouveau → lance un process Java sur le prochain port
732
+ async getOrCreate(dialect, uri, options?): Promise<BridgeInstance>;
733
+
734
+ // Arreter un bridge specifique
735
+ async stop(key: string): Promise<void>;
736
+
737
+ // Arreter TOUS les bridges (cleanup global)
738
+ async stopAll(): Promise<void>;
739
+
740
+ // Lister les bridges actifs
741
+ list(): BridgeInstance[];
742
+
743
+ // Verifier si un bridge existe pour cette cle
744
+ has(key: string): boolean;
745
+ }
746
+
747
+ interface BridgeInstance {
748
+ key: string; // cle unique
749
+ dialect: DialectType;
750
+ port: number; // port HTTP du bridge
751
+ url: string; // http://localhost:port
752
+ pid: number; // PID du process Java
753
+ jdbcUrl: string; // URL JDBC utilisee
754
+ startedAt: Date;
755
+ process: ChildProcess;
756
+ }
757
+ ```
758
+
759
+ ### 3.6 Integration avec SessionMostaORMFactory
760
+
761
+ ```typescript
762
+ // Dans SessionMostaORMFactory.connect() :
763
+
764
+ async connect(config: ConnectionConfig): Promise<void> {
765
+ // ...
766
+ if (hasJdbcDriver(this.dialectType) && JdbcNormalizer.isAvailable(...)) {
767
+ // Utilise le BridgeManager au lieu de creer un JdbcNormalizer directement
768
+ const manager = BridgeManager.getInstance();
769
+ this.bridgeInstance = await manager.getOrCreate(
770
+ this.dialectType,
771
+ config.uri,
772
+ { jarDir, bridgeJavaFile }
773
+ );
774
+ this.jdbcBridgeActive = true;
775
+ }
776
+ // ...
777
+ }
778
+
779
+ async disconnect(): Promise<void> {
780
+ // NE PAS arreter le bridge — d'autres dialects peuvent l'utiliser
781
+ // Le bridge sera arrete par BridgeManager.stopAll() ou manuellement
782
+ this.jdbcBridgeActive = false;
783
+ this.bridgeInstance = null;
784
+ }
785
+ ```
786
+
787
+ ---
788
+
789
+ ## 4. Auto-Start controlable par .env
790
+
791
+ ### 4.1 Modes de fonctionnement
792
+
793
+ ```
794
+ Mode 1 : MOSTA_BRIDGE_AUTOSTART=true (defaut si JAR present)
795
+ ─────────────────────────────────────────────────────────────
796
+ SessionMostaORMFactory.connect() lance automatiquement le bridge
797
+ si un JAR JDBC est detecte pour le dialect.
798
+
799
+ Comportement actuel — aucun changement.
800
+
801
+ Mode 2 : MOSTA_BRIDGE_AUTOSTART=false
802
+ ─────────────────────────────────────────────────────────────
803
+ Le bridge n'est JAMAIS lance automatiquement.
804
+ L'utilisateur doit :
805
+ a) Lancer le bridge manuellement :
806
+ java --source 11 -cp hsqldb.jar MostaJdbcBridge.java --jdbc-url ...
807
+ b) Ou utiliser le CLI :
808
+ npx mosta-bridge start --dialect hsqldb --port 8765
809
+
810
+ Le dialect tente de se connecter au bridge sur le port configure.
811
+ Si le bridge n'est pas la → erreur claire.
812
+
813
+ Mode 3 : MOSTA_BRIDGE_AUTOSTART=detect (nouveau)
814
+ ─────────────────────────────────────────────────────────────
815
+ Avant de lancer un nouveau bridge :
816
+ 1. Verifier si un bridge tourne deja sur le port
817
+ 2. Si oui → le reutiliser (pas de nouveau process)
818
+ 3. Si non → le lancer automatiquement
819
+
820
+ Evite les doublons apres un redemarrage de l'app.
821
+ ```
822
+
823
+ ### 4.2 Flux de decision au connect()
824
+
825
+ ```
826
+ connect(config) appelé
827
+
828
+
829
+ dialect est-il dans JDBC_REGISTRY ?
830
+
831
+ NON ─┤── ▶ doConnect() natif (npm driver)
832
+
833
+ OUI ─┤
834
+
835
+ MOSTA_BRIDGE_AUTOSTART ?
836
+
837
+ false ─┤── ▶ doConnect() natif (npm driver)
838
+ │ Si echec ET JAR present :
839
+ │ → message "Set MOSTA_BRIDGE_AUTOSTART=true or start bridge manually"
840
+
841
+ true ──┤── ▶ BridgeManager.getOrCreate()
842
+ │ Lance un nouveau bridge si necessaire
843
+
844
+ detect ┤── ▶ Verifie si bridge deja sur le port
845
+
846
+ OUI ─┤── ▶ Reutilise le bridge existant
847
+
848
+ NON ─┤── ▶ Lance un nouveau bridge
849
+ ```
850
+
851
+ ### 4.3 Detection d'un bridge existant (mode detect)
852
+
853
+ ```typescript
854
+ async function detectExistingBridge(port: number): Promise<boolean> {
855
+ try {
856
+ const res = await fetch(`http://localhost:${port}/health`, {
857
+ signal: AbortSignal.timeout(2000),
858
+ });
859
+ return res.ok;
860
+ } catch {
861
+ return false;
862
+ }
863
+ }
864
+ ```
865
+
866
+ ---
867
+
868
+ ## 5. Protection contre boucles et processus orphelins
869
+
870
+ ### 5.1 Protection anti-boucle
871
+
872
+ | Risque | Scenario | Protection |
873
+ |--------|----------|------------|
874
+ | Relance infinie | Bridge crash → connect() relance → crash → relance... | Compteur de tentatives : max 3, puis erreur fatale |
875
+ | Start simultane | 2 connect() en parallele lancent 2 bridges sur le meme port | Mutex/lock dans BridgeManager |
876
+ | Port occupe | Le port est pris par un autre process | Verifier avant de lancer, incrementer si occupe |
877
+
878
+ ```typescript
879
+ // Dans BridgeManager :
880
+
881
+ private startAttempts: Map<string, { count: number; lastAttempt: Date }> = new Map();
882
+ private MAX_START_ATTEMPTS = 3;
883
+ private ATTEMPT_RESET_MS = 60_000; // reset compteur apres 1 minute
884
+
885
+ async getOrCreate(dialect, uri, options?): Promise<BridgeInstance> {
886
+ const key = this.buildKey(dialect, uri);
887
+
888
+ // Bridge deja actif ? → reutiliser
889
+ if (this.bridges.has(key)) {
890
+ const bridge = this.bridges.get(key)!;
891
+ // Verifier qu'il est encore vivant
892
+ if (await this.isAlive(bridge)) return bridge;
893
+ // Mort → nettoyer et relancer
894
+ this.bridges.delete(key);
895
+ }
896
+
897
+ // Protection anti-boucle
898
+ const attempts = this.startAttempts.get(key);
899
+ if (attempts) {
900
+ const elapsed = Date.now() - attempts.lastAttempt.getTime();
901
+ if (elapsed < this.ATTEMPT_RESET_MS && attempts.count >= this.MAX_START_ATTEMPTS) {
902
+ throw new Error(
903
+ `JDBC bridge for "${key}" failed ${this.MAX_START_ATTEMPTS} times ` +
904
+ `in the last ${this.ATTEMPT_RESET_MS / 1000}s. Giving up.\n` +
905
+ `Check Java installation, JAR file, and SGBD server.`
906
+ );
907
+ }
908
+ if (elapsed >= this.ATTEMPT_RESET_MS) {
909
+ this.startAttempts.delete(key); // Reset
910
+ }
911
+ }
912
+
913
+ // Lancer le bridge
914
+ try {
915
+ const bridge = await this.startBridge(dialect, uri, options);
916
+ this.startAttempts.delete(key); // Succes → reset compteur
917
+ return bridge;
918
+ } catch (e) {
919
+ // Incrementer le compteur
920
+ const current = this.startAttempts.get(key) || { count: 0, lastAttempt: new Date() };
921
+ current.count++;
922
+ current.lastAttempt = new Date();
923
+ this.startAttempts.set(key, current);
924
+ throw e;
925
+ }
926
+ }
927
+ ```
928
+
929
+ ### 5.2 Protection processus orphelins
930
+
931
+ | Strategie | Detail |
932
+ |-----------|--------|
933
+ | **PID file** | Ecrire le PID dans `jar_files/.bridge-{port}.pid` au demarrage |
934
+ | **process.on('exit')** | Enregistrer un handler qui arrete tous les bridges a la sortie de Node |
935
+ | **process.on('SIGINT/SIGTERM')** | Idem pour Ctrl+C et kill |
936
+ | **uncaughtException** | Arreter les bridges meme en cas de crash |
937
+ | **Health check periodique** | Verifier toutes les 30s que les bridges sont vivants |
938
+ | **Cleanup au demarrage** | Verifier les PID files au demarrage, tuer les orphelins |
939
+
940
+ ```typescript
941
+ // Dans BridgeManager constructor :
942
+
943
+ constructor() {
944
+ // Cleanup global — arreter tous les bridges quand Node s'arrete
945
+ const cleanup = () => this.stopAllSync();
946
+
947
+ process.on('exit', cleanup);
948
+ process.on('SIGINT', () => { cleanup(); process.exit(0); });
949
+ process.on('SIGTERM', () => { cleanup(); process.exit(0); });
950
+ process.on('uncaughtException', (err) => {
951
+ console.error('[BridgeManager] Uncaught exception — stopping bridges', err);
952
+ cleanup();
953
+ process.exit(1);
954
+ });
955
+
956
+ // Nettoyer les orphelins du demarrage precedent
957
+ this.cleanupOrphans();
958
+ }
959
+
960
+ private cleanupOrphans(): void {
961
+ // Lire les fichiers .bridge-*.pid dans jar_files/
962
+ // Pour chaque PID : verifier si le process existe encore
963
+ // Si oui : le tuer (c'est un orphelin d'un run precedent)
964
+ // Supprimer le fichier PID
965
+ }
966
+
967
+ private stopAllSync(): void {
968
+ for (const [key, bridge] of this.bridges) {
969
+ try { bridge.process.kill('SIGTERM'); } catch {}
970
+ this.removePidFile(bridge.port);
971
+ }
972
+ this.bridges.clear();
973
+ }
974
+ ```
975
+
976
+ ### 5.3 PID File format
977
+
978
+ ```
979
+ jar_files/.bridge-8765.pid
980
+ contenu : 12345
981
+ ```
982
+
983
+ Au demarrage :
984
+ 1. Lire `.bridge-8765.pid` → PID 12345
985
+ 2. Verifier `kill(12345, 0)` → process existe ?
986
+ 3. Si oui → tuer `kill(12345, 'SIGTERM')`
987
+ 4. Supprimer le fichier `.bridge-8765.pid`
988
+ 5. Le port 8765 est maintenant libre
989
+
990
+ ---
991
+
992
+ ## 6. Variables .env
993
+
994
+ ### 6.1 Liste complete
995
+
996
+ ```bash
997
+ # ============================================================
998
+ # JDBC Bridge — Configuration (.env.local)
999
+ # ============================================================
1000
+
1001
+ # Demarrage automatique du bridge JDBC (defaut: true)
1002
+ # true = lance le bridge si JAR present
1003
+ # false = ne lance jamais (demarrage manuel requis)
1004
+ # detect = reutilise un bridge existant ou en lance un nouveau
1005
+ MOSTA_BRIDGE_AUTOSTART=true
1006
+
1007
+ # Port de base pour le premier bridge (defaut: 8765)
1008
+ MOSTA_BRIDGE_PORT_BASE=8765
1009
+
1010
+ # Incrementation automatique des ports (defaut: true)
1011
+ # true = chaque bridge prend le port suivant (8765, 8766, 8767...)
1012
+ # false = tous sur le meme port (erreur si conflit)
1013
+ MOSTA_BRIDGE_PORT_INCREMENT=true
1014
+
1015
+ # Repertoire des fichiers JAR JDBC (defaut: auto-detect jar_files/)
1016
+ MOSTA_JAR_DIR=/home/hmd/dev/MostaGare-Install/mostajs/jar_files
1017
+
1018
+ # Chemin vers MostaJdbcBridge.java (defaut: auto-detect bridge/)
1019
+ MOSTA_BRIDGE_JAVA=/home/hmd/dev/MostaGare-Install/mostajs/mosta-orm/bridge/MostaJdbcBridge.java
1020
+
1021
+ # Nombre max de tentatives de demarrage avant abandon (defaut: 3)
1022
+ MOSTA_BRIDGE_MAX_RETRIES=3
1023
+
1024
+ # Timeout en ms pour attendre que le bridge soit pret (defaut: 15000)
1025
+ MOSTA_BRIDGE_TIMEOUT=15000
1026
+ ```
1027
+
1028
+ ### 6.2 Exemple de configurations
1029
+
1030
+ #### Developpement local (multi-base)
1031
+
1032
+ ```bash
1033
+ MOSTA_BRIDGE_AUTOSTART=true
1034
+ MOSTA_BRIDGE_PORT_INCREMENT=true
1035
+ MOSTA_BRIDGE_PORT_BASE=8765
1036
+ ```
1037
+
1038
+ → HSQLDB sur 8765, Oracle sur 8766 si besoin
1039
+
1040
+ #### Production (bridge demarrage manuel)
1041
+
1042
+ ```bash
1043
+ MOSTA_BRIDGE_AUTOSTART=false
1044
+ ```
1045
+
1046
+ → L'admin lance le bridge avec systemd/docker
1047
+ → L'app se connecte au bridge deja en cours
1048
+
1049
+ #### CI/CD (detection)
1050
+
1051
+ ```bash
1052
+ MOSTA_BRIDGE_AUTOSTART=detect
1053
+ MOSTA_BRIDGE_PORT_BASE=9900
1054
+ ```
1055
+
1056
+ → Reutilise le bridge d'un test precedent si encore actif
1057
+ → Ports hauts pour eviter les conflits
1058
+
1059
+ ---
1060
+
1061
+ ## 7. Plan d'implementation
1062
+
1063
+ ### Phase 1 : Renommage (faible risque)
1064
+
1065
+ | Etape | Fichier | Action | Complexite |
1066
+ |-------|---------|--------|------------|
1067
+ | 1.1 | `abstract-sql.dialect.ts` | Renommer classe → `SessionMostaORMFactory` | Simple |
1068
+ | 1.2 | `abstract-sql.dialect.ts` | Ajouter alias `AbstractSqlDialect` pour retrocompatibilite | Simple |
1069
+ | 1.3 | `abstract-sql.dialect.ts` | Renommer fichier → `session-factory.ts` | Simple |
1070
+ | 1.4 | 8 dialects | Mettre a jour `import` et `extends` | Repetitif |
1071
+ | 1.5 | `index.ts` | Ajouter export `SessionMostaORMFactory` | Simple |
1072
+ | 1.6 | Commentaires | Mettre a jour les references dans JdbcNormalizer, docs | Simple |
1073
+ | 1.7 | Build + test | `tsc` + verification | Validation |
1074
+
1075
+ ### Phase 2 : BridgeManager multi-instance (risque moyen)
1076
+
1077
+ | Etape | Fichier | Action | Complexite |
1078
+ |-------|---------|--------|------------|
1079
+ | 2.1 | `src/bridge/BridgeManager.ts` | Creer la classe singleton BridgeManager | Moyen |
1080
+ | 2.2 | `src/bridge/BridgeManager.ts` | Gestion des ports (base + increment) | Simple |
1081
+ | 2.3 | `src/bridge/BridgeManager.ts` | Map des bridges actifs par cle | Simple |
1082
+ | 2.4 | `src/bridge/BridgeManager.ts` | Reutilisation si meme cle | Simple |
1083
+ | 2.5 | `session-factory.ts` | Remplacer JdbcNormalizer direct par BridgeManager | Moyen |
1084
+ | 2.6 | `session-factory.ts` | `disconnect()` ne tue plus le bridge (BridgeManager gere) | Simple |
1085
+ | 2.7 | `JdbcNormalizer.ts` | Deviens interne au BridgeManager (ou supprime) | Moyen |
1086
+ | 2.8 | `index.ts` | Exporter BridgeManager | Simple |
1087
+ | 2.9 | Build + test | Test avec 2 bridges simultanees | Validation |
1088
+
1089
+ ### Phase 3 : Auto-Start controlable (risque moyen)
1090
+
1091
+ | Etape | Fichier | Action | Complexite |
1092
+ |-------|---------|--------|------------|
1093
+ | 3.1 | `src/bridge/BridgeManager.ts` | Lire MOSTA_BRIDGE_AUTOSTART depuis process.env | Simple |
1094
+ | 3.2 | `session-factory.ts` | Implementer la logique true/false/detect | Moyen |
1095
+ | 3.3 | `src/bridge/BridgeManager.ts` | Mode detect : health check avant lancement | Simple |
1096
+ | 3.4 | `session-factory.ts` | Si autostart=false ET pas de driver npm → erreur claire | Simple |
1097
+ | 3.5 | Build + test | Test des 3 modes | Validation |
1098
+
1099
+ ### Phase 4 : Protections (risque faible)
1100
+
1101
+ | Etape | Fichier | Action | Complexite |
1102
+ |-------|---------|--------|------------|
1103
+ | 4.1 | `src/bridge/BridgeManager.ts` | Anti-boucle : compteur de tentatives + timeout | Moyen |
1104
+ | 4.2 | `src/bridge/BridgeManager.ts` | PID files : ecriture au start, lecture au cleanup | Simple |
1105
+ | 4.3 | `src/bridge/BridgeManager.ts` | Cleanup orphelins au demarrage du BridgeManager | Moyen |
1106
+ | 4.4 | `src/bridge/BridgeManager.ts` | process.on('exit/SIGINT/SIGTERM') → stopAll | Simple |
1107
+ | 4.5 | `src/bridge/BridgeManager.ts` | Detection port occupe avant lancement | Simple |
1108
+ | 4.6 | Build + test | Test crash + restart + orphelins | Validation |
1109
+
1110
+ ### Phase 5 : Documentation et cleanup
1111
+
1112
+ | Etape | Action | Complexite |
1113
+ |-------|--------|------------|
1114
+ | 5.1 | Mettre a jour jdbc-normalizer-study.md | Simple |
1115
+ | 5.2 | Mettre a jour audit-dialects-vs-hibernate.md | Simple |
1116
+ | 5.3 | Supprimer le backup `_hsqldb.dialect (copie).ts_` | Simple |
1117
+ | 5.4 | Build final `tsc` + verification dist/ | Validation |
1118
+
1119
+ ---
1120
+
1121
+ ## 8. Matrice de risques
1122
+
1123
+ | Risque | Probabilite | Impact | Mitigation |
1124
+ |--------|-------------|--------|------------|
1125
+ | Regression sur les dialects npm (pg, mysql2...) | Faible | Haut | Le renommage ne touche pas la logique, juste les noms |
1126
+ | Port deja occupe | Moyen | Moyen | Detection avant lancement + increment |
1127
+ | Process Java orphelin | Moyen | Faible | PID files + cleanup au demarrage + process.on('exit') |
1128
+ | Boucle de relance | Faible | Haut | Compteur max 3 tentatives + timeout |
1129
+ | Java non installe | Moyen | Moyen | Message d'erreur clair + mode autostart=false |
1130
+ | 2 bridges meme JDBC URL | Faible | Moyen | Cle unique par bridge, reutilisation |
1131
+ | Crash Node sans cleanup | Moyen | Moyen | PID files persistent + cleanup orphelins au restart |
1132
+ | Trop de bridges ouverts | Faible | Moyen | Limite configurable (max 10 bridges) |
1133
+
1134
+ ---
1135
+
1136
+ ## Annexe : Structure finale des fichiers
1137
+
1138
+ ```
1139
+ mosta-orm/
1140
+ ├── src/
1141
+ │ ├── index.ts
1142
+ │ ├── core/
1143
+ │ │ ├── types.ts
1144
+ │ │ ├── factory.ts
1145
+ │ │ ├── config.ts
1146
+ │ │ ├── registry.ts
1147
+ │ │ ├── base-repository.ts
1148
+ │ │ ├── normalizer.ts
1149
+ │ │ └── errors.ts
1150
+ │ ├── dialects/
1151
+ │ │ ├── session-factory.ts ← ex abstract-sql.dialect.ts (renomme)
1152
+ │ │ ├── mongo.dialect.ts
1153
+ │ │ ├── sqlite.dialect.ts
1154
+ │ │ ├── postgres.dialect.ts
1155
+ │ │ ├── mysql.dialect.ts
1156
+ │ │ ├── mariadb.dialect.ts
1157
+ │ │ ├── oracle.dialect.ts
1158
+ │ │ ├── mssql.dialect.ts
1159
+ │ │ ├── cockroachdb.dialect.ts
1160
+ │ │ ├── db2.dialect.ts
1161
+ │ │ ├── hana.dialect.ts
1162
+ │ │ ├── hsqldb.dialect.ts
1163
+ │ │ ├── spanner.dialect.ts
1164
+ │ │ └── sybase.dialect.ts
1165
+ │ └── bridge/
1166
+ │ ├── jdbc-registry.ts ← table dialect → JAR → JDBC URL
1167
+ │ ├── JdbcNormalizer.ts ← lancement d'UN bridge (bas niveau)
1168
+ │ └── BridgeManager.ts ← NOUVEAU : singleton multi-bridge
1169
+ ├── bridge/
1170
+ │ └── MostaJdbcBridge.java ← bridge HTTP-JDBC universel
1171
+ ├── jar_files/ ← JARs JDBC (gitignore)
1172
+ │ ├── hsqldb-2.7.2.jar
1173
+ │ ├── .bridge-8765.pid ← PID file (auto-genere)
1174
+ │ └── .bridge-8766.pid
1175
+ └── docs/
1176
+ ├── jdbc-normalizer-study.md
1177
+ ├── audit-dialects-vs-hibernate.md
1178
+ └── plan-session-factory-multi-bridge.md ← ce document
1179
+ ```
1180
+
1181
+ ### Vos points :
1182
+
1183
+ 1. Le fichier .env ne peut pas être en dur
1184
+
1185
+ Vous avez raison. Le BridgeManager lit déjà process.env dynamiquement — il
1186
+ ne hardcode pas de fichier .env. Peu importe si c'est .env, .env.local, ou
1187
+ .env.production, c'est Next.js (ou dotenv) qui charge les variables dans
1188
+ process.env, et le BridgeManager les lit de là. Rien à changer côté ORM.
1189
+
1190
+ 2. Ne changer le port que si on change de SGBD — si même SGBD déjà lancé,
1191
+ ne pas relancer
1192
+
1193
+ C'est exactement le comportement implémenté. Le BridgeManager.getOrCreate()
1194
+ utilise une clé unique par SGBD :
1195
+
1196
+ clé = `${dialect}:${host}:${port}/${database}`
1197
+
1198
+ - Même SGBD, même URI → la clé est identique → le bridge existant est
1199
+ réutilisé (pas de nouveau process, même port)
1200
+ - SGBD différent (ou URI différente) → nouvelle clé → nouveau bridge sur le
1201
+ port suivant
1202
+
1203
+ Laissez-moi ajuster le BridgeManager pour renforcer cette logique — le port
1204
+ ne doit s'incrémenter qu'au lancement d'un nouveau bridge, pas à chaque
1205
+ appel.
1206
+
1207
+ ● Read 1 file (ctrl+o to expand)
1208
+
1209
+ ● La logique est déjà correcte. Récapitulons le flux :
1210
+
1211
+ 1. getOrCreate("hsqldb", "hsqldb:hsql://localhost:9001/") → clé =
1212
+ hsqldb:localhost:9001/ → nouveau bridge port 8765
1213
+ 2. getOrCreate("hsqldb", "hsqldb:hsql://localhost:9001/") → même clé →
1214
+ réutilise bridge port 8765 (pas de relancement)
1215
+ 3. getOrCreate("oracle", "oracle://host:1521/db") → clé différente →
1216
+ nouveau bridge port 8766
1217
+ 4. getOrCreate("oracle", "oracle://host:1521/db") → même clé → réutilise
1218
+ bridge port 8766
1219
+
1220
+ Le port ne s'incrémente que quand un nouveau SGBD distinct est connecté. Si
1221
+ le même SGBD est déjà lancé, il est réutilisé directement sans toucher aux
1222
+ ports.
1223
+
1224
+ La compilation TypeScript passe sans erreurs. Les phases 2-4 du plan sont
1225
+ implémentées dans BridgeManager.ts :
1226
+ - Multi-bridge avec clé unique par SGBD
1227
+ - Autostart contrôlable via process.env.MOSTA_BRIDGE_AUTOSTART
1228
+ (true/false/detect)
1229
+ - Protection anti-boucle (max 3 tentatives en 60s)
1230
+ - PID files + cleanup orphelins au démarrage
1231
+ - process.on('exit/SIGINT/SIGTERM') → stopAll()
1232
+ - Détection port occupé avec incrémentation automatique
1233
+