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

knowledgepixels / nanodash / 30430669996

29 Jul 2026 07:10AM UTC coverage: 34.372% (+0.5%) from 33.91%
30430669996

push

github

web-flow
Merge pull request #569 from knowledgepixels/270-nanopub-not-found-handling

feat: add a "nanopub not found" page (#270)

2460 of 7910 branches covered (31.1%)

Branch coverage included in aggregate %.

4801 of 13215 relevant lines covered (36.33%)

5.62 hits per line

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

65.59
src/main/java/com/knowledgepixels/nanodash/WicketApplication.java
1
package com.knowledgepixels.nanodash;
2

3
import com.google.gson.Gson;
4
import com.google.gson.reflect.TypeToken;
5
import com.knowledgepixels.nanodash.connector.*;
6
import com.knowledgepixels.nanodash.connector.ios.DsNanopubPage;
7
import com.knowledgepixels.nanodash.connector.ios.DsOverviewPage;
8
import com.knowledgepixels.nanodash.connector.pensoft.BdjNanopubPage;
9
import com.knowledgepixels.nanodash.connector.pensoft.BdjOverviewPage;
10
import com.knowledgepixels.nanodash.connector.pensoft.RioNanopubPage;
11
import com.knowledgepixels.nanodash.connector.pensoft.RioOverviewPage;
12
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
13
import com.knowledgepixels.nanodash.domain.MaintainedResource;
14
import com.knowledgepixels.nanodash.domain.Space;
15
import com.knowledgepixels.nanodash.events.NanopubPublishedListener;
16
import com.knowledgepixels.nanodash.events.NanopubPublishedPublisher;
17
import com.knowledgepixels.nanodash.page.*;
18
import com.knowledgepixels.nanodash.repository.MaintainedResourceRepository;
19
import com.knowledgepixels.nanodash.repository.SpaceRepository;
20
import de.agilecoders.wicket.webjars.WicketWebjars;
21
import org.apache.http.HttpResponse;
22
import org.apache.http.client.methods.HttpGet;
23
import org.apache.http.impl.client.CloseableHttpClient;
24
import org.apache.http.impl.client.HttpClientBuilder;
25
import org.apache.wicket.RuntimeConfigurationType;
26
import org.apache.wicket.Session;
27
import org.apache.wicket.protocol.http.WebApplication;
28
import org.apache.wicket.request.Request;
29
import org.apache.wicket.request.Response;
30
import org.apache.wicket.request.IRequestHandler;
31
import org.apache.wicket.request.cycle.IRequestCycleListener;
32
import org.apache.wicket.request.cycle.RequestCycle;
33
import org.apache.wicket.request.http.WebResponse;
34
import org.apache.wicket.settings.ExceptionSettings;
35
import org.apache.wicket.util.lang.Bytes;
36
import org.nanopub.Nanopub;
37
import org.nanopub.extra.services.QueryRef;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40

41
import java.awt.*;
42
import java.io.IOException;
43
import java.io.InputStreamReader;
44
import java.lang.reflect.Type;
45
import java.net.URI;
46
import java.net.URISyntaxException;
47
import java.util.ArrayList;
48
import java.util.Collections;
49
import java.util.List;
50
import java.util.Properties;
51

52
/**
53
 * WicketApplication is the main application class for the Nanodash web application.
54
 * It initializes the application, mounts pages, and provides version information.
55
 */
56
public class WicketApplication extends WebApplication implements NanopubPublishedPublisher {
57

58
    /**
59
     * URL to fetch the latest release information from GitHub.
60
     * This URL points to the releases of the Nanodash repository.
61
     */
62
    public static final String LATEST_RELEASE_URL = "https://api.github.com/repos/knowledgepixels/nanodash/releases";
63
    private static final Logger logger = LoggerFactory.getLogger(WicketApplication.class);
9✔
64

65
    private final List<NanopubPublishedListener> publishListeners = Collections.synchronizedList(new ArrayList<>());
18✔
66

67
    private static String latestVersion = null;
6✔
68

69
    @Override
70
    public void registerListener(NanopubPublishedListener listener) {
71
        logger.info("Registering listener {} for nanopub published events", listener.getClass().getName());
18✔
72
        publishListeners.add(listener);
15✔
73
    }
3✔
74

75
    @Override
76
    public void notifyNanopubPublished(Nanopub nanopub, String target, long waitMs) {
77
        for (NanopubPublishedListener listener : publishListeners) {
×
78
            listener.onNanopubPublished(nanopub, target, waitMs);
×
79
            logger.info("Notifying listener {} with toRefresh target <{}>", listener.getClass().getName(), target);
×
80
        }
×
81
    }
×
82

83
    /**
84
     * Static method to get the current instance of the WicketApplication.
85
     *
86
     * @return The current instance of WicketApplication.
87
     */
88
    public static WicketApplication get() {
89
        return (WicketApplication) WebApplication.get();
×
90
    }
91

92
    /**
93
     * Constructor for the WicketApplication.
94
     * Displays version information and provides instructions for accessing the application.
95
     */
96
    public WicketApplication() {
6✔
97
        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
6!
98
            try {
99
                Desktop.getDesktop().browse(new URI("http://localhost:37373"));
×
100
            } catch (IOException | URISyntaxException ex) {
×
101
                logger.error("Error in opening browser", ex);
×
102
            }
×
103
        }
104
        String v = getThisVersion();
6✔
105
        String lv = getLatestVersion();
6✔
106
        System.err.println("");
9✔
107
        System.err.println("----------------------------------------");
9✔
108
        System.err.println("               Nanodash");
9✔
109
        System.err.println("----------------------------------------");
9✔
110
        System.err.println(" You are using version: " + v);
12✔
111
        System.err.println(" Latest public version: " + lv);
12✔
112
        System.err.println("----------------------------------------");
9✔
113
        System.err.println(" Your browser should show the Nanodash");
9✔
114
        System.err.println(" interface in a few seconds.");
9✔
115
        System.err.println("");
9✔
116
        System.err.println(" If not, point your browser to:");
9✔
117
        System.err.println(" http://localhost:37373");
9✔
118
        System.err.println("----------------------------------------");
9✔
119
        System.err.println("");
9✔
120
    }
3✔
121

122
    /**
123
     * Returns the home page class for the application.
124
     *
125
     * @return The HomePage class.
126
     */
127
    public Class<HomePage> getHomePage() {
128
        return HomePage.class;
6✔
129
    }
130

131
    /**
132
     * {@inheritDoc}
133
     * <p>
134
     * Initializes the application settings and mounts pages.
135
     */
136
    @Override
137
    protected void init() {
138
        super.init();
6✔
139
        WicketWebjars.install(this);
6✔
140

141
        Utils.initMainUrls();
3✔
142

143
        getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
15✔
144

145
        getExceptionSettings().setUnexpectedExceptionDisplay(ExceptionSettings.SHOW_NO_EXCEPTION_PAGE);
15✔
146

147
        mountPage(ErrorPage.MOUNT_PATH, ErrorPage.class);
15✔
148
        mountPage("/error/404", ErrorPage.class);
15✔
149
        mountPage("/error/500", ErrorPage.class);
15✔
150
        mountPage(NanopubNotFoundPage.MOUNT_PATH, NanopubNotFoundPage.class);
15✔
151

152
        mountPage(UserPage.MOUNT_PATH, UserPage.class);
15✔
153
        mountPage(ChannelPage.MOUNT_PATH, ChannelPage.class);
15✔
154
        mountPage(SearchPage.MOUNT_PATH, SearchPage.class);
15✔
155
        mountPage(ExplorePage.MOUNT_PATH, ExplorePage.class);
15✔
156
        mountPage(ReferencesPage.MOUNT_PATH, ReferencesPage.class);
15✔
157
        mountPage(PublishPage.MOUNT_PATH, PublishPage.class);
15✔
158
        mountPage(PreviewPage.MOUNT_PATH, PreviewPage.class);
15✔
159
        mountPage(ProfilePage.MOUNT_PATH, ProfilePage.class);
15✔
160
        mountPage(UserListPage.MOUNT_PATH, UserListPage.class);
15✔
161
        mountPage(GroupDemoPage.MOUNT_PATH, GroupDemoPage.class);
15✔
162
        mountPage(GroupDemoPageSoc.MOUNT_PATH, GroupDemoPageSoc.class);
15✔
163
        mountPage(OrcidLinkingPage.MOUNT_PATH, OrcidLinkingPage.class);
15✔
164
        mountPage(OrcidLoginPage.MOUNT_PATH, OrcidLoginPage.class);
15✔
165
        mountPage(SpaceListPage.MOUNT_PATH, SpaceListPage.class);
15✔
166
        mountPage(MyChannelPage.MOUNT_PATH, MyChannelPage.class);
15✔
167
        mountPage(TermForwarder.MOUNT_PATH, TermForwarder.class);
15✔
168
        mountPage(ViewPage.MOUNT_PATH, ViewPage.class);
15✔
169
        mountPage(GetViewPage.MOUNT_PATH, GetViewPage.class);
15✔
170
        mountPage(DsOverviewPage.MOUNT_PATH, DsOverviewPage.class);
15✔
171
        mountPage(DsNanopubPage.MOUNT_PATH, DsNanopubPage.class);
15✔
172
        mountPage(RioOverviewPage.MOUNT_PATH, RioOverviewPage.class);
15✔
173
        mountPage(RioNanopubPage.MOUNT_PATH, RioNanopubPage.class);
15✔
174
        mountPage(BdjOverviewPage.MOUNT_PATH, BdjOverviewPage.class);
15✔
175
        mountPage(BdjNanopubPage.MOUNT_PATH, BdjNanopubPage.class);
15✔
176
        mountPage(FdoForwarder.MOUNT_PATH, FdoForwarder.class);
15✔
177
        mountPage(GetNamePage.MOUNT_PATH, GetNamePage.class);
15✔
178
        mountPage(TestPage.MOUNT_PATH, TestPage.class);
15✔
179
        mountPage(ResultTablePage.MOUNT_PATH, ResultTablePage.class);
15✔
180
        mountPage(GenOverviewPage.MOUNT_PATH, GenOverviewPage.class);
15✔
181
        mountPage(GenSelectPage.MOUNT_PATH, GenSelectPage.class);
15✔
182
        mountPage(GenPublishPage.MOUNT_PATH, GenPublishPage.class);
15✔
183
        mountPage(GenConnectPage.MOUNT_PATH, GenConnectPage.class);
15✔
184
        mountPage(GenNanopubPage.MOUNT_PATH, GenNanopubPage.class);
15✔
185
        mountPage(ProjectPage.MOUNT_PATH, ProjectPage.class);
15✔
186
        mountPage(SpacePage.MOUNT_PATH, SpacePage.class);
15✔
187
        mountPage(QueryPage.MOUNT_PATH, QueryPage.class);
15✔
188
        mountPage(QueryListPage.MOUNT_PATH, QueryListPage.class);
15✔
189
        mountPage(ViewResultsPage.MOUNT_PATH, ViewResultsPage.class);
15✔
190
        mountPage(ListPage.MOUNT_PATH, ListPage.class);
15✔
191
        mountPage(MaintainedResourcePage.MOUNT_PATH, MaintainedResourcePage.class);
15✔
192
        mountPage(ResourcePartPage.MOUNT_PATH, ResourcePartPage.class);
15✔
193
        mountPage(DownloadRdfPage.MOUNT_PATH, DownloadRdfPage.class);
15✔
194
        mountPage(CalendarFeedPage.MOUNT_PATH, CalendarFeedPage.class);
15✔
195

196
        getCspSettings().blocking().disabled();
15✔
197
        getStoreSettings().setMaxSizePerSession(Bytes.megabytes(100));
18✔
198

199
        getRequestCycleListeners().add(new IRequestCycleListener() {
33✔
200
            @Override
201
            public void onBeginRequest(RequestCycle cycle) {
202
                Response response = cycle.getResponse();
9✔
203
                if (response instanceof WebResponse) {
9!
204
                    ((WebResponse) response).setHeader("Nanodash-Version", getThisVersion());
15✔
205
                }
206
            }
3✔
207

208
            @Override
209
            public IRequestHandler onException(RequestCycle cycle, Exception ex) {
210
                logger.error("Unhandled exception during request [{}]", cycle.getRequest().getUrl(), ex);
×
211
                Throwable cause = ex.getCause();
×
212
                int depth = 1;
×
213
                while (cause != null) {
×
214
                    logger.error("  Caused by (depth {}): {}", depth++, cause.getMessage(), cause);
×
215
                    cause = cause.getCause();
×
216
                }
217
                return null;
×
218
            }
219
        });
220

221
        registerListeners();
6✔
222

223
        String umamiScriptUrl = NanodashPreferences.get().getUmamiScriptUrl();
9✔
224
        if (umamiScriptUrl != null && !umamiScriptUrl.isBlank()) {
6!
225
            logger.info("Umami analytics configured: {}", umamiScriptUrl);
×
226
        } else {
227
            logger.info("Umami analytics not configured (set NANODASH_UMAMI_SCRIPT_URL and NANODASH_UMAMI_WEBSITE_ID)");
9✔
228
        }
229
    }
3✔
230

231
    /**
232
     * {@inheritDoc}
233
     * <p>
234
     * Returns the runtime configuration type for the application.
235
     */
236
    @Override
237
    public RuntimeConfigurationType getConfigurationType() {
238
        return RuntimeConfigurationType.DEPLOYMENT;
6✔
239
    }
240

241
    /**
242
     * Retrieves the latest version of the application from the GitHub API.
243
     *
244
     * @return The latest version as a string.
245
     */
246
    public static String getLatestVersion() {
247
        if (latestVersion != null) return latestVersion;
12✔
248
        try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
9✔
249
            HttpResponse resp = client.execute(new HttpGet(LATEST_RELEASE_URL));
21✔
250
            int c = resp.getStatusLine().getStatusCode();
12✔
251
            if (c < 200 || c >= 300) {
18!
252
                throw new HttpStatusException(c);
×
253
            }
254

255
            Gson gson = new Gson();
12✔
256
            Type nanopubReleasesType = new TypeToken<List<NanodashRelease>>() {
18✔
257
            }.getType();
6✔
258

259
            try (InputStreamReader reader = new InputStreamReader(resp.getEntity().getContent())) {
21✔
260
                List<NanodashRelease> releases = gson.fromJson(reader, nanopubReleasesType);
18✔
261
                if (!releases.isEmpty()) {
9!
262
                    latestVersion = releases.getFirst().getVersionNumber();
15✔
263
                }
264
            }
265
        } catch (Exception ex) {
×
266
            logger.error("Error in fetching latest version", ex);
×
267
        }
3✔
268
        return latestVersion;
6✔
269
    }
270

271
    /**
272
     * Properties object to hold application properties.
273
     */
274
    public final static Properties properties = new Properties();
12✔
275

276
    static {
277
        try {
278
            properties.load(WicketApplication.class.getClassLoader().getResourceAsStream("nanodash.properties"));
18✔
279
        } catch (IOException ex) {
×
280
            logger.error("Error in loading properties", ex);
×
281
        }
3✔
282
    }
3✔
283

284
    /**
285
     * Retrieves the current version of the application.
286
     *
287
     * @return The current version as a string.
288
     */
289
    public static String getThisVersion() {
290
        return properties.getProperty("nanodash.version");
12✔
291
    }
292

293
    /**
294
     * {@inheritDoc}
295
     */
296
    @Override
297
    public Session newSession(Request request, Response response) {
298
        return new NanodashSession(request);
15✔
299
    }
300

301
    private void registerListeners() {
302
        logger.info("Registering nanopub published event listeners for spaces, maintained resources, resource with profile and query ref refresh");
9✔
303
        registerListener((nanopub, target, waitMs) -> {
9✔
304
            logger.info("Received nanopub published event with target <{}> and waitMs {}", target, waitMs);
×
305
            if (target.equals("spaces")) {
×
306
                SpaceRepository.get().forceRootRefresh(waitMs);
×
307
            } else if (target.equals("maintainedResources")) {
×
308
                MaintainedResourceRepository.get().forceRootRefresh(waitMs);
×
309
            } else if (AbstractResourceWithProfile.isResourceWithProfile(target)) {
×
310
                AbstractResourceWithProfile resource = AbstractResourceWithProfile.get(target);
×
311
                resource.forceRefresh(waitMs);
×
312
                if (resource instanceof Space) {
×
313
                    SpaceRepository.get().forceRootRefresh(waitMs);
×
314
                    MaintainedResourceRepository.get().forceRootRefresh(waitMs);
×
315
                } else if (resource instanceof MaintainedResource) {
×
316
                    MaintainedResourceRepository.get().forceRootRefresh(waitMs);
×
317
                }
318
            } else {
×
319
                QueryRef queryRef = QueryRef.parseString(target);
×
320
                ApiCache.clearCache(queryRef, waitMs);
×
321
            }
322
        });
×
323
    }
3✔
324

325
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc