From f25af97571700e2c16da33dca677d2d2b1ac48f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Apr 2026 05:46:50 +0000 Subject: [PATCH] Warn (don't error) when optional services are unavailable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Entity-fishing (disambiguation) and Glutton (ref consolidation) are optional external services. When unconfigured or unreachable, datastet should warn clearly and continue, not log ERROR stacktraces or throw GrobidException. Entity-fishing (DatasetDisambiguator): - Single WARN at startup when host is blank or unreachable - All runtime failures → WARN with short reason, not ERROR/stacktrace - disambiguate() short-circuits with !serverStatus guard - Replaced all e.printStackTrace() with LOGGER.warn() Glutton (DatasetParser): - Gate all 3 Consolidation.getInstance() call sites with gluttonHost blank check (previously only 1 site was gated) - When gluttonHost unset: WARN once per JVM (AtomicBoolean flag) that consolidation will be skipped - When glutton call throws: WARN per-request instead of GrobidException (which crashed the whole request) Result: clean deployment logs when optional services are absent, but the user is still informed what was skipped and why. https://claude.ai/code/session_018EBZhK2RtGtsvN4E1rp2tF --- .../core/engines/DatasetDisambiguator.java | 41 +++++++---- .../grobid/core/engines/DatasetParser.java | 68 ++++++++++++------- 2 files changed, 70 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/grobid/core/engines/DatasetDisambiguator.java b/src/main/java/org/grobid/core/engines/DatasetDisambiguator.java index f229a7b..80741c1 100644 --- a/src/main/java/org/grobid/core/engines/DatasetDisambiguator.java +++ b/src/main/java/org/grobid/core/engines/DatasetDisambiguator.java @@ -69,11 +69,21 @@ private DatasetDisambiguator(DatastetConfiguration configuration) { try { nerd_host = configuration.getEntityFishingHost(); nerd_port = configuration.getEntityFishingPort(); + if (StringUtils.isBlank(nerd_host)) { + LOGGER.warn("entity-fishing host not configured, dataset disambiguation will be skipped"); + serverStatus = false; + return; + } serverStatus = checkIfAlive(); - if (serverStatus) + if (serverStatus) { ensureCustomizationReady(); + } else { + LOGGER.warn("entity-fishing service is not reachable at " + nerd_host + + (StringUtils.isNotBlank(nerd_port) ? ":" + nerd_port : "") + + ", dataset disambiguation will be skipped"); + } } catch (Exception e) { - LOGGER.error("Cannot read properties for disambiguation service", e); + LOGGER.warn("Cannot initialise entity-fishing disambiguation service, it will be skipped: " + e.getMessage()); } } @@ -111,7 +121,7 @@ public boolean checkIfAlive() { try (CloseableHttpResponse response = httpClient.execute(get)) { int code = response.getStatusLine().getStatusCode(); if (code != 200) { - LOGGER.error("Failed isalive service for disambiguation service entity-fishing, HTTP error code : " + code); + LOGGER.warn("entity-fishing isalive returned HTTP " + code + ", disambiguation will be skipped"); return false; } else { result = true; @@ -120,11 +130,11 @@ public boolean checkIfAlive() { } } catch (MalformedURLException e) { - LOGGER.error("Disambiguation service not available: MalformedURLException"); + LOGGER.warn("entity-fishing URL is malformed, disambiguation will be skipped"); } catch (HttpHostConnectException e) { - LOGGER.error("Cannot connect to the disambiguation service"); + LOGGER.warn("entity-fishing is not reachable, disambiguation will be skipped"); } catch (Exception e) { - LOGGER.error("Disambiguation service not available: generic error", e); + LOGGER.warn("entity-fishing is not available (" + e.getClass().getSimpleName() + "), disambiguation will be skipped"); } return result; @@ -167,11 +177,11 @@ public void ensureCustomizationReady() { response.close(); } } catch (MalformedURLException e) { - LOGGER.error("disambiguation service not available: MalformedURLException"); + LOGGER.warn("entity-fishing URL is malformed, customization skipped"); } catch (HttpHostConnectException e) { - LOGGER.error("cannot connect to the disambiguation service"); + LOGGER.warn("entity-fishing is not reachable, customization skipped"); } catch (Exception e) { - LOGGER.error("disambiguation service not available", e); + LOGGER.warn("entity-fishing customization lookup failed: " + e.getMessage()); } if (!result && url != null) { @@ -238,11 +248,13 @@ public void ensureCustomizationReady() { public List disambiguate(List entities, List tokens) { if ((entities == null) || (entities.size() == 0)) return entities; + if (!serverStatus) + return entities; String json = null; try { json = runNerd(entities, tokens, "en"); } catch (RuntimeException e) { - LOGGER.error("Call to entity-fishing failed.", e); + LOGGER.warn("Call to entity-fishing failed, disambiguation skipped: " + e.getMessage()); } if (json == null) return entities; @@ -448,8 +460,7 @@ public List disambiguate(List entities, List toke // e.g. [{"weight" : 0.16666666666666666, "source" : "wikipedia-en", "category" : "Bioinformatics", "page_id" : 726312}, ... } catch (Exception e) { - LOGGER.error("Invalid JSON answer from the NERD", e); - e.printStackTrace(); + LOGGER.warn("Invalid JSON answer from entity-fishing, disambiguation skipped: " + e.getMessage()); } return entities; @@ -566,7 +577,7 @@ public String runNerd(List entities, List subtokens, Strin int code = response.getStatusLine().getStatusCode(); if (code != 200) { - LOGGER.error("Failed annotating text segment: HTTP error code : " + code); + LOGGER.warn("entity-fishing annotation returned HTTP " + code + ", disambiguation skipped"); return null; } @@ -584,9 +595,9 @@ public String runNerd(List entities, List subtokens, Strin response.close(); } } catch (MalformedURLException e) { - e.printStackTrace(); + LOGGER.warn("entity-fishing URL is malformed, disambiguation skipped"); } catch (IOException e) { - e.printStackTrace(); + LOGGER.warn("entity-fishing request failed, disambiguation skipped: " + e.getMessage()); } return output.toString().trim(); } diff --git a/src/main/java/org/grobid/core/engines/DatasetParser.java b/src/main/java/org/grobid/core/engines/DatasetParser.java index cb46496..4d75fef 100644 --- a/src/main/java/org/grobid/core/engines/DatasetParser.java +++ b/src/main/java/org/grobid/core/engines/DatasetParser.java @@ -75,12 +75,22 @@ public class DatasetParser extends AbstractParser { private static volatile DatasetParser instance; + // guard to warn only once about Glutton not being configured + private static final java.util.concurrent.atomic.AtomicBoolean gluttonWarningLogged = + new java.util.concurrent.atomic.AtomicBoolean(false); + private EngineParsers parsers; private DatastetServiceConfiguration datastetConfiguration; private DataTypeClassifier dataTypeClassifier; private DatasetContextClassifier datasetContextClassifier; private DatasetDisambiguator disambiguator; + private static void warnGluttonNotConfiguredOnce() { + if (gluttonWarningLogged.compareAndSet(false, true)) { + LOGGER.warn("Glutton host not configured, bibliographical reference consolidation will be skipped"); + } + } + public static DatasetParser getInstance( DatastetServiceConfiguration configuration, DataTypeClassifier dataTypeClassifier, @@ -1053,19 +1063,23 @@ public Pair>, Document> processPDF(File file, } } - try { - Consolidation consolidator = Consolidation.getInstance(); - Map resConsolidation = consolidator.consolidate(citationsToConsolidate); - for (int j = 0; j < citationsToConsolidate.size(); j++) { - BiblioItem resCitation = citationsToConsolidate.get(j).getResBib(); - BiblioItem bibo = resConsolidation.get(j); - if (bibo != null) { - BiblioItem.correct(resCitation, bibo); + if (StringUtils.isNotBlank(datastetConfiguration.getDatastetConfiguration().getGluttonHost())) { + try { + Consolidation consolidator = Consolidation.getInstance(); + Map resConsolidation = consolidator.consolidate(citationsToConsolidate); + for (int j = 0; j < citationsToConsolidate.size(); j++) { + BiblioItem resCitation = citationsToConsolidate.get(j).getResBib(); + BiblioItem bibo = resConsolidation.get(j); + if (bibo != null) { + BiblioItem.correct(resCitation, bibo); + } } + } catch (Exception e) { + LOGGER.warn("Glutton is not reachable, bibliographical reference consolidation will be skipped: " + + e.getMessage()); } - } catch (Exception e) { - throw new GrobidException( - "An exception occured while running consolidation on bibliographical references.", e); + } else { + warnGluttonNotConfiguredOnce(); } // propagate the bib. ref. to the entities corresponding to the same dataset name without bib. ref. @@ -1403,19 +1417,23 @@ public Pair>, Document> processPDF(File file, } } - try { - Consolidation consolidator = Consolidation.getInstance(); - Map resConsolidation = consolidator.consolidate(citationsToConsolidate); - for (int j = 0; j < citationsToConsolidate.size(); j++) { - BiblioItem resCitation = citationsToConsolidate.get(j).getResBib(); - BiblioItem bibo = resConsolidation.get(j); - if (bibo != null) { - BiblioItem.correct(resCitation, bibo); + if (StringUtils.isNotBlank(datastetConfiguration.getDatastetConfiguration().getGluttonHost())) { + try { + Consolidation consolidator = Consolidation.getInstance(); + Map resConsolidation = consolidator.consolidate(citationsToConsolidate); + for (int j = 0; j < citationsToConsolidate.size(); j++) { + BiblioItem resCitation = citationsToConsolidate.get(j).getResBib(); + BiblioItem bibo = resConsolidation.get(j); + if (bibo != null) { + BiblioItem.correct(resCitation, bibo); + } } + } catch (Exception e) { + LOGGER.warn("Glutton is not reachable, bibliographical reference consolidation will be skipped: " + + e.getMessage()); } - } catch (Exception e) { - throw new GrobidException( - "An exception occured while running consolidation on bibliographical references.", e); + } else { + warnGluttonNotConfiguredOnce(); } // propagate the bib. ref. to the entities corresponding to the same dataset name without bib. ref. @@ -2227,9 +2245,11 @@ public Pair>, List> processTEIDocument(org.w3c.do } } } catch (Exception e) { - throw new GrobidException( - "An exception occurred while running consolidation on bibliographical references.", e); + LOGGER.warn("Glutton is not reachable, bibliographical reference consolidation will be skipped: " + + e.getMessage()); } + } else { + warnGluttonNotConfiguredOnce(); } // propagate the bib. ref. to the entities corresponding to the same dataset name without bib. ref.