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

knowledgepixels / nanopub-query / 17671313071

12 Sep 2025 10:05AM UTC coverage: 76.087% (-0.7%) from 76.74%
17671313071

push

github

web-flow
Merge pull request #47 from knowledgepixels/update-nanopub-java

Update nanopub java

231 of 318 branches covered (72.64%)

Branch coverage included in aggregate %.

574 of 740 relevant lines covered (77.57%)

3.92 hits per line

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

81.13
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

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();
3✔
37

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

40
    private String endpointBase = null;
3✔
41
    private String endpointType = null;
3✔
42

43
    private static TripleStore tripleStoreInstance;
44

45
    /**
46
     * Returns singleton triple store instance.
47
     *
48
     * @return Triple store instance
49
     */
50
    public static TripleStore get() {
51
        if (tripleStoreInstance == null) {
2!
52
            try {
53
                tripleStoreInstance = new TripleStore();
×
54
            } catch (IOException ex) {
×
55
                ex.printStackTrace();
×
56
            }
×
57
        }
58
        return tripleStoreInstance;
×
59
    }
60

61
    private TripleStore() throws IOException {
2✔
62
        Map<String, String> env = EnvironmentUtils.getProcEnvironment();
2✔
63
        endpointBase = env.get("ENDPOINT_BASE");
6✔
64
        System.err.println("Endpoint base: " + endpointBase);
5✔
65
        endpointType = env.get("ENDPOINT_TYPE");
6✔
66

67
        getRepository("empty");  // Make sure empty repo exists
×
68
    }
×
69

70
    private final CloseableHttpClient httpclient = HttpClients.createDefault();
3✔
71

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

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

120
    @GeneratedFlagForDependentElements
121
    private void createRepo(String repoName) {
122
        if (!repoName.equals(ADMIN_REPO)) {
123
            getRepository(ADMIN_REPO);  // make sure admin repo is loaded first
124
        }
125
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
126
            //System.err.println("Trying to creating repo " + name);
127

128
            // TODO new syntax somehow doesn't work for the Lucene case:
129

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

166
            String indexTypes = "spoc,posc,ospc,cspo,cpos,cosp";
167
            if (repoName.startsWith("meta") || repoName.startsWith("text")) {
168
                indexTypes = "spoc,posc,ospc";
169
            }
170

171
            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";
172

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

176
            String createRepoQueryString = createRegularRepoQueryString;
177
            if (repoName.startsWith("text")) {
178
                createRepoQueryString = createTextRepoQueryString;
179
            }
180

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

183
            HttpResponse response = httpclient.execute(createRepoRequest);
184
            int statusCode = response.getStatusLine().getStatusCode();
185
            if (statusCode == 409) {
186
                //System.err.println("Already exists.");
187
                getRepository(repoName).init();
188
            } else if (statusCode >= 200 && statusCode < 300) {
189
                //System.err.println("Successfully created.");
190
                initNewRepo(repoName);
191
            } else {
192
                System.err.println("Status code: " + response.getStatusLine().getStatusCode());
193
                System.err.println(response.getStatusLine().getReasonPhrase());
194
                System.err.println("Response: " + new BasicResponseHandler().handleResponse(response));
195
            }
196
        } catch (IOException ex) {
197
            ex.printStackTrace();
198
        }
199
    }
200

201
    /**
202
     * Sends shutdown signal to all repositories.
203
     */
204
    @GeneratedFlagForDependentElements
205
    public void shutdownRepositories() {
206
        for (Repository repo : repositories.values()) {
207
            if (repo != null && repo.isInitialized()) {
208
                repo.shutDown();
209
            }
210
        }
211
    }
212

213
    /**
214
     * Returns admin repo connection.
215
     *
216
     * @return repository connection to admin repository
217
     */
218
    @GeneratedFlagForDependentElements
219
    public RepositoryConnection getAdminRepoConnection() {
220
        return get().getRepoConnection(ADMIN_REPO);
221
    }
222

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

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

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