15
15
import java .nio .charset .StandardCharsets ;
16
16
import java .util .*;
17
17
import java .util .concurrent .*;
18
+ import java .util .concurrent .atomic .AtomicReference ;
18
19
19
20
/**
20
21
* Establishes a connection to KeePassXC via its build-in proxy.
@@ -32,9 +33,13 @@ public abstract class Connection implements AutoCloseable {
32
33
private byte [] nonce ;
33
34
34
35
final ExecutorService executorService = Executors .newFixedThreadPool (2 );
35
- final MessagePublisher messagePublisher = new MessagePublisher () ;
36
+ protected MessagePublisher messagePublisher ;
36
37
private final ConcurrentLinkedQueue <JSONObject > queue = new ConcurrentLinkedQueue <>();
37
38
39
+ private final int MAX_ERROR_COUNT = 4 ;
40
+ private final int RECONNECT_DELAY_S = 15 ;
41
+ private final AtomicReference <ScheduledFuture <?>> scheduledConnectCmd = new AtomicReference <>();
42
+
38
43
private final long RESPONSE_DELAY_MS = 500 ;
39
44
private final ScheduledExecutorService scheduler ;
40
45
@@ -56,6 +61,7 @@ public Connection() {
56
61
57
62
class MessagePublisher implements Runnable {
58
63
private boolean doStop = false ;
64
+ private int errorCount = 0 ;
59
65
60
66
public synchronized void doStop () {
61
67
this .doStop = true ;
@@ -68,7 +74,18 @@ private synchronized boolean keepRunning() {
68
74
@ Override
69
75
public void run () {
70
76
while (keepRunning ()) {
71
- queue .offer (getCleartextResponse ());
77
+ var response = getCleartextResponse ();
78
+ if (!response .isEmpty ()) {
79
+ queue .offer (response );
80
+ errorCount = 0 ;
81
+ } else {
82
+ errorCount ++;
83
+ if (errorCount > MAX_ERROR_COUNT ) {
84
+ log .info ("Too much errors - stopping MessagePublisher" );
85
+ doStop ();
86
+ reconnect ();
87
+ }
88
+ }
72
89
}
73
90
log .debug ("MessagePublisher stopped" );
74
91
}
@@ -108,10 +125,30 @@ public JSONObject call() throws Exception {
108
125
}
109
126
110
127
void lauchMessagePublisher () {
128
+ messagePublisher = new MessagePublisher ();
111
129
log .debug ("MessagePublisher started" );
112
130
executorService .execute (messagePublisher );
113
131
}
114
132
133
+ /**
134
+ * Tries to reconnect after a configured time in case connection to KeePassXC was lost.
135
+ * It keeps on trying until a new connection could be established.
136
+ */
137
+ private void reconnect () {
138
+ Runnable connect = () -> {
139
+ try {
140
+ this .connect ();
141
+ } catch (IOException e ) {
142
+ reconnect ();
143
+ }
144
+ };
145
+ var scheduledTask = scheduler .schedule (connect , RECONNECT_DELAY_S , TimeUnit .SECONDS );
146
+ var previouslyScheduledTask = scheduledConnectCmd .getAndSet (scheduledTask );
147
+ if (previouslyScheduledTask != null ) {
148
+ previouslyScheduledTask .cancel (false );
149
+ }
150
+ }
151
+
115
152
public void addPropertyChangeListener (PropertyChangeListener pcl ) {
116
153
support .addPropertyChangeListener (pcl );
117
154
}
0 commit comments