3131import de .uksh .medic .etl .settings .ConfigurationLoader ;
3232import de .uksh .medic .etl .settings .CxxMdrSettings ;
3333import de .uksh .medic .etl .settings .Mapping ;
34+ import de .uksh .medic .etl .settings .ServerCheckSettings ;
3435import de .uksh .medic .etl .settings .Settings ;
3536import groovy .lang .Binding ;
3637import groovy .lang .GroovyShell ;
@@ -98,6 +99,7 @@ public final class OpenEhrObds {
9899 private static FhirResolver fr ;
99100 private static UtilMethods um = new UtilMethods ();
100101 private static IGenericClient fc ;
102+ private static IGenericClient fhirTsClient ;
101103
102104 private OpenEhrObds () {
103105 }
@@ -112,7 +114,10 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
112114 ConfigurationLoader configLoader = new ConfigurationLoader ();
113115 configLoader .loadConfiguration (settingsYaml , Settings .class );
114116
117+ // Initialize FHIR terminology server client
115118 fr = new FhirResolver ();
119+ fhirTsClient = FhirResolver .getTerminologyClient ();
120+
116121 if (Settings .getFhirServerUrl () != null ) {
117122 FhirContext fcCtx = FhirContext .forR4 ();
118123
@@ -159,6 +164,9 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
159164
160165 openEhrClient = new DefaultRestClient (new OpenEhrClientConfig (ehrBaseUrl ));
161166
167+ // Initialize server availability monitoring
168+ ServerAvailability .init (openEhrClient , fhirTsClient );
169+
162170 ObjectMapper mapper ;
163171 JsonMapper jm = new JsonMapper ();
164172
@@ -170,6 +178,17 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
170178
171179 Settings .getMapping ().values ().forEach (m -> m .forEach (n -> initializeAttribute (n , jm )));
172180
181+ // Start server availability monitoring and wait for servers
182+ ServerCheckSettings serverCheck = Settings .getServerCheck ();
183+ if (serverCheck .isEnabled ()) {
184+ ServerAvailability .startMonitor (serverCheck .getIntervalMs ());
185+
186+ if (!ServerAvailability .waitUntilAvailable (serverCheck .getIntervalMs (), serverCheck .getTimeoutMs ())) {
187+ Logger .error ("Servers not available within timeout, shutting down" );
188+ System .exit (1 );
189+ }
190+ }
191+
173192 spark .Spark .get ("/health" , (request , response ) -> {
174193 response .type ("application/json" );
175194 return "{\" msgsPerMinute\" : " + SPEED .estimatedSize () + ", \" cacheSize\" : " + fr .getCacheSize () + "}" ;
@@ -179,6 +198,10 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
179198
180199 if (Settings .getKafka ().getUrl () == null || Settings .getKafka ().getUrl ().isEmpty ()) {
181200 Logger .debug ("Kafka URL not set, loading local file" );
201+ // Pause monitor during local file processing
202+ if (serverCheck .isEnabled ()) {
203+ ServerAvailability .pauseMonitor ();
204+ }
182205 File [] files = new File (Settings .getTestDataDir ()).listFiles ();
183206 for (File f : files ) {
184207 if (f .isDirectory ()) {
@@ -220,10 +243,27 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
220243 reconnectDelay = DEFAULT_RECONNECT_DELAY_MS ;
221244
222245 while (true ) {
246+ // Check server availability before polling
247+ if (serverCheck .isEnabled () && !ServerAvailability .allAvailable ()) {
248+ Logger .info ("Server unavailable, waiting before next poll..." );
249+ try {
250+ Thread .sleep (serverCheck .getIntervalMs ());
251+ } catch (InterruptedException ie ) {
252+ Thread .currentThread ().interrupt ();
253+ break ;
254+ }
255+ continue ;
256+ }
257+
223258 Logger .debug ("Polling Kafka topic" );
224259 ConsumerRecords <String , String > records = consumer .poll (
225260 Duration .ofMillis (Settings .getKafka ().getPollDuration ()));
226261
262+ // Pause server availability monitor during data processing
263+ // to avoid unnecessary FHIR server queries while processing
264+ if (serverCheck .isEnabled ()) {
265+ ServerAvailability .pauseMonitor ();
266+ }
227267 for (ConsumerRecord <String , String > record : records ) {
228268 Logger .debug ("Processing record." );
229269 try {
@@ -239,8 +279,9 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
239279 sendToErrorTopic (producer , record .value ());
240280 } catch (Exception e ) {
241281 Logger .error (
242- "Unexpected error processing record, "
243- + "wrapping as ProcessingException, writing to error topic!" , e );
282+ "Unexpected error processing record, "
283+ + "wrapping as ProcessingException, writing to error topic!" ,
284+ e );
244285 sendToErrorTopic (producer , record .value ());
245286 }
246287 SPEED .put (UUID .randomUUID ().toString (), "success" );
@@ -249,18 +290,27 @@ public static void main(String[] args) throws IOException, KeyStoreException, Ge
249290 consumer .commitSync ();
250291 } catch (CommitFailedException e ) {
251292 Logger .warn (
252- "CommitFailedException: consumer got kicked out of consumer "
253- + "group. Breaking inner loop to re-subscribe." );
293+ "CommitFailedException: consumer got kicked out of consumer "
294+ + "group. Breaking inner loop to re-subscribe." );
295+ // Resume monitor before breaking
296+ if (serverCheck .isEnabled ()) {
297+ ServerAvailability .resumeMonitor ();
298+ }
254299 break ; // Break inner loop to allow consumer group rebalance
255300 }
301+ // Resume server availability monitor after processing
302+ if (serverCheck .isEnabled ()) {
303+ ServerAvailability .resumeMonitor ();
304+ }
256305 }
257306 } catch (WakeupException e ) {
258307 Logger .info ("Caught WakeupException (shutdown requested). Exiting." );
259308 break ; // Shutdown was requested, exit the outer loop
260309 } catch (Exception e ) {
261310 Logger .error (
262- "Unexpected exception in consumer loop, "
263- + "restarting consumer in {}ms..." , reconnectDelay , e );
311+ "Unexpected exception in consumer loop, "
312+ + "restarting consumer in {}ms..." ,
313+ reconnectDelay , e );
264314 try {
265315 TimeUnit .MILLISECONDS .sleep (reconnectDelay );
266316 } catch (InterruptedException ie ) {
@@ -580,6 +630,7 @@ private static void buildOpenEhrComposition(String templateId, Map<String, Objec
580630 throw new ProcessingException ();
581631 }
582632 } catch (WrongStatusCodeException e ) {
633+ ServerAvailability .markOpenEhrUnavailable ();
583634 Logger .error ("Unable to create EHR due to internal server error. Trying again. Error was: {}" ,
584635 e .getMessage ());
585636 try {
@@ -603,6 +654,7 @@ private static void buildOpenEhrComposition(String templateId, Map<String, Objec
603654 try {
604655 openEhrClient .compositionEndpoint (ehrId ).mergeRaw (composition );
605656 } catch (WrongStatusCodeException e ) {
657+ ServerAvailability .markOpenEhrUnavailable ();
606658 String comp = "" ;
607659 try {
608660 JacksonUtil .getObjectMapper ().writeValueAsString (composition );
0 commit comments