|
5 | 5 |
|
6 | 6 | import java.sql.PreparedStatement; |
7 | 7 | import java.sql.Timestamp; |
8 | | -import java.time.Instant; |
9 | | -import java.time.LocalTime; |
10 | 8 |
|
11 | 9 | /** |
12 | 10 | * Static store methods — one per N21 table. |
13 | | - * Each method silently swallows failures so the server never crashes on a DB write. |
| 11 | + * Each method attempts MySQL first; on any failure it marks the DB unavailable |
| 12 | + * and seamlessly routes the record to the XML fallback. |
14 | 13 | */ |
15 | 14 | public class N21Store |
16 | 15 | { |
17 | | - // ── connections ────────────────────────────────────────────────────────── |
| 16 | + // ── connections ─────────────────────────────────────────────────────────── |
18 | 17 |
|
19 | 18 | public static void storeConnection(Connection c, int serverPort) |
20 | 19 | { |
21 | | - try |
| 20 | + String remoteAddr = c.remote_address != null ? c.remote_address : ""; |
| 21 | + String inetAddr = c.internet_address != null ? c.internet_address.getHostAddress() : ""; |
| 22 | + String telnet = Boolean.TRUE.equals(c.IS_TELNET_EXCELSIOR_CONNECTED) ? "1" : "0"; |
| 23 | + String inception = c.inception_date != null ? c.inception_date.toString() : ""; |
| 24 | + |
| 25 | + if (dbOk()) |
22 | 26 | { |
23 | | - PreparedStatement ps = N21DataSource.get().prepareStatement( |
24 | | - "INSERT INTO connections (remote_address, internet_address, server_port, is_telnet_excelsior_connected, inception_date) VALUES (?,?,?,?,?)"); |
25 | | - ps.setString(1, c.remote_address != null ? c.remote_address : ""); |
26 | | - ps.setString(2, c.internet_address != null ? c.internet_address.getHostAddress() : ""); |
27 | | - ps.setInt(3, serverPort); |
28 | | - ps.setBoolean(4, Boolean.TRUE.equals(c.IS_TELNET_EXCELSIOR_CONNECTED)); |
29 | | - ps.setTimestamp(5, c.inception_date != null ? new Timestamp(c.inception_date.getTime()) : new Timestamp(System.currentTimeMillis())); |
30 | | - ps.executeUpdate(); |
31 | | - ps.close(); |
| 27 | + try |
| 28 | + { |
| 29 | + PreparedStatement ps = N21DataSource.get().prepareStatement( |
| 30 | + "INSERT INTO connections (remote_address, internet_address, server_port, is_telnet_excelsior_connected, inception_date) VALUES (?,?,?,?,?)"); |
| 31 | + ps.setString(1, remoteAddr); |
| 32 | + ps.setString(2, inetAddr); |
| 33 | + ps.setInt(3, serverPort); |
| 34 | + ps.setBoolean(4, Boolean.TRUE.equals(c.IS_TELNET_EXCELSIOR_CONNECTED)); |
| 35 | + ps.setTimestamp(5, c.inception_date != null ? new Timestamp(c.inception_date.getTime()) : new Timestamp(System.currentTimeMillis())); |
| 36 | + ps.executeUpdate(); ps.close(); |
| 37 | + return; |
| 38 | + } |
| 39 | + catch (Exception e) { fail("connections", e); } |
32 | 40 | } |
33 | | - catch (Exception e) { System.err.println("[N21Store] connections: " + e.getMessage()); } |
| 41 | + N21XmlFallback.append("connections", |
| 42 | + "remote_address", remoteAddr, "internet_address", inetAddr, |
| 43 | + "server_port", String.valueOf(serverPort), "telnet", telnet, "inception_date", inception); |
34 | 44 | } |
35 | 45 |
|
36 | 46 | // ── geo_locations ───────────────────────────────────────────────────────── |
37 | 47 |
|
38 | 48 | public static void storeGeo(String ip, String city, String country) |
39 | 49 | { |
40 | | - try |
| 50 | + if (dbOk()) |
41 | 51 | { |
42 | | - PreparedStatement ps = N21DataSource.get().prepareStatement( |
43 | | - "INSERT INTO geo_locations (ip_address, city, country) VALUES (?,?,?) " + |
44 | | - "ON DUPLICATE KEY UPDATE city=VALUES(city), country=VALUES(country), resolved_at=NOW()"); |
45 | | - ps.setString(1, ip); |
46 | | - ps.setString(2, city != null ? city : ""); |
47 | | - ps.setString(3, country != null ? country : ""); |
48 | | - ps.executeUpdate(); |
49 | | - ps.close(); |
| 52 | + try |
| 53 | + { |
| 54 | + PreparedStatement ps = N21DataSource.get().prepareStatement( |
| 55 | + "INSERT INTO geo_locations (ip_address, city, country) VALUES (?,?,?) " + |
| 56 | + "ON DUPLICATE KEY UPDATE city=VALUES(city), country=VALUES(country), resolved_at=NOW()"); |
| 57 | + ps.setString(1, ip); ps.setString(2, city != null ? city : ""); ps.setString(3, country != null ? country : ""); |
| 58 | + ps.executeUpdate(); ps.close(); |
| 59 | + return; |
| 60 | + } |
| 61 | + catch (Exception e) { fail("geo_locations", e); } |
50 | 62 | } |
51 | | - catch (Exception e) { System.err.println("[N21Store] geo_locations: " + e.getMessage()); } |
| 63 | + N21XmlFallback.append("geo_locations", "ip_address", ip, "city", city, "country", country); |
52 | 64 | } |
53 | 65 |
|
54 | 66 | // ── exceptions ──────────────────────────────────────────────────────────── |
55 | 67 |
|
56 | 68 | public static void storeException(ExceptionRecord r, boolean isSecurityEvent) |
57 | 69 | { |
58 | | - try |
| 70 | + if (dbOk()) |
59 | 71 | { |
60 | | - PreparedStatement ps = N21DataSource.get().prepareStatement( |
61 | | - "INSERT INTO exceptions (exception_type, message, origin, stack_trace, is_security_event, recorded_at) VALUES (?,?,?,?,?,?)"); |
62 | | - ps.setString(1, r.exception().getClass().getSimpleName()); |
63 | | - ps.setString(2, r.exception().getMessage()); |
64 | | - ps.setString(3, r.origin()); |
65 | | - ps.setString(4, r.stackTrace()); |
66 | | - ps.setBoolean(5, isSecurityEvent); |
67 | | - ps.setTimestamp(6, Timestamp.from(r.timestamp())); |
68 | | - ps.executeUpdate(); |
69 | | - ps.close(); |
| 72 | + try |
| 73 | + { |
| 74 | + PreparedStatement ps = N21DataSource.get().prepareStatement( |
| 75 | + "INSERT INTO exceptions (exception_type, message, origin, stack_trace, is_security_event, recorded_at) VALUES (?,?,?,?,?,?)"); |
| 76 | + ps.setString(1, r.exception().getClass().getSimpleName()); |
| 77 | + ps.setString(2, r.exception().getMessage()); |
| 78 | + ps.setString(3, r.origin()); |
| 79 | + ps.setString(4, r.stackTrace()); |
| 80 | + ps.setBoolean(5, isSecurityEvent); |
| 81 | + ps.setTimestamp(6, Timestamp.from(r.timestamp())); |
| 82 | + ps.executeUpdate(); ps.close(); |
| 83 | + return; |
| 84 | + } |
| 85 | + catch (Exception e) { fail("exceptions", e); } |
70 | 86 | } |
71 | | - catch (Exception e) { System.err.println("[N21Store] exceptions: " + e.getMessage()); } |
| 87 | + N21XmlFallback.append("exceptions", |
| 88 | + "exception_type", r.exception().getClass().getSimpleName(), |
| 89 | + "message", r.exception().getMessage(), |
| 90 | + "origin", r.origin(), |
| 91 | + "stack_trace", r.stackTrace(), |
| 92 | + "security", String.valueOf(isSecurityEvent), |
| 93 | + "recorded_at", r.timestamp().toString()); |
72 | 94 | } |
73 | 95 |
|
74 | 96 | // ── security_events ─────────────────────────────────────────────────────── |
75 | 97 |
|
76 | 98 | public static void storeSecurityEvent(ExceptionRecord r, String sourceIp) |
77 | 99 | { |
78 | | - try |
| 100 | + if (dbOk()) |
79 | 101 | { |
80 | | - PreparedStatement ps = N21DataSource.get().prepareStatement( |
81 | | - "INSERT INTO security_events (event_type, message, origin, source_ip, recorded_at) VALUES (?,?,?,?,?)"); |
82 | | - ps.setString(1, r.exception().getClass().getSimpleName()); |
83 | | - ps.setString(2, r.exception().getMessage()); |
84 | | - ps.setString(3, r.origin()); |
85 | | - ps.setString(4, sourceIp != null ? sourceIp : ""); |
86 | | - ps.setTimestamp(5, Timestamp.from(r.timestamp())); |
87 | | - ps.executeUpdate(); |
88 | | - ps.close(); |
| 102 | + try |
| 103 | + { |
| 104 | + PreparedStatement ps = N21DataSource.get().prepareStatement( |
| 105 | + "INSERT INTO security_events (event_type, message, origin, source_ip, recorded_at) VALUES (?,?,?,?,?)"); |
| 106 | + ps.setString(1, r.exception().getClass().getSimpleName()); |
| 107 | + ps.setString(2, r.exception().getMessage()); |
| 108 | + ps.setString(3, r.origin()); |
| 109 | + ps.setString(4, sourceIp != null ? sourceIp : ""); |
| 110 | + ps.setTimestamp(5, Timestamp.from(r.timestamp())); |
| 111 | + ps.executeUpdate(); ps.close(); |
| 112 | + return; |
| 113 | + } |
| 114 | + catch (Exception e) { fail("security_events", e); } |
89 | 115 | } |
90 | | - catch (Exception e) { System.err.println("[N21Store] security_events: " + e.getMessage()); } |
| 116 | + N21XmlFallback.append("security_events", |
| 117 | + "event_type", r.exception().getClass().getSimpleName(), |
| 118 | + "message", r.exception().getMessage(), |
| 119 | + "origin", r.origin(), |
| 120 | + "source_ip", sourceIp != null ? sourceIp : "", |
| 121 | + "recorded_at", r.timestamp().toString()); |
91 | 122 | } |
92 | 123 |
|
93 | 124 | // ── national_ids ────────────────────────────────────────────────────────── |
94 | 125 |
|
95 | 126 | public static void storeNationalId(long eightDigit, long sixteenDigit) |
96 | 127 | { |
97 | | - try |
| 128 | + if (dbOk()) |
98 | 129 | { |
99 | | - PreparedStatement ps = N21DataSource.get().prepareStatement( |
100 | | - "INSERT IGNORE INTO national_ids (eight_digit_id, sixteen_digit_key) VALUES (?,?)"); |
101 | | - ps.setLong(1, eightDigit); |
102 | | - ps.setLong(2, sixteenDigit); |
103 | | - ps.executeUpdate(); |
104 | | - ps.close(); |
| 130 | + try |
| 131 | + { |
| 132 | + PreparedStatement ps = N21DataSource.get().prepareStatement( |
| 133 | + "INSERT IGNORE INTO national_ids (eight_digit_id, sixteen_digit_key) VALUES (?,?)"); |
| 134 | + ps.setLong(1, eightDigit); ps.setLong(2, sixteenDigit); |
| 135 | + ps.executeUpdate(); ps.close(); |
| 136 | + return; |
| 137 | + } |
| 138 | + catch (Exception e) { fail("national_ids", e); } |
105 | 139 | } |
106 | | - catch (Exception e) { System.err.println("[N21Store] national_ids: " + e.getMessage()); } |
| 140 | + N21XmlFallback.append("national_ids", |
| 141 | + "eight_digit_id", String.valueOf(eightDigit), |
| 142 | + "sixteen_digit_key", String.valueOf(sixteenDigit)); |
107 | 143 | } |
108 | 144 |
|
109 | 145 | // ── status_snapshots ────────────────────────────────────────────────────── |
110 | 146 |
|
111 | 147 | public static void storeStatusSnapshot(int activeConnections, long uptimeSecs, long totalMb, long usedMb) |
112 | 148 | { |
113 | | - try |
| 149 | + if (dbOk()) |
| 150 | + { |
| 151 | + try |
| 152 | + { |
| 153 | + PreparedStatement ps = N21DataSource.get().prepareStatement( |
| 154 | + "INSERT INTO status_snapshots (active_connections, server_uptime_secs, total_memory_mb, used_memory_mb, local_server_time) VALUES (?,?,?,?,NOW())"); |
| 155 | + ps.setInt(1, activeConnections); ps.setLong(2, uptimeSecs); |
| 156 | + ps.setLong(3, totalMb); ps.setLong(4, usedMb); |
| 157 | + ps.executeUpdate(); ps.close(); |
| 158 | + return; |
| 159 | + } |
| 160 | + catch (Exception e) { fail("status_snapshots", e); } |
| 161 | + } |
| 162 | + N21XmlFallback.append("status_snapshots", |
| 163 | + "active_connections", String.valueOf(activeConnections), |
| 164 | + "uptime_secs", String.valueOf(uptimeSecs), |
| 165 | + "total_memory_mb", String.valueOf(totalMb), |
| 166 | + "used_memory_mb", String.valueOf(usedMb)); |
| 167 | + } |
| 168 | + |
| 169 | + // ── helpers ─────────────────────────────────────────────────────────────── |
| 170 | + |
| 171 | + /** Returns true only if a live DB connection can be obtained. */ |
| 172 | + private static boolean dbOk() |
| 173 | + { |
| 174 | + if (!N21DataSource.isAvailable()) |
114 | 175 | { |
115 | | - PreparedStatement ps = N21DataSource.get().prepareStatement( |
116 | | - "INSERT INTO status_snapshots (active_connections, server_uptime_secs, total_memory_mb, used_memory_mb, local_server_time) VALUES (?,?,?,?,NOW())"); |
117 | | - ps.setInt(1, activeConnections); |
118 | | - ps.setLong(2, uptimeSecs); |
119 | | - ps.setLong(3, totalMb); |
120 | | - ps.setLong(4, usedMb); |
121 | | - ps.executeUpdate(); |
122 | | - ps.close(); |
| 176 | + // Attempt a reconnect once per call — if it throws, stay in fallback mode |
| 177 | + try { N21DataSource.get(); return true; } |
| 178 | + catch (Exception ignored) { return false; } |
123 | 179 | } |
124 | | - catch (Exception e) { System.err.println("[N21Store] status_snapshots: " + e.getMessage()); } |
| 180 | + return true; |
| 181 | + } |
| 182 | + |
| 183 | + /** Log the failure, mark the datasource down, and let the caller fall through to XML. */ |
| 184 | + private static void fail(String table, Exception e) |
| 185 | + { |
| 186 | + System.err.println("[N21Store] DB unavailable for table '" + table + "': " + e.getMessage() + " — routing to XML fallback."); |
| 187 | + N21DataSource.markFailed(); |
125 | 188 | } |
126 | 189 | } |
0 commit comments