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

knowledgepixels / nanopub-registry / 23976591988

04 Apr 2026 09:57AM UTC coverage: 31.828% (+0.5%) from 31.28%
23976591988

push

github

web-flow
Merge pull request #91 from knowledgepixels/perf/seqnum-batch-allocation

perf: batch seqNum allocation to reduce global counter contention

216 of 754 branches covered (28.65%)

Branch coverage included in aggregate %.

714 of 2168 relevant lines covered (32.93%)

5.65 hits per line

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

96.1
src/main/java/com/knowledgepixels/registry/Page.java
1
package com.knowledgepixels.registry;
2

3
import com.mongodb.client.ClientSession;
4
import io.vertx.core.http.HttpMethod;
5
import io.vertx.ext.web.RoutingContext;
6
import org.apache.commons.lang.StringEscapeUtils;
7

8
import java.io.IOException;
9
import java.text.DecimalFormat;
10

11
import static com.knowledgepixels.registry.RegistryDB.*;
12

13
public abstract class Page {
14

15
    protected static final DecimalFormat df8 = new DecimalFormat("0.00000000");
15✔
16
    protected static final DecimalFormat df1 = new DecimalFormat("0.0");
18✔
17

18
    private RoutingContext context;
19
    protected ClientSession mongoSession;
20

21
    private String presentationFormat;
22
    private String extension;
23
    private String requestString;
24

25
    /**
26
     * Constructor for the Page class.
27
     *
28
     * @param mongoSession The MongoDB client session.
29
     * @param context      The routing context.
30
     */
31
    public Page(ClientSession mongoSession, RoutingContext context) {
6✔
32
        this.mongoSession = mongoSession;
9✔
33
        this.context = context;
9✔
34
        context.response().setChunked(true);
15✔
35

36
        // TODO See whether we can cache these better on our side. Not sure how efficient the MongoDB caching is for these
37
        //      kinds of DB queries...
38
        context.response().putHeader("Nanopub-Registry-Status", getValue(mongoSession, Collection.SERVER_INFO.toString(), "status") + "");
36✔
39
        context.response().putHeader("Nanopub-Registry-Setup-Id", getValue(mongoSession, Collection.SERVER_INFO.toString(), "setupId") + "");
36✔
40
        context.response().putHeader("Nanopub-Registry-Trust-State-Counter", getValue(mongoSession, Collection.SERVER_INFO.toString(), "trustStateCounter") + "");
36✔
41
        context.response().putHeader("Nanopub-Registry-Last-Trust-State-Update", getValue(mongoSession, Collection.SERVER_INFO.toString(), "lastTrustStateUpdate") + "");
36✔
42
        context.response().putHeader("Nanopub-Registry-Trust-State-Hash", getValue(mongoSession, Collection.SERVER_INFO.toString(), "trustStateHash") + "");
36✔
43
        context.response().putHeader("Nanopub-Registry-SeqNum", getMaxValue(mongoSession, Collection.NANOPUBS.toString(), "seqNum") + "");
36✔
44
        context.response().putHeader("Nanopub-Registry-Nanopub-Count", collection(Collection.NANOPUBS.toString()).estimatedDocumentCount() + "");
30✔
45
        // TODO(transition): Remove after all peers upgraded
46
        // Must send max(seqNum) here, not document count — old peers use Load-Counter as a cursor
47
        // value for afterCounter, not just a count. Sending the count would cause re-fetching.
48
        context.response().putHeader("Nanopub-Registry-Load-Counter", getMaxValue(mongoSession, Collection.NANOPUBS.toString(), "seqNum") + "");
36✔
49
        context.response().putHeader("Nanopub-Registry-Test-Instance", String.valueOf(isSet(mongoSession, Collection.SERVER_INFO.toString(), "testInstance")));
33✔
50

51
        String r = context.request().path().substring(1);
18✔
52
        if (r.endsWith(".txt")) {
12✔
53
            presentationFormat = "text/plain";
9✔
54
            r = r.replaceFirst("\\.txt$", "");
18✔
55
        } else if (r.endsWith(".html")) {
12✔
56
            presentationFormat = "text/html";
9✔
57
            r = r.replaceFirst("\\.html$", "");
15✔
58
//                } else if (r.endsWith(".gz")) {
59
//                        presentationFormat = "application/x-gzip";
60
//                        r = r.replaceFirst("\\.gz$", "");
61
        }
62
        if (r.matches(".*\\.[a-z]{1,10}")) {
12✔
63
            extension = r.replaceFirst("^.*\\.([a-z]{1,10})$", "$1");
18✔
64
            requestString = r.replaceFirst("^(.*)\\.[a-z]{1,10}$", "$1");
21✔
65
        } else {
66
            requestString = r;
9✔
67
        }
68
    }
3✔
69

70
    /**
71
     * Get the routing context.
72
     *
73
     * @return The routing context.
74
     */
75
    public RoutingContext getContext() {
76
        return context;
9✔
77
    }
78

79
    /**
80
     * Print a line to the response.
81
     *
82
     * @param s The string to print.
83
     */
84
    public void println(String s) {
85
        print(s + "\n");
12✔
86
    }
3✔
87

88
    /**
89
     * Print a string to the response.
90
     *
91
     * @param s The string to print.
92
     */
93
    public void print(String s) {
94
        if (context.request().method() == HttpMethod.HEAD) {
18!
95
            return;
×
96
        }
97
        context.response().write(s);
18✔
98
    }
3✔
99

100
    /**
101
     * Set the response content type.
102
     *
103
     * @param contentType The content type.
104
     */
105
    public void setRespContentType(String contentType) {
106
        if (context.request().method() == HttpMethod.HEAD) {
18✔
107
            return;
3✔
108
        }
109
        context.response().putHeader("Content-Type", contentType);
21✔
110
    }
3✔
111

112
    /**
113
     * Show the page.
114
     *
115
     * @throws IOException
116
     */
117
    protected abstract void show() throws IOException;
118

119
    /**
120
     * Print the HTML header.
121
     *
122
     * @param title The title of the page.
123
     */
124
    public void printHtmlHeader(String title) {
125
        println("<!DOCTYPE HTML>");
9✔
126
        println("<html><head>");
9✔
127
        println("<title>" + title + "</title>");
12✔
128
        println("<meta charset=\"utf-8\"/>");
9✔
129
        println("<script type=\"text/javascript\" src=\"/scripts/nanopub.js\"></script>");
9✔
130
        println("<link rel=\"stylesheet\" href=\"/style.css\" type=\"text/css\" media=\"screen\" title=\"Stylesheet\" />");
9✔
131
        println("</head><body>");
9✔
132
    }
3✔
133

134
    /**
135
     * Print the HTML footer.
136
     */
137
    public void printHtmlFooter() {
138
        println("</body></html>");
9✔
139
    }
3✔
140

141
    /**
142
     * Escape HTML special characters in a string.
143
     *
144
     * @param text The text to escape.
145
     * @return The escaped text.
146
     */
147
    public String escapeHtml(String text) {
148
        return StringEscapeUtils.escapeHtml(text);
×
149
    }
150

151
    /**
152
     * Set the canonical link for this page.
153
     *
154
     * @param url The canonical URL.
155
     */
156
    public void setCanonicalLink(String url) {
157
        context.response().putHeader("Link", "<" + url + ">; rel=\"canonical\"");
24✔
158
    }
3✔
159

160
    /**
161
     * Get the presentation format requested, if any.
162
     *
163
     * @return The presentation format.
164
     */
165
    public String getPresentationFormat() {
166
        return presentationFormat;
9✔
167
    }
168

169
    /**
170
     * Get the extension requested, if any.
171
     *
172
     * @return The extension.
173
     */
174
    public String getExtension() {
175
        return extension;
9✔
176
    }
177

178
    /**
179
     * Get the request string with the leading slash and without extension.
180
     *
181
     * @return The request string.
182
     */
183
    public String getRequestString() {
184
        return "/" + requestString;
12✔
185
    }
186

187
    /**
188
     * Get the full request path.
189
     *
190
     * @return The full request path.
191
     */
192
    public String getFullRequest() {
193
        return context.request().path();
15✔
194
    }
195

196
    /**
197
     * Get a parameter from the request.
198
     *
199
     * @param name         The name of the parameter.
200
     * @param defaultValue The default value of the parameter.
201
     * @return The value of the parameter.
202
     */
203
    public String getParam(String name, String defaultValue) {
204
        String value = context.request().getParam(name);
18✔
205
        if (value == null) value = defaultValue;
12✔
206
        return value;
6✔
207
    }
208

209
    /**
210
     * Check whether the request is empty.
211
     *
212
     * @return True if the request is empty, false otherwise.
213
     */
214
    public boolean isEmpty() {
215
        return requestString.isEmpty();
12✔
216
    }
217

218
    /**
219
     * Check whether the request contains an artifact code.
220
     *
221
     * @return True if the request contains an artifact code, false otherwise.
222
     */
223
    public boolean hasArtifactCode() {
224
        return requestString.matches("RA[A-Za-z0-9\\-_]{43}");
15✔
225
    }
226

227
    /**
228
     * Get the artifact code from the request, if present.
229
     *
230
     * @return The artifact code, or null if not present.
231
     */
232
    public String getArtifactCode() {
233
        if (hasArtifactCode()) {
9✔
234
            return requestString;
9✔
235
        }
236
        return null;
6✔
237
    }
238

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