• 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

46.77
src/main/java/com/knowledgepixels/nanodash/page/NanodashPage.java
1
package com.knowledgepixels.nanodash.page;
2

3
import com.knowledgepixels.nanodash.NanodashPreferences;
4
import com.knowledgepixels.nanodash.NanodashThreadPool;
5
import com.knowledgepixels.nanodash.Utils;
6
import com.knowledgepixels.nanodash.WicketApplication;
7
import com.knowledgepixels.nanodash.domain.*;
8
import com.knowledgepixels.nanodash.template.TemplateData;
9
import org.apache.wicket.markup.head.CssHeaderItem;
10
import org.apache.wicket.markup.head.IHeaderResponse;
11
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
12
import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
13
import org.apache.wicket.markup.html.WebPage;
14
import org.apache.wicket.request.flow.RedirectToUrlException;
15
import org.apache.wicket.request.mapper.parameter.PageParameters;
16
import org.apache.wicket.request.resource.JavaScriptResourceReference;
17
import org.slf4j.Logger;
18
import org.slf4j.LoggerFactory;
19

20
import java.util.ResourceBundle;
21

22
/**
23
 * Abstract base class for Nanodash pages.
24
 * Provides functionality for auto-refreshing data and rendering JavaScript resources.
25
 */
26
public abstract class NanodashPage extends WebPage {
27

28
    private static final Logger logger = LoggerFactory.getLogger(NanodashPage.class);
9✔
29

30
    private static long lastRefresh = 0L;
6✔
31
    private static final long REFRESH_INTERVAL = 60 * 1000; // 1 minute
32
    private static boolean refreshRunning = false;
6✔
33

34
    private long state = 0L;
9✔
35

36
    private static JavaScriptResourceReference nanodashJs = new JavaScriptResourceReference(WicketApplication.class, "script/nanodash.js");
21✔
37

38
    /**
39
     * Returns the mount path for this page.
40
     *
41
     * @return the mount path as a String
42
     */
43
    public abstract String getMountPath();
44

45
    /**
46
     * Constructor for NanodashPage.
47
     *
48
     * @param parameters the page parameters
49
     */
50
    protected NanodashPage(PageParameters parameters) {
51
        super(parameters);
9✔
52
        ensureRefreshed();
6✔
53
    }
3✔
54

55
    private void ensureRefreshed() {
56
        synchronized (getClass()) {
15✔
57
            state = lastRefresh;
9✔
58
            if (!refreshRunning && System.currentTimeMillis() - lastRefresh > REFRESH_INTERVAL) {
24!
59
                lastRefresh = System.currentTimeMillis();
6✔
60
                refreshRunning = true;
6✔
61
                NanodashThreadPool.submit(() -> {
9✔
62
                    try {
63
                        logger.info("Refreshing data...");
9✔
64
                        User.refreshUsers();
3✔
65
                        TemplateData.refreshTemplates();
3✔
66
                        Space.refresh();
3✔
67
                        MaintainedResource.refresh();
3✔
68
                        AbstractResourceWithProfile.refresh();
3✔
69
                        Project.refresh();
3✔
70
                        logger.info("Refreshing data... done");
9✔
71
                        lastRefresh = System.currentTimeMillis();
6✔
72
                    } catch (Exception ex) {
×
73
                        logger.error("Error during refresh", ex);
×
74
                    } finally {
75
                        refreshRunning = false;
6✔
76
                    }
77
                });
3✔
78
            }
79
        }
9✔
80
    }
3✔
81

82
    /**
83
     * Checks if auto-refresh is enabled for this page.
84
     * Override this method in subclasses to enable auto-refresh.
85
     *
86
     * @return true if auto-refresh is enabled, false otherwise
87
     */
88
    protected boolean hasAutoRefreshEnabled() {
89
        return false;
×
90
    }
91

92
    /**
93
     * {@inheritDoc}
94
     */
95
    @Override
96
    protected void onRender() {
97
        if (hasAutoRefreshEnabled() && state < lastRefresh) {
×
98
            throw new RedirectToUrlException(getMountPath() + "?" + Utils.getPageParametersAsString(getPageParameters()));
×
99
        }
100
        super.onRender();
×
101
    }
×
102

103
    /**
104
     * {@inheritDoc}
105
     * <p>
106
     * Renders the head section of the page, including JavaScript references.
107
     */
108
    @Override
109
    public void renderHead(IHeaderResponse response) {
110
        super.renderHead(response);
×
111
        String version = ResourceBundle.getBundle("nanodash").getString("nanodash.version");
×
112
        response.render(CssHeaderItem.forUrl("style.css?v=" + version));
×
113
        response.render(JavaScriptHeaderItem.forReference(getApplication().getJavaScriptLibrarySettings().getJQueryReference()));
×
114
        response.render(JavaScriptReferenceHeaderItem.forReference(nanodashJs));
×
115
        String umamiScriptUrl = NanodashPreferences.get().getUmamiScriptUrl();
×
116
        String umamiWebsiteId = NanodashPreferences.get().getUmamiWebsiteId();
×
117
        if (umamiScriptUrl != null && !umamiScriptUrl.isBlank()
×
118
            && umamiWebsiteId != null && !umamiWebsiteId.isBlank()) {
×
119
            String umamiJs = "(function(){" +
×
120
                             "var s=document.createElement('script');" +
121
                             "s.src='" + umamiScriptUrl + "';" +
122
                             "s.defer=true;" +
123
                             "s.setAttribute('data-website-id','" + umamiWebsiteId + "');" +
124
                             "document.head.appendChild(s);" +
125
                             "})();";
126
            response.render(JavaScriptHeaderItem.forScript(umamiJs, "umami-loader"));
×
127
        }
128
    }
×
129

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