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

knowledgepixels / nanodash / 17375037963

01 Sep 2025 10:33AM UTC coverage: 11.982% (-0.04%) from 12.02%
17375037963

push

github

tkuhn
Merge branch 'master' of github.com:knowledgepixels/nanodash

330 of 3846 branches covered (8.58%)

Branch coverage included in aggregate %.

949 of 6828 relevant lines covered (13.9%)

0.61 hits per line

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

0.0
src/main/java/com/knowledgepixels/nanodash/ApiCache.java
1
package com.knowledgepixels.nanodash;
2

3
import org.nanopub.extra.services.ApiResponse;
4
import org.nanopub.extra.services.ApiResponseEntry;
5
import org.nanopub.extra.services.FailedApiCallException;
6

7
import java.util.*;
8
import java.util.concurrent.ConcurrentHashMap;
9
import java.util.concurrent.ConcurrentMap;
10

11
/**
12
 * A utility class for caching API responses and maps to reduce redundant API calls.
13
 * This class is thread-safe and ensures that cached data is refreshed periodically.
14
 */
15
public class ApiCache {
16

17
    private ApiCache() {
18
    } // no instances allowed
19

20
    private transient static ConcurrentMap<String,ApiResponse> cachedResponses = new ConcurrentHashMap<>();
×
21
    private transient static ConcurrentMap<String, Map<String,String>> cachedMaps = new ConcurrentHashMap<>();
×
22
    private transient static ConcurrentMap<String,Long> lastRefresh = new ConcurrentHashMap<>();
×
23
    private transient static ConcurrentMap<String,Long> refreshStart = new ConcurrentHashMap<>();
×
24

25
    /**
26
     * Checks if a cache refresh is currently running for the given cache ID.
27
     *
28
     * @param cacheId The unique identifier for the cache.
29
     * @return True if a refresh is running, false otherwise.
30
     */
31
    private static boolean isRunning(String cacheId) {
32
        if (!refreshStart.containsKey(cacheId)) return false;
×
33
        return System.currentTimeMillis() - refreshStart.get(cacheId) < 60 * 1000;
×
34
    }
35

36
    /**
37
     * Checks if a cache refresh is running for a specific query and parameters.
38
     *
39
     * @param queryName The name of the query.
40
     * @param params    The parameters for the query.
41
     * @return True if a refresh is running, false otherwise.
42
     */
43
    public static boolean isRunning(String queryName, Map<String, String> params) {
44
        return isRunning(getCacheId(queryName, params));
×
45
    }
46

47
    /**
48
     * Checks if a cache refresh is running for a specific query and a single parameter.
49
     *
50
     * @param queryName  The name of the query.
51
     * @param paramName  The name of the parameter.
52
     * @param paramValue The value of the parameter.
53
     * @return True if a refresh is running, false otherwise.
54
     */
55
    public static boolean isRunning(String queryName, String paramName, String paramValue) {
56
        Map<String, String> params = new HashMap<>();
×
57
        params.put(paramName, paramValue);
×
58
        return isRunning(getCacheId(queryName, params));
×
59
    }
60

61
    /**
62
     * Updates the cached API response for a specific query and parameters.
63
     *
64
     * @param queryName The name of the query.
65
     * @param params    The parameters for the query.
66
     * @throws FailedApiCallException If the API call fails.
67
     */
68
    private static void updateResponse(String queryName, Map<String, String> params) throws FailedApiCallException {
69
        Map<String, String> nanopubParams = new HashMap<>();
×
70
        for (String k : params.keySet()) nanopubParams.put(k, params.get(k));
×
71
        ApiResponse response = QueryApiAccess.get(queryName, nanopubParams);
×
72
        String cacheId = getCacheId(queryName, params);
×
73
        cachedResponses.put(cacheId, response);
×
74
        lastRefresh.put(cacheId, System.currentTimeMillis());
×
75
    }
×
76

77
    /**
78
     * Retrieves a cached API response for a specific query and a single parameter.
79
     *
80
     * @param queryName  The name of the query.
81
     * @param paramName  The name of the parameter.
82
     * @param paramValue The value of the parameter.
83
     * @return The cached API response, or null if not cached.
84
     */
85
    public static ApiResponse retrieveResponse(String queryName, String paramName, String paramValue) {
86
        Map<String, String> params = new HashMap<>();
×
87
        params.put(paramName, paramValue);
×
88
        return retrieveResponse(queryName, params);
×
89
    }
90

91
    /**
92
     * Retrieves a cached API response for a specific query and parameters.
93
     * If the cache is stale, it triggers a background refresh.
94
     *
95
     * @param queryName The name of the query.
96
     * @param params    The parameters for the query.
97
     * @return The cached API response, or null if not cached.
98
     */
99
    public static synchronized ApiResponse retrieveResponse(final String queryName, final Map<String, String> params) {
100
        long timeNow = System.currentTimeMillis();
×
101
        String cacheId = getCacheId(queryName, params);
×
102
        boolean isCached = false;
×
103
        boolean needsRefresh = true;
×
104
        if (cachedResponses.containsKey(cacheId) && cachedResponses.get(cacheId) != null) {
×
105
            long cacheAge = timeNow - lastRefresh.get(cacheId);
×
106
            isCached = cacheAge < 24 * 60 * 60 * 1000;
×
107
            needsRefresh = cacheAge > 60 * 1000;
×
108
        }
109
        if (needsRefresh && !isRunning(cacheId)) {
×
110
            refreshStart.put(cacheId, timeNow);
×
111
            new Thread(() -> {
×
112
                try {
113
                    Thread.sleep(100 + new Random().nextLong(400));
×
114
                } catch (InterruptedException ex) {
×
115
                    ex.printStackTrace();
×
116
                }
×
117
                try {
118
                    ApiCache.updateResponse(queryName, params);
×
119
                } catch (Exception ex) {
×
120
                    ex.printStackTrace();
×
121
                    cachedResponses.put(cacheId, null);
×
122
                    lastRefresh.put(cacheId, System.currentTimeMillis());
×
123
                } finally {
124
                    refreshStart.remove(cacheId);
×
125
                }
126
            }).start();
×
127
        }
128
        if (isCached) {
×
129
            if (cachedResponses.get(cacheId) == null) {
×
130
                cachedResponses.remove(cacheId);
×
131
                throw new RuntimeException("Query failed: " + cacheId);
×
132
            }
133
            return cachedResponses.get(cacheId);
×
134
        } else {
135
            return null;
×
136
        }
137
    }
138

139
    /**
140
     * Updates the cached map for a specific query and parameters.
141
     *
142
     * @param queryName The name of the query.
143
     * @param params    The parameters for the query.
144
     * @throws FailedApiCallException If the API call fails.
145
     */
146
    private static void updateMap(String queryName, Map<String, String> params) throws FailedApiCallException {
147
        Map<String, String> map = new HashMap<>();
×
148
        Map<String, String> nanopubParams = new HashMap<>();
×
149
        for (String k : params.keySet()) nanopubParams.put(k, params.get(k));
×
150
        List<ApiResponseEntry> respList = QueryApiAccess.get(queryName, nanopubParams).getData();
×
151
        while (respList != null && !respList.isEmpty()) {
×
152
            ApiResponseEntry resultEntry = respList.remove(0);
×
153
            map.put(resultEntry.get("key"), resultEntry.get("value"));
×
154
        }
×
155
        String cacheId = getCacheId(queryName, params);
×
156
        cachedMaps.put(cacheId, map);
×
157
        lastRefresh.put(cacheId, System.currentTimeMillis());
×
158
    }
×
159

160
    /**
161
     * Retrieves a cached map for a specific query and parameters.
162
     * If the cache is stale, it triggers a background refresh.
163
     *
164
     * @param queryName The name of the query.
165
     * @param params    The parameters for the query.
166
     * @return The cached map, or null if not cached.
167
     */
168
    public static synchronized Map<String, String> retrieveMap(String queryName, Map<String, String> params) {
169
        long timeNow = System.currentTimeMillis();
×
170
        String cacheId = getCacheId(queryName, params);
×
171
        boolean isCached = false;
×
172
        boolean needsRefresh = true;
×
173
        if (cachedMaps.containsKey(cacheId)) {
×
174
            long cacheAge = timeNow - lastRefresh.get(cacheId);
×
175
            isCached = cacheAge < 24 * 60 * 60 * 1000;
×
176
            needsRefresh = cacheAge > 60 * 1000;
×
177
        }
178
        if (needsRefresh && !isRunning(cacheId)) {
×
179
            refreshStart.put(cacheId, timeNow);
×
180
            new Thread(() -> {
×
181
                try {
182
                    Thread.sleep(100 + new Random().nextLong(400));
×
183
                } catch (InterruptedException ex) {
×
184
                    ex.printStackTrace();
×
185
                }
×
186
                try {
187
                    ApiCache.updateMap(queryName, params);
×
188
                } catch (Exception ex) {
×
189
                    ex.printStackTrace();
×
190
                    cachedResponses.put(cacheId, null);
×
191
                    lastRefresh.put(cacheId, System.currentTimeMillis());
×
192
                } finally {
193
                    refreshStart.remove(cacheId);
×
194
                }
195
            }).start();
×
196
        }
197
        if (isCached) {
×
198
            if (cachedResponses.get(cacheId) == null) {
×
199
                cachedResponses.remove(cacheId);
×
200
                throw new RuntimeException("Query failed: " + cacheId);
×
201
            }
202
            return cachedMaps.get(cacheId);
×
203
        } else {
204
            return null;
×
205
        }
206
    }
207

208
    /**
209
     * Converts a map of parameters to a sorted string representation.
210
     *
211
     * @param params The map of parameters.
212
     * @return A string representation of the parameters.
213
     */
214
    private static String paramsToString(Map<String, String> params) {
215
        List<String> keys = new ArrayList<>(params.keySet());
×
216
        Collections.sort(keys);
×
217
        String s = "";
×
218
        for (String k : keys) s += " " + k + "=" + params.get(k);
×
219
        return s;
×
220
    }
221

222
    /**
223
     * Generates a unique cache ID for a specific query and parameters.
224
     *
225
     * @param queryName The name of the query.
226
     * @param params    The parameters for the query.
227
     * @return The unique cache ID.
228
     */
229
    public static String getCacheId(String queryName, Map<String, String> params) {
230
        return queryName + " " + paramsToString(params);
×
231
    }
232
}
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