Skip to content

Commit 40487b6

Browse files
committed
System Touch
1 parent a102625 commit 40487b6

4 files changed

Lines changed: 178 additions & 0 deletions
Binary file not shown.
514 Bytes
Binary file not shown.

source/telnet/TelnetCommunicationProxy.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class TelnetCommunicationProxy
3030

3131
public TelnetInputBuilder input_builder;
3232

33+
public TelnetProxyLivenessMonitor liveness_monitor;
34+
3335
public TelnetCommunicationProxy(WebExpress web_express)
3436
{
3537
CommonRails.printSystemComponent(this, this.hashCode(),". WebExpress Telnet Communicator starts .");
@@ -53,6 +55,28 @@ public TelnetCommunicationProxy(WebExpress web_express)
5355
this.OUTPUT_BUILDER.start();
5456

5557
this.input_builder.start();
58+
59+
this.liveness_monitor = new TelnetProxyLivenessMonitor(this);
60+
61+
this.liveness_monitor.start();
62+
}
63+
64+
/** Returns true when the backing process is alive and the writer pipe is open. */
65+
public boolean isProxyAlive()
66+
{
67+
try
68+
{
69+
if (this.process == null || !this.process.isAlive()) return false;
70+
71+
if (this.writer != null) this.writer.flush();
72+
73+
return true;
74+
}
75+
catch (Exception e)
76+
{
77+
ExceptionHandler.dispatch(e);
78+
return false;
79+
}
5680
}
5781

5882
public static class TelnetProxyCommunicator extends Thread
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package telnet;
2+
3+
import commons.CommonRails;
4+
import exceptions.ExceptionHandler;
5+
6+
import java.util.concurrent.TimeUnit;
7+
8+
/**
9+
* Monitors liveness of the backend telnet proxy process.
10+
*
11+
* Behaviour:
12+
* - Runs continuously as a daemon thread.
13+
* - When at least one frontend client is connected it probes the proxy
14+
* every PROBE_INTERVAL_MS by writing a NOP (empty flush) to the writer.
15+
* - If the probe fails, or the backing process is no longer alive,
16+
* it calls TelnetCommunicationProxy.reconnect() to restart the session.
17+
* - When no clients are connected it sleeps at IDLE_INTERVAL_MS to avoid
18+
* unnecessary reconnection churn.
19+
*/
20+
public class TelnetProxyLivenessMonitor extends Thread
21+
{
22+
private static final long PROBE_INTERVAL_MS = 15_000L;
23+
private static final long IDLE_INTERVAL_MS = 5_000L;
24+
private static final long RECONNECT_DELAY_MS = 3_000L;
25+
26+
private final TelnetCommunicationProxy proxy;
27+
28+
public TelnetProxyLivenessMonitor(TelnetCommunicationProxy proxy)
29+
{
30+
this.proxy = proxy;
31+
this.setName("TelnetProxyLivenessMonitor");
32+
this.setDaemon(true);
33+
}
34+
35+
@Override
36+
public void run()
37+
{
38+
CommonRails.printSystemComponent(this, this.hashCode(), ". TelnetProxyLivenessMonitor starts .");
39+
40+
while (!Thread.currentThread().isInterrupted())
41+
{
42+
try
43+
{
44+
boolean clientsConnected = proxy.web_express.CURRENT_CONNECTIONS.size() > 0;
45+
46+
if (clientsConnected)
47+
{
48+
if (!isProxyAlive())
49+
{
50+
CommonRails.printSystemComponent(this, this.hashCode(),
51+
". TelnetProxyLivenessMonitor >> proxy dead with active clients — reconnecting .");
52+
53+
reconnect();
54+
}
55+
else
56+
{
57+
CommonRails.printSystemComponent(this, this.hashCode(),
58+
". TelnetProxyLivenessMonitor >> proxy alive, clients=" + proxy.web_express.CURRENT_CONNECTIONS.size() + " .");
59+
}
60+
61+
Thread.sleep(PROBE_INTERVAL_MS);
62+
}
63+
else
64+
{
65+
Thread.sleep(IDLE_INTERVAL_MS);
66+
}
67+
}
68+
catch (InterruptedException ie)
69+
{
70+
Thread.currentThread().interrupt();
71+
return;
72+
}
73+
catch (Exception e)
74+
{
75+
ExceptionHandler.dispatch(e);
76+
e.printStackTrace(System.err);
77+
}
78+
}
79+
}
80+
81+
/**
82+
* Returns true if the backing process is alive and the writer can be flushed.
83+
*/
84+
private boolean isProxyAlive()
85+
{
86+
try
87+
{
88+
// Check the OS process is still running
89+
if (proxy.process == null || !proxy.process.isAlive())
90+
{
91+
return false;
92+
}
93+
94+
// Attempt a NOP flush — will throw if the pipe is broken
95+
if (proxy.writer != null)
96+
{
97+
proxy.writer.flush();
98+
}
99+
100+
return true;
101+
}
102+
catch (Exception e)
103+
{
104+
ExceptionHandler.dispatch(e);
105+
return false;
106+
}
107+
}
108+
109+
/**
110+
* Tears down the dead proxy resources and restarts via TelnetInstaller,
111+
* then re-wires the proxy's reader/writer/process references.
112+
*/
113+
private void reconnect()
114+
{
115+
try
116+
{
117+
// Destroy the old process if still lingering
118+
try
119+
{
120+
if (proxy.process != null && proxy.process.isAlive())
121+
{
122+
proxy.process.destroyForcibly();
123+
proxy.process.waitFor(5, TimeUnit.SECONDS);
124+
}
125+
}
126+
catch (Exception e)
127+
{
128+
ExceptionHandler.dispatch(e);
129+
}
130+
131+
Thread.sleep(RECONNECT_DELAY_MS);
132+
133+
// Reinstall — TelnetInstaller starts a fresh telnet process and sets up new streams
134+
TelnetInstaller installer = new TelnetInstaller(proxy.web_express);
135+
136+
proxy.process_builder = installer.process_builder;
137+
proxy.process = installer.process;
138+
proxy.writer = installer.writer;
139+
proxy.reader = installer.reader;
140+
141+
CommonRails.printSystemComponent(this, this.hashCode(),
142+
". TelnetProxyLivenessMonitor >> proxy reconnected, process=" + proxy.process + " .");
143+
}
144+
catch (InterruptedException ie)
145+
{
146+
Thread.currentThread().interrupt();
147+
}
148+
catch (Exception e)
149+
{
150+
ExceptionHandler.dispatch(e);
151+
e.printStackTrace(System.err);
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)