• 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

70.34
src/main/java/com/knowledgepixels/query/StatusController.java
1
package com.knowledgepixels.query;
2

3
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
4
import org.eclipse.rdf4j.model.Literal;
5
import org.eclipse.rdf4j.repository.RepositoryConnection;
6

7
import java.util.Objects;
8

9
/**
10
 * Class to control the load status of the database.
11
 */
12
public class StatusController {
2✔
13

14
    /**
15
     * The load states in which the database can be.
16
     */
17
    public enum State {
3✔
18
        /**
19
         * The service is launching.
20
         */
21
        LAUNCHING,
6✔
22
        /**
23
         * The service is loading.
24
         */
25
        LOADING_INITIAL,
6✔
26
        /**
27
         * The service is loading updates.
28
         */
29
        LOADING_UPDATES,
6✔
30
        /**
31
         * The service is ready to serve requests.
32
         */
33
        READY,
6✔
34
    }
35

36
    /**
37
     * Represents the current status of the service, including the load counter.
38
     */
39
    public static class LoadingStatus {
40

41
        /**
42
         * The current state of the service.
43
         */
44
        public final State state;
45

46
        /**
47
         * The current load counter.
48
         */
49
        public final long loadCounter;
50

51
        private LoadingStatus(State state, long loadCounter) {
2✔
52
            this.state = state;
3✔
53
            this.loadCounter = loadCounter;
3✔
54
        }
1✔
55

56
        /**
57
         * Create a new LoadingStatus instance.
58
         *
59
         * @param state       the current state of the service
60
         * @param loadCounter the current load counter
61
         * @return a new LoadingStatus instance
62
         */
63
        public static LoadingStatus of(State state, long loadCounter) {
64
            return new LoadingStatus(state, loadCounter);
6✔
65
        }
66

67
        @Override
68
        public boolean equals(Object o) {
69
            if (o == null || getClass() != o.getClass()) {
7✔
70
                return false;
2✔
71
            }
72
            LoadingStatus that = (LoadingStatus) o;
3✔
73
            return loadCounter == that.loadCounter && state == that.state;
15✔
74
        }
75

76
        @Override
77
        public int hashCode() {
78
            return Objects.hash(state, loadCounter);
15✔
79
        }
80

81
    }
82

83
    /**
84
     * Get the singleton instance of the StatusController.
85
     *
86
     * @return the StatusController instance
87
     */
88
    public static StatusController get() {
89
        return instance;
2✔
90
    }
91

92
    private final static StatusController instance = new StatusController();
5✔
93

94
    private boolean initialized = false;
3✔
95
    private State state = null;
3✔
96
    private long lastCommittedCounter = -1;
4✔
97
    private RepositoryConnection adminRepoConn;
98

99
    /**
100
     * Initialize the StatusController, fetching the last known state from the DB.
101
     * This must be called right after service startup, before loading any nanopubs.
102
     *
103
     * @return the current state and the last committed counter
104
     */
105
    public LoadingStatus initialize() {
106
        synchronized (this) {
4✔
107
            if (initialized) {
3✔
108
                throw new IllegalStateException("Already initialized");
5✔
109
            }
110
            state = State.LAUNCHING;
3✔
111
            adminRepoConn = TripleStore.get().getAdminRepoConnection();
4✔
112
            // Serializable, as the service state needs to be strictly consistent
113
            adminRepoConn.begin(IsolationLevels.SERIALIZABLE);
4✔
114
            // Fetch the state from the DB
115
            try (var statements = adminRepoConn.getStatements(TripleStore.THIS_REPO_ID, TripleStore.HAS_STATUS, null, NanopubLoader.ADMIN_GRAPH)) {
13✔
116
                if (!statements.hasNext()) {
3!
117
                    adminRepoConn.add(TripleStore.THIS_REPO_ID, TripleStore.HAS_STATUS, stateAsLiteral(state), NanopubLoader.ADMIN_GRAPH);
16✔
118
                } else {
119
                    var stateStatement = statements.next();
×
120
                    state = State.valueOf(stateStatement.getObject().stringValue());
×
121
                }
122
            }
123
            // Fetch the load counter from the DB
124
            try (var statements = adminRepoConn.getStatements(TripleStore.THIS_REPO_ID, TripleStore.HAS_REGISTRY_LOAD_COUNTER, null, NanopubLoader.ADMIN_GRAPH)) {
13✔
125
                if (!statements.hasNext()) {
3!
126
                    adminRepoConn.add(TripleStore.THIS_REPO_ID, TripleStore.HAS_REGISTRY_LOAD_COUNTER, adminRepoConn.getValueFactory().createLiteral(-1L), NanopubLoader.ADMIN_GRAPH);
17✔
127
                } else {
128
                    var counterStatement = statements.next();
×
129
                    var stringVal = counterStatement.getObject().stringValue();
×
130
                    lastCommittedCounter = Long.parseLong(stringVal);
×
131
                }
132
                adminRepoConn.commit();
3✔
133
            } catch (Exception e) {
×
134
                if (adminRepoConn.isActive()) adminRepoConn.rollback();
×
135
                throw new RuntimeException(e);
×
136
            }
1✔
137
            initialized = true;
3✔
138
            return getState();
5✔
139
        }
140
    }
141

142
    /**
143
     * Get the current state of the service.
144
     *
145
     * @return the current state and the last committed counter
146
     */
147
    public LoadingStatus getState() {
148
        synchronized (this) {
4✔
149
            return LoadingStatus.of(state, lastCommittedCounter);
8✔
150
        }
151
    }
152

153
    /**
154
     * Transition the service to the LOADING_INITIAL state and update the load counter.
155
     * This should be called in two situations:
156
     * - By the main loading thread (after calling initialize()) to start loading the initial nanopubs.
157
     * - By the initial nanopub loader, as it processes the initial nanopubs.
158
     *
159
     * @param loadCounter the new load counter
160
     */
161
    public void setLoadingInitial(long loadCounter) {
162
        synchronized (this) {
4✔
163
            if (state != State.LAUNCHING && state != State.LOADING_INITIAL) {
8✔
164
                throw new IllegalStateException("Cannot transition to LOADING_INITIAL, as the " + "current state is " + state);
8✔
165
            }
166
            if (lastCommittedCounter > loadCounter) {
5✔
167
                throw new IllegalStateException("Cannot update the load counter from " + lastCommittedCounter + " to " + loadCounter);
8✔
168
            }
169
            updateState(State.LOADING_INITIAL, loadCounter);
4✔
170
        }
3✔
171
    }
1✔
172

173
    /**
174
     * Transition the service to the LOADING_UPDATES state and update the load counter.
175
     * This should be called by the updates loader, when it starts processing new nanopubs, or
176
     * when it finishes processing a batch of nanopubs.
177
     *
178
     * @param loadCounter the new load counter
179
     */
180
    public void setLoadingUpdates(long loadCounter) {
181
        synchronized (this) {
×
182
            if (state != State.LAUNCHING && state != State.LOADING_UPDATES && state != State.READY) {
×
183
                throw new IllegalStateException("Cannot transition to LOADING_UPDATES, as the " + "current state is " + state);
×
184
            }
185
            if (lastCommittedCounter > loadCounter) {
×
186
                throw new IllegalStateException("Cannot update the load counter from " + lastCommittedCounter + " to " + loadCounter);
×
187
            }
188
            updateState(State.LOADING_UPDATES, loadCounter);
×
189
        }
×
190
    }
×
191

192
    /**
193
     * Transition the service to the READY state.
194
     * This should be called by the loaders, after they finish their work.
195
     */
196
    public void setReady() {
197
        synchronized (this) {
4✔
198
            if (state != State.READY) {
4!
199
                updateState(State.READY, lastCommittedCounter);
×
200
            }
201
        }
3✔
202
    }
1✔
203

204
    void updateState(State newState, long loadCounter) {
205
        synchronized (this) {
4✔
206
            try {
207
                // Serializable, as the service state needs to be strictly consistent
208
                adminRepoConn.begin(IsolationLevels.SERIALIZABLE);
4✔
209
                adminRepoConn.remove(TripleStore.THIS_REPO_ID, TripleStore.HAS_STATUS, null, NanopubLoader.ADMIN_GRAPH);
12✔
210
                adminRepoConn.add(TripleStore.THIS_REPO_ID, TripleStore.HAS_STATUS, stateAsLiteral(newState), NanopubLoader.ADMIN_GRAPH);
14✔
211
                adminRepoConn.remove(TripleStore.THIS_REPO_ID, TripleStore.HAS_REGISTRY_LOAD_COUNTER, null, NanopubLoader.ADMIN_GRAPH);
12✔
212
                adminRepoConn.add(TripleStore.THIS_REPO_ID, TripleStore.HAS_REGISTRY_LOAD_COUNTER, adminRepoConn.getValueFactory().createLiteral(loadCounter), NanopubLoader.ADMIN_GRAPH);
16✔
213
                adminRepoConn.commit();
3✔
214
                state = newState;
3✔
215
                lastCommittedCounter = loadCounter;
3✔
216
            } catch (Exception e) {
×
217
                if (adminRepoConn.isActive()) adminRepoConn.rollback();
×
218
                throw new RuntimeException(e);
×
219
            }
1✔
220
        }
3✔
221
    }
1✔
222

223
    private Literal stateAsLiteral(State s) {
224
        return adminRepoConn.getValueFactory().createLiteral(s.toString());
7✔
225
    }
226
}
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