33import com .google .inject .Inject ;
44import com .google .inject .Singleton ;
55import jakarta .ws .rs .*;
6+ import jakarta .ws .rs .client .Client ;
67import jakarta .ws .rs .core .MediaType ;
78import jakarta .ws .rs .core .Response ;
89import org .glassfish .jersey .media .multipart .FormDataParam ;
9- import org .grobid .core .utilities .GrobidProperties ;
1010import org .grobid .core .utilities .SoftwareConfiguration ;
1111import org .grobid .core .utilities .Versioner ;
1212import org .grobid .service .configuration .SoftwareServiceConfiguration ;
1515import org .slf4j .LoggerFactory ;
1616
1717import java .io .InputStream ;
18+ import java .util .Collections ;
19+ import java .util .Map ;
1820
1921/**
2022 * RESTful service for GROBID Software extension.
@@ -35,9 +37,11 @@ public class SoftwareController implements SoftwarePaths {
3537 private static final String INPUT = "input" ;
3638
3739 private SoftwareConfiguration configuration ;
40+ private final SoftwareServiceConfiguration serviceConfiguration ;
41+ private final Client httpClient ;
3842
3943 @ Inject
40- public SoftwareController (SoftwareServiceConfiguration serviceConfiguration ) {
44+ public SoftwareController (SoftwareServiceConfiguration serviceConfiguration , Client httpClient ) {
4145 /*try {
4246 ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
4347 this.configuration = mapper.readValue(new File("resources/config/config.yml"), SoftwareConfiguration.class);
@@ -46,6 +50,8 @@ public SoftwareController(SoftwareServiceConfiguration serviceConfiguration) {
4650 this.configuration = null;
4751 }*/
4852 this .configuration = serviceConfiguration .getSoftwareConfiguration ();
53+ this .serviceConfiguration = serviceConfiguration ;
54+ this .httpClient = httpClient ;
4955 }
5056
5157 @ Path (PATH_IS_ALIVE )
@@ -138,4 +144,114 @@ public ServiceInfo getVersion() {
138144 return new ServiceInfo (Versioner .getVersion (), Versioner .getRevision ());
139145 }
140146
147+ // New endpoint: return concept service base URL derived from entity-fishing host/port
148+ @ Path (PATH_CONFIG_CONCEPT_BASE_URL )
149+ @ Produces (MediaType .APPLICATION_JSON )
150+ @ GET
151+ public Response getConceptServiceBaseUrl () {
152+ String base = buildConceptBaseUrl ();
153+ Map <String , String > payload = Collections .singletonMap ("conceptBaseUrl" , base );
154+ return Response .ok (payload ).build ();
155+ }
156+
157+ // New proxy endpoint: forward concept lookup using configured host/port
158+ @ Path ("kb/concept/{identifier}" )
159+ @ Produces (MediaType .APPLICATION_JSON )
160+ @ GET
161+ public Response proxyKbConcept (@ PathParam ("identifier" ) String identifier , @ QueryParam ("lang" ) String lang ) {
162+ String base = buildConceptBaseUrl ();
163+ String sep = base .endsWith ("/" ) ? "" : "/" ;
164+ String target = base + sep + identifier ;
165+ if (lang != null && !lang .isEmpty ()) {
166+ target = target + "?lang=" + lang ;
167+ }
168+ try {
169+ String json = httpClient .target (target ).request (MediaType .APPLICATION_JSON_TYPE ).get (String .class );
170+ return Response .ok (json , MediaType .APPLICATION_JSON_TYPE ).build ();
171+ } catch (Exception e ) {
172+ LOGGER .error ("Error proxying concept lookup to {}" , target , e );
173+ return Response .status (Response .Status .BAD_GATEWAY )
174+ .entity (Collections .singletonMap ("error" , "Failed to fetch concept from upstream" ))
175+ .build ();
176+ }
177+ }
178+
179+ // Build the concept base URL from entityFishingHost/Port, with sensible defaults
180+ private String buildConceptBaseUrl () {
181+ String host = serviceConfiguration != null ? serviceConfiguration .getEntityFishingHost () : null ;
182+ String port = serviceConfiguration != null ? serviceConfiguration .getEntityFishingPort () : null ;
183+ if (host == null || host .isEmpty ()) {
184+ // fall back to public endpoint
185+ return "https://cloud.science-miner.com/nerd/service/kb/concept" ;
186+ }
187+
188+ String original = host .trim ();
189+ String lower = original .toLowerCase ();
190+ boolean hasScheme = lower .startsWith ("http://" ) || lower .startsWith ("https://" );
191+
192+ String scheme ;
193+ if (hasScheme ) {
194+ scheme = lower .startsWith ("https://" ) ? "https" : "http" ;
195+ } else {
196+ scheme = (port != null && ("443" .equals (port ) || "8443" .equals (port ))) ? "https" : "http" ;
197+ }
198+
199+ // Extract hostPart and pathPart if scheme is present
200+ String hostPart = original ;
201+ String pathPart = "" ;
202+ if (hasScheme ) {
203+ String noScheme = original .substring (original .indexOf ("://" ) + 3 );
204+ int slash = noScheme .indexOf ("/" );
205+ if (slash >= 0 ) {
206+ hostPart = noScheme .substring (0 , slash );
207+ pathPart = noScheme .substring (slash ); // includes leading '/'
208+ } else {
209+ hostPart = noScheme ;
210+ pathPart = "" ;
211+ }
212+ } else {
213+ // original may already include a path like 'traces1.inria.fr/nerd'
214+ int slash = original .indexOf ("/" );
215+ if (slash >= 0 ) {
216+ hostPart = original .substring (0 , slash );
217+ pathPart = original .substring (slash );
218+ } else {
219+ hostPart = original ;
220+ pathPart = "" ;
221+ }
222+ }
223+
224+ // Append port if missing in hostPart and provided in config (and non-default for scheme)
225+ boolean hostHasPort = hostPart .contains (":" );
226+ if (!hostHasPort && port != null && !port .isEmpty ()) {
227+ boolean defaultForScheme = ("https" .equals (scheme ) && "443" .equals (port )) || ("http" .equals (scheme ) && "80" .equals (port ));
228+ if (!defaultForScheme ) {
229+ hostPart = hostPart + ":" + port ;
230+ }
231+ }
232+
233+ // Ensure '/nerd' is present at the beginning of pathPart
234+ if (pathPart == null || pathPart .isEmpty () || !pathPart .matches ("(?i)^/nerd(/.*)?$" )) {
235+ // if pathPart is empty or doesn't start with '/nerd', prepend it
236+ if (pathPart == null || pathPart .isEmpty ()) {
237+ pathPart = "/nerd" ;
238+ } else {
239+ // avoid double slashes
240+ if (!pathPart .startsWith ("/" )) {
241+ pathPart = "/" + pathPart ;
242+ }
243+ pathPart = "/nerd" + pathPart ;
244+ }
245+ }
246+
247+ // Build final base
248+ String base = scheme + "://" + hostPart ;
249+ // remove trailing slash from pathPart
250+ if (pathPart .endsWith ("/" )) {
251+ pathPart = pathPart .substring (0 , pathPart .length () - 1 );
252+ }
253+ base += pathPart + "/service/kb/concept" ;
254+ return base ;
255+ }
256+
141257}
0 commit comments