Struts 1 without an API: the screen's CSV export, read as it comes out
Goal Read a Struts 1 application whose only output is its screen's CSV export, and be told when a customer return has waited for a part for more than 30 days.
A pump manufacturer handles its customer returns in a Struts 1 intranet written in 2007: Tomcat, JSP, DisplayTag. No API; the screen has a CSV export link, and a scheduled job fetches it every night.
no header, ISO-8859-1, a quote escaped by a backslash;
amounts grouped by a no-break space: "2 310,00";
the next day, the closed returns leave the table, seven returns waiting for a part raise a decision.
RMA-26-0001,20/08/2026,Agri-Services Dubreuil,Vanne V-40,Surchauffe,Reçu,"89,90",78
RMA-26-0002,21/08/2026,"Léon, Fils & Cie",Surpresseur S-3,Casse au transport,Avoir à émettre,"2 310,00",77
RMA-26-0003,22/08/2026,Établissements Morvan,Pompe P-220,Erreur de référence,Ouvert,"1 234,50",76
RMA-26-0005,28/08/2026,Agri-Services Dubreuil,Vanne V-40,"Client: \"pièce jamais reçue\", relancé",En expertise,"179,80",70
RMA-26-0006,28/08/2026,Quincaillerie Bérard,Carte électronique CE-12,Ne démarre pas,En attente pièce,"636,00",70
RMA-26-0007,29/08/2026,Côté Jardin Négoce,Carte électronique CE-12,Erreur de référence,En attente pièce,"318,00",69
RMA-26-0008,31/08/2026,Thermique de l'Ouest,Surpresseur S-3,"Bruit anormal, vibrations",En attente pièce,"4 620,00",67
RMA-26-0009,01/09/2026,Plomberie Garnier SARL,Carte électronique CE-12,Erreur de référence,Ouvert,"318,00",66
RMA-26-0010,03/09/2026,Hydro Saône,Carte électronique CE-12,Fuite au joint,En attente pièce,"636,00",64
RMA-26-0011,04/09/2026,"Léon, Fils & Cie",Pompe P-220,"Client: \"pièce jamais reçue\", relancé",Avoir à émettre,"1 234,50",63
RMA-26-0012,08/09/2026,Plomberie Garnier SARL,Carte électronique CE-12,"Client: \"pièce jamais reçue\", relancé",Avoir à émettre,"636,00",59
RMA-26-0013,11/09/2026,Thermique de l'Ouest,Surpresseur S-3,Surchauffe,En attente pièce,"2 310,00",56
RMA-26-0014,13/09/2026,"Léon, Fils & Cie","Moteur M-1,5 kW","Client: \"pièce jamais reçue\", relancé",En expertise,"612,75",54
RMA-26-0015,14/09/2026,Thermique de l'Ouest,Vanne V-40,Ne démarre pas,Ouvert,"89,90",53
RMA-26-0016,16/09/2026,Établissements Morvan,Vanne V-40,Fuite au joint,Reçu,"89,90",51
RMA-26-0017,17/09/2026,Plomberie Garnier SARL,Pressostat PR-6,"Bruit anormal, vibrations",En attente pièce,"46,20",50
RMA-26-0018,19/09/2026,Plomberie Garnier SARL,Pompe P-220,Casse au transport,Ouvert,"1 234,50",48
RMA-26-0020,21/09/2026,"Léon, Fils & Cie",Pompe P-220,Surchauffe,En expertise,"1 234,50",46
RMA-26-0021,23/09/2026,Hydro Saône,Surpresseur S-3,"Bruit anormal, vibrations",Reçu,"2 310,00",44
RMA-26-0022,24/09/2026,Hydro Saône,Pressostat PR-6,Surchauffe,En attente pièce,"46,20",43
… 25 more lines
$ lm connector run scenario-struts-arun scheduled: c963af0f-d820-4fec-abd0-760c0f7a0505$ lm run get c963af0f-d820-4fec-abd0-760c0f7a0505ID CONNECTOR ENDPOINT STATUS IN OUT SKIPPED DEAD-LETTERS STARTED ENDED EVALUATED
c963af0f scenario-struts-a default Completed 45 45 0 0 2026-11-07 16:14 2026-11-07 16:14 yes• 1 row(s) removed — the source no longer holds them$ lm inbox --process scenario-struts-waiting-aID WHEN PROCESS MESSAGE ENTITY_KEY
25492 just now scenario-struts-waiting-a RMA-26-0022 (Hydro Saône, Pressostat PR-6): waiting for a part for 43 days RMA-26-0022
25491 just now scenario-struts-waiting-a RMA-26-0017 (Plomberie Garnier SARL, Pressostat PR-6): waiting for a part for 50 days RMA-26-0017
25490 just now scenario-struts-waiting-a RMA-26-0013 (Thermique de l'Ouest, Surpresseur S-3): waiting for a part for 56 days RMA-26-0013
25489 just now scenario-struts-waiting-a RMA-26-0010 (Hydro Saône, Carte électronique CE-12): waiting for a part for 64 days RMA-26-0010
25488 just now scenario-struts-waiting-a RMA-26-0008 (Thermique de l'Ouest, Surpresseur S-3): waiting for a part for 67 days RMA-26-0008
25487 just now scenario-struts-waiting-a RMA-26-0007 (Côté Jardin Négoce, Carte électronique CE-12): waiting for a part for 69 days RMA-26-0007
25486 just now scenario-struts-waiting-a RMA-26-0006 (Quincaillerie Bérard, Carte électronique CE-12): waiting for a part for 70 days RMA-26-0006
default
Completed · IN 45 · OUT 45 · DEAD-LETTERS 0
hub
45 RMAs in the table; gone: RMA-26-0004, RMA-26-0019, RMA-26-0030; RMA-26-0040: Reçu
RMA-26-0006
RMA-26-0006 (Quincaillerie Bérard, Carte électronique CE-12): waiting for a part for 70 days
RMA-26-0007
RMA-26-0007 (Côté Jardin Négoce, Carte électronique CE-12): waiting for a part for 69 days
RMA-26-0008
RMA-26-0008 (Thermique de l'Ouest, Surpresseur S-3): waiting for a part for 67 days
RMA-26-0010
RMA-26-0010 (Hydro Saône, Carte électronique CE-12): waiting for a part for 64 days
RMA-26-0013
RMA-26-0013 (Thermique de l'Ouest, Surpresseur S-3): waiting for a part for 56 days
RMA-26-0017
RMA-26-0017 (Plomberie Garnier SARL, Pressostat PR-6): waiting for a part for 50 days
RMA-26-0022
RMA-26-0022 (Hydro Saône, Pressostat PR-6): waiting for a part for 43 days
The run says "1 row(s) removed": that is how much the table shrank. The lines have no key; three returns closed and two opened make 1.
The age changes every night, so every line is rewritten: that is how the rule, declared after the first read, sees the seven returns. What becomes of the decision of a return still waiting on the third night was not replayed.
The export link is called without a session. An application that requires a login for its screen needs a session cookie: not replayed here.
The cron job is played by the scenario's script, not by a crontab.
Returns, customers and amounts synthetic; Struts 1.3.10, DisplayTag 1.2 and Tomcat 9 are real.
After-sales · Struts 1
The customer returns live in a 2007 intranet.
Struts 1, DisplayTag, Tomcat. No API, one "Export CSV" link.
?6578706f7274=1&d-16516-e=1
The problem
The hub read 5 lines out of 46.
DisplayTag writes \" inside a quote instead of doubling it.
And 2 310,00 is grouped by a no-break space: the amount was lost.
What is not changed
The application stays as it is.
No line of Java, no JSP touched.
The export is the screen's own, called every night.
What had to be written
escape: "\\"
encoding: ISO-8859-1
Two lines in the connector.
The reason keeps its quotes.
One file: the table follows it.
What was fixed
parser.escape exists (#1877).
parse-number strips the no-break space (#1878).
46 returns read, 7 decisions the next day.
Try it
The scenario replays locally.
Every step is published with its output.
This scenario runs on lumnik Open, free and open source.