1515import java .nio .charset .StandardCharsets ;
1616import java .util .*;
1717import java .util .concurrent .*;
18+ import java .util .concurrent .atomic .AtomicReference ;
1819
1920/**
2021 * Establishes a connection to KeePassXC via its build-in proxy.
@@ -32,9 +33,13 @@ public abstract class Connection implements AutoCloseable {
3233 private byte [] nonce ;
3334
3435 final ExecutorService executorService = Executors .newFixedThreadPool (2 );
35- final MessagePublisher messagePublisher = new MessagePublisher () ;
36+ protected MessagePublisher messagePublisher ;
3637 private final ConcurrentLinkedQueue <JSONObject > queue = new ConcurrentLinkedQueue <>();
3738
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+
3843 private final long RESPONSE_DELAY_MS = 500 ;
3944 private final ScheduledExecutorService scheduler ;
4045
@@ -56,6 +61,7 @@ public Connection() {
5661
5762 class MessagePublisher implements Runnable {
5863 private boolean doStop = false ;
64+ private int errorCount = 0 ;
5965
6066 public synchronized void doStop () {
6167 this .doStop = true ;
@@ -68,7 +74,18 @@ private synchronized boolean keepRunning() {
6874 @ Override
6975 public void run () {
7076 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+ }
7289 }
7390 log .debug ("MessagePublisher stopped" );
7491 }
@@ -108,10 +125,30 @@ public JSONObject call() throws Exception {
108125 }
109126
110127 void lauchMessagePublisher () {
128+ messagePublisher = new MessagePublisher ();
111129 log .debug ("MessagePublisher started" );
112130 executorService .execute (messagePublisher );
113131 }
114132
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+
115152 public void addPropertyChangeListener (PropertyChangeListener pcl ) {
116153 support .addPropertyChangeListener (pcl );
117154 }
0 commit comments