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

knowledgepixels / nanodash / 23138432878

16 Mar 2026 10:09AM UTC coverage: 15.99% (+0.2%) from 15.811%
23138432878

push

github

web-flow
Merge pull request #402 from knowledgepixels/fix/401-bounded-api-cache

Fix unbounded memory growth and resource exhaustion

717 of 5509 branches covered (13.02%)

Branch coverage included in aggregate %.

1810 of 10295 relevant lines covered (17.58%)

2.39 hits per line

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

71.88
src/main/java/com/knowledgepixels/nanodash/NanopubElement.java
1
package com.knowledgepixels.nanodash;
2

3
import org.eclipse.rdf4j.model.IRI;
4
import org.eclipse.rdf4j.model.Statement;
5
import org.eclipse.rdf4j.model.vocabulary.RDF;
6
import org.nanopub.Nanopub;
7
import org.nanopub.NanopubUtils;
8
import org.nanopub.SimpleTimestampPattern;
9
import org.nanopub.extra.security.MalformedCryptoElementException;
10
import org.nanopub.extra.security.NanopubSignatureElement;
11
import org.nanopub.extra.security.SignatureUtils;
12
import org.slf4j.Logger;
13
import org.slf4j.LoggerFactory;
14

15
import com.google.common.cache.Cache;
16
import com.google.common.cache.CacheBuilder;
17

18
import java.io.Serializable;
19
import java.security.GeneralSecurityException;
20
import java.util.*;
21
import java.util.concurrent.TimeUnit;
22

23
/**
24
 * Represents a wrapper for a Nanopub object, providing additional metadata and utility methods.
25
 * This class includes caching mechanisms to avoid redundant Nanopub retrievals.
26
 */
27
public class NanopubElement implements Serializable {
28

29
    private static final Cache<String, NanopubElement> nanopubCache = CacheBuilder.newBuilder()
6✔
30
        .maximumSize(10_000)
9✔
31
        .expireAfterAccess(24, TimeUnit.HOURS)
3✔
32
        .build();
6✔
33
    private static final Logger logger = LoggerFactory.getLogger(NanopubElement.class);
12✔
34

35
    /**
36
     * Retrieves a NanopubElement instance for the given URI.
37
     * If the instance is not already cached, it creates a new one and caches it.
38
     *
39
     * @param uri The URI of the Nanopub.
40
     * @return The NanopubElement instance.
41
     */
42
    public static NanopubElement get(String uri) {
43
        NanopubElement cached = nanopubCache.getIfPresent(uri);
15✔
44
        if (cached == null) {
6!
45
            cached = new NanopubElement(uri);
×
46
            nanopubCache.put(uri, cached);
×
47
        }
48
        return cached;
6✔
49
    }
50

51
    /**
52
     * Retrieves a NanopubElement instance for the given Nanopub object.
53
     * If the instance is not already cached, it creates a new one and caches it.
54
     *
55
     * @param nanopub The Nanopub object.
56
     * @return The NanopubElement instance.
57
     */
58
    public static NanopubElement get(Nanopub nanopub) {
59
        if (nanopub == null) {
6✔
60
            throw new IllegalArgumentException("Nanopub cannot be null");
15✔
61
        }
62
        String uri = nanopub.getUri().stringValue();
12✔
63
        NanopubElement cached = nanopubCache.getIfPresent(uri);
15✔
64
        if (cached == null) {
6✔
65
            cached = new NanopubElement(nanopub);
15✔
66
            nanopubCache.put(uri, cached);
12✔
67
        }
68
        return cached;
6✔
69
    }
70

71
    private Nanopub nanopub;
72
    private String uriString; // Keeping URI separately, as nanopub might be null when it cannot be fetched
73
    private String label;
74
    private Calendar creationTime;
75
    private Boolean seemsToHaveSignature;
76
    private Boolean hasValidSignature;
77
    private IRI signerId;
78
    private List<IRI> types;
79
    private Map<String, String> foafNameMap;
80

81
    /**
82
     * Constructs a NanopubElement for the given URI.
83
     * Fetches the Nanopub object using the URI.
84
     *
85
     * @param uri The URI of the Nanopub.
86
     * @throws IllegalArgumentException If no Nanopub is found for the given URI.
87
     */
88
    private NanopubElement(String uri) {
×
89
        this.uriString = uri;
×
90
        this.nanopub = Utils.getNanopub(uri);
×
91
        if (nanopub == null) {
×
92
            throw new IllegalArgumentException("No nanopublication found for URI: " + uri);
×
93
        }
94
    }
×
95

96
    /**
97
     * Constructs a NanopubElement for the given Nanopub object.
98
     *
99
     * @param nanopub The Nanopub object.
100
     */
101
    private NanopubElement(Nanopub nanopub) {
6✔
102
        this.uriString = nanopub.getUri().stringValue();
15✔
103
        this.nanopub = nanopub;
9✔
104
    }
3✔
105

106
    /**
107
     * Returns the Nanopub object.
108
     *
109
     * @return The Nanopub object.
110
     */
111
    public Nanopub getNanopub() {
112
        return nanopub;
9✔
113
    }
114

115
    /**
116
     * Returns the URI of the Nanopub.
117
     *
118
     * @return The URI as a string.
119
     */
120
    public String getUri() {
121
        return uriString;
9✔
122
    }
123

124
    /**
125
     * Returns the label of the Nanopub.
126
     * If the label is not already set, it retrieves it from the Nanopub.
127
     *
128
     * @return The label of the Nanopub.
129
     */
130
    public String getLabel() {
131
        if (label != null) return label;
18✔
132
        if (nanopub == null) return null;
9!
133
        label = NanopubUtils.getLabel(nanopub);
15✔
134
        if (label == null) label = "";
9!
135
        return label;
9✔
136
    }
137

138
    /**
139
     * Returns the creation time of the Nanopub.
140
     * If the creation time is not already set, it retrieves it using a timestamp pattern.
141
     *
142
     * @return The creation time as a Calendar object.
143
     */
144
    public Calendar getCreationTime() {
145
        if (nanopub == null) return null;
9!
146
        if (creationTime == null) {
9✔
147
            creationTime = SimpleTimestampPattern.getCreationTime(nanopub);
15✔
148
        }
149
        return creationTime;
9✔
150
    }
151

152
    /**
153
     * Checks if the Nanopub seems to have a signature.
154
     *
155
     * @return True if the Nanopub seems to have a signature, false otherwise.
156
     */
157
    public boolean seemsToHaveSignature() {
158
        if (nanopub == null) return false;
9!
159
        if (seemsToHaveSignature == null) {
9✔
160
            seemsToHaveSignature = SignatureUtils.seemsToHaveSignature(nanopub);
18✔
161
        }
162
        return seemsToHaveSignature;
12✔
163
    }
164

165
    /**
166
     * Returns the public key of the Nanopub's signature.
167
     * If the signature is not valid, returns null.
168
     *
169
     * @return The public key as a string, or null if invalid.
170
     */
171
    public String getPubkey() {
172
        if (!hasValidSignature()) return null;
9!
173
        try {
174
            return SignatureUtils.getSignatureElement(nanopub).getPublicKeyString();
15✔
175
        } catch (MalformedCryptoElementException ex) {
×
176
            logger.error("Error in getting the signature element of the nanopub {}", uriString, ex);
×
177
            return null;
×
178
        }
179
    }
180

181
    /**
182
     * Returns the SHA-256 hash of the public key of the Nanopub's signature
183
     *
184
     * @return The SHA-256 hash of the public key as a hexadecimal string, or null if the public key is not available.
185
     */
186
    public String getPubkeyhash() {
187
        String pubkey = getPubkey();
9✔
188
        if (pubkey == null) return null;
6!
189
        return Utils.createSha256HexHash(pubkey);
9✔
190
    }
191

192
    /**
193
     * Checks if the Nanopub has a valid signature.
194
     *
195
     * @return True if the Nanopub has a valid signature, false otherwise.
196
     */
197
    public boolean hasValidSignature() {
198
        if (nanopub == null) return false;
9!
199
        if (hasValidSignature == null) {
9✔
200
            try {
201
                NanopubSignatureElement se;
202
                se = SignatureUtils.getSignatureElement(nanopub);
12✔
203
                if (se != null) {
6!
204
                    hasValidSignature = SignatureUtils.hasValidSignature(se);
15✔
205
                    if (se.getSigners().size() == 1) signerId = se.getSigners().iterator().next();
39!
206
                } else {
207
                    hasValidSignature = false;
×
208
                }
209
            } catch (MalformedCryptoElementException | GeneralSecurityException ex) {
×
210
                logger.error("Error in checking the signature of the nanopub {}", uriString, ex);
×
211
                return false;
×
212
            }
3✔
213
        }
214
        return hasValidSignature;
12✔
215
    }
216

217
    /**
218
     * Returns the ID of the signer of the Nanopub.
219
     * If the signature is not valid, returns null.
220
     *
221
     * @return The signer ID as an IRI, or null if invalid.
222
     */
223
    public IRI getSignerId() {
224
        if (nanopub == null) return null;
9!
225
        if (!hasValidSignature()) return null;
9!
226
        return signerId;
9✔
227
    }
228

229
    /**
230
     * Returns the list of RDF types associated with the Nanopub.
231
     * If the types are not already set, it retrieves them from the Nanopub's pubinfo.
232
     *
233
     * @return A list of RDF types as IRIs.
234
     */
235
    public List<IRI> getTypes() {
236
        if (types == null) {
9!
237
            types = new ArrayList<>();
15✔
238
            if (nanopub == null) return types;
9!
239
            for (Statement st : nanopub.getPubinfo()) {
36✔
240
                if (st.getSubject().equals(nanopub.getUri()) && st.getPredicate().equals(RDF.TYPE) && st.getObject() instanceof IRI) {
48!
241
                    types.add((IRI) st.getObject());
21✔
242
                }
243
            }
3✔
244
        }
245
        return types;
9✔
246
    }
247

248
    /**
249
     * Returns a map of FOAF names associated with the Nanopub.
250
     * If the map is not already set, it retrieves it using a utility method.
251
     *
252
     * @return A map of FOAF names.
253
     */
254
    public Map<String, String> getFoafNameMap() {
255
        if (foafNameMap == null) foafNameMap = Utils.getFoafNameMap(nanopub);
×
256
        return foafNameMap;
×
257
    }
258

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