SparqlConnector.java

package de.dlr.shepard.semantics;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.logging.Log;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.core.MediaType;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class SparqlConnector implements ISemanticRepositoryConnector {

  private static final String SELECT_TEMPLATE =
    """
    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

    SELECT DISTINCT ?o WHERE {
        ?s rdfs:label ?o .
        FILTER ( ?s = <%s> )
    }""";

  private static final String ASK_TEMPLATE = "ASK { ?x ?y ?z }";

  private final String endpoint;
  private final ObjectMapper mapper = new ObjectMapper();
  private Client client = ClientBuilder.newClient();

  public SparqlConnector(String endpoint) {
    this.endpoint = endpoint;
  }

  @Override
  public boolean healthCheck() {
    var invocation = client
      .target(endpoint)
      .queryParam("query", ASK_TEMPLATE)
      .request(MediaType.APPLICATION_JSON)
      .buildGet();
    String requestResult = request(invocation);
    client.close();
    return parseJson(requestResult).map(t -> t.get("boolean")).map(JsonNode::asBoolean).orElse(false);
  }

  @Override
  public Map<String, String> getTerm(String termIri) {
    var requestResult = requestTerm(termIri);
    if (requestResult == null || requestResult.isBlank()) {
      Log.error("Could not retrieve request result");
      return Collections.emptyMap();
    }

    return parseResult(requestResult);
  }

  private String requestTerm(String termIri) {
    var query = String.format(SELECT_TEMPLATE, termIri);
    var invocation = client.target(endpoint).queryParam("query", query).request(MediaType.APPLICATION_JSON).buildGet();
    String responseEntity = request(invocation);
    client.close();
    return responseEntity;
  }

  private Map<String, String> parseResult(String requestResult) {
    var bindings = parseJson(requestResult).map(t -> t.get("results")).map(r -> r.get("bindings"));

    if (bindings.isEmpty()) return Collections.emptyMap();

    var result = new HashMap<String, String>();
    for (var binding : bindings.get()) {
      var oBinding = Optional.of(binding);
      var object = oBinding.map(b -> b.get("o")).map(p -> p.get("value")).map(JsonNode::asText).orElse("");
      var language = oBinding.map(b -> b.get("o")).map(p -> p.get("xml:lang")).map(JsonNode::asText).orElse("");
      if (!object.isBlank()) result.put(language, object);
    }
    return result;
  }

  private Optional<JsonNode> parseJson(String string) {
    JsonNode tree;
    try {
      tree = mapper.readTree(string);
    } catch (JsonProcessingException e) {
      return Optional.empty();
    }
    return Optional.of(tree);
  }

  /**
   * You have to close the client yourself afterwards
   *
   * @param invocation
   * @return Response as String or null
   */
  private String request(Invocation invocation) {
    String responseEntity;
    try {
      var response = invocation.invoke();
      responseEntity = response.readEntity(String.class);
    } catch (ProcessingException e) {
      Log.error("Could not execute request");
      return null;
    }
    return responseEntity;
  }
}