@@ -78,6 +78,8 @@ public class OpcUaProtocolAdapter implements WritingProtocolAdapter {
7878 private final @ NotNull OpcUaSpecificAdapterConfig config ;
7979 private final @ NotNull ScheduledExecutorService retryScheduler = Executors .newSingleThreadScheduledExecutor ();
8080 private final @ NotNull AtomicReference <ScheduledFuture <?>> retryFuture = new AtomicReference <>();
81+ private final @ NotNull ScheduledExecutorService healthCheckScheduler = Executors .newSingleThreadScheduledExecutor ();
82+ private final @ NotNull AtomicReference <ScheduledFuture <?>> healthCheckFuture = new AtomicReference <>();
8183
8284 // Stored for reconnection - set during start()
8385 private volatile ParsedConfig parsedConfig ;
@@ -159,8 +161,9 @@ public synchronized void stop(
159161 final @ NotNull ProtocolAdapterStopOutput output ) {
160162 log .info ("Stopping OPC UA protocol adapter {}" , adapterId );
161163
162- // Cancel any pending retries
164+ // Cancel any pending retries and health checks
163165 cancelRetry ();
166+ cancelHealthCheck ();
164167
165168 // Clear stored configuration to prevent reconnection after stop
166169 this .parsedConfig = null ;
@@ -189,8 +192,9 @@ private void reconnect() {
189192 return ;
190193 }
191194
192- // Cancel any pending retries
195+ // Cancel any pending retries and health checks
193196 cancelRetry ();
197+ cancelHealthCheck ();
194198
195199 // Stop and clean up current connection
196200 final OpcUaClientConnection oldConn = opcUaClientConnection .getAndSet (null );
@@ -225,12 +229,54 @@ public ModuleServices moduleServices() {
225229 }
226230 }
227231
232+ /**
233+ * Schedules periodic health check that monitors connection health and triggers reconnection if needed.
234+ */
235+ private void scheduleHealthCheck () {
236+ final int healthCheckIntervalSeconds = config .getHealthCheckInterval ();
237+ final ScheduledFuture <?> future = healthCheckScheduler .scheduleAtFixedRate (() -> {
238+ final OpcUaClientConnection conn = opcUaClientConnection .get ();
239+ if (conn == null ) {
240+ log .debug ("Health check skipped - no active connection for adapter '{}'" , adapterId );
241+ return ;
242+ }
243+
244+ if (!conn .isHealthy ()) {
245+ log .warn ("Health check failed for adapter '{}' - triggering reconnection" , adapterId );
246+ reconnect ();
247+ } else {
248+ log .debug ("Health check passed for adapter '{}'" , adapterId );
249+ }
250+ }, healthCheckIntervalSeconds , healthCheckIntervalSeconds , TimeUnit .SECONDS );
251+
252+ // Store future so it can be cancelled if needed
253+ final ScheduledFuture <?> oldFuture = healthCheckFuture .getAndSet (future );
254+ if (oldFuture != null && !oldFuture .isDone ()) {
255+ oldFuture .cancel (false );
256+ }
257+
258+ log .debug ("Scheduled connection health check every {} seconds for adapter '{}'" ,
259+ healthCheckIntervalSeconds , adapterId );
260+ }
261+
262+ /**
263+ * Cancels any pending health check.
264+ */
265+ private void cancelHealthCheck () {
266+ final ScheduledFuture <?> future = healthCheckFuture .getAndSet (null );
267+ if (future != null && !future .isDone ()) {
268+ future .cancel (false );
269+ log .debug ("Cancelled health check for adapter '{}'" , adapterId );
270+ }
271+ }
272+
228273 @ Override
229274 public void destroy () {
230275 log .info ("Destroying OPC UA protocol adapter {}" , adapterId );
231276
232- // Cancel any pending retries
277+ // Cancel any pending retries and health checks
233278 cancelRetry ();
279+ cancelHealthCheck ();
234280
235281 // Shutdown retry scheduler
236282 retryScheduler .shutdown ();
@@ -243,6 +289,17 @@ public void destroy() {
243289 retryScheduler .shutdownNow ();
244290 }
245291
292+ // Shutdown health check scheduler
293+ healthCheckScheduler .shutdown ();
294+ try {
295+ if (!healthCheckScheduler .awaitTermination (5 , TimeUnit .SECONDS )) {
296+ healthCheckScheduler .shutdownNow ();
297+ }
298+ } catch (final InterruptedException e ) {
299+ Thread .currentThread ().interrupt ();
300+ healthCheckScheduler .shutdownNow ();
301+ }
302+
246303 final OpcUaClientConnection conn = opcUaClientConnection .getAndSet (null );
247304 if (conn != null ) {
248305 CompletableFuture .runAsync (() -> {
@@ -398,8 +455,9 @@ private void attemptConnection(
398455
399456 CompletableFuture .supplyAsync (() -> conn .start (parsedConfig )).whenComplete ((success , throwable ) -> {
400457 if (success && throwable == null ) {
401- // Connection succeeded - cancel any pending retries
458+ // Connection succeeded - cancel any pending retries and start health check
402459 cancelRetry ();
460+ scheduleHealthCheck ();
403461 log .info ("OPC UA adapter '{}' connected successfully" , adapterId );
404462 } else {
405463 // Connection failed - clean up and schedule retry
0 commit comments