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

knowledgepixels / nanopub-query / 16947417686

13 Aug 2025 07:32PM UTC coverage: 22.72% (+4.6%) from 18.109%
16947417686

push

github

ashleycaselli
test(TripleStore): add unit tests for getRepoConnection method

106 of 496 branches covered (21.37%)

Branch coverage included in aggregate %.

295 of 1269 relevant lines covered (23.25%)

1.15 hits per line

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

18.29
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.IRI;
13
import org.eclipse.rdf4j.model.ValueFactory;
14
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
15
import org.eclipse.rdf4j.repository.Repository;
16
import org.eclipse.rdf4j.repository.RepositoryConnection;
17
import org.eclipse.rdf4j.repository.http.HTTPRepository;
18
import org.nanopub.NanopubUtils;
19

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

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

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

36
    private static ValueFactory vf = SimpleValueFactory.getInstance();
2✔
37

38
    /**
39
     * IRI for the repo init ID.
40
     */
41
    public static final IRI HAS_REPO_INIT_ID = vf.createIRI("http://purl.org/nanopub/admin/hasRepoInitId");
4✔
42

43
    /**
44
     * IRI for the nanopub count.
45
     */
46
    public static final IRI HAS_NANOPUB_COUNT = vf.createIRI("http://purl.org/nanopub/admin/hasNanopubCount");
4✔
47

48
    /**
49
     * IRI for the nanopub checksum.
50
     */
51
    public static final IRI HAS_NANOPUB_CHECKSUM = vf.createIRI("http://purl.org/nanopub/admin/hasNanopubChecksum");
4✔
52

53
    /**
54
     * IRI for the nanopub load number.
55
     */
56
    public static final IRI HAS_LOAD_NUMBER = vf.createIRI("http://purl.org/nanopub/admin/hasLoadNumber");
4✔
57

58
    /**
59
     * IRI for the nanopub load checksum.
60
     */
61
    public static final IRI HAS_LOAD_CHECKSUM = vf.createIRI("http://purl.org/nanopub/admin/hasLoadChecksum");
4✔
62

63
    /**
64
     * IRI for the nanopub load timestamp.
65
     */
66
    public static final IRI HAS_LOAD_TIMESTAMP = vf.createIRI("http://purl.org/nanopub/admin/hasLoadTimestamp");
4✔
67

68
    /**
69
     * IRI for the nanopub load status.
70
     */
71
    public static final IRI HAS_STATUS = vf.createIRI("http://purl.org/nanopub/admin/hasStatus");
4✔
72

73
    /**
74
     * IRI for the nanopub registry load counter.
75
     */
76
    public static final IRI HAS_REGISTRY_LOAD_COUNTER = vf.createIRI("http://purl.org/nanopub/admin/hasRegistryLoadCounter");
4✔
77

78
    /**
79
     * IRI for the nanopub repository ID.
80
     */
81
    public static final IRI THIS_REPO_ID = vf.createIRI("http://purl.org/nanopub/admin/thisRepo");
4✔
82

83
    /**
84
     * IRI for the nanopub coverage item.
85
     */
86
    public static final IRI HAS_COVERAGE_ITEM = vf.createIRI("http://purl.org/nanopub/admin/hasCoverageItem");
4✔
87

88
    /**
89
     * IRI for the nanopub coverage hash.
90
     */
91
    public static final IRI HAS_COVERAGE_HASH = vf.createIRI("http://purl.org/nanopub/admin/hasCoverageHash");
4✔
92

93
    /**
94
     * IRI for the nanopub coverage filter.
95
     */
96
    public static final IRI HAS_COVERAGE_FILTER = vf.createIRI("http://purl.org/nanopub/admin/hasCoverageFilter");
5✔
97

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

100
    private String endpointBase = null;
3✔
101
    private String endpointType = null;
3✔
102

103
    private static TripleStore tripleStoreInstance;
104

105
    /**
106
     * Returns singleton triple store instance.
107
     *
108
     * @return Triple store instance
109
     */
110
    public static TripleStore get() {
111
        if (tripleStoreInstance == null) {
2!
112
            try {
113
                tripleStoreInstance = new TripleStore();
×
114
            } catch (IOException ex) {
×
115
                ex.printStackTrace();
×
116
            }
×
117
        }
118
        return tripleStoreInstance;
×
119
    }
120

121
    private TripleStore() throws IOException {
2✔
122
        Map<String, String> env = EnvironmentUtils.getProcEnvironment();
2✔
123
        endpointBase = env.get("ENDPOINT_BASE");
6✔
124
        System.err.println("Endpoint base: " + endpointBase);
5✔
125
        endpointType = env.get("ENDPOINT_TYPE");
6✔
126

127
        getRepository("empty");  // Make sure empty repo exists
×
128
    }
×
129

130
    private final CloseableHttpClient httpclient = HttpClients.createDefault();
3✔
131

132
    Repository getRepository(String name) {
133
        synchronized (this) {
4✔
134
            while (repositories.size() > 100) {
5!
135
                Entry<String, Repository> e = repositories.entrySet().iterator().next();
×
136
                repositories.remove(e.getKey());
×
137
                System.err.println("Shutting down repo: " + e.getKey());
×
138
                e.getValue().shutDown();
×
139
                System.err.println("Shutdown complete");
×
140
            }
×
141
            if (repositories.containsKey(name)) {
5!
142
                // Move to the end of the list:
143
                Repository repo = repositories.remove(name);
×
144
                repositories.put(name, repo);
×
145
            } else {
×
146
                Repository repository = null;
2✔
147
                if (endpointType == null || endpointType.equals("rdf4j")) {
3!
148
                    HTTPRepository hr = new HTTPRepository(endpointBase + "repositories/" + name);
×
149
                    hr.setHttpClient(httpclient);
×
150
                    repository = hr;
×
151
//                        } else if (endpointType.equals("virtuoso")) {
152
//                                repository = new VirtuosoRepository(endpointBase + name, username, password);
153
                } else {
×
154
                    throw new RuntimeException("Unknown repository type: " + endpointType);
×
155
                }
156
                repositories.put(name, repository);
×
157
                createRepo(name);
×
158
                getRepoConnection(name).close();
×
159
            }
160
            return repositories.get(name);
×
161
        }
162
    }
163

164
    /**
165
     * Return the repository connection for the given repository name.
166
     *
167
     * @param name repository name
168
     * @return repository connection
169
     */
170
    public RepositoryConnection getRepoConnection(String name) {
171
        Repository repo = getRepository(name);
×
172
        if (repo == null) {
×
173
            return null;
×
174
        }
175
        return repo.getConnection();
×
176
    }
177

178
    private void createRepo(String repoName) {
179
        if (!repoName.equals(ADMIN_REPO)) {
×
180
            getRepository(ADMIN_REPO);  // make sure admin repo is loaded first
×
181
        }
182
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
×
183
            //System.err.println("Trying to creating repo " + name);
184

185
            // TODO new syntax somehow doesn't work for the Lucene case:
186

187
//                        String createRegularRepoQueryString = "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n"
188
//                                        + "@prefix config: <tag:rdf4j.org,2023:config/>.\n"
189
//                                        + "[] a config:Repository ;\n"
190
//                                        + "    config:rep.id \"" + name + "\" ;\n"
191
//                                        + "    rdfs:label \"" + name + " native store\" ;\n"
192
//                                        + "    config:rep.impl [\n"
193
//                                        + "        config:rep.type \"openrdf:SailRepository\" ;\n"
194
//                                        + "        config:sail.impl [\n"
195
//                                        + "            config:sail.type \"openrdf:NativeStore\" ;\n"
196
//                                        + "            config:sail.iterationCacheSyncThreshold \"10000\";\n"
197
//                                        + "            config:native.tripleIndexes \"spoc,posc,ospc,opsc,psoc,sopc,spoc,cpos,cosp,cops,cpso,csop\" ;\n"
198
//                                        + "            config:sail.defaultQueryEvaluationMode \"STANDARD\"\n"
199
//                                        + "        ]\n"
200
//                                        + "    ].";
201
//                        String createTextRepoQueryString = "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\n"
202
//                                        + "@prefix config: <tag:rdf4j.org,2023:config/>.\n"
203
//                                        + "[] a config:Repository ;\n"
204
//                                        + "    config:rep.id \"" + name + "\" ;\n"
205
//                                        + "    rdfs:label \"" + name + " native store\" ;\n"
206
//                                        + "    config:rep.impl [\n"
207
//                                        + "        config:rep.type \"openrdf:SailRepository\" ;\n"
208
//                                        + "        config:sail.impl [\n"
209
//                                        + "            config:sail.type \"openrdf:LuceneSail\" ;\n"
210
//                                        + "            config:sail.lucene.indexDir \"index/\" ;\n"
211
//                                        + "            config:delegate [\n"
212
//                                        + "                config:rep.type \"openrdf:SailRepository\" ;\n"
213
//                                        + "                config:sail.impl [\n"
214
//                                        + "                    config:sail.type \"openrdf:NativeStore\" ;\n"
215
//                                        + "                    config:sail.iterationCacheSyncThreshold \"10000\";\n"
216
//                                        + "                    config:native.tripleIndexes \"spoc,posc,ospc,opsc,psoc,sopc,spoc,cpos,cosp,cops,cpso,csop\" ;\n"
217
//                                        + "                    config:sail.defaultQueryEvaluationMode \"STANDARD\"\n"
218
//                                        + "                ]\n"
219
//                                        + "            ]\n"
220
//                                        + "        ]\n"
221
//                                        + "    ].";
222

223
            String indexTypes = "spoc,posc,ospc,cspo,cpos,cosp";
×
224
            if (repoName.startsWith("meta") || repoName.startsWith("text")) {
×
225
                indexTypes = "spoc,posc,ospc";
×
226
            }
227

228
            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";
×
229

230
            // TODO Index npa:hasFilterLiteral predicate too (see https://groups.google.com/g/rdf4j-users/c/epF4Af1jXGU):
231
            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" + "    ].";
×
232

233
            String createRepoQueryString = createRegularRepoQueryString;
×
234
            if (repoName.startsWith("text")) {
×
235
                createRepoQueryString = createTextRepoQueryString;
×
236
            }
237

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

240
            HttpResponse response = httpclient.execute(createRepoRequest);
×
241
            int statusCode = response.getStatusLine().getStatusCode();
×
242
            if (statusCode == 409) {
×
243
                //System.err.println("Already exists.");
244
                getRepository(repoName).init();
×
245
            } else if (statusCode >= 200 && statusCode < 300) {
×
246
                //System.err.println("Successfully created.");
247
                initNewRepo(repoName);
×
248
            } else {
249
                System.err.println("Status code: " + response.getStatusLine().getStatusCode());
×
250
                System.err.println(response.getStatusLine().getReasonPhrase());
×
251
                System.err.println("Response: " + new BasicResponseHandler().handleResponse(response));
×
252
            }
253
        } catch (IOException ex) {
×
254
            ex.printStackTrace();
×
255
        }
×
256
    }
×
257

258
    /**
259
     * Sends shutdown signal to all repositories.
260
     */
261
    public void shutdownRepositories() {
262
        for (Repository repo : repositories.values()) {
×
263
            if (repo != null && repo.isInitialized()) {
×
264
                repo.shutDown();
×
265
            }
266
        }
×
267
    }
×
268

269
    /**
270
     * Returns admin repo connection.
271
     *
272
     * @return repository connection to admin repository
273
     */
274
    public RepositoryConnection getAdminRepoConnection() {
275
        return get().getRepoConnection(ADMIN_REPO);
×
276
    }
277

278
    /**
279
     * Returns set of all repository names.
280
     *
281
     * @return Repository name set
282
     */
283
    public Set<String> getRepositoryNames() {
284
        Map<String, Boolean> repositoryNames = null;
×
285
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
×
286
            HttpResponse resp = httpclient.execute(RequestBuilder.get()
×
287
                    .setUri(endpointBase + "/repositories")
×
288
                    .addHeader("Content-Type", "text/csv")
×
289
                    .build());
×
290
            BufferedReader reader = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
×
291
            int code = resp.getStatusLine().getStatusCode();
×
292
            if (code < 200 || code >= 300) return null;
×
293
            repositoryNames = new HashMap<>();
×
294
            int lineCount = 0;
×
295
            while (true) {
296
                String line = reader.readLine();
×
297
                if (line == null) break;
×
298
                if (lineCount > 0) {
×
299
                    String repoName = line.split(",")[1];
×
300
                    repositoryNames.put(repoName, true);
×
301
                }
302
                lineCount = lineCount + 1;
×
303
            }
×
304
        } catch (IOException ex) {
×
305
            ex.printStackTrace();
×
306
            return null;
×
307
        }
×
308
        return repositoryNames.keySet();
×
309
    }
310

311
    private void initNewRepo(String repoName) {
312
        String repoInitId = new Random().nextLong() + "";
×
313
        getRepository(repoName).init();
×
314
        if (!repoName.equals("empty")) {
×
315
            RepositoryConnection conn = getRepoConnection(repoName);
×
316
            try (conn) {
×
317
                // Full isolation, just in case.
318
                conn.begin(IsolationLevels.SERIALIZABLE);
×
319
                conn.add(THIS_REPO_ID, HAS_REPO_INIT_ID, vf.createLiteral(repoInitId), NanopubLoader.ADMIN_GRAPH);
×
320
                conn.add(THIS_REPO_ID, HAS_NANOPUB_COUNT, vf.createLiteral(0L), NanopubLoader.ADMIN_GRAPH);
×
321
                conn.add(THIS_REPO_ID, HAS_NANOPUB_CHECKSUM, vf.createLiteral(NanopubUtils.INIT_CHECKSUM), NanopubLoader.ADMIN_GRAPH);
×
322
                if (repoName.startsWith("pubkey_") || repoName.startsWith("type_")) {
×
323
                    String h = repoName.replaceFirst("^[^_]+_", "");
×
324
                    conn.add(THIS_REPO_ID, HAS_COVERAGE_ITEM, Utils.getObjectForHash(h), NanopubLoader.ADMIN_GRAPH);
×
325
                    conn.add(THIS_REPO_ID, HAS_COVERAGE_HASH, vf.createLiteral(h), NanopubLoader.ADMIN_GRAPH);
×
326
                    conn.add(THIS_REPO_ID, HAS_COVERAGE_FILTER, vf.createLiteral("_" + repoName), NanopubLoader.ADMIN_GRAPH);
×
327
                }
328
                conn.commit();
×
329
            }
330
        }
331
    }
×
332

333
}
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