• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanopub-query / 17767759901

16 Sep 2025 01:40PM UTC coverage: 71.59% (-0.2%) from 71.741%
17767759901

push

github

Ziroli Plutschow
Replacing newest System.err with slf4j logging

- Note: Also upgraded mockito, since it's transient dependency byte-buddy could not handle a jvm > 21

233 of 350 branches covered (66.57%)

Branch coverage included in aggregate %.

591 of 801 relevant lines covered (73.78%)

3.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.48
src/main/java/com/knowledgepixels/query/TripleStore.java
1
package com.knowledgepixels.query;
2

3
import org.apache.commons.exec.environment.EnvironmentUtils;
4
import org.apache.http.HttpResponse;
5
import org.apache.http.client.methods.HttpUriRequest;
6
import org.apache.http.client.methods.RequestBuilder;
7
import org.apache.http.entity.StringEntity;
8
import org.apache.http.impl.client.BasicResponseHandler;
9
import org.apache.http.impl.client.CloseableHttpClient;
10
import org.apache.http.impl.client.HttpClients;
11
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
12
import org.eclipse.rdf4j.model.ValueFactory;
13
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
14
import org.eclipse.rdf4j.repository.Repository;
15
import org.eclipse.rdf4j.repository.RepositoryConnection;
16
import org.eclipse.rdf4j.repository.http.HTTPRepository;
17
import org.nanopub.NanopubUtils;
18
import org.nanopub.vocabulary.NPA;
19
import org.slf4j.Logger;
20
import org.slf4j.LoggerFactory;
21

22
import java.io.BufferedReader;
23
import java.io.IOException;
24
import java.io.InputStreamReader;
25
import java.util.*;
26
import java.util.Map.Entry;
27

28
/**
29
 * Class to access the database in the form of triple stores.
30
 */
31
public class TripleStore {
32

33
    /**
34
     * Name of the admin graph.
35
     */
36
    public static final String ADMIN_REPO = "admin";
37

38
    private static ValueFactory vf = SimpleValueFactory.getInstance();
2✔
39

40
    private static final Logger log = LoggerFactory.getLogger(TripleStore.class);
4✔
41

42
    private final Map<String, Repository> repositories = new LinkedHashMap<>();
5✔
43

44
    private String endpointBase = null;
3✔
45
    private String endpointType = null;
3✔
46

47
    private static TripleStore tripleStoreInstance;
48

49
    /**
50
     * Returns singleton triple store instance.
51
     *
52
     * @return Triple store instance
53
     */
54
    public static TripleStore get() {
55
        if (tripleStoreInstance == null) {
2!
56
            try {
57
                tripleStoreInstance = new TripleStore();
×
58
            } catch (IOException ex) {
×
59
                log.info("Could not init TripleStore. ", ex);
×
60
            }
×
61
        }
62
        return tripleStoreInstance;
×
63
    }
64

65
    private TripleStore() throws IOException {
2✔
66
        Map<String, String> env = EnvironmentUtils.getProcEnvironment();
2✔
67
        endpointBase = env.get("ENDPOINT_BASE");
6✔
68
        log.info("Endpoint base: {}", endpointBase);
5✔
69
        endpointType = env.get("ENDPOINT_TYPE");
6✔
70

71
        getRepository("empty");  // Make sure empty repo exists
×
72
    }
×
73

74
    private final CloseableHttpClient httpclient = HttpClients.createDefault();
3✔
75

76
    @GeneratedFlagForDependentElements
77
    Repository getRepository(String name) {
78
        synchronized (this) {
79
            while (repositories.size() > 100) {
80
                Entry<String, Repository> e = repositories.entrySet().iterator().next();
81
                repositories.remove(e.getKey());
82
                log.info("Shutting down repo: {}", e.getKey());
83
                e.getValue().shutDown();
84
                log.info("Shutdown complete");
85
            }
86
            if (repositories.containsKey(name)) {
87
                // Move to the end of the list:
88
                Repository repo = repositories.remove(name);
89
                repositories.put(name, repo);
90
            } else {
91
                Repository repository = null;
92
                if (endpointType == null || endpointType.equals("rdf4j")) {
93
                    HTTPRepository hr = new HTTPRepository(endpointBase + "repositories/" + name);
94
                    hr.setHttpClient(httpclient);
95
                    repository = hr;
96
//                        } else if (endpointType.equals("virtuoso")) {
97
//                                repository = new VirtuosoRepository(endpointBase + name, username, password);
98
                } else {
99
                    throw new RuntimeException("Unknown repository type: " + endpointType);
100
                }
101
                repositories.put(name, repository);
102
                createRepo(name);
103
                getRepoConnection(name).close();
104
            }
105
            return repositories.get(name);
106
        }
107
    }
108

109
    /**
110
     * Return the repository connection for the given repository name.
111
     *
112
     * @param name repository name
113
     * @return repository connection
114
     */
115
    @GeneratedFlagForDependentElements
116
    public RepositoryConnection getRepoConnection(String name) {
117
        Repository repo = getRepository(name);
118
        if (repo == null) {
119
            return null;
120
        }
121
        return repo.getConnection();
122
    }
123

124
    @GeneratedFlagForDependentElements
125
    private void createRepo(String repoName) {
126
        if (!repoName.equals(ADMIN_REPO)) {
127
            getRepository(ADMIN_REPO);  // make sure admin repo is loaded first
128
        }
129
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
130
            //log.info("Trying to creating repo " + name);
131

132
            // TODO new syntax somehow doesn't work for the Lucene case:
133

134
//                        String createRegularRepoQueryString = "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n"
135
//                                        + "@prefix config: <tag:rdf4j.org,2023:config/>.\n"
136
//                                        + "[] a config:Repository ;\n"
137
//                                        + "    config:rep.id \"" + name + "\" ;\n"
138
//                                        + "    rdfs:label \"" + name + " native store\" ;\n"
139
//                                        + "    config:rep.impl [\n"
140
//                                        + "        config:rep.type \"openrdf:SailRepository\" ;\n"
141
//                                        + "        config:sail.impl [\n"
142
//                                        + "            config:sail.type \"openrdf:NativeStore\" ;\n"
143
//                                        + "            config:sail.iterationCacheSyncThreshold \"10000\";\n"
144
//                                        + "            config:native.tripleIndexes \"spoc,posc,ospc,opsc,psoc,sopc,spoc,cpos,cosp,cops,cpso,csop\" ;\n"
145
//                                        + "            config:sail.defaultQueryEvaluationMode \"STANDARD\"\n"
146
//                                        + "        ]\n"
147
//                                        + "    ].";
148
//                        String createTextRepoQueryString = "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n"
149
//                                        + "@prefix config: <tag:rdf4j.org,2023:config/>.\n"
150
//                                        + "[] a config:Repository ;\n"
151
//                                        + "    config:rep.id \"" + name + "\" ;\n"
152
//                                        + "    rdfs:label \"" + name + " native store\" ;\n"
153
//                                        + "    config:rep.impl [\n"
154
//                                        + "        config:rep.type \"openrdf:SailRepository\" ;\n"
155
//                                        + "        config:sail.impl [\n"
156
//                                        + "            config:sail.type \"openrdf:LuceneSail\" ;\n"
157
//                                        + "            config:sail.lucene.indexDir \"index/\" ;\n"
158
//                                        + "            config:delegate [\n"
159
//                                        + "                config:rep.type \"openrdf:SailRepository\" ;\n"
160
//                                        + "                config:sail.impl [\n"
161
//                                        + "                    config:sail.type \"openrdf:NativeStore\" ;\n"
162
//                                        + "                    config:sail.iterationCacheSyncThreshold \"10000\";\n"
163
//                                        + "                    config:native.tripleIndexes \"spoc,posc,ospc,opsc,psoc,sopc,spoc,cpos,cosp,cops,cpso,csop\" ;\n"
164
//                                        + "                    config:sail.defaultQueryEvaluationMode \"STANDARD\"\n"
165
//                                        + "                ]\n"
166
//                                        + "            ]\n"
167
//                                        + "        ]\n"
168
//                                        + "    ].";
169

170
            String indexTypes = "spoc,posc,ospc,cspo,cpos,cosp";
171
            if (repoName.startsWith("meta") || repoName.startsWith("text")) {
172
                indexTypes = "spoc,posc,ospc";
173
            }
174

175
            String createRegularRepoQueryString = "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n" + "@prefix rep: <http://www.openrdf.org/config/repository#>.\n" + "@prefix sr: <http://www.openrdf.org/config/repository/sail#>.\n" + "@prefix sail: <http://www.openrdf.org/config/sail#>.\n" + "@prefix sail-luc: <http://www.openrdf.org/config/sail/lucene#>.\n" + "@prefix lmdb: <http://rdf4j.org/config/sail/lmdb#>.\n" + "@prefix sb: <http://www.openrdf.org/config/sail/base#>.\n" + "\n" + "[] a rep:Repository ;\n" + "    rep:repositoryID \"" + repoName + "\" ;\n" + "    rdfs:label \"" + repoName + " LMDB store\" ;\n" + "    rep:repositoryImpl [\n" + "        rep:repositoryType \"openrdf:SailRepository\" ;\n" + "        sr:sailImpl [\n" + "            sail:sailType \"rdf4j:LmdbStore\" ;\n" + "            sail:iterationCacheSyncThreshold \"10000\";\n" + "            lmdb:tripleIndexes \"" + indexTypes + "\" ;\n" + "            sb:defaultQueryEvaluationMode \"STANDARD\"\n" + "        ]\n" + "    ].\n";
176

177
            // TODO Index npa:hasFilterLiteral predicate too (see https://groups.google.com/g/rdf4j-users/c/epF4Af1jXGU):
178
            String createTextRepoQueryString = "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n" + "@prefix rep: <http://www.openrdf.org/config/repository#>.\n" + "@prefix sr: <http://www.openrdf.org/config/repository/sail#>.\n" + "@prefix sail: <http://www.openrdf.org/config/sail#>.\n" + "@prefix sail-luc: <http://www.openrdf.org/config/sail/lucene#>.\n" + "@prefix lmdb: <http://rdf4j.org/config/sail/lmdb#>.\n" + "@prefix sb: <http://www.openrdf.org/config/sail/base#>.\n" + "\n" + "[] a rep:Repository ;\n" + "    rep:repositoryID \"" + repoName + "\" ;\n" + "    rdfs:label \"" + repoName + " store\" ;\n" + "    rep:repositoryImpl [\n" + "        rep:repositoryType \"openrdf:SailRepository\" ;\n" + "        sr:sailImpl [\n" + "            sail:sailType \"openrdf:LuceneSail\" ;\n" + "            sail-luc:indexDir \"index/\" ;\n" + "            sail:delegate [" + "              sail:sailType \"rdf4j:LmdbStore\" ;\n" + "              sail:iterationCacheSyncThreshold \"10000\";\n" + "              lmdb:tripleIndexes \"" + indexTypes + "\" ;\n" + "              sb:defaultQueryEvaluationMode \"STANDARD\"\n" + "            ]\n" + "        ]\n" + "    ].";
179

180
            String createRepoQueryString = createRegularRepoQueryString;
181
            if (repoName.startsWith("text")) {
182
                createRepoQueryString = createTextRepoQueryString;
183
            }
184

185
            HttpUriRequest createRepoRequest = RequestBuilder.put().setUri(endpointBase + "repositories/" + repoName).addHeader("Content-Type", "text/turtle").setEntity(new StringEntity(createRepoQueryString)).build();
186

187
            HttpResponse response = httpclient.execute(createRepoRequest);
188
            int statusCode = response.getStatusLine().getStatusCode();
189
            if (statusCode == 409) {
190
                //log.info("Already exists.");
191
                getRepository(repoName).init();
192
            } else if (statusCode >= 200 && statusCode < 300) {
193
                //log.info("Successfully created.");
194
                initNewRepo(repoName);
195
            } else {
196
                log.info("Status code: {}", response.getStatusLine().getStatusCode());
197
                log.info(response.getStatusLine().getReasonPhrase());
198
                String handledResponse = new BasicResponseHandler().handleResponse(response);
199
                log.info("Response: ", handledResponse);
200
            }
201
        } catch (IOException ex) {
202
            log.info("Could not create repo.", ex);
203
        }
204
    }
205

206
    /**
207
     * Sends shutdown signal to all repositories.
208
     */
209
    @GeneratedFlagForDependentElements
210
    public void shutdownRepositories() {
211
        for (Repository repo : repositories.values()) {
212
            if (repo != null && repo.isInitialized()) {
213
                repo.shutDown();
214
            }
215
        }
216
    }
217

218
    /**
219
     * Returns admin repo connection.
220
     *
221
     * @return repository connection to admin repository
222
     */
223
    @GeneratedFlagForDependentElements
224
    public RepositoryConnection getAdminRepoConnection() {
225
        return get().getRepoConnection(ADMIN_REPO);
226
    }
227

228
    /**
229
     * Returns set of all repository names.
230
     *
231
     * @return Repository name set
232
     */
233
    public Set<String> getRepositoryNames() {
234
        Map<String, Boolean> repositoryNames = null;
2✔
235
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
2✔
236
            HttpResponse resp = httpclient.execute(RequestBuilder.get()
7✔
237
                    .setUri(endpointBase + "/repositories")
3✔
238
                    .addHeader("Content-Type", "text/csv")
1✔
239
                    .build());
1✔
240
            BufferedReader reader = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
10✔
241
            int code = resp.getStatusLine().getStatusCode();
4✔
242
            if (code < 200 || code >= 300) return null;
10!
243
            repositoryNames = new HashMap<>();
4✔
244
            int lineCount = 0;
2✔
245
            while (true) {
246
                String line = reader.readLine();
3✔
247
                if (line == null) break;
3✔
248
                if (lineCount > 0) {
2✔
249
                    String repoName = line.split(",")[1];
6✔
250
                    repositoryNames.put(repoName, true);
6✔
251
                }
252
                lineCount = lineCount + 1;
4✔
253
            }
1✔
254
        } catch (IOException ex) {
5!
255
            log.info("Could not get repository names.", ex);
4✔
256
            return null;
2✔
257
        }
1✔
258
        return repositoryNames.keySet();
3✔
259
    }
260

261
    @GeneratedFlagForDependentElements
262
    private void initNewRepo(String repoName) {
263
        String repoInitId = new Random().nextLong() + "";
264
        getRepository(repoName).init();
265
        if (!repoName.equals("empty")) {
266
            RepositoryConnection conn = getRepoConnection(repoName);
267
            try (conn) {
268
                // Full isolation, just in case.
269
                conn.begin(IsolationLevels.SERIALIZABLE);
270
                conn.add(NPA.THIS_REPO, NPA.HAS_REPO_INIT_ID, vf.createLiteral(repoInitId), NPA.GRAPH);
271
                conn.add(NPA.THIS_REPO, NPA.HAS_NANOPUB_COUNT, vf.createLiteral(0L), NPA.GRAPH);
272
                conn.add(NPA.THIS_REPO, NPA.HAS_NANOPUB_CHECKSUM, vf.createLiteral(NanopubUtils.INIT_CHECKSUM), NPA.GRAPH);
273
                if (repoName.startsWith("pubkey_") || repoName.startsWith("type_")) {
274
                    String h = repoName.replaceFirst("^[^_]+_", "");
275
                    conn.add(NPA.THIS_REPO, NPA.HAS_COVERAGE_ITEM, Utils.getObjectForHash(h), NPA.GRAPH);
276
                    conn.add(NPA.THIS_REPO, NPA.HAS_COVERAGE_HASH, vf.createLiteral(h), NPA.GRAPH);
277
                    conn.add(NPA.THIS_REPO, NPA.HAS_COVERAGE_FILTER, vf.createLiteral("_" + repoName), NPA.GRAPH);
278
                }
279
                conn.commit();
280
            }
281
        }
282
    }
283

284
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc