This is an automated email from the ASF dual-hosted git repository. ddanielr pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/accumulo.git
commit 2529d83754a7d4f6036f101fd692d289560f0294 Merge: 9c3497bfdc 8779467e73 Author: Daniel Roberts ddanielr <[email protected]> AuthorDate: Fri Jul 18 02:37:09 2025 +0000 Merge branch '2.1' assemble/pom.xml | 5 ++ .../org/apache/accumulo/core/conf/Property.java | 5 ++ pom.xml | 5 ++ server/monitor/pom.xml | 4 + .../apache/accumulo/monitor/EmbeddedWebServer.java | 21 ++++- .../java/org/apache/accumulo/monitor/Monitor.java | 11 ++- .../org/apache/accumulo/monitor/view/WebViews.java | 8 +- .../accumulo/monitor/resources/js/bulkImport.js | 2 +- .../accumulo/monitor/resources/js/compactions.js | 4 +- .../org/apache/accumulo/monitor/resources/js/ec.js | 8 +- .../accumulo/monitor/resources/js/functions.js | 28 +++---- .../org/apache/accumulo/monitor/resources/js/gc.js | 2 +- .../apache/accumulo/monitor/resources/js/global.js | 2 + .../accumulo/monitor/resources/js/manager.js | 6 +- .../apache/accumulo/monitor/resources/js/scans.js | 4 +- .../apache/accumulo/monitor/resources/js/server.js | 4 +- .../apache/accumulo/monitor/resources/js/table.js | 6 +- .../accumulo/monitor/resources/js/tservers.js | 8 +- .../apache/accumulo/monitor/templates/default.ftl | 31 +++---- .../apache/accumulo/monitor/templates/modals.ftl | 2 +- .../apache/accumulo/monitor/templates/navbar.ftl | 24 +++--- .../apache/accumulo/monitor/templates/overview.ftl | 8 +- .../apache/accumulo/monitor/templates/tables.ftl | 4 +- .../accumulo/monitor/EmbeddedWebServerTest.java | 98 ++++++++++++++++++++++ 24 files changed, 227 insertions(+), 73 deletions(-) diff --cc core/src/main/java/org/apache/accumulo/core/conf/Property.java index 0f47eee39e,625cb0ed1f..6526d04565 --- a/core/src/main/java/org/apache/accumulo/core/conf/Property.java +++ b/core/src/main/java/org/apache/accumulo/core/conf/Property.java @@@ -885,18 -1037,46 +885,23 @@@ public enum Property + " The resources that are used by default can be seen in" + " `accumulo/server/monitor/src/main/resources/templates/default.ftl`.", "2.0.0"), + MONITOR_FETCH_TIMEOUT("monitor.fetch.timeout", "5m", PropertyType.TIMEDURATION, + "The Monitor fetches information for display in a set of background threads. This property" + + " controls the amount of time that process should wait before cancelling any remaining" + + " tasks to fetch information. These background threads could end up waiting on servers" + + " to respond or for scans to complete.", + "4.0.0"), + MONITOR_DEAD_LIST_RG_EXCLUSIONS("monitor.dead.server.rg.exclusions", "", PropertyType.STRING, + "The Monitor displays information about servers that it believes have died recently." + + " This property accepts a comma separated list of resource group names. If" + + " the dead servers resource group matches a resource group in this list," + + " then it will be suppressed from the dead servers list in the monitor.", + "4.0.0"), + MONITOR_ROOT_CONTEXT("monitor.root.context", "/", PropertyType.STRING, - "The root context path of the monitor application. If this value is set, all paths for the" ++ "The root context path of the monit application. If this value is set, all paths for the" + + " monitor application will be hosted using this context. As an example, setting this to `/accumulo/`" + + " would cause all `/rest/` endpoints to be hosted at `/accumulo/rest/*`.", + "2.1.4"), - @Deprecated(since = "2.1.0") - TRACE_PREFIX("trace.", null, PropertyType.PREFIX, - "Properties in this category affect the behavior of distributed tracing.", "1.3.5"), - @Deprecated(since = "2.1.0") - TRACE_SPAN_RECEIVERS("trace.span.receivers", "org.apache.accumulo.tracer.ZooTraceClient", - PropertyType.CLASSNAMELIST, "A list of span receiver classes to send trace spans.", "1.7.0"), - @Deprecated(since = "2.1.0") - TRACE_SPAN_RECEIVER_PREFIX("trace.span.receiver.", null, PropertyType.PREFIX, - "Prefix for span receiver configuration properties.", "1.7.0"), - @Deprecated(since = "2.1.0") - TRACE_ZK_PATH("trace.zookeeper.path", Constants.ZTRACERS, PropertyType.STRING, - "The zookeeper node where tracers are registered.", "1.7.0"), - @Deprecated(since = "2.1.0") - TRACE_PORT("trace.port.client", "12234", PropertyType.PORT, - "The listening port for the trace server.", "1.3.5"), - @Deprecated(since = "2.1.0") - TRACE_TABLE("trace.table", "trace", PropertyType.STRING, - "The name of the table to store distributed traces.", "1.3.5"), - @Deprecated(since = "2.1.0") - TRACE_USER("trace.user", "root", PropertyType.STRING, - "The name of the user to store distributed traces.", "1.3.5"), - @Sensitive - @Deprecated(since = "2.1.0") - TRACE_PASSWORD("trace.password", "secret", PropertyType.STRING, - "The password for the user used to store distributed traces.", "1.3.5"), - @Sensitive - @Deprecated(since = "2.1.0") - TRACE_TOKEN_PROPERTY_PREFIX("trace.token.property.", null, PropertyType.PREFIX, - "The prefix used to create a token for storing distributed traces. For" - + " each property required by trace.token.type, place this prefix in front of it.", - "1.5.0"), - @Deprecated(since = "2.1.0") - TRACE_TOKEN_TYPE("trace.token.type", PasswordToken.class.getName(), PropertyType.CLASSNAME, - "An AuthenticationToken type supported by the authorizer.", "1.5.0"), - // per table properties TABLE_PREFIX("table.", null, PropertyType.PREFIX, "Properties in this category affect tablet server treatment of tablets," diff --cc pom.xml index 837e5d7cbe,fdcab6190e..e7c235c160 --- a/pom.xml +++ b/pom.xml @@@ -322,10 -322,21 +322,15 @@@ <artifactId>commons-logging</artifactId> <version>1.3.5</version> </dependency> + <dependency> + <groupId>commons-validator</groupId> + <artifactId>commons-validator</artifactId> + <version>1.10.0</version> + </dependency> - <dependency> - <!-- legacy junit version specified here for dependency convergence --> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - </dependency> <dependency> <groupId>org.apache.accumulo</groupId> - <artifactId>accumulo-compaction-coordinator</artifactId> - <version>${project.version}</version> + <artifactId>accumulo-access</artifactId> + <version>${version.accumulo-access}</version> </dependency> <dependency> <groupId>org.apache.accumulo</groupId> diff --cc server/monitor/pom.xml index 2fc8822fc0,23ecd0d691..a9cf60ede1 --- a/server/monitor/pom.xml +++ b/server/monitor/pom.xml @@@ -56,10 -52,10 +56,14 @@@ <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency> + <dependency> + <groupId>commons-validator</groupId> + <artifactId>commons-validator</artifactId> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-core</artifactId> + </dependency> <dependency> <groupId>jakarta.inject</groupId> <artifactId>jakarta.inject-api</artifactId> diff --cc server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java index ba5a3a858e,f9672ed196..e77a9171dd --- a/server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java +++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java @@@ -102,8 -105,8 +102,9 @@@ import org.glassfish.jersey.servlet.Ser import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; +import com.google.common.net.HostAndPort; /** * Serve manager statistics with an embedded web server. @@@ -397,27 -503,28 +402,31 @@@ public class Monitor extends AbstractSe } HostAndPort monitorHostAndPort = getAdvertiseAddress(); log.debug("Using {} to advertise monitor location in ZooKeeper", monitorHostAndPort); + try { - monitorLock.replaceLockData(monitorHostAndPort.toString().getBytes(UTF_8)); - } catch (KeeperException | InterruptedException e) { - throw new IllegalStateException("Exception updating monitor lock with host and port", e); + getMonitorLock(monitorHostAndPort); + } catch (Exception e) { + log.error("Failed to get Monitor ZooKeeper lock"); + throw new RuntimeException(e); } + getContext().setServiceLock(monitorLock); MetricsInfo metricsInfo = getContext().getMetricsInfo(); + metricsInfo.addMetricsProducers(this); metricsInfo.init(MetricsInfo.serviceTags(getContext().getInstanceName(), getApplicationName(), - monitorHostAndPort, "")); + monitorHostAndPort, getResourceGroup())); + // Needed to support the existing zk monitor address format + if (!rootContext.endsWith("/")) { + rootContext = rootContext + "/"; + } try { URL url = new URL(server.isSecure() ? "https" : "http", monitorHostAndPort.getHost(), - server.getPort(), "/"); + server.getPort(), rootContext); - final String path = context.getZooKeeperRoot() + Constants.ZMONITOR_HTTP_ADDR; - final ZooReaderWriter zoo = context.getZooReaderWriter(); + final ZooReaderWriter zoo = context.getZooSession().asReaderWriter(); // Delete before we try to re-create in case the previous session hasn't yet expired - zoo.delete(path); - zoo.putEphemeralData(path, url.toString().getBytes(UTF_8)); + zoo.delete(Constants.ZMONITOR_HTTP_ADDR); + zoo.putEphemeralData(Constants.ZMONITOR_HTTP_ADDR, url.toString().getBytes(UTF_8)); log.info("Set monitor address in zookeeper to {}", url); } catch (Exception ex) { log.error("Unable to advertise monitor HTTP address in zookeeper", ex); diff --cc server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/bulkImport.js index b5c0e18912,a89bc95ad4..28a1883c90 --- a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/bulkImport.js +++ b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/bulkImport.js @@@ -37,9 -38,9 +37,9 @@@ function refresh() /** * Initializes the bulk import DataTables */ -$(document).ready(function () { +$(function () { - const url = '/rest/bulkImports'; + const url = contextPath + 'rest/bulkImports'; console.debug('REST url used to fetch data for the DataTables in bulkImport.js: ' + url); // Generates the manager bulk import status table diff --cc server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js index 639d935cf8,30705be653..8a4db14119 --- a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js +++ b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js @@@ -444,32 -445,17 +444,32 @@@ function getTableServers(tableID) } /** - * REST GET call for the logs, stores it on a sessionStorage variable + * REST GET call for the server status, stores it on a sessionStorage variable */ -function getLogs() { - return getJSONForTable(contextPath + 'rest/logs', 'logs'); +function getStatus() { - return getJSONForTable('/rest/status', 'status'); ++ return getJSONForTable(contextPath + 'rest/status', 'status'); } +/* + * Jquery call to clear all data from cells of a table + */ +function clearAllTableCells(tableId) { + console.log("Clearing all table cell data for " + tableId); + $("#" + tableId + " > tbody > tr > td").each(function () { + $(this).text(""); + }); +} + +// NEW REST CALLS + - const REST_V2_PREFIX = '/rest-v2'; ++const REST_V2_PREFIX = contextPath + 'rest-v2'; + /** - * REST POST call to clear logs + * REST GET call for /problems, + * stores it on a sessionStorage variable */ -function clearLogs() { - doLoggedPostCall(contextPath + 'rest/logs/clear', refresh, false); +function getProblems() { + return getJSONForTable(REST_V2_PREFIX + '/problems', 'problems'); } /** diff --cc server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/table.js index 0734b573f6,9a37094149..0dd150c89e --- a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/table.js +++ b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/table.js @@@ -35,17 -34,24 +35,17 @@@ function refresh() refreshTable(); } -function getQueuedAndRunning(data) { - return `${data.running}(${data.queued})`; -} - /** - * Initialize the table - * - * @param {String} tableID the accumulo table ID + * Makes the REST call to fetch tablet details and render them. */ -function initTableServerTable(tableID) { - - const url = contextPath + 'rest/tables/' + tableID; - console.debug('REST url used to fetch data for table.js DataTable: ' + url); +function initTabletsTable(tableId) { - var tabletsUrl = '/rest-v2/tables/' + tableId + '/tablets'; ++ var tabletsUrl = contextPath + 'rest-v2/tables/' + tableId + '/tablets'; + console.debug('Fetching tablets info from: ' + tabletsUrl); - tableServersTable = $('#participatingTServers').DataTable({ + tabletsTable = $('#tabletsList').DataTable({ "ajax": { - "url": url, - "dataSrc": "servers" + "url": tabletsUrl, + "dataSrc": "" }, "stateSave": true, "columnDefs": [{ @@@ -68,128 -74,111 +68,128 @@@ } ], "columns": [{ - "data": "hostname", - "type": "html", - "render": function (data, type, row) { - if (type === 'display') { - data = `<a href="tservers?s=${row.id}">${data}</a>`; - } - return data; - } + "data": "tabletId", + "title": "Tablet ID" }, { - "data": "tablets" + "data": "estimatedSize", + "title": "Estimated Size" }, { - "data": "lastContact" + "data": "estimatedEntries", + "title": "Estimated Entries" }, { - "data": "entries" + "data": "tabletAvailability", + "title": "Availability" }, { - "data": "ingest" + "data": "numFiles", + "title": "Files" }, { - "data": "query" + "data": "numWalLogs", + "title": "WALs" }, { - "data": "holdtime" + "data": "location", + "title": "Location" + } + ] + }); +} + +/** + * Initialize the table - * ++ * + * @param {String} tableId the accumulo table ID + */ +function initTableServerTable(tableId) { - const url = '/rest-v2/tables/' + tableId; ++ const url = contextPath + 'rest-v2/tables/' + tableId; + console.debug('REST url used to fetch summary data: ' + url); + + tableServersTable = $('#participatingTServers').DataTable({ + "ajax": { + "url": url, + "dataSrc": function (json) { + // Convert the JSON object into an array for DataTables consumption. + return [json]; + } + }, + "stateSave": true, + "searching": false, + "paging": false, + "info": false, + "columnDefs": [{ + "targets": "big-num", + "render": function (data, type) { + if (type === 'display') { + data = bigNumberForQuantity(data); + } + return data; + } }, { - "data": function (row) { - return getQueuedAndRunning(row.compactions.scans); + "targets": "big-size", + "render": function (data, type) { + if (type === 'display') { + data = bigNumberForSize(data); + } + return data; } + } + ], + "columns": [{ + "data": "totalEntries", + "title": "Entry Count" }, { - "data": function (row) { - return getQueuedAndRunning(row.compactions.minor); - } + "data": "totalSizeOnDisk", + "title": "Size on disk" }, { - "data": function (row) { - return getQueuedAndRunning(row.compactions.major); - } + "data": "totalFiles", + "title": "File Count" }, { - "data": "indexCacheHitRate" + "data": "totalWals", + "title": "WAL Count" }, { - "data": "dataCacheHitRate" + "data": "totalTablets", + "title": "Total Tablet Count" }, { - "data": "osload" + "data": "availableAlways", + "title": "Always Hosted Count" }, { - "data": "scansRunning", - "visible": false + "data": "availableOnDemand", + "title": "On Demand Count" }, { - "data": "scansQueued", - "visible": false + "data": "availableNever", + "title": "Never Hosted Count" }, { - "data": "minorRunning", - "visible": false + "data": "totalAssignedTablets", + "title": "Assigned Tablet Count" }, { - "data": "minorQueued", - "visible": false + "data": "totalAssignedToDeadServerTablets", + "title": "Tablets Assigned to Dead Servers Count" }, { - "data": "majorRunning", - "visible": false + "data": "totalHostedTablets", + "title": "Hosted Tablet Count" }, { - "data": "majorQueued", - "visible": false + "data": "totalSuspendedTablets", + "title": "Suspended Tablet Count" + }, + { + "data": "totalUnassignedTablets", + "title": "Unassigned Tablet Count" } ] }); diff --cc server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/default.ftl index 92b70794de,037001569e..3d802c533c --- a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/default.ftl +++ b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/default.ftl @@@ -30,13 -31,23 +31,13 @@@ ${val} </#list> <#else> - <script src="/resources/external/jquery/jquery-3.7.1.js"></script> - <script src="/resources/external/bootstrap/js/bootstrap.bundle.js"></script> - <script src="/resources/external/datatables/js/jquery.dataTables.js"></script> - <script src="/resources/external/datatables/js/dataTables.bootstrap5.js"></script> - <link rel="stylesheet" href="/resources/external/bootstrap/css/bootstrap.css" /> - <link rel="stylesheet" href="/resources/external/bootstrap/css/bootstrap-icons.css" /> - <link rel="stylesheet" href="/resources/external/datatables/css/dataTables.bootstrap5.css" /> + <script src="resources/external/jquery/jquery-3.7.1.js"></script> + <script src="resources/external/bootstrap/js/bootstrap.bundle.js"></script> + <script src="resources/external/datatables/js/jquery.dataTables.js"></script> + <script src="resources/external/datatables/js/dataTables.bootstrap5.js"></script> - <script src="resources/external/flot/jquery.canvaswrapper.js"></script> - <script src="resources/external/flot/jquery.colorhelpers.js"></script> - <script src="resources/external/flot/jquery.flot.js"></script> - <script src="resources/external/flot/jquery.flot.saturated.js"></script> - <script src="resources/external/flot/jquery.flot.browser.js"></script> - <script src="resources/external/flot/jquery.flot.drawSeries.js"></script> - <script src="resources/external/flot/jquery.flot.uiConstants.js"></script> - <script src="resources/external/flot/jquery.flot.legend.js"></script> - <script src="resources/external/flot/jquery.flot.time.js"></script> - <script src="resources/external/flot/jquery.flot.resize.js"></script> + <link rel="stylesheet" href="resources/external/bootstrap/css/bootstrap.css" /> + <link rel="stylesheet" href="resources/external/bootstrap/css/bootstrap-icons.css" /> + <link rel="stylesheet" href="resources/external/datatables/css/dataTables.bootstrap5.css" /> </#if> <!-- accumulo resources --> @@@ -54,11 -65,10 +55,11 @@@ }); </script> <#if js??> - <script src="/resources/js/${js}"></script> + <script src="resources/js/${js}"></script> </#if> - <script src="/resources/js/navbar.js"></script> - <script src="/resources/js/systemAlert.js"></script> - <script src="/resources/js/modals.js"></script> + <script src="resources/js/navbar.js"></script> + <script src="resources/js/systemAlert.js"></script> ++ <script src="resources/js/modals.js"></script> </head> <body> diff --cc server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl index 7135936d88,7e77c4c3fe..b6424668ee --- a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl +++ b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl @@@ -52,10 -52,20 +52,10 @@@ Activity </a> <ul class="dropdown-menu col-xs-12" aria-labelledby="navbarDropdown"> - <li><a class="dropdown-item" href="/compactions">Active Compactions</a></li> - <li><a class="dropdown-item" href="/scans">Active Scans</a></li> - <li><a class="dropdown-item" href="/bulkImports">Bulk Imports</a></li> - <li><a class="dropdown-item" href="/ec">External Compactions</a></li> + <li><a class="dropdown-item" href="compactions">Active Compactions</a></li> + <li><a class="dropdown-item" href="scans">Active Scans</a></li> + <li><a class="dropdown-item" href="bulkImports">Bulk Imports</a></li> + <li><a class="dropdown-item" href="ec">External Compactions</a></li> - <li><a class="dropdown-item" href="replication">Replication</a></li> - </ul> - </li> - <li class="dropdown"> - <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" - role="button" data-bs-toggle="dropdown" aria-expanded="false">Debug <span id="errorsNotification" class="badge"></span><span class="caret"></span> - </a> - <ul class="dropdown-menu"> - <li><a class="dropdown-item" href="log">Recent Logs <span id="recentLogsNotifications" class="badge"></span></a></li> - <li><a class="dropdown-item" href="problems">Table Problems <span id="tableProblemsNotifications" class="badge"></span></a></li> </ul> </li> <li class="dropdown"> diff --cc server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/tables.ftl index dcbfc71900,c010c68dae..8f481388cc --- a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/tables.ftl +++ b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/tables.ftl @@@ -29,13 -29,8 +29,13 @@@ tableList = $('#tableList').DataTable({ "ajax": { - "url": "/rest-v2/tables", - "url": '${rootContext}rest/tables', - "dataSrc": "table" ++ "url": '${rootContext}rest-v2/tables', + "dataSrc": function (json) { + return Object.keys(json).map(function (key) { + json[key].tableId = key; + return json[key]; + }); + } }, "stateSave": true, "columnDefs": [ @@@ -64,7 -79,7 +64,7 @@@ "type": "html", "render": function (data, type, row, meta) { if (type === 'display') { - data = '<a href="/tables/' + row.tableId + '">' + row.tableName + '</a>'; - data = '<a href="tables/' + row.tableId + '">' + row.tablename + '</a>'; ++ data = '<a href="tables/' + row.tableId + '">' + row.tableName + '</a>'; } return data; } diff --cc server/monitor/src/test/java/org/apache/accumulo/monitor/EmbeddedWebServerTest.java index 0000000000,1b1cd90b0c..188db48225 mode 000000,100644..100644 --- a/server/monitor/src/test/java/org/apache/accumulo/monitor/EmbeddedWebServerTest.java +++ b/server/monitor/src/test/java/org/apache/accumulo/monitor/EmbeddedWebServerTest.java @@@ -1,0 -1,93 +1,98 @@@ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.accumulo.monitor; + + import static org.easymock.EasyMock.createMock; + import static org.easymock.EasyMock.expect; + import static org.easymock.EasyMock.replay; + import static org.easymock.EasyMock.verify; + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertThrows; + + import java.util.concurrent.atomic.AtomicReference; + + import org.apache.accumulo.core.conf.ConfigurationCopy; + import org.apache.accumulo.core.conf.DefaultConfiguration; + import org.apache.accumulo.core.conf.Property; + import org.apache.accumulo.server.ServerContext; ++import org.eclipse.jetty.io.ConnectionStatistics; + import org.junit.jupiter.api.AfterAll; + import org.junit.jupiter.api.BeforeAll; + import org.junit.jupiter.api.Test; + + /** + * Basic tests for EmbeddedWebServer + */ + public class EmbeddedWebServerTest { + + private static final AtomicReference<Monitor> monitor = new AtomicReference<>(null); + + private static final AtomicReference<ConfigurationCopy> configuration = new AtomicReference<>(); + + @BeforeAll + public static void createMocks() { + + // Mock a configuration with the new context Path + ConfigurationCopy config = new ConfigurationCopy(DefaultConfiguration.getInstance()); + config.set(Property.MONITOR_ROOT_CONTEXT, "/test/"); + configuration.set(config); + + ServerContext contextMock = createMock(ServerContext.class); + expect(contextMock.getConfiguration()).andReturn(config).atLeastOnce(); + + Monitor monitorMock = createMock(Monitor.class); + expect(monitorMock.getContext()).andReturn(contextMock).atLeastOnce(); ++ ConnectionStatistics connectionStatisticMock = createMock(ConnectionStatistics.class); ++ expect(connectionStatisticMock.isRunning()).andReturn(true).atLeastOnce(); ++ expect(monitorMock.getConnectionStatisticsBean()).andReturn(connectionStatisticMock) ++ .atLeastOnce(); + expect(monitorMock.getConfiguration()).andReturn(config).atLeastOnce(); + expect(monitorMock.getBindAddress()).andReturn("localhost:9995").atLeastOnce(); + - replay(contextMock, monitorMock); ++ replay(contextMock, monitorMock, connectionStatisticMock); + monitor.set(monitorMock); + } + + @AfterAll + public static void finishMocks() { + Monitor m = monitor.get(); - verify(m.getContext(), m); ++ verify(m.getContext(), m, m.getConnectionStatisticsBean()); + } + + @Test + public void testContextPath() { + // Test removal of trailing slash + EmbeddedWebServer ews = new EmbeddedWebServer(monitor.get(), + Integer.parseInt(Property.MONITOR_PORT.getDefaultValue())); + assertEquals("/test", ews.getContextPath(), + "Context path of " + ews.getContextPath() + " does not match"); + // Test redirect URL + configuration.get().set(Property.MONITOR_ROOT_CONTEXT, "/../test"); + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> new EmbeddedWebServer(monitor.get(), + Integer.parseInt(Property.MONITOR_PORT.getDefaultValue()))); + assertEquals("Root context: \"/../test\" is not a valid URL", exception.getMessage()); + // Test whitespace in URL + configuration.get().set(Property.MONITOR_ROOT_CONTEXT, "/whitespace /test"); + exception = + assertThrows(IllegalArgumentException.class, () -> new EmbeddedWebServer(monitor.get(), + Integer.parseInt(Property.MONITOR_PORT.getDefaultValue()))); + assertEquals("Root context: \"/whitespace /test\" is not a valid URL", exception.getMessage()); + } + }
